AOP(面向切面编程)

本文深入解析了Spring的AOP(面向切面编程)原理,包括使用Aop对Bean增强的步骤、@EnableAspectJAutoProxy注解的作用及内部实现,以及程序启动时Bean的创建和注册过程。通过对AOP的理解,可以更好地掌握Spring框架如何实现非功能代码的动态代理,提高代码的可读性和维护性。
摘要由CSDN通过智能技术生成

1、概述

  我们在编写代码时,只想关注逻辑代码的实现。但是我们的程序是要放到服务器上去跑的,我们在代码中如果只有业务逻辑代码。虽然代码简洁,但是万一程序在服务器跑的时候数据出现问题时,没办法快速定位到问题所在。但是将日志等非逻辑代码和我们的逻辑代码放在一起,会使得代码冗余,代码的可读性难度加大。
  基于上面的问题,Spring为我们提供了一种解决方案,将业务功能逻辑代码和非功能代码分开来写,然后使用动态代理,将非功能性代码嵌入到功能代码中,这就是所谓的AOP(面向切面编程)。

1.1、使用Aop对Bean增强的步骤。

  1、我们要使用Aop对我们的bean进行增强,我们要先导aspects包(我这里用的是SpringBoot)

        <!--这个包用来配置增强类(非业务逻辑类)-->
       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.20</version>
        </dependency>

  2、编写业务逻辑类

@Slf4j
@Component
public class Function {
   
    /**
     *
     * @param name 执行人
     * @param taskName 任务名
     * @return
     */
    public int add(int a, int b) {
   //业务逻辑代码
        return a+b;
    }
}

  3、编写增强类代码,并通过切入点表达式对业务逻辑代码(指定到方法)进行增强

@Component
@Slf4j
@Aspect//告诉spring这是一个切面增强类
public class LogAop {
   
    /**
     * 切入点表达式:指定要增强的方法
     * 切入点表达式格式: 访问修饰符(可省略)  返回值类型 方法的类路径.方法名(参数类1,参数类型2)
     * 【*代表所有,方法中的..代表任意参数】
     */
    @Pointcut("execution(public * com.tellhow.review.Aop.Function.*(..))")
    public void pointcut() {
   
    }

    @Before("pointcut()")//前置通知
    public void testbefore() {
   
        log.info("前置通知,去青罗司接任务" );
    }
    @After("com.tellhow.review.Aop.LogAop.pointcut()")//后置通知
    public void testAfter(){
   
        log.info("后置通知"+"任务完成,通知青罗司验收");
    }

    /**
     * 发生异常,走异常通知后也会走后置通知
     * @AfterReturning的属性信息 1、value指定切入点表达式。2、returning将目标方法的返回值与通知方法的参数绑定
     * @param joinPoint 切入点信息(包括方法名、参数等信息)
     */
    @AfterReturning(value = "execution(public * com.tellhow.review.Aop.Function.GhostInfestation(String,String))",returning = "result")//返回通知
    public void testReturn(JoinPoint joinPoint,String result){
   
        String name = joinPoint.getSignature().getName();//获取方法名
        log.info("返回通知"+"返回值是:"+result);
    }
    /**
     * JoinPoint一定要放参数表的第一位
     * @AfterThrowing的 value属性指定切入点表达式 throwing属性将抛出的异常绑定到的通知签名中的参数的名称
     * @param exception
     */
    @AfterThrowing(value="pointcut()",throwing = "exception")//异常通知
    public void testException(JoinPoint joinPoint,Exception exception){
   
        log.info("异常通知");
    }

    /**
     *
     * @param proceedingJoinPoint procedingjoinpoint公开proceed(..)方法,以支持@AspectJ方面的around通知,proceedingJoinPoint.proceed(args)可以修改参数的值
     * @throws Throwable
     */
    @Around(value="pointcut()")//环绕通知
    public void testAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
   
        log.info("前置环绕通知");
        Object[] args = proceedingJoinPoint.getArgs();//可以动态修改参数的值
        proceedingJoinPoint.proceed(args);
        log.info("后置环绕通知");
    }
}

4、在配置类上使用@EnableAspectJAutoProxy注解开启对切面的支持

@EnableAspectJAutoProxy//@EnableAspectJAutoProxy开启切面代理功能,切面的底层就是代理
@Configuration
@ComponentScan(value = "com.tellhow.review.Aop")
@Slf4j
public class AopConfguration {
   
    public static void main(String[] args) {
   
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfguration.class);
        Function bean = context.getBean(Function.class);
        bean.GhostInfestation("陈兴","除掉伥鬼");
       log.info(bean+"");
    }
}

2、原理解析

  上面我们讲过,我们要用Aop,一定要在再配置类上添加@EnableAspectJAutoProxy注解。我们来看一下这个注解做了些什么,以及怎么起作用的。

2.1、@EnableAspectJAutoProxy做了什么

  1、进入到@EnableAspectJAutoProxy注解,发现它用@Import注解导入了一个bean【AspectJAutoProxyRegistrar】到容器中。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   
	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;
	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;
}

  2、之前我们了解过@Import注解可以为我们导入配置类、普通类、ImportBeanDefinitionRegistrar【bean的定义信息】、以及ImportSelector类型的组件。由于AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口。所以这里的话,属于使用@Import注解导入一个ImportBeanDefinitionRegistrar类型的组件。
  3、ImportBeanDefinitionRegistrar接口有一个registerBeanDefinitions()方法,用来注册bean的定义信息。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
   
	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@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);
			}
		}
	}
}

  4、调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)方法,这里才是注册bean的定义信息。registerAspectJAnnotationAutoProxyCreatorIfNecessary()->registerOrEscalateApcAsRequired()->

	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
   
		  return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

//一直在这里才看到注册了AnnotationAwareAspectJAutoProxyCreator的定义信息。
 private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值