擴展Spring切面


概述

Spring的切面(Spring動態代理)在Spring中應用十分廣泛,例如還有事務管理,重試等等。網上介紹SpringAop源碼很多,這里假設你對SpringAop有基本的了解。如果你認為Spring代理類會創建多重代理,那說明你真的沒了解。

需求背景

假設我現在想提供一個jar包,這個jar包會攔截制定注解方法,並做一些記錄。這里要分析一下具體需求

攔截的注解是在方法上

如果注解是放在方法上,那么我們完全可以使用SpringAop的方式,通過自己定義一個注解,並對該注解進行攔截即可。這個不是本篇文章的重點,攔截制定注解的很簡單。

攔截的注解是在類上

如果我們需要攔截的注解是類上,這里首先介紹第一個接口

Advisor

public interface Advisor {
    Advice getAdvice();
    boolean isPerInstance();
}

 

這個接口簡單來說就是持有一個通知(Advice),但是一般不直接使用這個接口,因為這個對通知沒有一個有效的過濾(當然如果你想對所有方法都進行攔截),所以一般是使用PointcutAdvisor,

public interface PointcutAdvisor extends Advisor {
    Pointcut getPointcut();
}

 

這個接口就可以通過Pointcut做一些限制。為什么呢? 直接看AopUtils#findAdvisorsThatCanApply方法,這個方法就是從所有Advisor接口實現Bean選擇對某個Class有效。

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        //!!!有興趣的可以重點看一下重點看這里實現,就不大段貼代碼了
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
    }

 

好了,我們知道了,要想讓我們的自定義的切面生效,首先,

第一步,創建一個Advisor
public class MyAdvisor implements PointcutAdvisor {

    //這個是Spring提供的一個簡單的通過注解來獲取切點類
    AnnotationMatchingPointcut annotationMatchingPointcut = new AnnotationMatchingPointcut(MyAop.class);
    //這個是我們自己實現的繼承於Advice,
    MyAdvice myAdvice =new MyAdvice();

    @Override
    public Pointcut getPointcut() {
        return annotationMatchingPointcut;
    }

    @Override
    public Advice getAdvice() {
        return myAdvice;
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
    
    //簡單起見,就直接使用MethodInterceptor
    class MyAdvice implements MethodInterceptor{

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("haha");
            return invocation.proceed();
        }
    }

 

第二步,定義一個開關注解,讓我們的自定義切面生效

創建一個開關注解,就是一個套路,先創建一個開關注解,@import我們一個Bean注冊類

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImporter.class)
public @interface EnableMyAop {
}

 

在Bean注冊類中把我們的MyAdvisor注冊進去吧

public class MyImporter implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        register(registry,MyAdvisor.class);
    }

    private void register(BeanDefinitionRegistry registry, Class<?> aopBeanFactoryPostClass) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(aopBeanFactoryPostClass);
        registry.registerBeanDefinition(aopBeanFactoryPostClass.getSimpleName(),rootBeanDefinition);
    }
}

 至此,只要使用MyAop注解的類,在調用他的任何方法的時候(類內部調用不算),都會進入到我們的切面中。

小結

本篇文章是簡單看了一下Spring Aop源碼后,嘗試自己進行擴展,因此可能不是最優的方法。不過希望能起到拋磚迎玉作用,在其基礎上可以做很多有很意思的事情。如果有興趣可以進一步研究spring自己的Advice,Pointcut的實現類。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM