静态代理是在编译期已经创建好了代理类,并生产 class 文件,详见代理模式。动态代理是在代码运行过程中,动态生成代理类,其优势在于可以方便地对代理类的所有函数做统一的处理,而无需手动修改每个方法。

Java 动态代理主要包括:

  • 基于 JDK 代理
  • 基于 CGlib 代理

基于 JDK 代理

编写一个 Handler ,实现InvocationHandler,在invoke方法中增加代理逻辑:

public class DynamicInvocationHandler implements InvocatioHandler {
    private static Logger LOGGER = 
            LoggerFactory.getLogger(DynamicInvocationHandler.class);
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable{
        LOGGER.info("Invoked method: {}", method.getName());
        return 42;
    }
}

创建代理对象:

Map proxyInstance = (Map) Proxy.newProxyInstance(
        DynamicProxyTest.class.getClassLoader(),
        new Class[] { Map.class },
        new DynamicInvocationHandler());
)

Jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时默认不会保存在文件,放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建代理对象实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。

可以把 InvocationHandler 看做一个中介类,中介类持有一个被代理对象,被 Proxy 类回调。在invoke 方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把客户端对invoke 的调用最终都转为对被代理对象的调用。

因为生成的代理类继承了Proxy类,由Java的继承机制决定了,JDK 代理只能代理接口

JDK 代理自动代理 Object 的 hashCode, equalstoString 方法。

基于 CGlib

Cglib是针对类来实现代理的,原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷。

但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。

业务类:

public class Dog {
    public String call() {
         System.out.println("wang wang");
         return "Dog wang";   
    }
}

拦截器实现 MethodInterceptor 接口:

public class CglibMethodInterceptor implements MethodInterceptor {
    public Object CglibProxyGeneratory(Class target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target);
        enhancer.setCallback(this);
        return enhancer.create();
    }
 
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before");
        MonitorUtil.start();
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("after");
        MonitorUtil.finish(method.getName());
        return result;
    }
}

切面,实现增强逻辑:

public class MonitorUtil { 
    private static ThreadLocal<Long> tl = new ThreadLocal<>(); 
    
    public static void start() { 
        tl.set(System.currentTimeMillis()); 
    } 
    
    public static void finish(String methodName) { 
        long finishTime = System.currentTimeMillis();
        System.out.println(methodName + "方法执行耗时" + (finishTime - tl.get()) + "ms"); 
    } 
}

测试类:

public class CglibTest { 
    @Test 
    public void testCglib() throws Exception {
        System.out.println(System.getProperty("user.dir"));
        saveGeneratedCGlibProxyFiles(System.getProperty("user.dir")); 
        
        /** 第一种方法: 创建cglib 代理类 start */ 
        // 创建加强器,用来创建动态代理类 
        Enhancer enhancer = new Enhancer(); 
        // 为代理类指定需要代理的类,也即是父类 
        enhancer.setSuperclass(Dog.class);
        // new 一个新的方法拦截器 
         CglibMethodInterceptor cglibMethodInterceptor = 
                 new CglibMethodInterceptor(); 
         // 设置方法拦截器回调引用,对于代理类上所有方法的调用,都会调用CallBack
         // ,而Callback则需要实现intercept() 方法进行拦截
        enhancer.setCallback(cglibMethodInterceptor); // 获取动态代理类对象并返回
        Dog dog = (Dog) enhancer.create(); /** 创建cglib 代理类 end */
        System.out.println(dog.call()); 
        
        
        // 对于上面这几步,可以新增一个工具方法 放置在 CglibMethodInterceptor 里面
        // ;也就有了第二种方法 
        // new 一个新的方法拦截器,该拦截器还顺带一个用于创建代理类的工具方法。
        // 看起来简单很多 
        cglibMethodInterceptor = new CglibMethodInterceptor(); 
        dog = (Dog) cglibMethodInterceptor.CglibProxyGeneratory(Dog.class);
        System.out.println(dog.call()); 
    } 
    
    /** 
     * 设置保存Cglib代理生成的类文件。
     * @throws Exception 
     */ 
    public void saveGeneratedCGlibProxyFiles(String dir) throws Exception {
        Field field = System.class.getDeclaredField("props");
        field.setAccessible(true); 
        Properties props = (Properties) field.get(null);
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, dir);
        //dir为保存文件路径
        props.put("net.sf.cglib.core.DebuggingClassWriter.traceEnabled"
                , "true"); 
    } 
}

CGlib 默认代理 Object 中的 equalstoStringhashCode, clone等方法,比 JDK 代理多了 clone