spring学习笔记整理(二)-----AOP讲解
一、什么是AOP?
AOP:面向切面编程,采用横向抽取机制,取代了传统的纵向继承
AOP是一种面向切面的思想,但是我们平常说的spring使用了AOP,实际上说的是spring实现AOP思想的底层原理,而底层原理就是使用动态代理来增强某个方法。所以平常说AOP技术实际上就是指通过动态代理来对方法进行增强。
比如:我们需要对一个已经写好的类中的方法进行增强,在不改动该类方法的代码的情况下,如何做呢?
传统纵向继承
编写一个类,继承该类,重写该类中的这种需要增强的方法,这样确实可以达到我们的目的,但是一旦需要修改的方法所属的类不是一个类,那么就需要在多写很多子类,增加很多方法,编程量就是一个很大的问题,并且后期要修改,工作量也很大。

横向抽取机制,将要增强所用到的代码提取到一个类中,然后对需要增强的方法通过代理类去将其增强即可。
二、动态代理的两个方式
JDK动态代理。接口+实现类
cglib字节码增强。 实现类
为了更好的理解spring的AOP技术,我们应该手动编写以上两种实现动态代理的方法,然后才能体会到spring实现AOP技术所带来的便利。
2.1、JDK动态代理。
要使用JDK动态代理的类,必须要有接口。这是前提条件。

编写UserService接口和UserServiceImpl。

创建代理类的工厂proxyFactory

增强代码的类

测试testApp

测试结果 成功增强了userServiceImpl对象的add方法。如果别的类有需要被增强的方法,那么同样通过创建工厂代理就可以拿到对应的代理对象。然后进行加强

2.2、cglib动态代理。
被代理的对象不需要在实现接口,要求就放松了很多。很多时候都采用cglib来进行动态代理。
需要导入两个jar包

spring已经将cglib和asm整合到了spring-core-3.2.0.jar中,所以我们导入spring的jar包就不需要在重复导这两个包了。
跟JDK动态代理的编写流程是一样的
UserServiceImpl。没有接口了

增强方法的类
创建代理的工厂(和jdk动态代理有区别)
测试
成功增强方法。
注意:CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,所以说被代理的类不能有final关键字
三、spring中使用AOP的相关术语
3.1、Target:目标类,需要被增强的类,也就是上面我们写的UserServiceImpl。
3.2、JointPoint:连接点,目标类上需要被增强的方法,(这些方法可以被增强,也可以不增强,也就是说目标类中所有的方法都可以称为是连接点)
3.3、PointCut:切入点,被增强的方法(已经确定这个方法要被增强),切入点就是一个连接点的子集
3.4、Advice:增强/通知,增强的代码,也就是上面将增强的代码凑成的一个类。类中的每个方法都代表一个增强的功能代码,这个类中的方法就被称为通知
前置通知:在方法之前执行
后置通知:在方法之后执行
异常通知:方法出现异常
最终通知:在后置之后执行
环绕通知:在方法之前和之后执行
3.5、weaving:织入,将切入点和通知结合,从没被增强到已经增强的过程
3.6、Aspect:切面,切入点和通知结合,切入点和通知点多点形成面特殊情况:一个切入点 和 一个通知
画张图就可以理解了。

3.7、Introduction(引介) 特殊的通知,可以对类增强,添加方法或字段。(知道)
四、使用AspectJ框架(注解方式)实现aop
使用注解的话,很简单,就是把xml方式用注解给替代,其中也就做6件事
1、将切面类配置给spring,相当于<bean id="" class="切面类全限定类名">
@component
2、将切面类申明为切面
@Aspect

3、将目标类配置给spring
@component 如何不编写名称的话,那么要获取该对象,则使用userServiceImpl进行获取。

4、申明目标类切入点范围
1.方法必须private,没有返回值,没有参数
2.之后使用将其当成方法调用。例如:@After("myPointcut()")
@Pointcut("execution(* com.wuhao.aspectj.annotation.*.*(..))")
5、编写相应的通知
@Before 前置
@AfterReturning 后置,可以获得返回值,必须在注解中确定返回值参数名称。
@AfterThrowing 抛出异常,可以获得具体异常信息,必须在注解确定第二个参数名称
@Around 环绕[]
@After 最终

6:必须在xml中扫描注解和启用aop

7、测试

三、AspectJ切点函数
1. 方法切点函数
execution():根据匹配规则匹配
eg:前置增强@Before(execution(public * *Service(..)))
匹配所有以Service结尾的方法
@annotation():根据相应的的注解匹配
eg:后置增强@AfterReturning(“@annotation(com.sxd.annotation.Tag)”)
只有方法注解了@Tag才能匹配
2. 方法入参切点函数
args:根据输入参数类型参数匹配
eg:环绕增强@Around(com.sxd.domain.Student)
输入参数为Student才能匹配(包含子类)
@args:根据输入参数类型是否注解指定注解匹配
eg:抛出增强@AfterThrowing(com.sxd.annotation.Respository)
比如输入参数StudentDao类上注解了@Repository才能匹配(包含子类)
3. 目标类切点函数
within(): 根据匹配条件匹配
eg:Fianl增强@After(within(com.sxd.service.*Service))
service包下的以Service结尾类所有的方法才能匹配,与execution类似,但只精确到类级别
@within():根据输入参数注解类型匹配
eg:引介增强@DeclareParents(@within(com.sxd.annotation.Tag))
匹配标注了@Tag的类的子类和子孙类
target():根据类型匹配指定类来决定连接点是否匹配
eg:@Before(target(com.sxd.service.BaseService))
匹配BaseService的实现类StudentService的所有方法(接口方法以及没有在接口定义的方法)
@target():根据输入参数注解类型匹配
eg:@Before(@Target(com.sxd.annotation.Tag))
匹配只有标注了@Tag的类(不包含其子类)
4. 代理类切点函数
this()
判断代理对象的类是否按类型匹配于指定类,如果匹配,则代理对象的所有连接点匹配切点
浙公网安备 33010602011771号