前言
练习Spring的AOP面向切面编程
代理模式
1.代理模式的概念
2.传统模式中把事务写在service层中非常不合适
3.静态代理模式
静态代理模式需要实现与被代理者相同的接口。为了看起来和目标对象一致需要修改ID
静态代理模式中包含了
1、目标对象
2、在目标对象完成操作之外完成额外的工作
好处:
实现了代码的分离。让业务层代码只处理业务。事务代码只处理事务,两者松耦合。
缺点:
事务添加的代码重复,复用性不好。基本不用。
代理模式:
4.动态代理
一、JDK的动态代理
1.能够解决重复代码的编写,并且继承了静态代理的所有的优点。
缺点:
1.所有的方法都添加上了事务。这样做不合理。虽然可以在invoke方法中可以根据方法判断是否要加上事务但是这样代码的可读性就变差了。
2.动态代理的局限性高,不能做到灵活的运用。比如添加事务需要一个动态代理,添加日志需要写一个动态代理,添加权限需要写一个动态代理。
3.target.getClass().getInterfaces()要求被代理者必须实现接口。这样才能创建出代理对象
二、Cglib动态代理模式
代码spring03-5特点:将来生成的代理对象都是目标对象的子类。
无论目标对象是否有接口,都能生成代理对象。
代码实现:public static Object getProxy(final Object target,final TransactionManager tx){
//cgLib动态代理的实现
//增强器
Enhancer enhancer = new Enhancer();
//设置参数
enhancer.setInterfaces(target.getClass().getInterfaces());
//设置目标对象为父类
enhancer.setSuperclass(target.getClass());
//设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
try {
tx.begin();
method.invoke(target, args);
tx.commit();
} catch (Exception e) {
tx.rollBack();
}
return null;
}
});
//真正生成代理对象
return enhancer.create();
}
三:spring底层用CGlib和JDK的动态代理帮我们生成代理对象,这种方式更加的灵活
AOP的学习:
1.切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。做额外的操作。
2.连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。目标对象的执行方法。
3.通知(Advice):在切面的某个特定的连接点上执行的动作。
4.切入点(Pointcut):匹配连接点的断言。匹配规则只有规则匹配了才能执行通知。
5.目标对象(Target Object): 被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。就是方法真正执行的对象
6.AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
7.织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。织入就是形成切面的过程(通知+目标对象整合)
AOP的实现步骤
1.导入jar包(5个jar包)
2.导入头文件
<?xmlversion="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
红色是相比以前增加的
蓝色是复制后更改的地方(要改5个地方)
3. 编写切面和通知(1).编写切面
//自定义的切面
@Component("txAspect")
public classTransactionAspect {
@Resource
privateTransactionManager tx;
(2).编写通知
public voidaround(ProceedingJoinPoint joinPoint) throws Throwable{
try {
tx.begin(); //事务开始
joinPoint.proceed(); //目标对象执行
tx.commit();
} catch(Exception e) {
tx.rollBack();
}
}
}
(3).xml配置切面:
<aop:config>
<!--切入点 -->
<aop:pointcutexpression="within(service.*)"id="pc"/>
<aop:aspectref="txAspect">
<aop:aroundmethod="around" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
(4)调用关系
通知的类型
1.aop:before 前置通知 在目标方法执行之前要执行的。日志
2.aop:around 环绕通知 在目标方法执行之前和执行之后都需要执行的通知
3.aop:after-returning 后置通知在目标方法执行之后再执行的通知。
4.aop:after-throwing 异常通知当执行目标方法之后抛出异常才执行异常通知。
5.aop:aftermethod 最终通知 finally最后肯定会被执行的通知。
环绕通知中必须手动的放行目标方法(否则程序会中断)而其他的通知目标方法都会自动执行,和通知没关系(没赋予除环绕通知之外的其他通知管理目标方法的权利)
注意事项
<aop:after-throwingmethod="afterThrow" pointcut-ref="pc1"throwing="thro"/>
throwing="thro"参数要和接收参数匹配否则会报如下错
注意
ProceedingJoinPoint 只能用于环绕通知。
JoinPointjoinPoint 其他通知使用的参数
这些参数必须位于参数列表的第一位否则会报错。
两个方法:
System.out.println(joinPoint.getSignature().getName());
System.out.println(joinPoint.getTarget().getClass());
结果如下图:
后置通知的返回值问题
1后置通知如果与环绕通知一起执行,后置通知想拿到返回值结果。则环绕通知中必须加return
2如果切面中没有环绕通知,则后置通知可以获得返回值
如果后置通知 返回值参数 不匹配(不一致)会报错