1. 程式人生 > >Spring AOP之---基於ProxyFactory的類編碼方式和XML配置方式實現AOP

Spring AOP之---基於ProxyFactory的類編碼方式和XML配置方式實現AOP

前一篇文章Spring AOP之—基於JDK動態代理和CGLib動態代理的AOP實現 介紹了AOP的底層實現,基於JDK動態代理和CGLib動態代理。手工編碼的方式很繁瑣,本文介紹通過ProxyFactory和配置的方式實現AOP,方便快捷。

一、Spring支援的5種增強型別

增強是織入目標類連線點上的一段程式程式碼。

  • 前置增強:MethodBeforeAdvice方法執行前實施增強
  • 後置增強:AfterReturningAdvice方法執行後實施增強
  • 環繞增強: MethodInterceptor方法執行前後實施增強
  • 異常丟擲增強: ThrowsAdvice方法丟擲異常後實施增強,最適合的應用場景就是事物管理。
  • 引介增強:IntroductionInterceptor在目標類中新增一些新的方法和屬性

二、類編碼的方式實現增強織入

1、目標類需要實現的介面 — Waiter

package com.mistra.aop.createAdvice;

/**
 * @author Mistra-WangRui
 * @create 2018/3/28 15:50
 * @desc 目標類要實現的介面
 */
public interface Waiter {
    void greetTo(String name);
    void serveTo(String name);
}

2、目標類 — NaiveWaiter

package com.mistra.aop.createAdvice;

/**
 * @author Mistra-WangRui
 * @create 2018/3/28 15:51
 * @desc 目標類
 */
public class NaiveWaiter implements Waiter{
    public void greetTo(String name) {
        System.out.println("greet to  + "+name+"...");
    }

    public void serveTo(String name) {
        System.out.println("serving to  + "
+name+"..."); } }

3、前置增強類 — GreetingBeforeAdvice

package com.mistra.aop.createAdvice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * @author Mistra-WangRui
 * @create 2018/3/28 15:55
 * @desc 前置增強類
 */
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        String clientName = (String)objects[0];
        System.out.println("How are you! Mr."+clientName+".");
    }
}

MethodBeforeAdvice 介面唯一的方法before(),method為目標類的方法,objects為method的入參,o為目標類例項。

4、類編碼方式實現增強織入

package com.mistra.aop.createAdvice;

import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.testng.annotations.Test;

/**
 * @author Mistra-WangRui
 * @create 2018/3/28 15:58
 * @desc 編碼方式實現橫切邏輯增強
 */
public class BeforeAdviceTest {
    @Test
    public void before(){
        Waiter waiter = new NaiveWaiter();
        BeforeAdvice advice = new GreetingBeforeAdvice();
        ProxyFactory pf = new ProxyFactory();//Spring提供的代理工廠
        //pf.setInterfaces(waiter.getClass().getInterfaces());
        //pf.setOptimize(true);
        pf.setTarget(waiter);//設定代理目標
        pf.addAdvice(advice);//為代理目標新增增強
        Waiter proxy = (Waiter)pf.getProxy();//生成代理例項
        proxy.greetTo("古天樂");
        proxy.serveTo("吳彥祖");
    }
}

執行結果:
這裡寫圖片描述

5、ProxyFactory 解析

在BeforeAdviceTest中使用ProxyFactory代理工廠將GreetingBeforeAdvice的增強織入目標類NaiveWaiter中。ProxyFactory 內部就是使用JDK或CGLib動態代理技術將增強應用到目標類中的。
setInterfaces()指定目標介面進行代理,則ProxyFactory使用JDK動態代理(實現類:JdkDynamicAopProxy)技術建立代理。如果針對類的代理,則使用CGLib動態代理(實現類:Cglib2AopProxy)。
setOptimize(true)是啟動優化處理方式,這樣針對介面的代理也會使用CGLib動態代理。
可以為該方法新增多個增強,形成一個增強鏈,它們的呼叫順序和新增順序一致。可以通過addAdvice(int,Advice)方法將增強新增到增強鏈具體位置(第一個位置為0)。

三、Spring配置的方式實現增強織入

包結構目錄放在前面:
這裡寫圖片描述

1、配置檔案 — beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="greetingAdvice" class="com.mistra.aop.createAdvice.GreetingBeforeAdvice"/>
    <bean id="target" class="com.mistra.aop.createAdvice.NaiveWaiter"/>
    <bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="com.mistra.aop.createAdvice.Waiter"
          p:interceptorNames="greetingAdvice"
          p:target-ref="target"
        />
</beans>

ProxyFactoryBean是FactoryBean介面的實現類,FactoryBean負責例項化一個Bean,ProxyFactoryBean負責為其他Bean建立代理例項,內部使用ProxyFactory來完成這項工作,ProxyFactoryBean的幾個常用配置屬性:

  • target:代理的目標物件
  • proxyInterfaces:代理所需實現的介面,可以是多個
  • interceptorNames:需要織入目標物件的Bean列表,配置中的順序對應呼叫的順序
  • singleton:返回的代理例項是否是單例項,預設為單例項
  • optimize:設定為true時,強制使用CGLib動態代理(對於singleton的代理用CGLib,其他作用域的用JDK),CGLib代理時速度慢,但是建立的代理物件執行效率高,JDK反之
  • proxyTargetClass:是否對類進行代理,設定為true時使用CGLib代理

2、配置方式實現橫切邏輯增強 — BeforeAdviceTest2

package com.mistra.aop.createAdvice;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;

/**
 * @author Mistra-WangRui
 * @create 2018/3/29 11:11
 * @desc 配置方式實現橫切邏輯增強
 */
public class BeforeAdviceTest2 {
    @Test
    public void test(){
        String configPath = "com.mistra/aop/createAdvice/beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter");
        waiter.greetTo("古天樂");
    }
}

本文只介紹了前置增強,其他的增強都大同小異。
本文這樣實施增強的話是把增強織入了目標類的所有方法中,呼叫目標類的所有方法時都會執行增強邏輯程式碼。有時候希望有選擇的把增強織入目標類的特定方法中,就需要使用切點進行目標類連線點的定位。

四、Spring AOP其他

1、定義切面Advisor:有選擇性的織入增強到某些類的某些方法

2、自動建立代理:上文都是通過ProxyFactoryBean建立的目標類代理,Spring AOP通過BeanPostProcessor可以自動為目標類建立代理

3、無法實現增強的問題:在JDK動態代理中通過介面來實現方法攔截,必須確保要攔截的目標方法在介面中有定義。在CGLib動態代理中通過動態代理子類來實現方法攔截,必須確保要攔截的目標方法可被子類訪問,也就是目標方法不能被定義為final。

五、基於@AspectJ和Schema的AOP

@AspectJ和Schema都是實現AOP的方式,@AspectJ支援編碼的方式和配置的方式實現AOP,Schema就是運用
< aop:config>< /aop:config>標籤配置實現AOP。難點就是@AspectJ的語法基礎和其中的配置切點表示式函式吧。無論用哪種方式實現AOP,底層原理都是運用JDK或者是CGLib動態代理技術。在執行期動態生成目標類的代理類實現的。