使用JDK的动态代理,只能针对目标对象存在接口的情况,如果目标对象没有接口,此时可以考虑使用CGLIB动态代理方式。
CGLIB是通过生成代理类,然后继承于目标类,再对目标类中可以继承的方法做覆盖,并在该方法中做功能增强,因为多态的关系,实则调用的是子类中的方法
1.xml配置:与JDK动态代理时一样
<bean id="employeeDAO" class="com.bigfong.dao.impl.EmployeeDAOImpl" />
<bean id="transactionManager" class="com.bigfong.tx.TransactionManager" />
<bean id="employeeService" class="com.bigfong.service.impl.EmployeeServiceImpl">
<property name="dao" ref="employeeDAO" />
</bean>
<!-- 配置一个事务增强的类 -->
<bean id="transactionManagerAdvice" class="com.bigfong.tx.TransactionManagerAdvice">
<property name="target" ref="employeeService"/>
<property name="txManager" ref="transactionManager"/>
</bean>
2.EmployeeServiceImpl类不需要实现接口
3.TransactionManager类:与JDK动态代理时一样
//模拟事务管理器:
public class TransactionManager {
public void begin() {
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
public void rollback() {
System.out.println("回滚事务");
}
}
4.TransactionManagerAdvice事务的增强类
@SuppressWarnings("all")
//事务的增强操作-CGLIB
public class TransactionManagerAdvice implements org.springframework.cglib.proxy.InvocationHandler {
private Object target;//真实对象(对谁做增强)
private TransactionManager txManager;//事务管理器(模拟)
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public void setTarget(Object target) {
this.target = target;
}
//创建一个代理对象
public <T> T getProxyObject() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());//将继承于哪一个类,去做增强
enhancer.setCallback(this);//设置增强的对象
return (T) enhancer.create();//创建代理对象
}
//如何为真实对象的方法做增强的具体操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = null;
txManager.begin();
try {
//---------------------------------------------------------------
ret = method.invoke(target, args);//调用真实对象的方法
//---------------------------------------------------------------
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
return ret;
}
}
增强类与使用JDK代理方式时的不同
1)增强类实现的类不同,实现的是org.springframework.cglib.proxy.InvocationHandler
2)创建一个代理对象方式不同
5.调用
@Autowired
private TransactionManagerAdvice advice;
//JDK代理对象:com.sun.proxy.$Proxy19
//CGLIB代理对象:com.bigfong.service.impl.EmployeeServiceImpl$$EnhancerByCGLIB$$c0c52615
@Test
void testSave() throws Exception {
EmployeeServiceImpl proxy = advice.getProxyObject();
proxy.save(new Employee());
}
@Test
void testUpdate() throws Exception {
EmployeeServiceImpl proxy = advice.getProxyObject();
proxy.update(new Employee());
}
总结:
1.CGLIB可以生成委托类的子类,并重写父类非final修饰的方法
2.要求类不能是final的,要拦截的方法要是非final,非static,非private的
3.动态代理的最小单位是类(类中所有的方法都会被处理)
代理选择要点
1.JDK动态代理是基于实现接口的,CGLIB和Javassit是基于继承委托的
2.性能上:Javassit > CGLIB > JDK
3.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规范
4.若委托对象实现了接口,优先选用JDK动态代理
5.若委托对象没有实现任何接口,使用Javassit和CGLIB动态代理
下一篇:spring5整理:(七)AOP