spring AOP 程式設計式應用

weixin_33895657發表於2018-07-15

一、spring AOP 和 AspectJ

spirng AOP: 是基於物件代理模式進行織入邏輯程式碼,但對於static和final方法上應用織入是無法做到的
AspectJ :其是一個獨立的在編輯期進行邏輯程式碼的織入框架,不受限於必須獲取代理

二、AOP織入

1. Advice、Pointcut、Advisor、ProxyFactory

Advice (通知)是AOP聯盟定義的一個介面,描述了連線點做什麼,為切面增強提供織入介面。編寫邏輯程式碼的基類
Pointcut (切點)決定Advice通知應該作用於哪個連線點。也就是說這些連線點集合(決定切入的方法)是通過Pointcut來決定的
Advisor (通知器)是對Advice和Pointcut的整合,作為IoC容器使用AOP的基礎設施。所有
ProxyFactory(生成代理物件的工廠類)可以讓我們通過程式設計式的方法實現spirng AOP功能
在spring中有三個代理工廠類,除了上面提到的還有ProxyFactoryBean(宣告式應用)、AspectJProxyFactory(AspectJ應用),它們都繼承了ProxyCreatorSupport類

2. 應用演示

目標類,提供切入點

class TargetImpl {
    public void print() {
        System.out.println("this is target");
    }
}
MethodInterceptor

MethodInterceptor是一個最基礎的繼承自->Interceptor->Advice的通知類。ProxyFactory生成bean代理有jdk proxy和Cglib兩種方式,但spirng把它們的回撥都統一封裝,回撥執行MethodInterceptor的invoke方法
具體封裝在了ReflectiveMethodInvocation裡,執行process來調起invoke

    private static void basics() {

        Advice advice = (MethodInterceptor) (invocation) -> {
            System.out.println("enter advice->interceptor->methodInterceptor");
            return invocation.proceed();
        };


        Pointcut pointcut = new Pointcut() {
            @Override
            public ClassFilter getClassFilter() {
                return ClassFilter.TRUE;
            }

            @Override
            public MethodMatcher getMethodMatcher() {
                return MethodMatcher.TRUE;
            }
        };
        Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

        ProxyFactory proxyFactory = new ProxyFactory(new TargetImpl());
        proxyFactory.addAdvisor(advisor);

        TargetImpl target = (TargetImpl) proxyFactory.getProxy();
        target.print();
    }

程式碼中出現的DefaultPointcutAdvisor是spirng提供的一個預設通知器

MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor
  private static void before() {
        Advice advice = new MethodBeforeAdviceInterceptor((method, objects, o) -> System.out.println("enter before advice"));
        ProxyFactory proxyFactory = new ProxyFactory(new TargetImpl());
        proxyFactory.addAdvice(advice);

        TargetImpl target = (TargetImpl) proxyFactory.getProxy();
        target.print();

    }

   private static void after() {

        Advice advice = new AfterReturningAdviceInterceptor((rv, method, objects, o) -> System.out.println("enter after advice"));
        ProxyFactory proxyFactory = new ProxyFactory(new TargetImpl());
        proxyFactory.addAdvice(advice);

        TargetImpl target = (TargetImpl) proxyFactory.getProxy();
        target.print();


    }

ProxyFactory雖然可以直接新增Advice,但這並不表示它不需要Ponitcut的實現。閱讀add方法原始碼可以看到它會生成一個預設的DefaultPointcutAdvisor新增在addAdvisor列表裡

@Override
    public void addAdvice(int pos, Advice advice) throws AopConfigException {
        Assert.notNull(advice, "Advice must not be null");
        if (advice instanceof IntroductionInfo) {
            // We don't need an IntroductionAdvisor for this kind of introduction:
            // It's fully self-describing.
            addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
        }
        else if (advice instanceof DynamicIntroductionAdvice) {
            // We need an IntroductionAdvisor for this kind of introduction.
            throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
        }
        else {
            addAdvisor(pos, new DefaultPointcutAdvisor(advice)); //建立預設Advisor
        }
    }
AspectJExpressionPointcut

spring通過AspectJExpressionPointcut來定義實現AspectJ中的切點語言

 private static void aspectjBefore() {
        TargetImpl target = new TargetImpl();
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution( * com.programmatic.springprogrammatic.TargetImpl.print(..) )");
        Advice advice = new MethodBeforeAdviceInterceptor(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.println("enter before advice");
            }
        });

        Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
        ProxyFactory proxyFactory = new ProxyFactory(target);
        proxyFactory.addAdvisor(advisor);


        TargetImpl proxyTarget = (TargetImpl) proxyFactory.getProxy();
        proxyTarget.print();

    }

結語

spring AOP可以說是通過兩個流程來實現對程式碼的切入和執行邏輯程式碼。
(1)通知器的裝配;spring AOP會把要切入點的位置和邏輯程式碼封裝為一個標準呼叫流程。
(2)生成代理類的時候注入通知器;這樣代理類在回撥方法時就會執行切入邏輯程式碼了。
程式設計式的實現spring AOP是最基礎的運用,它可以更好的使我們看到實現spring AOP裝配過程,為更好的理解spring AOP作為一個基礎篇

github地址:https://github.com/lotusfan/spring-programmatic ->ProxyFactoryTest

相關文章