Spring|面向切面编程(AOP)

Spring|面向切面编程(AOP)_第1张图片

认识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

  1. 导入依赖的jar包
    构建的maven项目,就在pom.xml里面添加
    
        org.springframework
        spring-aspects
        5.0.5.RELEASE
    
  1. 创建一个实现主要业务的类DeptService
@Service  //自动注册到Spring容器,不要配置文件定义bean了
public class DeptService {

    public void deptAdd(){
        System.out.println("添加部门(主要业务)");
    }
}
  1. 创建一个次要业务类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方法执行完毕......");
    }
}
  1. 写个配置类ApplicationConfig 替换掉XML
//一定要在配置类添加【EnableAspectJAutoProxy】
@EnableAspectJAutoProxy //开启AOP
@ComponentScan(value={"com.beans"}) //定义扫描路径从中找出标识了需要配置的类自动装配到spring的bean容器中
@Configuration//相当于配置文件xml
public class ApplicationConfig {
  
}
  1. 开始测试
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)

  1. 查看@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;

}
  1. 核心是@Import(AspectJAutoProxyRegistrar.class)
    @Import():用指定类进行加载,把某一个类(AspectJAutoProxyRegistrar.class)加载到Spring容器中
  2. 查看这个类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走一遍。

  1. 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);
    }

看看下面这个类关系图:


Spring|面向切面编程(AOP)_第2张图片

AspectJAwareAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator
AbstractAutoProxyCreator
实现SmartInstantiationAwareBeanPostProcessor接口-->BeanPostProcessor
实现BeanFactoryAware接口 -->Aware
这里可以看下:Spring中的Aware接口
Aware
被【bean后置处理器】拦截的bean对象,只有实现了指定的Aware接口,才可以在初始化过程中得到Spring框架提供内部的资源。

  1. 核心方法
    在 AbstractAutoProxyCreator 代替AnnotationAwareAspectJAutoProxyCreator实现postProcessBeforeInitialization(这里用到了适配器模式)
  2. 依赖知识点
    BeanPostProcessor对Spring容器中所有的bean对象的初始化进行拦截。所以【主要业务类】与【次要业务类】绑定关键点就在下面两个方法中。
    postProcessBeforeInitialization:在bean对象初始化之前
    postProcessAfterInitialization :在bean对象初始化之后,用的是CGLIB代理模式绑定处理。//这里JDK代理需要接口

这里很模糊 断点跟上走走
spring中的BeanPostProcessor
Spring Framework
spring @EnableAspectJAutoProxy背后的那些事(spring AOP源码赏析)

你可能感兴趣的:(Spring|面向切面编程(AOP))