认识Spring AOP(Aspect Oriented Program)
AOP简介
AOP(面向切面编程)是Spring的一个关键组件,AOP补充了Spring IOC ,为其提供了中间件解决方案,还在数据库事务中被广泛使用。
AOP目的
将核心业务(核心关注点)和周边功能开发(称为横切关注点,例如日志记录、权限认证、事务处理)进行隔离,从而耦合度降低,提高了软件的可重用性和开发效率。
其实AOP的作用就是在不改变代码的情况下,对功能进行了增强,符合开闭原则。
AOP核心概念
- 横切关注点
就是对哪个方法进行拦截,拦截后怎么处理,这些关注点就是横切关注点。 - 连接点(joinpoint)
被拦截到的方法,实际上连接点还可以是字段或者构造器。 - 切入点(Pointcut)
对连接点的拦截进行定义,在哪些方法上进行切入。 - 通知/增强(advice)
就是对拦截到的之后要执行的代码,比如说日志、事务等。通知分为前置、后置、异常、最终、环绕通知五类。 - 切面(aspect)
切入点和通知的组合,就是在什么时候,什么地方,做什么增强。 - 织入(weave)
将切面应用到目标对象中,并创建代理的过程。 - 目标对象
代理的目标对象,就是表现成、业务层、持久层相关代码。 - 代理(Proxy)
最后生成的对象
注解式通知类型
注解 | 说明 | 应用场景 |
---|---|---|
@Before | 前置通知,在连接点之前执行通知,不能阻止连接点之前执行的操作。 | 方法开始时可以进行效验 |
@After | 后置通知,在连接点之后执行通知。 | 修改方法的返回值 |
@Around | 环绕通知,环绕通知可以在方法调用前后完成自定义的行为 | 事务 |
@AfterReturning | 返回通知,在连接点执行正常返回后调用 | 资源释放 |
@AfterThrowing | 异常通知,在某方法抛出异常退出时执行通知 | 包装异常 |
使用注解实现Spring AOP
- 导入依赖的jar包
构建的maven项目,就在pom.xml里面添加
org.springframework
spring-aspects
5.0.5.RELEASE
- 创建一个实现主要业务的类DeptService
@Service //自动注册到Spring容器,不要配置文件定义bean了
public class DeptService {
public void deptAdd(){
System.out.println("添加部门(主要业务)");
}
}
- 创建一个次要业务类LogAspect
@Aspect //只有添加了@Aspect注解的Java类才会被Spring容器解释为InvocationHandler
@Component //把普通的pojo实例化到Spring容器中,相当于配置文件中的
public class LogAspect{
//切入点,"execution(里面配置的是方法的路径)"
@Pointcut("execution(public void com.beans.DeptService.deptAdd())")
public void pointCut(){
}
//前置通知,在连接点方法前调用
@Before("pointCut()")
public void startMethod(){
System.out.println("deptAdd方法将要执行......");
}
//后置通知,在连接点方法后调用
@After(" ()")
public void endMethod(){
System.out.println("deptAdd方法执行完毕......");
}
}
- 写个配置类ApplicationConfig 替换掉XML
//一定要在配置类添加【EnableAspectJAutoProxy】
@EnableAspectJAutoProxy //开启AOP
@ComponentScan(value={"com.beans"}) //定义扫描路径从中找出标识了需要配置的类自动装配到spring的bean容器中
@Configuration//相当于配置文件xml
public class ApplicationConfig {
}
- 开始测试
public class TestMain {
public static void main(String[] args) {
// 1.获得Spring容器对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
DeptService service = (DeptService) context.getBean("deptService");
service.deptAdd();S
context.close();
}
}
运行测试的代码结果如下:
deptAdd方法将要执行......
添加部门(主要业务)
deptAdd方法执行完毕......
使用XML配置文件
Spring配置文件,替换上面的@Configuration
主要业务(DeptService )和次要业务(LogAspect)是怎么关联到一起的
自定义代理模式
Proxy.newInstance(DeptService.class.getClassLoader(),
DeptService.class.getIntgerfaces(),logAspect)
Spring中开启AOP(@EnableAspectJAutoProxy)
- 查看@EnableAspectJAutoProxy源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//AOP具体实现方式,是否创建基于CGLIB代理,而不是java的JDK代理
boolean proxyTargetClass() default false;
//控制代理的暴露方式,解决内部调用不能使用代理的场景,默认为false.
boolean exposeProxy() default false;
}
- 核心是@Import(AspectJAutoProxyRegistrar.class)
@Import():用指定类进行加载,把某一个类(AspectJAutoProxyRegistrar.class)加载到Spring容器中 - 查看这个类AspectJAutoProxyRegistrar.class
发现了AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar 接口,进去看这个接口类,看翻译介绍,原来是个Bean定义注册器,它有权利直接将某个java类注册到Spring容器中。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
重点就在AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);这里可以跟着debug走一遍。
- AnnotationAwareAspectJAutoProxyCreator.class
AnnotationAwareAspectJAutoProxyCreator.class把这个类定义为BeanDefinition放到spring容器中
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
看看下面这个类关系图:
AspectJAwareAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator
AbstractAutoProxyCreator
实现SmartInstantiationAwareBeanPostProcessor接口-->BeanPostProcessor
实现BeanFactoryAware接口 -->Aware
这里可以看下:Spring中的Aware接口
Aware
被【bean后置处理器】拦截的bean对象,只有实现了指定的Aware接口,才可以在初始化过程中得到Spring框架提供内部的资源。
- 核心方法
在 AbstractAutoProxyCreator 代替AnnotationAwareAspectJAutoProxyCreator实现postProcessBeforeInitialization(这里用到了适配器模式) - 依赖知识点
BeanPostProcessor对Spring容器中所有的bean对象的初始化进行拦截。所以【主要业务类】与【次要业务类】绑定关键点就在下面两个方法中。
postProcessBeforeInitialization:在bean对象初始化之前
postProcessAfterInitialization :在bean对象初始化之后,用的是CGLIB代理模式绑定处理。//这里JDK代理需要接口
这里很模糊 断点跟上走走
spring中的BeanPostProcessor
Spring Framework
spring @EnableAspectJAutoProxy背后的那些事(spring AOP源码赏析)