Spring 教程(二)

1、Spring AOP介紹
開發其實就是在不斷的重構,抽象重複代碼,而後進行封裝。從最原始的模塊化編程到面向對象編程,代碼的封裝愈來愈整齊清晰,可是依然存在重複的代碼,而這些重複代碼幾乎都是與業務邏輯無關的系統邏輯代碼。好比在數據操做類中的插入、更新、刪除數據等方法中都存在數據庫事務的處理,重要業務邏輯方法中都有日誌記錄的邏輯等等。每一個應用系統都存在着這種系統級的重複邏輯代碼,而咱們並無更好的方法去將這些代碼抽象出來並進行管理。然而AOP的出現彌補了這一缺陷,AOP能夠在不改變原有業務邏輯代碼的狀況下對原有業務進行橫切攔截,處理那些重複的系統邏輯。
 
與Ioc容器同樣,AOP也是Spring的核心模塊之一。AOP是Aspect-Oriented Programming的簡稱,如今一般稱爲面向切面編程。咱們學了OOP,面向對象編程,而AOP並不是是OOP的替代技術,它只是OOP的一個有益補充。
 
須要指出的是AOP的應用場合是受限的,它通常只適合於那些具備橫切邏輯的應用場合:如性能監測、訪問控制、事務管理以及日誌記錄,它並不適合處理具體的業務邏輯,分散處理業務邏輯會使得邏輯混亂、增長維護成本。
 
 
2、如何使用Spring AOP
下面以對用戶操做類UserDao的AOP攔截演示Spring AOP的使用。
一、建立Java項目,添加Spring AOP依賴支持
aopalliance-1.0.jar
commons-logging-1.1.1.jar
spring-aop-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
 
二、添加User及UserDao類
User類:
  1. public class User {  
  2.     private Integer id;  
  3.     private String name;  
  4. }  
UserDao類:
  1. public class UserDao {  
  2.     public void save(User user){  
  3.         System.out.println("save user....");  
  4.     }  
  5.       
  6.     public void delete(int id){  
  7.         System.out.println("delete user....");  
  8.     }  
  9.       
  10.     public void update(User user) {  
  11.         System.out.println("update user ....");  
  12.     }  
  13.       
  14.     public User query(String name) {  
  15.         System.out.println("getUser ....");  
  16.         return new User();  
  17.     }  
  18. }  
三、添加AOP攔截處理
AOP前置通知:
  1. public class UserBeforeAdvice implements MethodBeforeAdvice {      
  2.     public void before(Method method, Object[] args, Object target) {  
  3.         System.out.println("調用方法:"+method.getName() + "()前攔截處理");  
  4.     }     
  5. }  
AOP後置通知:
  1. public class UserAfterAdvice implements AfterReturningAdvice {  
  2.     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {  
  3.         System.out.println("方法:"+method.getName() + "()返回後攔截處理");  
  4.     }  
  5. }  
AOP環繞通知:
  1. public class UserAroundAdvice implements MethodInterceptor {  
  2.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  3.         System.out.println("調用方法:"+invocation.getMethod().getName() + "()前攔截處理");  
  4.         Object o = invocation.proceed();  
  5.         System.out.println("調用方法:"+invocation.getMethod().getName() + "()後攔截處理");  
  6.         return o;  
  7.     }  
  8. }  
四、添加Spring配置文件applicationContext.xml
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans   
  5.            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">  
  6.     <bean id="userDaoTarget" class="com.boya.spring.dao.UserDao" />  
  7.     <bean id="userBeforeAdvice" class="com.boya.spring.aop.UserBeforeAdvice" />  
  8.     <bean id="userAfterAdvice" class="com.boya.spring.aop.UserAfterAdvice" />  
  9.     <bean id="userAroundAdvice" class="com.boya.spring.aop.UserAroundAdvice" />  
  10.     <bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean">  
  11.         <property name="interceptorNames">  
  12.             <list><value>userAroundAdvice</value></list>  
  13.         </property>  
  14.         <property name="target" ref="userDaoTarget"></property>  
  15.     </bean>  
  16. </beans>  
五、測試AOP
  1. public static void main(String[] args) {  
  2.     ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");  
  3.     UserDao userDao = context.getBean("userDao", UserDao.class);  
  4.     userDao.save(new User());  
  5. }  
輸出結果:
調用方法:save()前攔截處理
save user....
調用方法:save()後攔截處理
 
回過頭來再看剛纔的示例。
一、首先,原來的業務邏輯代碼不變
    再也不關心重複的系統邏輯代碼
二、編寫AOP切面處理邏輯
    把原業務邏輯中的重複代碼抽象出來,封裝入切面代碼中,如上面示例的三種Advice通知封裝不一樣的系統處理邏輯。
    前置通知:實現MethodBeforeAdvice 接口,在調用業務方法前調用該接口
    後置通知:實現AfterReturningAdvice 接口,在業務方法返回後調用該接口,在該接口中能夠查看返回值(但不能修改返回值)
    環繞通知:實現MethodInterceptor 接口,在該接口中invocation.proceed();這個方法會調用真實對象的方法
三、使用Spring配置文件將業務邏輯和AOP切面邏輯進行組裝
    AOP代理Bean類型須要設置爲org.springframework.aop.framework.ProxyFactoryBean
    必須設置代理目標(target屬性設置)和通知類型(interceptorNames屬性設置)
    代理目標並不是必須實現接口,做爲POJO被代理時,會對目標全部方法進行攔截
 
3、AOP實現原理
Spring AOP是基於Java反射和動態代理實現的。在講解動態代理以前,咱們先回顧一下代理模式。
代理模式,就是爲某一對象提供一個代理,經過代理操做該對象的方法。一般狀況下,真實對象和代理對象須要實現相同的接口,在代理對象中保存真實對象的引用,以此來控制操做真實對象。
 
咱們以班長代理老師簽到來演示代理模式。
建立簽到接口:
  1. public interface SignInterface {  
  2.     public Object sign(String nameList);  
  3. }  
建立真實對象,Teacher類:
  1. public   class  Teacher  implements  SignInterface {  
  2.      public  Object sign(String nameList) {  
  3.         System. out .println( "Teacher sign..." );  
  4.          return   new  Object();  
  5.     }  
  6. }  
建立代理對象,Leader類:
  1. public class Leader implements SignInterface {  
  2.     private Teacher teacher;  
  3.     public Object sign(String nameList) {  
  4.         if (teacher == null) {  
  5.             teacher = new Teacher();  
  6.         }  
  7.         Object o = teacher.sign(nameList);  
  8.         return o;  
  9.     }  
  10. }  
測試代理:
  1. public static void main(String[] args) {  
  2.     SignInterface s = new Leader();  
  3.     s.sign("names");  
  4. }  
以上就是一個代理模式的例子,代理類在編譯時就已經被建立了,而動態代理是在運行時動態建立代理類來實現代理模式。以下代碼:
  1. public class ProxyObject implements InvocationHandler {  
  2.     private Object proxy_obj;  
  3.     ProxyObject(Object obj) {  
  4.         this.proxy_obj = obj;  
  5.     }  
  6.     public static Object getProxy(Object obj) {  
  7.         Class cls = obj.getClass();  
  8.         // 經過Proxy類的newProxyInstance方法來返回代理對象  
  9.         return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new ProxyObject(obj));  
  10.     }  
  11.     /** 
  12.      * 實現InvocationHandler接口的invoke 
  13.      */  
  14.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  15.         System.out.println("調用方法:" + method + "()以前攔截處理");  
  16.         if (args != null) {  
  17.             System.out.println("方法有" + args.length + "個參數");  
  18.             for (int i = 0; i < args.length; i++) {  
  19.                 System.out.println(args[i]);  
  20.             }  
  21.         }  
  22.         // 利用反射機制動態調用真實對象的方法  
  23.         Object o = method.invoke(proxy_obj, args);  
  24.         System.out.println("調用方法:" + method + "()以後攔截處理");  
  25.         return o;  
  26.     }  
  27.     // 測試代碼  
  28.     public static void main(String agr[]) {  
  29.         SignInterface si = (SignInterface) getProxy(new Teacher());  
  30.         si.sign("names");  
  31.     }  
  32. }  
以上就是使用JDK的Proxy實現的動態代理,不過JDK的動態代理實現只支持針對接口的動態代理實現。Spring AOP實現默認也是動態代理方式,不過,Spring AOP支持CGLib Proxy的實現方式,能夠針對POJO進行動態代理,實現AOP攔截。
 
咱們來看一下CGLib實現的一個簡單AOP攔截
建立業務POJO:
  1. public class CGLibTeacher {  
  2.     public Object sign(String nameList) {  
  3.         System.out.println("Teacher sign...");  
  4.         return new Object();  
  5.     }  
  6. }  
建立AOP攔截:
  1. public class CGLibAop implements MethodInterceptor {  
  2.         public Object intercept(Object arg0, Method arg1, Object[] arg2,     
  3.                 MethodProxy arg3) throws Throwable {     
  4.             System.out.println("before...");  
  5.             Object o = arg3.invokeSuper(arg0, arg2);  
  6.             System.out.println("after...");  
  7.             return o;  
  8.         }     
  9. }  
CGLib代理對象建立及測試:
  1. public class CGLibProxy {  
  2.     public static CGLibTeacher create(CGLibAop aop){  
  3.         Enhancer en = new Enhancer();     
  4.         //進行代理    
  5.         en.setSuperclass(CGLibTeacher.class);     
  6.         en.setCallback(aop);     
  7.         //生成代理實例    
  8.         return (CGLibTeacher)en.create();     
  9.     }  
  10.       
  11.     public static void main(String[] args) {  
  12.         CGLibTeacher t = CGLibProxy.create(new CGLibAop());  
  13.         t.sign("names");  
  14.     }  
  15. }  
從CGLib的代理對象建立中能夠看到,代理對象須要設置代理目標以及AOP攔截實現,和Spring AOP的實現很是相似。
相關文章
相關標籤/搜索