Spring-Aop詳細教程

南夏發表於2015-12-13
問題?SpringAop的定義及使用

Aop定義:

aop:

   1、切面

        事務、日誌、安全性框架、許可權等都是切面

   2、通知

      切面中的方法就是通知

   3、目標類

   4、切入點

         只有符合切入點,才能讓通知和目標方法結合在一起

   5、織入:

         形成代理物件的方法的過程

         

 

好處:

   事務、日誌、安全性框架、許可權、目標方法之間完全是鬆耦合的



execution詳解:



executionpublic * *..)) 所有的公共方法

execution(*set*..)) 以set開頭的任意方法

execution(*com.xyz.service.AccountService.*..))com.xyz.service.AccountService類中的所有的方法

execution(*com.xyz.service.*.*..)) com.xyz.service包中的所有的類的所有的方法

execution(*com.xyz.service..*.*..))com.xyz.service包及子包中所有的類的所有的方法

execution(*cn.itcast.spring.sh..*.*(String,?,Integer)) cn.itcast.spring.sh包及子包中所有的類的有三個引數

                                                           第一個引數為String,第二個引數為任意型別,

                                                           第三個引數為Integer型別的方法


載入過程:

springAOP的具體載入步驟:

   1、當spring容器啟動的時候,載入了spring的配置檔案

   2、為配置檔案中所有的bean建立物件

   3spring容器會解析aop:config的配置

       1、解析切入點表示式,用切入點表示式和納入spring容器中的bean做匹配

            如果匹配成功,則會為該bean建立代理物件,代理物件的方法=目標方法+通知

            如果匹配不成功,不會建立代理物件

   4、在客戶端利用context.getBean獲取物件時,如果該物件有代理物件則返回代理物件,如果代理物件,則返回目標物件

說明:如果目標類沒有實現介面,則spring容器會採用cglib的方式產生代理物件,如果實現了介面,會採用jdk的方式


通知:

通知:

   1、前置通知

      1、在目標方法執行之前執行

      2、無論目標方法是否丟擲異常,都執行,因為在執行前置通知的時候,目標方法還沒有執行,還沒有遇到異常

   2、後置通知

      1、在目標方法執行之後執行

      2、當目標方法遇到異常,後置通知將不再執行

      3、後置通知可以接受目標方法的返回值,但是必須注意:

               後置通知的引數的名稱和配置檔案中returning="var"的值是一致的

   3、最終通知:

      1、在目標方法執行之後執行

      2、無論目標方法是否丟擲異常,都執行,因為相當於finally

   4、異常通知

      1、接受目標方法丟擲的異常資訊

      2、步驟

           在異常通知方法中有一個引數Throwable  ex

           在配置檔案中

              <aop:after-throwingmethod="throwingMethod" pointcut-ref="perform"throwing="ex"/>

   5、環繞通知

       1、如果不在環繞通知中呼叫ProceedingJoinPointproceed,目標方法不會執行

       2、環繞通知可以控制目標方法的執行


聯合hibernate+spring的案例:

hibernate工具類:

  1. package cn.itcast.spring.sh.aop;  
  2.   
  3. import org.hibernate.Session;  
  4. import org.hibernate.SessionFactory;  
  5. import org.hibernate.Transaction;  
  6. import org.hibernate.cfg.Configuration;  
  7.   
  8. public class HibernateUtils {  
  9.     public static SessionFactory sessionFactory;  
  10.     static{  
  11.         Configuration configuration = new Configuration();  
  12.         configuration.configure("cn/itcast/spring/sh/aop/hibernate.cfg.xml");  
  13.         sessionFactory = configuration.buildSessionFactory();  
  14. //      Session session = sessionFactory.openSession();  
  15. //      Transaction transaction = session.beginTransaction();  
  16. //      transaction.commit();  
  17.     }  
  18. }  


持久化類:

  1. package cn.itcast.spring.sh.aop;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class Person implements Serializable{  
  6.     private Long pid;  
  7.     private String pname;  
  8.     private String psex;  
  9.       
  10.     public Person(){}  
  11.       
  12.     public Person(String pname){  
  13.         this.pname = pname;  
  14.     }  
  15.       
  16.     public Long getPid() {  
  17.         return pid;  
  18.     }  
  19.     public void setPid(Long pid) {  
  20.         this.pid = pid;  
  21.     }  
  22.     public String getPname() {  
  23.         return pname;  
  24.     }  
  25.     public void setPname(String pname) {  
  26.         this.pname = pname;  
  27.     }  
  28.     public String getPsex() {  
  29.         return psex;  
  30.     }  
  31.     public void setPsex(String psex) {  
  32.         this.psex = psex;  
  33.     }  
  34.       
  35. }  

對映檔案:Person.hbm.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  4.   
  5. <hibernate-mapping>  
  6.     <!--   
  7.         用來描述一個持久化類  
  8.         name  類的全名  
  9.         table 可以不寫  預設值和類名一樣   
  10.         catalog  資料庫的名稱  一般不寫  
  11.      -->  
  12.     <class name="cn.itcast.spring.sh.aop.Person">  
  13.         <!--   
  14.             標示屬性  和資料庫中的主鍵對應  
  15.             name  屬性的名稱  
  16.             column 列的名稱  
  17.          -->  
  18.         <id name="pid" column="pid" length="200" type="java.lang.Long">  
  19.             <!--   
  20.                 主鍵的產生器  
  21.                   就該告訴hibernate容器用什麼樣的方式產生主鍵  
  22.              -->  
  23.             <generator class="increment"></generator>  
  24.         </id>  
  25.         <!--  
  26.             描述一般屬性 
  27.          -->  
  28.         <property name="pname" column="pname" length="20" type="string"></property>  
  29.           
  30.         <property name="psex" column="psex" length="10" type="java.lang.String"></property>  
  31.     </class>  
  32. </hibernate-mapping>  


hibernate.cfg.xml配置檔案

  1. <?xml version='1.0' encoding='utf-8'?>  
  2. <!DOCTYPE hibernate-configuration PUBLIC  
  3.         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
  4.         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">  
  5. <hibernate-configuration>  
  6.     <!--  
  7.         一個session-factory只能連線一個資料庫 
  8.     -->  
  9. <session-factory>  
  10.     <!--  
  11.         資料庫的使用者名稱 
  12.     -->  
  13.     <property name="connection.username">root</property>  
  14.     <!--  
  15.         密碼 
  16.     -->  
  17.     <property name="connection.password">root</property>  
  18.     <!--  
  19.         url 
  20.     -->  
  21.     <property name="connection.url">  
  22.         jdbc:mysql://localhost:3306/Use_Aop  
  23.     </property>  
  24.     <!--   
  25.         作用:根據持久化類和對映檔案生成表  
  26.         validate  
  27.         create-drop  
  28.         create  
  29.         update  
  30.     -->  
  31.     <property name="hbm2ddl.auto">update</property>  
  32.     <!--  
  33.         顯示hibernate內部生成的sql語句 
  34.     -->  
  35.     <property name="show_sql">true</property>  
  36.     <property name="current_session_context_class">thread</property>  
  37.     <!-- 新增對映檔案 -->  
  38.     <mapping resource="cn/itcast/spring/sh/aop/Person.hbm.xml" />  
  39.   
  40. </session-factory>  
  41. </hibernate-configuration>  

dao層介面:

  1. package cn.itcast.spring.sh.aop;  
  2.   
  3. public interface PersonDao {  
  4.     public String savePerson(Person person);  
  5. }  

dao實現層

  1. package cn.itcast.spring.sh.aop;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import org.junit.Test;  
  6.   
  7. public class PersonDaoImpl extends HibernateUtils implements PersonDao  {  
  8.   
  9.     public String savePerson(Person person) {  
  10.         //在這裡首先得開啟事務才能crud操作,Springaop配置已經準備好動態代理了  
  11.         //int a=1/0;  
  12.         sessionFactory.getCurrentSession().save(person);  
  13.         System.out.println("執行儲存操作了。");  
  14.         return "aaa";  
  15.     }  
  16.       
  17.   
  18.   
  19. }  

事務層

  1. package cn.itcast.spring.sh.aop;  
  2.   
  3. import org.aspectj.lang.JoinPoint;  
  4. import org.aspectj.lang.ProceedingJoinPoint;  
  5. import org.hibernate.Transaction;  
  6.   
  7. public class MyTransaction extends HibernateUtils {  
  8.     private Transaction transaction;  
  9.       
  10.     /** 
  11.      * 通過一些引數可以獲取目標方法的相關資訊,報告目標方法名稱、返回值引數、異常資訊等 
  12.      * 不過相應的也要在配置檔案中進行設定(通過實驗,好像只配置後置及異常就可以只用了),不然就獲取不了。 
  13.      * 注:環繞通知不可以跟前置通知和後置通知一起用,因為環繞通知已經把兩個通知的東西做了,它可以呼叫目標方法 
  14.      *  
  15.      * */  
  16.       
  17.     public void beginTransaction(JoinPoint joinPoint){  
  18.         //可以通過該引數得到目標方法名稱(導的是這個包:import org.aspectj.lang.JoinPoint;)  
  19.         System.out.println(joinPoint.getSignature().getName());  
  20.         //開啟事務  
  21.         System.out.println("開啟事務");  
  22.         this.transaction = sessionFactory.getCurrentSession().beginTransaction();  
  23.           
  24.     }  
  25.     //這裡的var引數是後置通知,得到目標方法的返回值  
  26.     public void commit(Object var){  
  27.         //提交事務  
  28.         this.transaction.commit();  
  29.         System.out.println(var);  
  30.         System.out.println("關閉事務");  
  31.     }  
  32.       
  33.     //測試最終通知  
  34. //  public void finallFun(){  
  35. //      System.out.println("最終通知發生了");  
  36. //  }  
  37.       
  38.     //測試異常通知  
  39.     //throwing="ag":表示異常資訊引數,和配置檔案中是相對應的  
  40. //  public void exception(Throwable ag){  
  41. //      System.out.println("異常資訊:"+ag);  
  42. //  }  
  43.       
  44.     //測試環繞異常通知  
  45. //  public void aroundException(ProceedingJoinPoint joitPoint) throws Throwable{  
  46. //      //joitPoint.proceed();//呼叫目標方法,不然目標方法不能執行,這個時候前置和後置都已經不用了  
  47. //      System.out.println("環繞通知發生");  
  48. //  }  
  49. }  

spring配置檔案(applicationContext.xml):

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:aop="http://www.springframework.org/schema/aop"   
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  7.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  
  8.   
  9.     <!-- SpringAop案例  
  10.         使用條件:必須有  
  11.         xmlns:aop="http://www.springframework.org/schema/aop"   
  12.         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"   
  13.     -->  
  14.       
  15.     <!-- 匯入目標類 -->  
  16.     <bean id="personDao" class="cn.itcast.spring.sh.aop.PersonDaoImpl"></bean>  
  17.     <bean id="myTransaction" class="cn.itcast.spring.sh.aop.MyTransaction"></bean>  
  18.     <!-- aop配置 -->  
  19.     <aop:config>  
  20.       <!--  
  21.         切入點表示式 
  22.       -->  
  23.       <aop:pointcut expression="execution(* cn.itcast.spring.sh.aop.PersonDaoImpl.*(..))" id="perform"/>  
  24.       <!--  
  25.       aop:aspect:引入切面  
  26.      前置通知:aop:before:目標方法沒執行前執行該方法  
  27.       後置通知:aop:after-returning:目標方法執行後執行該方法。目標方法有異常,不會執行了,  
  28.                 這裡的var是後置通知的引數,可以得到目標方法的返回值,並且和方法中所帶引數(Object var)一致  
  29.   最終通知::aop:after :表示目標方法執行後才執行,不管目標方法有沒有異常,都會執行。  
  30.         異常通知:aop:after-throwing:當目標方法出現錯誤了,就會執行該方法,throwing="ag":表示異常資訊引數,在方法中帶引數(Throwable ag)(可以使用  
  31.    環繞通知:aop:around 可以控制目標方法的執行(注:環繞與前置、後置不能一起用.)  
  32.      
  33.       pointcut-ref: 引入切入點  
  34.         returning="var":代表接受目標類方法中的返回值  
  35.        -->  
  36.       <aop:aspect ref="myTransaction">  
  37.             <aop:before method="beginTransaction" pointcut-ref="perform"/>  
  38.             <aop:after-returning method="commit" pointcut-ref="perform" returning="var"/>  
  39.             <!-- <aop:after method="finallFun" pointcut-ref="perform"/>  
  40.             <aop:after-throwing method="exception" throwing="ag" pointcut-ref="perform"/> -->  
  41.             <!-- <aop:around method="aroundException" pointcut-ref="perform"/> -->  
  42.       </aop:aspect>  
  43.     <!--  
  44.     通知型別:  
  45.   
  46.     前置通知(Before advice):在某連線點之前執行的通知,  
  47.                             但這個通知不能阻止連線點之前的執行流程(除非它丟擲一個異常)。   
  48.   
  49.     後置通知(After returning advice):在某連線點正常完成後執行的通知:  
  50.                                     例如,一個方法沒有丟擲任何異常,正常返回。   
  51.   
  52.     異常通知(After throwing advice):在方法丟擲異常退出時執行的通知。   
  53.   
  54.     最終通知(After (finally) advice):當某連線點退出的時候執行的通知  
  55.                                     (不論是正常返回還是異常退出)。   
  56.   
  57.     環繞通知(Around Advice):包圍一個連線點的通知,如方法呼叫。  
  58.                             這是最強大的一種通知型別。環繞通知可以在方法呼叫前後完成自定義的行為。  
  59.                             它也會選擇是否繼續執行連線點或直接返回它自己的返回值或丟擲異常來結束執行。   
  60.        
  61.      -->  
  62. </beans>  

測試類:

  1. package cn.itcast.spring.sh.aop;  
  2.   
  3. import org.junit.Test;  
  4. import org.springframework.context.ApplicationContext;  
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  6.   
  7. public class PersonTest {  
  8.     @Test  
  9.     public void test(){  
  10.         ApplicationContext applicationContext = new  ClassPathXmlApplicationContext("cn/itcast/spring/sh/aop/applicationContext.xml");  
  11.         PersonDao personDao = (PersonDao)applicationContext.getBean("personDao");  
  12.         Person person1 = new Person();  
  13.         person1.setPname("李大鵬");  
  14.         person1.setPsex("男");  
  15.         personDao.savePerson(person1);  
  16.     }  
  17. }  
  18. 結果:

    spring動態代理:



    異常資訊的執行結果:



    注意:繼承介面的是cglib動態代理方式,而在getbean的時候產生的不是介面的物件而是動態代理的物件


相關文章