annotation与httpClient(3) -- Annotation与动态代理(Proxy)

Annotation与动态代理(Proxy

本文是作者个人总结,如有纰漏请指正

在阅读下面内容前,希望读者有动态代理和Annotation方面的相关知识。

 

mybatis这样的开源框架,在接口上进行标记,之后框架在运行时会生成实际的逻辑代码,执行后将结果返回给用户。例如:

publicinterfaceUserMapper{

  @Select("SELECT * FROM users WHERE id = #{userId}")

  User getUser(@Param("userId")String userId);

}

本文主要是探讨这个使用AnnotationProxy如何达到mybatis这种效果。

本文以weather获取为例,weather的接口定义如下:

@URL("http://m.weather.com.cn/data/{city}.html")

publicinterface WeatherHttp {

    public String getWeather(@ParamUrl("city") String city);

}

接口中定义了两个Annotation分别是URLParamUrlURL表示接口要访问的网站,而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获取接口的实例,当调用接口的方法后会触发MapperProxyinvoke方法的调用,在invoke方法中创建了MapperMethod对象,并调用了其excute方法,在MapperMethod中最终解析Annotation,并组织远程访问。

因此MapperProxy实现了动态代理,而MapperMethod最终解释Annotation并执行相应的处理程序。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值