Spring_04 AOP
5. AOP
1. AOP在Spring中的作用
允许用户自定义切面, 提供声明式事务
横切关注点: 跨越应用程序多个模块的方法和功能(与业务逻辑无关)如日志,安全,缓存,事务
ASPECT 切面: 横切关注点 被模块化的的对象(如Log类)
Advice 通知: 切面需要完成的工作 Log中的一个方法
Target 目标: 被通知的对象
Proxy 代理: 向目标对象应用 通知 ,之后创建的对象
PointCut 切入点: 切面通知 执行的“地点”的定义
JointPoint 连接点: 与切入点匹配的执行点
Advice类型:
MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor, ThrowsAdvice, IntroductionInterceptor
前置通知, 后置通知, 环绕通知, 异常抛出通知, 引介通知
AOP在不改变原有代码的方式下,增加新功能
导入maven包:
aspectjweaver: 织入包
2. Spring API接口实现AOP
写好怎删改查的接口和实现类: UserService和UserServiceImpl
写Log类:
public class BeforeLog implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"类的"+method.getName()+"被执行了");
}
}
在beans.xml中注册:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userServiceImpl" class="com.roy.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.roy.log.BeforeLog"/>
<bean id="afterLog" class="com.roy.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.roy.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
-
头文件中添加aop约束
-
aop标签中添加两个元素: 切入点 和 通知
-
测试类中正常调用
其中,切入点的expression是一个表达式,固定为: *表示通配符,..表示任意参数
execution(要执行的位置! (*(修饰词) *(返回值) *(类名) *(方法名) *(参数)))
3. 切面定义实现AOP
缺点:过程中无法添加和上述方法一样的,对业务类的交互(通过反射获取名称等)
写Aspect切面类: 其中包含 Advice通知;对于这部分的定义在xml文件中才说明。
public class AspectLog {
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
}
xml文件中,先注册AspectLog的bean,再定义切面:
<aop:config>
<aop:aspect id="" ref="aspectLog">定义切面(定义切入点,定义通知
<aop:pointcut id="pointcut" expression="execution(* com.roy.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
4. 注解实现AOP
Aspect类:(注解不要导错包,都是Aspectj下的)
@Aspect
public class AnnotationLog {
@Before("execution(* com.roy.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("before");
}
@After("execution(* com.roy.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("after");
}
@Around("execution(* com.roy.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint pj) throws Throwable {
System.out.println("around_Before");
pj.proceed();//表示在这里执行业务的方法
System.out.println("around_After");
}
}
ProceedingJointPoint类:可以通过这个类实现第二种方式中无法和业务类交互的东西:
如: .proced()表示指定业务类实现的位置
.getSignature()表示获取在业务类中正在执行的方法名
.get*, 可以获得更多信息
xml中注册: (注意添加对aspect注解的支持)
<bean id="userServiceImpl" class="com.roy.service.UserServiceImpl"/>
<bean id="annotationLog" class="com.roy.log.AnnotationLog"/>
<aop:aspectj-autoproxy/><!--添加对aspect注解的支持 -->
Spring中动态代理的实现方式:
基于接口的JDK,和基于类的cgLib
当<aop:aspectj-autoproxy proxy-target-class="false"
时,(默认为false, 使用JDK产生代理类
当<aop:aspectj-autoproxy proxy-target-class="true"
时,使用cgLib产生代理类
区别: (存疑)实测目标类没有接口,都能实现
-
当被代理类是通过接口实现的,则用JDK(通过动态代理实现);
-
当被代理类是一个类, CGLIB通过字节码生成子类,继承被代理类来实现代理。
Spring5版本对around、before、after的执行顺序:
around_Before 环绕
before
方法执行
afterReturning
after
around_After 环绕