AOP【动态代理】
指在程序运行期间动态的讲某段代码切入到指定方法指定位置进行运行的编程方式
1导入AOP模块:Spring AOP:
<dependencies>
<!-- Spring依赖 -->
<!-- 1.Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 2.Spring dao依赖 -->
<!-- spring-jdbc包括了一些如jdbcTemplate的工具类 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 3.Spring web依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 4.Spring test依赖:方便做单元测试和集成测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--5 AOP依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
</dependencies>
2使用案例体会AOP的功能
- 定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前,方法运行结束,方法异常位置,xxxx)
/**
* 业务逻辑类
* @author lzc
* @version 1.0.0
* @ClassName MathCalculator.java
* @Description TODO
* @createTime 2021年04月23日 10:12:00
*/
public class MathCalculator {
/**
* 除法
* @param num1
* @param num2
* @return
*/
public int div(int num1,int num2){
return num1/num2;
}
}
- 定义一个日志切面类(LogAspects),切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行
通知方法:
前置通知(@Before):在目标方法(div)运行之前运行
后置通知(@After):在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning):在目标方法(div)返回之后运行
异常通知(@AfterThrowing):在目标方法(div)出现异常之后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced)
其中需要注意的是切入点:@Pointcut的表达式
格式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
括号中各个pattern分别表示:
修饰符匹配(modifier-pattern?)
返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
类路径匹配(declaring-type-pattern?)
方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set开头的所有方法
参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“”来表示- 匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(…)表示零个或多个任意参数
异常类型匹配(throws-pattern?)
其中后面跟着“?”的是可选项
示例:
1)execution(* (…))
//表示匹配所有方法
2)execution(public * com. savage.service.UserService.(…))
//表示匹配com.savage.server.UserService中所有的公有方法
3)execution(* com.savage.server….(…))
//表示匹配com.savage.server包及其子包下的所有方法
给切面类的目标方法标注何时何地运行
必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
/**
* 日志切面类
* @author lzc
* @version 1.0.0
* @ClassName LogAspects.java
* @Description TODO
* @createTime 2021年04月23日 10:17:00
*/
@Aspect
public class LogAspects {
/**
* 切入点
*/
@Pointcut("execution(public int com.lzc.aop.MathCalculator.*(..))")
public void pointCut(){}
/**
* 前置通知
* joinPoint:切入的一些信息
*/
@Before("pointCut()")
public void before(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
//获取参数列表
Object[] args = joinPoint.getArgs();
System.out.println("切面前置通知@Before,方法名为"+name+"参数列表为"+args[0]);
}
/**
* 后置通知
*/
@After("pointCut()")
public void after(JoinPoint joinPoint){
System.out.println("切面后置通知@After方法名"+joinPoint.getSignature().getName());
}
/**
* 正常返回通知
*/
@AfterReturning(value = "pointCut()",returning = "result")
public void returning(Object result){
System.out.println("切面返回通知@AfterReturning,返回值为"+result);
}
/**
* 异常通知
* @param e
*/
@AfterThrowing(value = "pointCut()",throwing = "e")
public void afterThrowing(Exception e){
System.out.println("切面异常@AfterThrowing,异常信息为"+e);
}
}
- 将切面类和业务逻辑类(目标方法)都加入到容器中
给配置类中加@EnableAspectJAutoProxy注解【开启基于注解的aop模式】
/**
* AOP
*
* @author lzc
* @version 1.0.0
* @ClassName AopConfig.java
* @Description TODO
* @createTime 2021年04月23日 09:22:00
*/
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
@Bean
public MathCalculator mathCalculator(){
return new MathCalculator();
}
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
- 测试查看结果
* @author lzc
* @version 1.0.0
* @ClassName AopTest.java
* @Description TODO
* @createTime 2021年04月23日 10:29:00
*/
public class AopTest {
public static void main(String[] args) {
//获取容器(注解版)
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
//获取MathCalculator业务类
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
//调用div方法
mathCalculator.div(1,1);
//关闭容器
applicationContext.close();
}
}
效果
总结:使用AOP步骤
1 将业务逻辑组件和切面类都加入到容器中:告诉Spring哪个是切面类(@Aspect)
2定义切入点方法通知添加注解通知Spring等
3开启@EnableAspectJAutoProxy注解
源码探究:
使用@EnableAspectJAutoProxy注解开启切面:
1@Import({AspectJAutoProxyRegistrar.class})导入了该组件
AspectJAutoProxyRegistrar该组件注册了一个名字为internalAutoProxyRegister=类型为org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator的组件
2AnnotationAwareAspectJAutoProxyCreator作用:
继承->AspectJAwareAdvisorAutoProxyCreator
->AbstractAdvisorAutoProxyCreator
->AbstractAutoProxyCreator
->ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor,(后置处理器)BeanFactoryAware
查找有关后置处理器和BeanFactoryAware相关的方法:
AbstractAutoProxyCreator.setBeanFactory()方法
AbstractAutoProxyCreator.postProcessBeforeInstantiation()方法处理后置处理器
AbstractAdvisorAutoProxyCreator->setBeanFactory()方法包含initBeanFactory()方法
2AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()具体实现
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
if (this.aspectJAdvisorFactory == null) {
this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
}
this.aspectJAdvisorsBuilder = new AnnotationAwareAspectJAutoProxyCreator.BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}