反射与动态代理

来源:互联网 发布:php微信关注事件推送 编辑:程序博客网 时间:2024/06/14 02:46

java中反射与动态代理是java程序员不可忽略的一门功课,关于其概念、基本使用方式,网上一大堆,本文主要讲解本人对于这两者的认识,以及关于开源项目中典型用法。

反射

Java反射机制:在程序运行时获取已知名称的类或已有对象的相关信息的一种机制,包括类的方法、属性、父类等,还包括实例的创建和实例类型的判断等。

是不是很抽象?来看个例子,在Spring中经常有如下的配置:

<bean id="person" class="com.linuer.test.Person"></bean>

(在此不讨论具体Spring容器启动细节,只是讨论与反射相关的知识点。)

可以想象到的是Spring框架会读取上面的配置,获取类的名称。然后根据已知类的名称创建实例类型。是否很熟悉描述,在看看反射机制的描述。

看不懂?没事,下面一个实际的例子

  /**  * 此字符串的获取你可以想象是从配置文件中,读取XMl,获取相关字符串。  **/String className = "com.linuer.test.Person";  Class cls = Class.forName(className);Object object = cls.newInstance();  //创建实例

上面的操作,用大白话说就是:根据一字符串 来得到类信息或者类方法,或者创建类实例。

至于这个 字符串 你是从哪里得来,配置文件?网络?都行,只要你想象到的方式。

除了上面操作,在实际使用过程中还有许多使用的方式。

在实际学习中关于反射相关的类主要关注:

Class类、类得属性:Field、类得方法: Method 。

动态代理

说动态代理之前,先说代理这个行为。
代理简单来说就是通过中间人来做。
来个图来说明吧
代理.png
图中可以看出,有个代理之后依旧是送花这个动作,可是送花的动作的含义与原先不相同了哦。
大家都知道,代理分为:静态代理与动态代理,具体有什么区别呢?以下结合代码来说:

public interface Calculator {   public void add(Integer x, Integer y);    public void div(Integer x, Integer y);}--------------------------------------------------------------------------------------------------public class CalculatorImpl implements Calculator{ @Override    public void add(Integer x, Integer y)    {        System.out.println(x+y);    }    @Override    public void div(Integer x, Integer y)    {        System.out.println(x-y);    }}--------------------------------------------------------------------------------------------------//正常的使用方式(对应与图中的上面两个小人的情况)  Calculator calculator = new CalculatorImpl();  calculator.add(7,6)输出:  13

上面是计算数值,现在我想在计算数值的前后,分别要输出一些信息,那该怎么做呢?直接在CalculatorImpl 类中的方法添加,这么做肯定不够优雅,作为立志成为资深程序员的我们,是绝对不能这么干的。
我们要用代理模式来做。来看看静态代理怎么做的吧

public class CalcProxy implements Calculator {  private Calculator calc;    public CalcProxy(){        calc = new CalculatorImpl();    }    @Override    public void add(Integer x, Integer y) {        before();        calc.add(x,y);        after();    }    @Override    public void div(Integer x, Integer y) {        before();        calc.div(x,y);        after();    }    private void before() {        System.out.println("before");    }    private void after() {        System.out.println("after");    }}---------------------------------------------------------------------   Calculator calc = new CalcProxy();   calc.add(7,6);输出:before13after8

既然静态代理能够完成工作,为什么还需要改进,变成动态代理呢?原因在于:一个静态代理只能代理一个接口,这样就导致项目中到处都是XxxProxy。看动态代理是如何解决这个方案的。

public class DynamicProxy implements InvocationHandler{    private Object target;    public DynamicProxy(Object target){        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        before();        Object result = method.invoke(target,args);        after();        return result;    }.......}代码中target变量就是我们被代理的目标对象,如果用来做Calculator 代理的话,就是Calculator 对象。调用:  Calculator calculator = new CalculatorImpl();  DynamicProxy dynamicProxy = new DynamicProxy(calculator);  Calculator calc = (Calculator) Proxy.newProxyInstance(                calculator.getClass().getClassLoader(),                calculator.getClass().getInterfaces(),                dynamicProxy        );        calc.add(7,6);输出:before13after

上面的意思在于用dynamicProxy 去包装CalculatorImpl实例,然后调用JDK给我们提供的Proxy类的newProxyInstance去动态创建一个Calculator 接口的代理类。
对于newProxyInstance方法的参数真是让人“蓝廋”。
* 参数1:ClassLoader
* 参数2:该实现类的所有接口
* 参数3:动态代理对象
其实这块可以进一步封装下,避免程序中到处调用newProxyInstance

public class DynamicProxy implements InvocationHandler{   .....   public <T> T getProxy(){        return (T) Proxy.newProxyInstance(                target.getClass().getClassLoader(),                target.getClass().getInterfaces(),                this        );    }  ......}----------------------调用:  Calculator calculator = new CalculatorImpl();  DynamicProxy dynamicProxy = new DynamicProxy(calculator);  Calculator calc = dynamicProxy.getProxy();  calc.add(7,6);这样是不是简单很多了。

不知道你们注意到了没有,在DynamicProxy 中不管其代理的对象,如CalculatorImpl实例中接口增加或者减少,DynamicProxy 都不会改动,这也算是动态代理这个优点。但是如果被代理对象,是没有任何接口的类,那么它是不是就没有用武之地了呢?是的。那有没什么办法解决呢?依旧使用动态代理,但是其被代理的对象不必要是一个接口类。答案是:有的。CGLIB的动态代理。

public class CGLibProxy implements MethodInterceptor {    public <T> T getProxy(Class<T> cls){        return (T) Enhancer.create(cls,this);    }    @Override    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {        before();        Object result = methodProxy.invokeSuper(obj,args);        after();        return result;    }......}我们需要实现CGLIB给我们提供的MethodInterceptor 实现类,并编写intercept方法,此方法的最后一个参数MethodProxy值得注意,这个是一个方法的代理。什么意思?看下面的调用:CGlibProxy cgLibProxy = new CGlibProxy();Calculator calc = cgLibProxy.getProxy(CalculatorImpl.class);calc.add(7,6);注意到没,在传入参数的时候,并没有传入接口信息。

关于反射与动态代理讲解远远不止这些,还有许多实际的应用,接下来的两篇文章中会针对动态代理的应用进行论述。
由于本人水平有限,有什么问题可以评论,喜欢的可以关注。

原创粉丝点击