Java动态代理

Java动态代理篇

在Java中,代理对象往往实现和目标对象一致的接口,并作为目标对象的代替,接受来自其他的调用,并将全部或者部分调用转发给目标对象,在这过程中实现代理接口和调用转发是代理对象必须要完成的重要任务;
在完成上述两个任务的同时,代理对象可以在调用转发前和转发后执行一些功能,例如日志输出,访问控制,或者是一些复杂的附加逻辑,像spring的AOP的实现;而这些附加逻辑正是体现代理对象的重要价值;
如下代理序列图:

image

实现代理接口

java.lang.reflect.Proxy提供了用于创建代理类和对象的静态方法,也就是说通过java.lang.reflect.Proxy可以动态的创建某个接口实现代理对象;代理对象的另一个重要重要的任务是调用转发,一般由java.lang.reflect.InvocationHandler该接口的实例来完成,接口实例负责处理接口上的方法调用并返回结果;
Proxy比较重要的方法如下:

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;
    
    //用于获取代理类的class对象,该代理类将定义在指定的类加载器中,并将实现参数interfaces的所有接口,该类只创建一次
    @CallerSensitive
    public static Class getProxyClass(ClassLoader loader,
                                         Class... interfaces)
        throws IllegalArgumentException
    {
       ........
    }
    
    //创建代理对象
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
         ........
    }
    
    //判断class对象是否是代理类        
    public static boolean isProxyClass(Class cl) {
        return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
    }

    //获取代理实例对应的调用处理程序           
    @CallerSensitive
    public static InvocationHandler getInvocationHandler(Object proxy)
        throws IllegalArgumentException
    {
        ........
    }
    
}    

调用转发
创建接口实现由java.lang.reflect.Proxy完成,而调用转发由java.lang.reflect.InvocationHandler的实例来完成,InvocationHandler的实例也叫调用句柄实例,InvocationHandler是一个接口,invoke是该接口的唯一方法,当用户调用newProxyInstance()返回的对象上的方法时,该对象将方法调用转发调用句柄实例,即调用InvocationHandler的invoke方法;而invoke方法的返回值将作为代理对象方法的调用结果返回给用户;

public interface InvocationHandler {
    //proxy:代表代理对象本身  method:用户调用的代理对象上的方法    args:传递给该方法的参数
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

更复杂的invoke实现,可以包含调用前和调用后的参数处理,将调用转发给不同的对象或者多个对象,如下图展示了调用转发过程中重要的配合对象:


image

动态代理实例
如下代码所示,定义了Hello接口,并用HelloImp实现了Hello接口,LogInvocationHandler作为调用转发实例,LogInvocationHandlerTest作为测试类,在该类中使用 Proxy.newProxyInstance()生成一个代理类实例,在该接口的方法调用,将指派到指定的调用处理程序上,即实现了InvocationHandlerLogInvocationHandler实例;

package proxys;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

import org.apache.log4j.Logger;

/**
 * 动态代理实现案例
 */
// 接口
interface Hello {
    String sayHello(String str);
}

// 实现
class HelloImp implements Hello {

    public String sayHello(String str) {
        return "HelloImp: " + str;
    }
}

//实现invocationHandler ,方法调用会被转发到该类的invoke()方法。
public class LogInvocationHandler implements InvocationHandler {
    private static Logger logger = Logger.getLogger(LogInvocationHandler.class);
    //目标类,在LogInvocationHandler实例化的时候传入进来
    private Hello hello;

    public LogInvocationHandler(Hello hello) {
        this.hello = hello;
    }

    //关键方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("sayHello".equals(method.getName())) {
            logger.info("You said: " + Arrays.toString(args));
        }

        logger.info("------实现前置通知------");
        //代理对象调用转发给目标对象,这里相当于调用了HelloImp的sayHello方法
        Object obj = method.invoke(hello, args);
        logger.info("------实现后置通知------");
        logger.info("------实现其他附加功能------");
        return obj;
    }

}

//测试类
class LogInvocationHandlerTest {
    public static void main(String[] args) {
        // 然后在需要使用Hello的时候,通过JDK动态代理获取Hello的代理对象。
        Hello hello = (Hello) Proxy.newProxyInstance(
                HelloImp.class.getClassLoader(), // 1. 类加载器
                new Class[]{Hello.class}, // 2. 代理需要实现的接口,可以有多个
                new LogInvocationHandler(new HelloImp()));// 3. 方法调用的实际处理者
        //使用代理对象调用目标对象的sayHello方法     
        System.out.println(hello.sayHello("ending!!!!!!!!"));

    }
}

你可能感兴趣的:(Java动态代理)