spring學習三-AOP之面向切面編程

目錄

 

1 AOP的使用

1.1 沒有異常通知

1.2 有異常通知

2 AOP源碼簡析

2.1 從EnableAspectJAutoProxy註解說起

2.2 導入的AspectJAutoProxyRegistrar類

2.3 AnnotationAwareAspectJAutoProxyCreator類的作用

2.4 創建代理的方法


1 AOP的使用

AOP有如下幾種通知
@Before::前置通知,在方法執行之前執行
@After:後置通知,在方法執行之後執行 
@AfterRunning:返回通知,在方法返回結果之後執行
@AfterThrowing:異常通知,在方法拋出異常之後
@Around:環繞通知,圍繞着方法執行

1.1 沒有異常通知

案例模型:用戶登錄
測試環境
LoggingAspect
//當有多個切面的時候,可以用Order註解來設置切面優先級。值越小,優先級越高。

@Order(1)
@Aspect
@Component
public class LoggingAspect {
/*聲明該方法是一個前置通知。這裏可以用通配符替代void、BaseServiceImpl、login,
    匹配特定返回類型、特定類、特定類名的方法。
    不管方法有沒有異常都會執行*/
    @Before("execution(* com.hwl.aspectj.BaseServiceImpl.*())")
    public void beforeMethod(JoinPoint point){
        System.out.println("前置通知:" + point.getSignature().getName()+"方法執行前執行");
    }
    
    /*聲明方法是一個後置通知。
    不管方法有沒有異常都會執行*/
    @After("execution(* com.hwl.aspectj.BaseServiceImpl.*())")
    public void afterMethod(JoinPoint point){
        System.out.println("後置通知:" + point.getSignature().getName()+"方法執行後執行");
    }
    
    @AfterReturning ("execution(* com.hwl.aspectj.BaseServiceImpl.*())")
    public void afterMethodRunning(JoinPoint point){
        System.out.println("返回通知:" + point.getSignature().getName()+"方法返回結果後執行");
    }

    //環繞通知:需要攜帶ProceedingJoinPoint類型的參數,可以決定是否執行目標方法。
    //環繞通知還需要捕獲異常。
    @Around("execution(String com.hwl.aspectj.BaseServiceImpl.login())")
    public void aroundMethod(ProceedingJoinPoint point){
        //執行目標方法
        try {
            System.out.println("環繞通知:" + point.getSignature().getName()+"方法執行前執行");
            point.proceed();
            System.out.println("環繞通知:" + point.getSignature().getName()+"方法執行後執行");
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("異常通知");
        }
    }
}

BaseServiceImpl:

@Component
public class BaseServiceImpl implements BaseService{

    @Override
    public String login() {
        // TODO Auto-generated method stub
        System.out.println("登錄……");
        return "用戶";
    }
}

測試代碼:

@Autowired
private BaseServiceInter baseService;

@Test
public void testLogin() {
    baseService.login();
}

輸出:
環繞通知:login方法執行前執行
前置通知:login方法執行前執行
登錄……
環繞通知:login方法執行後執行
後置通知:login方法執行後執行
返回通知:login方法返回結果後執行

1.2 有異常通知

在上述基礎上
LoggingAspect類中增加異常通知:

    /*
     * 將 throwing 屬性添加到 @AfterThrowing 註解中, 也可以訪問連接點拋出的異常。
     * Throwable 是所有錯誤和異常類的超類. 所以在異常通知方法可以捕獲到任何錯誤和異常.
     * 如果只對某種特殊的異常類型感興趣, 可以將參數聲明爲其他異常的參數類型。
     * 然後通知就只在拋出這個類型及其子類的異常時才被執行
     */
@AfterThrowing(value = "execution(void com.hwl.aspectj.BaseServiceImpl.testException())", throwing = "e")
public void afterThrowing(JoinPoint point ,Exception e){
        System.out.println(point.getSignature().getName()+"方法發生" + e.getMessage() + "異常後執行");
}

BaseServiceImpl中增加異常測試代碼:

@Override
public void testException() {
    System.out.println("方法發生異常");
    int i = 9/0;
}

測試代碼:

@Autowired
private BaseService baseService;

@Test
public void testException() {
    baseService.testException();
}

輸出:
前置通知:testException方法執行前執行
方法發生異常
後置通知:testException方法執行後執行
異常通知:testException方法發生/by zero異常後執行
由此可見前置通知和後置通知不管方法有沒有異常都會執行。返回通知在方法發生異常不執行。由於環繞通知會捕獲異常,故兩者一般不共用。

2 AOP源碼簡析

2.1 從EnableAspectJAutoProxy註解說起

首先看一下EnableAspectJAutoProxy註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    /**
     * AOP的具體實現方式,該值默認是false,表示是jdk動態代理。如果爲true,表示用CGLIB基於子類繼承動態代理
     */
    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.2 導入的AspectJAutoProxyRegistrar類

該類重實現了registerBeanDefinitions方法

@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);
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
        @Nullable Object source) {

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

這行代碼主要目的是把AnnotationAwareAspectJAutoProxyCreator這個類註冊到spring容器中

2.3 AnnotationAwareAspectJAutoProxyCreator類的作用

下面我們在看看AnnotationAwareAspectJAutoProxyCreator這個類,它實現了一系列Aware的接口。我們看一看這個類的頂層抽象類AbstractAutoProxyCreator
該類父接口實現了BeanPostProcessor接口,實現了postProcessBeforeInstantiation方法
postProcessBeforeInstantiation方法中和代理相關的代碼如下

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;

可以看到,這裏返回的就是代理對象而不是真正的對象了。

2.4 創建代理的方法

我們再看看createProxy方法,最終會執行
proxyFactory.getProxy(getProxyClassLoader());
接着往下走

public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

先獲取AopProxy,然後創建代理對象
獲取AOPProxy

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        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.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            // 如果代理對象是接口,則返回Jdk動態代理
            return new JdkDynamicAopProxy(config);
        }
        // 否者返回cglib動態代理
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

創建代理對象的時候就是根據不同的代理方式創建了,有興趣的同學可以閱讀一下源碼,筆者之後講動態代理的時候會詳細講jdk動態代理和cglib動態代理的具體實現方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章