annotation与httpClient(3) -- Annotation与动态代理(Proxy)
2013-08-26 16:55
495 查看
Annotation与动态代理(Proxy)
本文是作者个人总结,如有纰漏请指正
在阅读下面内容前,希望读者有动态代理和Annotation方面的相关知识。
像mybatis这样的开源框架,在接口上进行标记,之后框架在运行时会生成实际的逻辑代码,执行后将结果返回给用户。例如:
publicinterfaceUserMapper{
@Select("SELECT * FROM users WHERE id = #{userId}")
User getUser(@Param("userId")String userId);
}
本文主要是探讨这个使用Annotation和Proxy如何达到mybatis这种效果。
本文以weather获取为例,weather的接口定义如下:
@URL("http://m.weather.com.cn/data/{city}.html")
publicinterface WeatherHttp {
public String getWeather(@ParamUrl("city")
String city);
}
接口中定义了两个Annotation分别是URL和ParamUrl,URL表示接口要访问的网站,而ParamUrl表示访问网站Url中需要替换的参数。
两个Annotation定义如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public@interfaceURL {
publicabstract String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public@interfaceParamUrl {
publicabstract String value();
}
现在,我期望用动态代理的形式为这个类生成一个远程的访问,对于客户端,可以使用如下的方式获取信息:
publicclass WeatherTest {
publicstaticvoid main(String[] args) {
WeatherHttp weatherHttp = MapperProxy.newMapperProxy(WeatherHttp.class);
System.out.println(weatherHttp.getWeather("101010100"));
}
}
通过MapperProxy获取一个代理实例,然后执行这个代理实例,最终获取期望的结果。
动态代理的代码如下:
publicclass MapperProxy
implements InvocationHandler {
privatestaticfinal Set<String>
OBJECT_METHODS =
new
HashSet<String>() {
{
add("toString");
add("getClass");
add("hashCode");
add("equals");
add("wait");
add("notify");
add("notifyAll");
}
};
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try {
if (!OBJECT_METHODS.contains(method.getName()))
{
final Class<?> declaringInterface = findDeclaringInterface(proxy,
method);
if (declaringInterface ==
null) {
thrownew Exception(
"Could not find interface with the given method "
+ method);
}
MapperMethod mapMethod =
new MapperMethod(declaringInterface,method);
return mapMethod.execute(args);
}
} catch (Exception e) {
e.printStackTrace();
}
returnnull;
}
private Class<?> findDeclaringInterface(Object proxy, Method method) {
Class<?> declaringInterface =
null;
for (Class<?> iface : proxy.getClass().getInterfaces()) {
try {
Method m = iface.getMethod(method.getName(),
method.getParameterTypes());
if (declaringInterface !=
null) {
thrownew Exception(
"Ambiguous method mapping.
Two mapper interfaces contain the identical method signature for "
+ method);
} elseif (m !=
null) {
declaringInterface = iface;
}
} catch (Exception e) {
}
}
if (declaringInterface ==
null) {
}
return declaringInterface;
}
@SuppressWarnings("unchecked")
publicstatic <T> T newMapperProxy(Class<T> mapperInterface) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class<?>[] interfaces =
new Class[]{mapperInterface};
MapperProxy proxy =
new MapperProxy();
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
}
动态代理实现了InvocationHandler接口,这样当用户调用接口时,会调用invoke方法,在invoke方法中会创建MapperMethod实例,并调用其中的excute方法,MapperMethod方法定义如下:
publicclass MapperMethod {
private Class<?>
declaringInterface;
private Method
method;
private String
url;
private Map<String,Integer>
paramUrl =
new HashMap<String, Integer>();
private RestTemplate
template =
new RestTemplate();
public MapperMethod(Class<?> declaringInterface, Method method) {
this.declaringInterface =declaringInterface;
this.method = method;
prepareUrl();
prepareParam();
}
privatevoid prepareParam() {
for (int i = 0; i <
method.getParameterTypes().length; i++) {
Object[] paramAnnos =
method.getParameterAnnotations()[i];
for (int j = 0; j < paramAnnos.length;
j++) {
if (paramAnnos[j]
instanceofParamUrl) {
paramUrl.put(((ParamUrl) paramAnnos[j]).value(), i);
}
}
}
}
privatevoid prepareUrl() {
Annotation[] annotations =
declaringInterface.getAnnotations();
for(Annotation anno: annotations){
if(anno
instanceofURL){
url = ((URL)anno).value();
}
}
}
public
Object execute(Object[] args) {
Map<String,Object> newMap =
new HashMap<String, Object>();;
if(paramUrl !=
null & !paramUrl.isEmpty()){
for(Map.Entry<String, Integer> e :
paramUrl.entrySet()){
newMap.put(e.getKey(), args[e.getValue()]);
}
}
returntemplate.getForObject(url,
String.class,newMap);
}
}
可见,最终MapperMethod方法,根据Annotation的标记信息,最终执行需要的逻辑。
简要回顾:用户使用Annotation标记其使用的接口,通过MapperProxy获取接口的实例,当调用接口的方法后会触发MapperProxy的invoke方法的调用,在invoke方法中创建了MapperMethod对象,并调用了其excute方法,在MapperMethod中最终解析Annotation,并组织远程访问。
因此MapperProxy实现了动态代理,而MapperMethod最终解释Annotation并执行相应的处理程序。
本文是作者个人总结,如有纰漏请指正
在阅读下面内容前,希望读者有动态代理和Annotation方面的相关知识。
像mybatis这样的开源框架,在接口上进行标记,之后框架在运行时会生成实际的逻辑代码,执行后将结果返回给用户。例如:
publicinterfaceUserMapper{
@Select("SELECT * FROM users WHERE id = #{userId}")
User getUser(@Param("userId")String userId);
}
本文主要是探讨这个使用Annotation和Proxy如何达到mybatis这种效果。
本文以weather获取为例,weather的接口定义如下:
@URL("http://m.weather.com.cn/data/{city}.html")
publicinterface WeatherHttp {
public String getWeather(@ParamUrl("city")
String city);
}
接口中定义了两个Annotation分别是URL和ParamUrl,URL表示接口要访问的网站,而ParamUrl表示访问网站Url中需要替换的参数。
两个Annotation定义如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public@interfaceURL {
publicabstract String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public@interfaceParamUrl {
publicabstract String value();
}
现在,我期望用动态代理的形式为这个类生成一个远程的访问,对于客户端,可以使用如下的方式获取信息:
publicclass WeatherTest {
publicstaticvoid main(String[] args) {
WeatherHttp weatherHttp = MapperProxy.newMapperProxy(WeatherHttp.class);
System.out.println(weatherHttp.getWeather("101010100"));
}
}
通过MapperProxy获取一个代理实例,然后执行这个代理实例,最终获取期望的结果。
动态代理的代码如下:
publicclass MapperProxy
implements InvocationHandler {
privatestaticfinal Set<String>
OBJECT_METHODS =
new
HashSet<String>() {
{
add("toString");
add("getClass");
add("hashCode");
add("equals");
add("wait");
add("notify");
add("notifyAll");
}
};
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try {
if (!OBJECT_METHODS.contains(method.getName()))
{
final Class<?> declaringInterface = findDeclaringInterface(proxy,
method);
if (declaringInterface ==
null) {
thrownew Exception(
"Could not find interface with the given method "
+ method);
}
MapperMethod mapMethod =
new MapperMethod(declaringInterface,method);
return mapMethod.execute(args);
}
} catch (Exception e) {
e.printStackTrace();
}
returnnull;
}
private Class<?> findDeclaringInterface(Object proxy, Method method) {
Class<?> declaringInterface =
null;
for (Class<?> iface : proxy.getClass().getInterfaces()) {
try {
Method m = iface.getMethod(method.getName(),
method.getParameterTypes());
if (declaringInterface !=
null) {
thrownew Exception(
"Ambiguous method mapping.
Two mapper interfaces contain the identical method signature for "
+ method);
} elseif (m !=
null) {
declaringInterface = iface;
}
} catch (Exception e) {
}
}
if (declaringInterface ==
null) {
}
return declaringInterface;
}
@SuppressWarnings("unchecked")
publicstatic <T> T newMapperProxy(Class<T> mapperInterface) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class<?>[] interfaces =
new Class[]{mapperInterface};
MapperProxy proxy =
new MapperProxy();
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
}
动态代理实现了InvocationHandler接口,这样当用户调用接口时,会调用invoke方法,在invoke方法中会创建MapperMethod实例,并调用其中的excute方法,MapperMethod方法定义如下:
publicclass MapperMethod {
private Class<?>
declaringInterface;
private Method
method;
private String
url;
private Map<String,Integer>
paramUrl =
new HashMap<String, Integer>();
private RestTemplate
template =
new RestTemplate();
public MapperMethod(Class<?> declaringInterface, Method method) {
this.declaringInterface =declaringInterface;
this.method = method;
prepareUrl();
prepareParam();
}
privatevoid prepareParam() {
for (int i = 0; i <
method.getParameterTypes().length; i++) {
Object[] paramAnnos =
method.getParameterAnnotations()[i];
for (int j = 0; j < paramAnnos.length;
j++) {
if (paramAnnos[j]
instanceofParamUrl) {
paramUrl.put(((ParamUrl) paramAnnos[j]).value(), i);
}
}
}
}
privatevoid prepareUrl() {
Annotation[] annotations =
declaringInterface.getAnnotations();
for(Annotation anno: annotations){
if(anno
instanceofURL){
url = ((URL)anno).value();
}
}
}
public
Object execute(Object[] args) {
Map<String,Object> newMap =
new HashMap<String, Object>();;
if(paramUrl !=
null & !paramUrl.isEmpty()){
for(Map.Entry<String, Integer> e :
paramUrl.entrySet()){
newMap.put(e.getKey(), args[e.getValue()]);
}
}
returntemplate.getForObject(url,
String.class,newMap);
}
}
可见,最终MapperMethod方法,根据Annotation的标记信息,最终执行需要的逻辑。
简要回顾:用户使用Annotation标记其使用的接口,通过MapperProxy获取接口的实例,当调用接口的方法后会触发MapperProxy的invoke方法的调用,在invoke方法中创建了MapperMethod对象,并调用了其excute方法,在MapperMethod中最终解析Annotation,并组织远程访问。
因此MapperProxy实现了动态代理,而MapperMethod最终解释Annotation并执行相应的处理程序。
相关文章推荐
- Poco 1.4.2 HTTPClientSession/HTTPRequest 使用使用代理(proxy)需要注意的一点
- NO.19 HttpConnection、HttpClient使用注意点(附http连接工具:采用HttpClient支持代理Proxy、支持用户名密码、支持域Domain模式)
- httpClient通过代理(Http Proxy)进行请求
- annotation与httpclient -- HttpClient使用Annotation和Proxy
- 使用JAVA中的动态代理实现数据库连接池(载自:http://www.ibm.com/developerworks/cn/java/l-connpoolproxy/)
- httpClient通过代理(Http Proxy)进行请求
- 【转】Poco 1.4.2 HTTPClientSession/HTTPRequest 使用使用代理(proxy)需要注意的一点
- HttpClient4基础2--通过认证代理访问网页
- 通过动态代理(Proxy)实现的数据库连接池的创建连接与归还链接的操作的简单的实现流程
- Design Pattern—— Dynamic Proxy Pattern(三) 动态代理模式
- spring中BeanNameAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator两种AOP代理方式使用总结
- Java动态代理一——动态类Proxy的使用
- 动态代理Proxy
- Atitit. http 代理原理 atiHttpProxy 大木马
- 为 httpclient 设置代理, 设置http头.
- 新增免费 代理(Proxy)验证助手 自用 支持http/socks
- Spring AOP之 java 动态代理(Proxy 与 InvocationHandler)理解
- 动态代理 Proxy InvocationHandler
- JAVA HTTP连接(HttpURLConnection)中使用代理(Proxy)及其验证(Authentication)
- java动态代理原理(Proxy,InvocationHandler),含$Proxy0源码