1. 程式人生 > >Spring面向切面編程

Spring面向切面編程

spring

AOP面向切面編程,可以說是oop的完善補充,眾所周知,面向對象編程特點是封裝繼承和多態,構建了一種對象層次結構,可以很好的組織代碼,通過繼承關系實現代碼重用,但程序中總會有以一些重復的代碼不太方便使用繼承將他們重用和管理起來,這些代碼的功能重復且需要用在不同的地方,雖然可以將它封裝成公共函數,但在這種顯示調用中並不是很方便。

AOP能夠將重復的代碼抽取出來單獨維護,與設計公共子模塊相比,公共子模塊要調用除了直接硬調用外沒有其他辦法,而AOP處理這一類問題提供了靈活多樣的方法。

AOP把軟件系統分為兩個部分:核心關註點和橫切關註點。業務處理的主要流程是核心關註點,與之關系不大的部分是橫切關註點。橫切關註點的一個特點是,他們經常發生在核心關註點的多處,而各處都基本相似。

比如權限認證、日誌、事務處理。Aop 的作用在於分離系統中的各種關註點,將核心關註點和橫切關註點分離開來。

AOP使用場景:

AOP用來封裝橫切關註點,具體可以在下面的場景中使用:

Authentication 權限

Caching 緩存

Context passing 內容傳遞

Error handling 錯誤處理

Lazy loading 懶加載

Debugging  調試

logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準

Performance optimization 性能優化

Persistence  持久化

Resource pooling 資源池

Synchronization 同步

Transactions 事務

AOP的相關一些概念:

方面(aspect):一個關註點的模塊化,這個關註點的實現可能另外橫切多個對象,比如事務管理。方面用Springadvidor或攔截器實現。

連接點(joinpoint):程序執行過程中明確的點,比如方法的調用或者特定的異常被拋出。

通知(advice):在特定的連接點,AOP框架執行的動作。

切入點(pointcut):指定一個通知將被引發的一系列連接點的集合。

引入(introduction):添加方法或字段到被通知的類。

目標對象(target object):包含連接點的對象,也稱作被通知或被代理的對象。Pojo

Aop代理(aop proxy):aop框架創建的對象,包含通知,在spring中,aop代理通常是jdk動態代理或者cglib代理。

織入(weaving):組裝方面來創建一個被通知對象。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。

spring如何使用aop:

一、直接使用ProxyFactory來以編程方式使用spring aop,通過proxyFactory提供的方法可以設置target對象,最終通過getProxy方法獲取代理對象。這裏不做講解。

二、配置XML文件,方式有四種:

1、配置ProxyFactoryBean,顯式地設置advisors,advice, target

2、配置AutoProxyCreator,這種方式下,還是如以前一樣使用定義的bean,但是從容器中獲得的其實已經是代理對象

3、通過<aop:config>來配置

4、通過<aop: aspectj-autoproxy>來配置,使用AspectJ的註解來標識通知及切入點

Xml通過<aop:config>實現示例:

依賴Spring-context會引入core beans context aoplliance aop commons-logging expression要使用aop還需要引入aspectjweaver包。

定義接口:

public interface You {
    voidbuyApple();
}

實現類:

public class YouImpl implements You {
    public void buyApple() {
      System.out.println("買蘋果!");
    }
}

橫切關註點:

public class Me {
    public void beatYou(){
      System.out.println("揍你!");
  }
  public void giveYouCandy(){
    System.out.println("給你糖!");
  }
}

配置文件:

<?xml version = "1.0" encoding= "UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
 
   <bean id="youImpl"class="com.gg.test.aop.YouImpl"></bean>
   <bean id="me"class="com.gg.test.aop.Me"></bean>
   
   <aop:config>
        <aop:aspect id="me"ref="me">
          <aop:pointcutid="addMethod" expression="execution(*com.gg.test.aop.You.*(..))" />
            <aop:before method="beatYou"pointcut-ref="addMethod" />
          <aop:after method="giveYouCandy"pointcut-ref="addMethod" />
      </aop:aspect>
   </aop:config>
</beans>

測試:

public class Test {
    public static void main(String[] args) {
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aopdemo.xml");
    You you = (You)applicationContext.getBean("youImpl");
      you.buyApple();
  }
}

結果:

技術分享

通過配置ProxyFactoryBean實現示例:

實現前置增強接口:

public class HijackBefore implementsMethodBeforeAdvice{
    public void before(Method method, Object[] args, Object target)throws Throwable {
      System.out.println("揍你!");
    }
}

配置文件:

<?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:aop="http://www.springframework.org/schema/aop"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
 
   <bean id="youImpl"class="com.gg.test.aop.YouImpl"></bean>
   
   <!-- 定義劫持類的bean -->
   <bean id="hijackBeforeBean"class="com.gg.test.aop.HijackBefore" />
 
   <!--創建代理 -->
   <bean id="youProxy"class="org.springframework.aop.framework.ProxyFactoryBean">
     <property name="target" ref="youImpl" />
     <property name="interceptorNames">
         <list>
             <value>hijackBeforeBean</value>
         </list>
     </property>
   </bean>
</beans>

測試:

public class Test {
    public static void main(String[] args) {
      ApplicationContext applicationContext = newClassPathXmlApplicationContext("aopdemo.xml");
        You youProxy = (You)applicationContext.getBean("youProxy");
      youProxy.buyApple();
  }
}

測試結果:

技術分享

通過advisor實現示例:

如果需求改為:你去買蘋果。我在路上揍你,買香蕉時不揍你。

通知AdviceSpring提供的一種切面(Aspect)。但其功能過於簡單,只能

將切面織入到目標類的所有目標方法中,無法完成將切面織入到指定目標方法中。

顧問AdvisorSpring提供的另一種切面。其可以完成更為復雜的切面織入功能。PointcutAdvisor是顧問的一種,可以指定具體的切入點。顧問將通知進行了包裝,會根據不同的通知類型,在不同的時間點,將切面織入到不同的切入點。

public interface You {
    void buyApple();
  void buyBunana();
}
public class YouImpl implements You {
    public void buyApple() {
      System.out.println("買蘋果!");
  }
 
  public void buyBunana() {
    System.out.println("買香蕉!");
  }
}

實現前置增強接口:

public classHijackBefore implements MethodBeforeAdvice{
    public void before(Method method,Object[] args, Object target)throws Throwable {
  System.out.println("揍你!");
  }
}

配置文件:

<!--1.*:NameMatchMethodPointcutAdvisor 名稱匹配方法切入點顧問 -->
    <!-- 切面:顧問 顧問(Advisor)要包裝通知(Advice)-->   
    <bean id="beforeAdvisor"class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice"ref="beforeAdvice"></property>
        <!--指定需要增強的方法:這裏是buyApplet()方法,而buyBunana()方法則不會增強   -->
        <propertyname="mappedName" value="buyApple"></property>
        <!-- 也可以使用mappedNames指定多個方法
        <propertyname="mappedNames" value="doFirst,doSecond"></property>
        -->
    </bean>
       
    <!--*********************************************** -->
 
    <!-- 2.*:RegexpMethodPointcutAdvisor 正則表達式匹配方法切入點顧問 -->
    <!-- 切面: 顧問 顧問(Advisor)要包裝通知(Advice) -->
    <!-- <bean id="beforeAdvisor"
       class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice"ref="beforeAdvice"></property>
        <property name="pattern"value=".*doF.*t"></property>
        </bean> -->

測試:

public class Test{
    public static void main(String[] args){
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aopdemo.xml");
      You youProxy =(You)applicationContext.getBean("youProxy");
      youProxy.buyApple();
      youProxy.buyBunana();
  }
}

測試結果:

技術分享

總結:

SpringAOP代理由SpringIOC容器負責生成、管理,其依賴關系也由IOC容器負責管理。因此,AOP代理可以直接使用容器中的其它bean實例作為目標,這種關系可由IOC容器的依賴註入提供。

Spring創建代理的規則為:

1、默認使用Java動態代理來創建AOP代理,這樣就可以為任何接口實例創建代理了

2、當需要代理的類不是代理接口的時候,Spring會切換為使用CGLIB代理,也可強制使用CGLIB

AOP編程的關鍵就是定義切入點和定義增強處理,一旦定義了合適的切入點和增強處理,AOP框架將自動生成AOP代理,即:代理對象的方法=增強處理+被代理對象的方法。


Spring面向切面編程