天天看點

SpringAOP使用及源碼分析(SpringBoot下)

SpringAOP使用及源碼分析(SpringBoot下)

一、SpringAOP應用

先搭建一個SpringBoot項目

<?xml version="1.0" encoding="UTF-8"?>

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mmc</groupId>
<artifactId>springboot-study</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-study</name>
<description>Demo project for Spring Boot</description>

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
           

定義一個業務邏輯類,作為切面

public interface CalculationService {

/**
 * 加法運算
 * @param x
 * @param y
 * @return
 */
public Integer add(Integer x,Integer y);           

}

/**

  • @description:
  • @author: mmc
  • @create: 2020-06-01 14:22

    **/

@Service

public class CalculationServiceImpl implements CalculationService {

@Override
public Integer add(Integer x, Integer y) {
    if(x==null||y==null){
        throw  new NullPointerException("參數不能為空");
    }
    return x+y;
}           

定義一個切面類,添加通知方法

前置通知(@Before):logStart:在目标方法(div)運作之前運作

後置通知(@After):logEnd:在目标方法(add)運作結束之後運作(無論方法正常結束還是異常結束)

傳回通知(@AfterReturning):logReturn:在目标方法(add)正常傳回之後運作

異常通知(@AfterThrowing):logException:在目标方法(add)出現異常以後運作

環繞通知(@Around):動态代理,手動推進目标方法運作(joinPoint.procced())

  • @description: 切面類
  • @create: 2020-06-01 14:24

@Aspect

@Component

public class LogAspects {

//抽取公共的切入點表達式
//1、本類引用
//2、其他的切面引用
@Pointcut("execution(public Integer com.mmc.springbootstudy.service.CalculationService.*(..))")
public void pointCut(){};

@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
    Object[] args = joinPoint.getArgs();
    System.out.println(""+joinPoint.getSignature().getName()+"運作。。。@Before:參數清單是:{"+Arrays.asList(args)+"}");
}

@After("pointCut()")
public void logEnd(JoinPoint joinPoint){
    System.out.println(""+joinPoint.getSignature().getName()+"結束。。。@After");
}
           
//JoinPoint一定要出現在參數表的第一位
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
    System.out.println(""+joinPoint.getSignature().getName()+"正常傳回。。。@AfterReturning:運作結果:{"+result+"}");
}

@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
    System.out.println(""+joinPoint.getSignature().getName()+"異常。。。異常資訊:{"+exception+"}");
}           

寫一個controller測試

@RequestMapping("/testaop")

@ResponseBody

public Integer testaop(Integer x,Integer y){
   Integer result = calculationService.add(x, y);
   return result;           

測試

add運作。。。@Before:參數清單是:{[2, 3]}

add結束。。。@After

add正常傳回。。。@AfterReturning:運作結果:{5}

二、源碼分析

主線流程圖:

spring.factories檔案裡引入了AopAutoConfiguration類

@Configuration

@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })

@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)

public class AopAutoConfiguration {

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
//看配置檔案,如果配置的spring.aop.proxy-target-class為false則引入JdkDynamicAutoProxyConfiguration
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
        matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {

}

@Configuration
//開啟AspectJAutoProxy
@EnableAspectJAutoProxy(proxyTargetClass = true)
//看配置檔案,如果配置的spring.aop.proxy-target-class為true則引入CglibAutoProxyConfiguration 
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
        matchIfMissing = true)
public static class CglibAutoProxyConfiguration {

}
           

在包目錄下找到配置檔案,并且發現他的值為true

在上面的方法上有EnableAspectJAutoProxy注解,并傳入了proxyTargetClass=true

進入@EnableAspectJAutoProxy注解

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

//引入了AspectJAutoProxyRegistrar

@Import({AspectJAutoProxyRegistrar.class})

public @interface EnableAspectJAutoProxy {

boolean proxyTargetClass() default false;

boolean exposeProxy() default false;           

進入AspectJAutoProxyRegistrar類

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

AspectJAutoProxyRegistrar() {
}

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

}           

進入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法裡面

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {

return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}           

可以看到傳回了一個BeanDefinition,裡面的BeanClass類型是AnnotationAwareAspectJAutoProxyCreator,這個類看名字是一個AOP的動态代理建立類,裡面沒有啥可疑的方法。在IDEA裡按Ctrl+H看他的繼承結構。有一個父類AbstractAutoProxyCreator,這個類實作了BeanPostProcessor接口。這個接口是Bean的擴充接口,在bean初始化完成後會調用到他的postProcessAfterInitialization(Object bean, String beanName)方法。

方法内容如下

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {

if (bean != null) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            //如果有必要,進行包裝  
            return this.wrapIfNecessary(bean, beanName, cacheKey);
        }
    }

    return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
    //擷取切面的方法,第9點那裡展開讨論
        Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //建立動态代理
            Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    } else {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
}           

可以看出這裡已經在開始建立動态代理了

protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);
    }
    //動态代理工廠
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
    if (!proxyFactory.isProxyTargetClass()) {
        if (this.shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        } else {
            this.evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
    //切面那裡的方法
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    this.customizeProxyFactory(proxyFactory);
    proxyFactory.setFrozen(this.freezeProxy);
    if (this.advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    //擷取動态代理類
    return proxyFactory.getProxy(this.getProxyClassLoader());
}           

學過AOP的人都知道動态代理的方式有兩種,一種JDK代理,一種CGLIB動态代理。那麼Spring裡面是怎麼選擇的呢?答案就在這裡:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {

// 1.config.isOptimize()是否使用優化的代理政策,目前使用與CGLIB

// config.isProxyTargetClass() 是否目标類本身被代理而不是目标類的接口
    // hasNoUserSuppliedProxyInterfaces()是否存在代理接口

    if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
        return new JdkDynamicAopProxy(config);
    } else {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
        } else {
            //目标類不是接口或不是代理類就使用cglib代理
            return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
        }
    }
}           

Cglib的代理類是CglibAopProxy、ObjenesisCglibAopProxy,JDK的代理類是JdkDynamicAopProxy。在這些類裡面對目标類進行了代理,在執行方法的時候就是執行的代理類的方法,而實作了切面程式設計的效果。

主線流程就是這些了,還有一個沒說的就是我們如何擷取的切面方法,@Before("pointCut()")這些注解又是如何生效的?再回到AbstractAutoProxyCreator的wrapIfNecessary()方法

裡面有這句代碼:

Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);

@Nullable

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);
    return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    //查找候選的要切面附加的方法,這裡加進去的
    List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    this.extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
    }

    return eligibleAdvisors;
}


           

他會找到Aspect類,然後周遊裡面的方法,并擷取Pointcut,然後構造出Advisor,加入到集合List advisors裡,供動态代理時使用

原文位址

https://www.cnblogs.com/javammc/p/13028129.html