最近复习了一下动态代理的两种实现方式,我觉得学习动态代理的方式就是从springAOP入手,因为springAop中的代理就很好的诠释了两种动态代理的实现方式,springAOP默认的策略是如果目标类是接口,则使用JDK动态代理技术,如果目标对象没有实现接口,则默认会采用CGLIB代理。
一.什么是代理?
先来讲讲我所理解的什么是代理吧,生活中有很多代理的角色,比如银行代理,驾校代理,信用卡代理等等,其实所谓的代理就是帮你去干你不想干的事情的人,那么在程序的世界里,代理其实就是去帮你实现你不想到处都写的业务逻辑,有了代理,你可以不用显式的调用就可以实现业务增强,aop的切面增强技术就是很好的动态代理的实现,它可以很好的解耦我们的代码,比如操作拦截,日志打印等等,我们不必在每个操作都去写上一段相同的代码,而是通过代理技术来拦截我们的操作,动态的进行增强,岂不是一劳永逸,而且便于维护?废话不多说,直接上两个自己写的通过jdk代理以及cglib实现的代理的demo,便于大家理解。
二.jdk的动态代理
1.jdk的动态代理核心就是InvocationHandler接口和Proxy类,要想通过jdk的动态代理去实现自己的业务,就必须要实现InvocationHandler接口,并实现该接口的invoke方法,代理增强的业务代码就写在invoke方法体中,然后通过Proxy类的newInstance方法创建代理对象,通过代理对象执行目标对象的方法时,实际上执行的是增强之后的invoke方法,直接上代码:
需求一:有一个service接口,接口中有一个doService方法,现在我想通过jdk的动态代理去对doService方法进行增强,该怎么做?如下:
1.新建一个Service接口,包含一个doService方法
package com.example.dynamicProxy;
/**
* @author herong22384
* @date 2019/7/30 15:32
*/
public interface Waiter {
String doService();
}
给该接口写一个简单的实现类
package com.example.dynamicProxy.impl;
import com.example.dynamicProxy.Waiter;
/**
* @author herong22384
* @date 2019/7/30 15:32
*/
public class ManWaiter implements Waiter {
@Override
public String doService() {
System.out.println("jdk动态代理服务处理中");
return "hello";
}
}
2.新建一个动态代理工厂类,实现InvocationHandler接口
package com.example.dynamicProxy;
import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author herong22384
* @date 2019/7/30 15:05
*
* 1、创建代理工厂(通过jdk实现动态代理)
* 2、给工厂设置目标对象、前置增强、后置增强
* 3、调用doProxy()得到代理对象
* 4、执行代理对象方法时,先执行前置增强,然后是目标方法,最后是后置增强
* 5、其实在Spring中的AOP的动态代理实现的一个织入器也是叫做ProxyFactory
*/
public class JdkProxyFactory implements InvocationHandler{
private Object targetObject;
private BeforeAdvice beforeAdvice;
private AfterAdvice afterAdvice;
public JdkProxyFactory(){
}
public JdkProxyFactory(Object targetObject, BeforeAdvice beforeAdvice, AfterAdvice afterAdvice){
this.targetObject = targetObject;
this.beforeAdvice = beforeAdvice;
this.afterAdvice = afterAdvice;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置增强
if (beforeAdvice != null){
beforeAdvice.before();
}
//通过反射执行主业务逻辑
Object result = method.invoke(targetObject,args);
//后置增强
if (afterAdvice != null){
afterAdvice.after();
}
return result;
}
public Object doProxy(){
//获取类加载器
ClassLoader classLoader = this.getClass().getClassLoader();
//获取当前类所实现的所有接口类型
Class[] interfaces = targetObject.getClass().getInterfaces();
//获取代理对象
Object object = Proxy.newProxyInstance(classLoader,interfaces,this);
return object;
}
public Object getTargetObject() {
return targetObject;
}
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}
public BeforeAdvice getBeforeAdvice() {
return beforeAdvice;
}
public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
this.beforeAdvice = beforeAdvice;
}
public AfterAdvice getAfterAdvice() {
return afterAdvice;
}
public void setAfterAdvice(AfterAdvice afterAdvice) {
this.afterAdvice = afterAdvice;
}
}
3.模拟aop的实现,创建两个增强接口(也可通过自定义注解实现)
前置增强接口
package com.example.dynamicProxy.advice;
/**
* @author herong22384
* @date 2019/7/30 15:07
*/
/**
* 前置增强
* */
public interface BeforeAdvice {
void before();
}
后置增强接口
package com.example.dynamicProxy.advice;
/**
* @author herong22384
* @date 2019/7/30 15:08
*/
/**
* 后置增强
* */
public interface AfterAdvice {
void after();
}
4.创建一个main函数,测试一下这个通过jdk实现的动态代理工厂类是否能达到预期增强doService方法的目的
package com.example.dynamicProxy;
import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;
import com.example.dynamicProxy.impl.CglibServiceImpl;
import com.example.dynamicProxy.impl.ManWaiter;
/**
* @author herong22384
* @date 2019/7/30 15:23
*/
public class DynamicProxyTest {
public static void main(String[] args) {
BeforeAdvice beforeAdvice = new BeforeAdvice() {
@Override
public void before() {
System.out.println("前置增强啦");
}
};
AfterAdvice afterAdvice = new AfterAdvice() {
@Override
public void after() {
System.out.println("后置增强啦");
}
};
System.out.println("开始jdk动态代理模式");
JdkProxyFactory proxy = new JdkProxyFactory(new ManWaiter(),beforeAdvice,afterAdvice);
Waiter waiter = (Waiter) proxy.doProxy();
//调用动态代理出来的对象的方法,实际上是调用的动态代理器InvocationHandler的invoke方法
waiter.doService();
}
}
执行结果如下:
可以看到,通过jdk的动态代理,我们已经基本实现了对Waiter接口的doService方法进行增强,通过这个一个简单的实现,我们可以了解到,在通过Proxy类的NewProxyInstance方法创建代理对象时,我们需要传入三个参数,其中ClassLoader用来加载类对象,Class[] 类型的interface用来获取当前类所实现的所有接口类型,最后还要传入一个InvocationHnadler接口的实现类对象,通过跟踪newProxyInstance方法的源码发现,jdk的动态代理在创建代理对象时,是通过实现代理对象所实现的接口,并实现接口中的所有方法,最后对目标对象的方法通过invoke方法进行增强(invoke方法中通过反射来执行目标对象的方法,并在该方法的前后进行相应的增强),那么代理对象在执行目标对象的方法时,实际上执行的就是增强之后的invoke方法,从而达到对目标对象进行增强的目的。
三.cglib实现的动态代理
1.cglib实现的动态代理的核心就是MethodInterceptor接口和Enhancer类(这两个相当于jdk动态代理的核心invocationHandler接口和Proxy类)MethodInterceptor接口的intercept方法用来动态增强目标对象的方法,Enhancer类的create方法用来生成代理对象。通过cglib动态代理实现方法的增强的实现如下:
2.直接创建一个类,他有一个doDynamicService方法,我们直接对这个方法进行增强
package com.example.dynamicProxy.impl;
/**
* @author herong22384
* @date 2019/7/31 15:30
*/
public class CglibServiceImpl {
public void doDynamicService(){
System.out.println("cglib动态代理服务处理中");
}
}
3.创建一个cglib的动态代理工厂类,实现MethodInterceptor接口
package com.example.dynamicProxy;
import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author herong22384
* @date 2019/7/31 13:55
*
* 1、创建代理工厂(通过cglib实现动态代理)
* 2、给工厂设置目标对象、前置增强、后置增强
* 3、调用getInstance()得到代理对象
* 4、执行代理对象方法时,先执行前置增强,然后是目标方法,最后是后置增强(实际执行的是经过增强的intercept()方法)
*/
public class CglibProxyFactory implements MethodInterceptor {
private Object targetObject;
private BeforeAdvice beforeAdvice;
private AfterAdvice afterAdvice;
public CglibProxyFactory(){}
public CglibProxyFactory(Object targetObject,BeforeAdvice beforeAdvice,AfterAdvice afterAdvice){
this.targetObject = targetObject;
this.beforeAdvice = beforeAdvice;
this.afterAdvice = afterAdvice;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//前置增强
if (beforeAdvice != null){
beforeAdvice.before();
}
//执行原本的主业务逻辑
Object object = methodProxy.invokeSuper(o,objects);
//后置增强
if (afterAdvice != null){
afterAdvice.after();
}
return object;
}
public Object getInstance(){
//创建生成代理对象的工具类,相当于jdk的proxy类
Enhancer enhancer = new Enhancer();
//设置需要被代理的对象类
enhancer.setSuperclass(this.targetObject.getClass());
//设置增强的方法
enhancer.setCallback(this);
//创建代理对象
Object object = enhancer.create();
return object;
}
public Object getTargetObject() {
return targetObject;
}
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}
public BeforeAdvice getBeforeAdvice() {
return beforeAdvice;
}
public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
this.beforeAdvice = beforeAdvice;
}
public AfterAdvice getAfterAdvice() {
return afterAdvice;
}
public void setAfterAdvice(AfterAdvice afterAdvice) {
this.afterAdvice = afterAdvice;
}
}
4.通过一个main函数来测试cglib动态代理的增强
package com.example.dynamicProxy;
import com.example.dynamicProxy.advice.AfterAdvice;
import com.example.dynamicProxy.advice.BeforeAdvice;
import com.example.dynamicProxy.impl.CglibServiceImpl;
import com.example.dynamicProxy.impl.ManWaiter;
/**
* @author herong22384
* @date 2019/7/30 15:23
*/
public class DynamicProxyTest {
public static void main(String[] args) {
BeforeAdvice beforeAdvice = new BeforeAdvice() {
@Override
public void before() {
System.out.println("前置增强啦");
}
};
AfterAdvice afterAdvice = new AfterAdvice() {
@Override
public void after() {
System.out.println("后置增强啦");
}
};
System.out.println("开始cglib动态代理模式");
CglibProxyFactory factory = new CglibProxyFactory(new CglibServiceImpl(),beforeAdvice,afterAdvice);
CglibServiceImpl cglibService = (CglibServiceImpl)factory.getInstance();
cglibService.doDynamicService();
}
}
5.执行一下,看下结果
6.可以看出,我们通过cglib的动态代理也基本实现了针对某个目标对象的方法的增强,那么cglib的动态代理和我们前面jdk的动态代理到底有什么区别呢,大体上看起来其实这两种方式实现的动态代理差不多,都是通过实现一个接口来实现目标对象方法的增强,然后通过一个代理类的方法创建出代理对象,然后执行代理对象的方法实际上执行的是增强之后的方法,是啊,套路都是这么一个套路,但其实他们还是有本质上的区别的,要不然就没有存在两种方式实现的动态代理的必要了。接下来我们就来探讨一下这两种动态代理的区别,以及在什么场景下用jdk的动态代理好点,什么场景下用cglib实现的额动态代理好点,我想这才是大家关注的重点:
JDK动态代理
1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
cglib动态代理
1、 CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、 用CGlib生成代理类是目标类的子类。
3、 用CGlib生成 代理类不需要接口
4、 用CGLib生成的代理类重写了父类的各个方法。
5、 拦截器中的intercept方法内容正好就是代理类中的方法体
spring两种代理方式
-
若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口 -
若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。