AOP
面向切面编程(Aspect-oriented programming,AOP)是spring 中另外一种编程思想,作用是可以在不修改原业务代码的情况下,在类调用关系之间新增加一些操作,优点,将新增的代码和原有业务代码解耦,方便对新增代码的后续维护。代理技术是对AOP思想实现的一种方式。
问题引入
根据代理类创建的时机分为静态代理和动态代理,静态代理在程序运行之前由用户提前定义好,动态代理在程序运行过程中进行创建和加载。
静态代理
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
//两个用户之间转账
@Override
public void transferMoney(Account from, Account to,double amount) throws Exception {
//1 查询账户from
Account accountFrom = accountDao.getAccountByName(from.getName());
//2 查询账户to
Account accountTo = accountDao.getAccountByName(to.getName());
//3 from 账户扣钱 amount
accountFrom.setMoney(accountFrom.getMoney()-amount);
accountDao.updateAccount(accountFrom);
int i =1/0;//出现异常,导致转账失败,并且已经扣款的账户from金额丢失
//4 to 账户加钱
accountTo.setMoney(accountTo.getMoney()+amount);
accountDao.updateAccount(accountTo);
}
}
AccountServiceImpl的代理类
public class AccountServiceImplProxy implements AccountService {
private AccountService accountService;
private TransactionManager tx;
// 转账操作增加事务,保证整个操作
public AccountServiceImplProxy (AccountService accountService){
this.accountService = accountService();
}
@Override
public void transferMoney(Account from, Account to, double amount) throws Exception {
try{
// 1 开启事务
tx.beginTransaction();
// 2 执行事务
accountService.transferMoney(from,to);
// 3 提交事务
tx.commitTransaction();
}catch (Exception e){
// 4回滚事务
tx.rollbackTransaction();
}finally {
// 5 关闭事务
tx.closeTransaction();
}
}
}
通过AccountService代理调用转账业务
AccountService accountService = new AccountServiceImplProxy(new AccountServiceImpl());
accountService.transfer(from,to);
两种动态代理技术
实现动态代理的方式有两种,基于类的动态代理和基于接口的动态代理。
基于接口的动态代理
要求被代理类必须实现了某一个接口,有JDK提供的Proxy类实现,通过newProxyInstance方法动态创建一个代理类实现,下面是该方法定义原型。
/**
* loader- 用来加载创加载新的代理类,由于创建的代理类和被代理实现了相同的接口,所有两个类使用同一个加载器
* interfaces- 被代理类实现的接口,新的代理类也会实现这些接口
* InvocationHandler- 用户实现该接口,在里面的invoke方法实现对修饰方法的代码的添加。
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
实现代码
public class AccountProxy implements InvocationHandler {
private Object proxyedBean;
private TransactionManager transactionManager;
//重写该方法,在该方法里面实现新增一些操作
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"被成功代理");
Object returnValue = null;
try{
// 1 开启事务
transactionManager.beginTransaction();
// 2 执行事务
returnValue = method.invoke(proxyedBean,args);//调用旧的业务逻辑
// 3 提交事务
transactionManager.commitTransaction();
}catch (Exception e){
// 5回滚事务
transactionManager.rollbackTransaction();
}finally {
transactionManager.closeTransaction();
// 4 关闭任务
}
return returnValue;//动态新创建AccountServiceImpl的代理对象
}
}
使用AccountProxy代理对象调用AccountServiceImpl中转账操作
AccountProxy accountProxy = new AccountProxy();
AccountService accountService = new accountServiceImpl();
accountProxy.setProxyedBean(accountService );
AccountService accountService1 = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), accountProxy);
accountService1.transferMoney(from,to,amount);
不足:要求代理类必须实现一个接口。
思考1:为什么被代理类必须要实现一个接口?
JDKProxy生成的动态代理类为 **public final class $Proxy0 extends Proxy implements XXX(被代理类实现的接口) **,通过观察代理类的实现方式可知,动态代理类已经默认继承Proxy类,Java不支持多继承,只能通过实现的方式实现动态代理。至于为什么一定要继承或者实现被代理类,个人理解:是为了从用户的角度,保证被代理类的父类引用可以指向新生成的代理类,不用对后面的业务代码进行修改,这也是动态代理可以支持AOP的原因。
思考2:同一个被代理类中的方法a调用同一个代理类中的方法b,为什么不会走动态代理类?
public void methodA(){
...
}
public void methodB(){
methodA();//等价于this.methodA();
}
外部类调用methodB() 调用对象是新生成动态代理类,当动态内部类使用的代理类调用methodB()方法,此时当前对象变成了被代理类,在methodB方法内部调用methodB,此时当前对象是被代理类,所以不会走动态代理类。
基于类的动态代理
第三方提供的动态代理库cglib中的Enhancer类也能实现动态代理,代理类不必实现接口,只要不是被final修饰的最终类进行。
public class TransactionProxy implements MethodInterceptor {
//被代理对象
private Object proxyedBean;
private TransactionManager tx;
/**
*
* @param o 被代理对象引用
* @param method 被代理的方法
* @param objects 被代理方法的参数
* @param methodProxy 当前执行方法的代理对象????
* @return 动态新创建AccountServiceImpl的代理对象
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
try{
// 1 开启事务
tx.beginTransaction();
// 2 执行事务
returnValue = method.invoke(proxyedBean,objects);
// 3 提交事务
tx.commitTransaction();
}catch (Exception e){
// 4 回滚事务
tx.rollbackTransaction();
}finally {
tx.closeTransaction();
// 5 关闭任务
}
return returnValue;
}
}
使用AccountProxy代理对象调用AccountServiceImpl中转账操作
AccountProxy accountProxy = new AccountProxy();
AccountService accountService = new accountServiceImpl();
accountProxy.setProxyedBean(accountService );
AccountService accountService1 = (AccountService) Enhancer.create(AccountService.class,transactionProxy);
accountService1.transferMoney(from,to,amount);
总结
静态代理实现简单,缺点是当需要有很多类需要增强同样内容是,静待代理需要为每个类编写代理类,而动态代理只需编写一个代理类,具体代理的是哪个类,通过反射机制可以在运行过程中自动识别。动态代理应用广泛,structs拦截器,spring中AOP,mybatis中的sql查询。Spring里会自动在JDK的代理和CGLIB之间切换,同时我们也可以强制Spring使用CGLIB。