您的位置:首页 > 理论基础 > 计算机网络

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 Proxy