java 动态代理的两种方式 -- reflect 与 cglib

2017-04-03 17:15:23   最后更新: 2017-04-03 17:22:25   访问数量:308




在此前的博客中,我们介绍了代理模式

代理模式 -- Proxy

java 提供了动态代理机制以巧妙的方式实现了代理模式的设计理念,最为常用的 AOP 就是基于动态代理实现的,他通过创建代理类,实现在目标类的基础上增加切面逻辑,生成增强版的目标类,从而实现面向切面的特性

 

java 拥有两套动态代理的实现方式,分别是 jdk 动态代理和 cglib 动态代理

jdk 实现的动态代理是通过 java.lang.reflect 包中的类实现的,他通过 java 的反射机制实现了对被代理对象的控制,因此,代理类在创建时与其他任何 java 原生类是一样的,但是由于反射效率较低,所以在代理类实际运行过程中,效率是较低的,同时,只有通过接口 -> 实现这样方式定义的实体类才能够被 jdk 代理,否则 jdk 将因为无法获取到实体类的类型而无法通过反射获取他

cglib 动态代理则是通过底层字节码技术实现的,通过字节码技术为一个类创建子类,并在子类中采用方法拦截技术拦截所有父类方法的调用,从而实现代理的效果,他解决了 jdk 动态代理的实体类必须实现于抽象接口的限制,在实际运行过程中,效率也要高于 jdk 动态代理,但是他的创建过程是较慢的

综上所述,jdk 和 cglib 实现的动态代理各具优点和缺点,AOP 的源码中同时用到了这两种动态代理实现,但是由于 jdk 动态代理的局限性,cglib 实现的动态代理具有更加广泛的应用

 

如上面介绍的,JDK 动态代理通过反射实现,包含于 java.lang.reflect 包中,使用起来较为简单

 

被代理接口和实现

package com.techlog.test.testspring.service; /** * Created by techlog on 2017/4/3. */ public interface WorkService { String work(); }

 

 

package com.techlog.test.testspring.service; import org.springframework.stereotype.Service; /** * Created by techlog on 2017/4/3. */ @Service public class WorkServiceImpl implements WorkService { @Override public String work() { return "Working"; } }

 

 

代理类

package com.techlog.test.testspring.service; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Created by techlog on 2017/4/3. */ public class WorkInvocationHandler implements InvocationHandler { private WorkService workService; public WorkInvocationHandler(WorkService workService) { super(); this.workService = workService; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("work")) { String result = (String) method.invoke(workService, args); result = "prepare to work\n" + result + "\nwork finished"; return result; } else { return method.invoke(workService, args); } } }

 

通过实现 java.lang.reflect.InvocationHandler 我们就可以创建一个代理类了

这个接口只有一个方法,就是 invoke 方法,所有被代理的类的方法都会自动进入这个类中,我们通过传入参数中的 Method 对象和第三个参数就可以获取到实际被调用的类方法与参数了

 

Main 方法

那么,如何将我们的代理类与原本的实现类关联并实现对原本实现类的代理呢?

package com.techlog.test.testspring.service; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * Created by techlog on 2017/4/3. */ public class WorkMain { public static void main(String[] argv) { WorkService workService = new WorkServiceImpl(); InvocationHandler invocationHandler = new WorkInvocationHandler(workService); WorkService workServiceProxy = (WorkService) Proxy.newProxyInstance(workService.getClass().getClassLoader(), workService.getClass().getInterfaces(), invocationHandler); System.out.println(workServiceProxy.work()); } }

 

 

我们通过 java.lang.reflect.Proxy 的 Proxy.newProxyInstance 方法实现了接口、实现类、代理类三者的关联,从而让我们的代理类实现了对被代理对象所有方法的代理

这样,WorkServiceImpl 中所有的接口实现方法的执行都会自动去调用 WorkInvocationHandler 的 invoke 方法,于是打印出了:

prepare to work Working work finished

 

 

与 reflect 一样,CGLIB 的设计也十分简单易用,同时,我们可以不用提供上面代码中的 WorkService 接口的声明,因为 CGLIB 是通过字节码创建被代理类的子类实现的,在代理类创建完成后,代理类便拥有了被代理类的类型信息,因此即使没有一个接口的声明,代理类依然可以获取到被代理类的各种信息

 

依赖

使用 cglib 首先我们需要引入 pom 依赖

<dependency> <groupId>com.kenai.nbpwr</groupId> <artifactId>net-sf-cglib</artifactId> <version>2.1.3-201002241208</version> </dependency>

 

或是下载 net-sf-cglib.jar

 

代理类

package com.techlog.test.testspring.service; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * Created by techlog on 2017/4/3. */ public class WorkCglibProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if (method.getName().equals("work")) { String result = (String) methodProxy.invokeSuper(o, args); result = "prepare to work\n" + result + "\nwork finished"; return result; } else { return method.invoke(o, args); } } }

 

 

通过实现 MethodInterceptor 我们就可以创建一个 cglib 代理类,这个接口仅有一个方法 intercept 与 InvocationHandler 接口的 invoke 方法类似,被代理的所有方法均将进入被代理到该方法执行

与实现了 InvocationHandler 的 jdk 代理类不同,我们无需向代理类注入被代理对象来实现被代理对象方法的调用,这得益于 CGLIB 代理类继承自被代理类,这样 CGLIB 代理类中将隐含得到了父类的实例引用,就是intercept 方法的第一个参数 o,从而省去了我们的注入

 

Main 方法

package com.techlog.test.testspring.service; import net.sf.cglib.proxy.Enhancer; /** * Created by techlog on 2017/4/3. */ public class WorkMain { public static void main(String[] argv) { WorkCglibProxy workCglibProxy = new WorkCglibProxy(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(WorkServiceImpl.class); enhancer.setCallback(workCglibProxy); WorkServiceImpl workService = (WorkServiceImpl) enhancer.create(); System.out.println(workService.work()); } }

 

 

同样,在上述工作完成以后,我们需要让代理类与被代理类联系起来,这个工作就是通过 net.sf.cglib.proxy.Enhancer 对象实现的

最终,打印出了:

prepare to work Working work finished

 

 

 

 






技术帖      技术分享      java      jdk      面向对象      oop      aop      面向切面      设计模式      design pattern      reflect      代理模式      cglib      字节码     


京ICP备15018585号