我们先看一下下面这段代码:
可以看到,这里为了增加事务,对业务代码的侵入性较强,不利于后期的维护,那么有没有办法使业务代码和事务代码分开呢?答案肯定是有的,在Spring中可以使用AOP进行解耦,但是其底层其实是使用的动态代理实现的,那么我们在这里就介绍一下两种常见的动态代理模式:JDK动态代理及CGLIB动态代理。
常用的动态代理技术
JDK 代理 : 基于接口的动态代理技术·:利用拦截器(必须实现InvocationHandler)加上反射机制生成
一个代理接口的匿名类,再调用具体方法前调用InvokeHandler来处理,从而实现方法增强
CGLIB 代理 : 基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是
final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行
增强
如上图,目标对象(目标类)就是被代理的对象,
JDK动态代理:代理类是去实现目标类的接口(目标类必须实现接口),和目标类同级
CGLIB动态代理:代理类是采用ASM生成目标类的子类,和目标类是继承关系
Spring在选择用JDK还是CGLib的依据:
当Bean实现接口时,Spring就会用JDK的动态代理
当Bean没有实现接口时,Spring使用CGLib来实现
可以强制使用CGLib(在Spring配置中加入
性能比较:
在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点
实现方式:
JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,需要满足以下要求:
1. 实现InvocationHandler接口,重写invoke()
2. 使用 Proxy.newProxyInstance() 产生代理对象
3. 被代理的对象必须要实现接口
CGLib 必须依赖于CGLib的类库,需要满足以下要求:
1. 实现MethodInterceptor接口,重写intercept()
2. 使用 Enhancer对象.create() 产生代理对象
3. 被代理的对象不能使用final修饰
Jdk工厂类
/*
JDK动态代理工厂类
*/
@Component
public class JDKProxyFactory {
@Autowired
private AccountService accountService;
@Autowired
private TransactionManager transactionManager;
/*
采用JDK动态代理技术来生成目标类的代理对象
ClassLoader loader, : 类加载器:借助被代理对象获取到类加载器
Class[] interfaces, : 被代理类所需要实现的全部接口
InvocationHandler h : 当代理对象调用接口中的任意方法时,那么都会执行InvocationHandler中invoke方法
*/
public AccountService createAccountSericeJDKProxy(){
AccountService accountServiceProxy= (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
// proxy: 当前的代理对象引用 method:被调用的目标方法的引用
// args:被调用的目标方法所用到的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if(method.getName().equals("transfer")) {
System.out.println("进行了前置增强");
// 手动开启事务:调用事务管理器类中的开启事务方法
transactionManager.beginTransaction();
// 让被代理对象的原方法执行
method.invoke(accountService, args);
System.out.println("进行了后置增强");
// 手动提交事务
transactionManager.commit();
}else {
method.invoke(accountService, args);
}
} catch (Exception e) {
e.printStackTrace();
// 手动回滚事务
transactionManager.rollback();
} finally {
// 手动释放资源
transactionManager.release();
}
return null;
}
});
return accountServiceProxy;
}
}
这里, method.invoke(accountService, args) 进行业务方法的调用,然后在前后增加了事务的处理
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})
public class AccountServiceImplTest {
@Autowired
private JDKProxyFactory proxyFactory;
/*
测试JDK动态代理优化转账案例
*/
@Test
public void testTransferProxyJDK(){
// 当前返回的实际上是AccountService的代理对象proxy
AccountService accountSericeJDKProxy = proxyFactory.createAccountSericeJDKProxy();
// 代理对象proxy调用接口中的任意方法时,都会执行底层的invoke方法
accountSericeJDKProxy.transfer("tom","jerry",100d);
accountSericeJDKProxy.save();
}
}
Cglib工厂类
/*
该类就是采用cglib动态代理来对目标类(AccountServiceImpl)进行方法(transfer)的动态增强(添加上事务控制)
*/
@Component
public class CglibProxyFactory {
@Autowired
private AccountService accountService;
@Autowired
private TransactionManager transactionManager;
public AccountService createAccountServiceCglibProxy(){
// 编写cglib对应的API来生成代理对象进行返回
// 参数1 : 目标类的字节码对象
// 参数2: 动作类,当代理对象调用目标对象中原方法时,那么会执行intercept方法
AccountService accountServiceproxy = (AccountService) Enhancer.create(accountService.getClass(), new MethodInterceptor() {
// o : 代表生成的代理对象 method:调用目标方法的引用
// objects:方法入参 methodProxy:代理方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
// 手动开启事务:调用事务管理器类中的开启事务方法
transactionManager.beginTransaction();
method.invoke(accountService, objects);
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
// 手动回滚事务
transactionManager.rollback();
} finally {
// 手动释放资源
transactionManager.release();
}
return null;
}
});
return accountServiceproxy;
}
}
这里, method.invoke(accountService, objects) 进行业务方法的调用,然后在前后增加了事务的处理
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})
public class AccountServiceImplTest {
@Autowired
private CglibProxyFactory cglibProxyFactory;
/*
测试Cglib动态代理优化转账案例
*/
@Test
public void testTransferProxyCglib(){
// accountServiceCglibProxy: proxy
AccountService accountServiceCglibProxy = cglibProxyFactory.createAccountServiceCglibProxy();
accountServiceCglibProxy.transfer("tom","jerry",100d);
}
}