Spring框架的瞭解、SpringIOC的部份內容請閱讀23-Java-Spring框架(一)html
SpringwebMVC的瞭解、請求流程、運用等請閱讀24-Java-Spring框架(二)前端
4、Spring AOP(Aspect Oriented Programming)java
1.SpringAOP瞭解web
SpringAOP將通用業務和傳統業務隔離,經過AOP配置進行結合實現功能疊加的效果(能夠利用低耦合模式實現通用業務的調用)spring
三個相關術語:1.Aspect(方面/切面):指封裝了通用業務邏輯,能夠切入到其餘目標上組件上的方法spring-mvc
2.Pointcut(切入點):指表達式,用於指定被切的目標組件和方法安全
(1)方法限定表達式:execution(修飾符 返回類型 方法名(參數列表) 拋出異常)mvc
execution(* query*()) 表示全部以query開頭的方法會被切入功能(第一個*表示方法返回類型,第二個*表示任意)app
execution(* com.spring.*.*(..)) 表示全部以com.spring包下面的全部的類中的全部的方法會被切入功能框架
execution(* com.spring..*.*(..)) 表示全部以com.spring包以及子包下面的全部的類中的全部的方法會被切入功能
(2)類型限定表達式:within(包名.類名)
within(com.spring.controller.springtest) 表示com.spring.controller包下的springtest組件類中的全部方法都會被切入
within(com.spring.controller..*) 表示com.spring.controller包以及子包下的全部組件類中的全部方法都會被切入
(3)名稱限定表達式:bean(id名稱)
bean(student) 表示id名稱爲student的bean對象的全部方法
bean(*ent) 表示id名稱爲以ent結尾的bean對象的全部方法
bean(std*) 表示id名稱爲以std開頭的bean對象的全部方法
3.Advice(通知):指定切入時機,好比方法調用前,方法調用後,異常發生後等等
1 try{ 2 //環繞通知@Around(能夠理解爲前置通知加後置通知) 3 4 //前置通知@Before 5 //執行目標組件方法 6 //後置通知@AfterReturning 7 } catch{ 8 //異常通知@AfterThrowing 9 } finally{ 10 //最終通知@After 11 }
2.AOP的運用案例一(案例:計時演示)
第一步:創建web項目
第二步:配置SpringIOC、SpringAOP、SpringMVC環境,引入相關jar包
第三步:配置web.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="3.0" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 7 <display-name></display-name> 8 <welcome-file-list> 9 <welcome-file>index.jsp</welcome-file> 10 </welcome-file-list> 11 12 <servlet> 13 <servlet-name>springmvc</servlet-name> 14 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 15 16 <init-param> 17 <param-name>contextConfigLocation</param-name> 18 <param-value>classpath:applicationContext.xml</param-value> 19 </init-param> 20 21 <load-on-startup>1</load-on-startup> 22 </servlet> 23 24 <servlet-mapping> 25 <servlet-name>springmvc</servlet-name> 26 <url-pattern>*.do</url-pattern> 27 </servlet-mapping> 28 </web-app>
第四步:編寫被切入的目標測試類(實現傳統業務)
1 package com.springAOP.Target; 2 3 import org.springframework.stereotype.Component; 4 5 //切面的觸發,都是service的對應代理去觸發的,因此,你在service裏面直接調用觸發切面的方法,是達不到這個效果的, 6 //因此,須要使用這個ioc注入的代理對象,就會觸發切面的方法啦 7 @Component("targetclass") 8 public class TargetClass { 9 public void Studentfirst(){ 10 System.out.println("first Student"); 11 } 12 public void Studentsecond(){ 13 System.out.println("second Student"); 14 } 15 public void Studentthird(){ 16 System.out.println("third Student"); 17 } 18 }
第五步:編寫經過AOP實現的通用業務的計時功能
1 package com.springAOP.AOPTimeTest; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.Around; 5 import org.aspectj.lang.annotation.Aspect; 6 import org.springframework.stereotype.Component; 7 import org.springframework.util.StopWatch; 8 9 @Component//加載SpringAOPTimeTest到Spring框架中 10 @Aspect//加載一個實現切面AOP功能的對象 11 public class SpringAOPTimeTest { 12 13 @Around("within(com.springAOP.Target.*)")//表示com.springAOP.Target包的全部組件類中的全部方法都會被切入環繞通知 14 public Object time(ProceedingJoinPoint pjp) throws Throwable{ 15 16 StopWatch watch = new StopWatch();//調用Spring框架封裝好的計時類 17 watch.start();//開始計時 18 Object obj = pjp.proceed();//執行被切入的目標組件方法 19 watch.stop();//結束計時 20 21 String ClassName = pjp.getTarget().getClass().getName();//獲取包名+類名 22 String AdviceName = pjp.getSignature().getName();//獲取方法名 23 long time = watch.getTotalTimeMillis();//獲取時間差 24 System.out.println(ClassName+"."+AdviceName+"運行了"+time); 25 26 return obj; 27 } 28 }
第六步:配置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 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:tx="http://www.springframework.org/schema/tx" 7 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 8 xmlns:jee="http://www.springframework.org/schema/jee" 9 xmlns:mvc="http://www.springframework.org/schema/mvc" 10 xmlns:util="http://www.springframework.org/schema/util" 11 xsi:schemaLocation=" 12 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 13 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 14 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 15 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd 16 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd 17 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd 18 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 19 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 20 21 <!-- 配置AOP組件aspectj自動代理 --> 22 <aop:aspectj-autoproxy/> 23 24 <!-- 配置組件掃描 --> 25 <context:component-scan base-package="com.springAOP"/> 26 27 <!-- 配置註解handler --> 28 <mvc:annotation-driven/> 29 30 <!-- 配置ViewResolver視圖解析器 --> 31 <!-- 完整的頁面路徑:前綴+視圖名稱+後綴 --> 32 <bean id = "viewresolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 33 <!-- 路徑前綴 --> 34 <property name="prefix" value="/"></property> 35 <!-- 路徑後綴 --> 36 <property name="suffix" value=".jsp"></property> 37 </bean> 38 </beans>
第七步:編寫springMVC部分的Controller前端控制器,調用傳統業務方法
1 package com.springAOP.mvccontroller; 2 3 import javax.annotation.Resource; 4 5 import org.springframework.stereotype.Controller; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.servlet.ModelAndView; 8 9 import com.springAOP.Target.TargetClass; 10 11 @Controller//此處至關於繼承了Controller接口的做用 12 public class StudentController { 13 14 //切面的觸發,都是service的對應代理去觸發的,因此,你在service裏面直接調用觸發切面的方法,是達不到這個效果的, 15 //因此,須要使用這個ioc注入的代理對象,就會觸發切面的方法啦 16 @Resource(name = "targetclass") 17 private TargetClass tc; 18 19 @RequestMapping("/stu.do")//此處等價於applicationContext.xml中的配置HandlerMapping組件 20 public ModelAndView student(){ 21 tc.Studentfirst(); 22 tc.Studentsecond(); 23 tc.Studentthird(); 24 25 ModelAndView mac = new ModelAndView(); 26 mac.setViewName("index"); 27 mac.getModel(); 28 return mac; 29 } 30 }
第八步:測試運行
3.AOP運用案例二(案例:AOP實現異常拋出到日誌)
第一步:創建web項目
第二步:配置SpringIOC、SpringAOP、SpringMVC環境,引入相關jar包(同案例一)
第三步:配置web.xml(同案例一)
第四步:編寫被切入的目標測試類(實現傳統業務)(同案例一)
第五步:編寫經過AOP實現的通用業務的異常拋出功能
1 package com.springAOP.test; 2 3 import java.io.FileWriter; 4 import java.io.IOException; 5 import java.io.PrintWriter; 6 import java.util.Date; 7 8 import org.aspectj.lang.ProceedingJoinPoint; 9 import org.aspectj.lang.annotation.AfterThrowing; 10 import org.aspectj.lang.annotation.Around; 11 import org.aspectj.lang.annotation.Aspect; 12 import org.springframework.stereotype.Component; 13 import org.springframework.util.StopWatch; 14 15 @Component//加載SpringAOPTimeTest到Spring框架中 16 @Aspect//加載一個實現切面AOP功能的對象 17 public class SpringAOPTest { 18 19 @Around("within(com.springAOP.Target.*)")//表示com.springAOP.Target包的全部組件類中的全部方法都會被切入環繞通知 20 public Object time(ProceedingJoinPoint pjp) throws Throwable{ 21 22 StopWatch watch = new StopWatch();//調用Spring框架封裝好的計時類 23 watch.start();//開始計時 24 Object obj = pjp.proceed();//執行被切入的目標組件方法 25 watch.stop();//結束計時 26 27 String ClassName = pjp.getTarget().getClass().getName();//獲取包名+類名 28 String AdviceName = pjp.getSignature().getName();//獲取方法名 29 long time = watch.getTotalTimeMillis();//獲取時間差 30 System.out.println(ClassName+"."+AdviceName+"運行了"+time); 31 32 return obj; 33 } 34 35 @AfterThrowing(throwing="ex",pointcut="within(com.springAOP..*)")//表示com.springAOP包以及子包的全部組件類中的全部方法都會被切入異常通知 36 public void logException(Exception ex){ 37 38 //異常信息寫入日誌文件 39 try { 40 FileWriter fw = new FileWriter("F:\\error.log",true); 41 PrintWriter pw = new PrintWriter(fw); 42 43 pw.println(new Date()+"發生了"+ex+"異常"+"詳情以下:"); 44 StackTraceElement[] stackTrace = ex.getStackTrace(); 45 for(StackTraceElement s:stackTrace){ 46 if(s.toString().contains("com.springAOP")){//只寫入重要的報錯語句 47 pw.println(s); 48 } 49 } 50 pw.println("========================================================="); 51 pw.close(); 52 } catch (IOException e) { 53 // TODO Auto-generated catch block 54 e.printStackTrace(); 55 } 56 } 57 }
第六步:配置applicationContext.xml(同案例一)
第七步:編寫springMVC部分的Controller前端控制器,調用傳統業務方法
1 package com.springAOP.mvccontroller; 2 3 import javax.annotation.Resource; 4 5 import org.springframework.stereotype.Controller; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.servlet.ModelAndView; 8 9 import com.springAOP.Target.TargetClass; 10 11 @Controller//此處至關於繼承了Controller接口的做用 12 public class StudentController { 13 14 //切面的觸發,都是service的對應代理去觸發的,因此,你在service裏面直接調用觸發切面的方法,是達不到這個效果的, 15 //因此,須要使用這個ioc注入的代理對象,就會觸發切面的方法啦 16 @Resource(name = "targetclass") 17 private TargetClass tc; 18 19 @RequestMapping("/stu.do")//此處等價於applicationContext.xml中的配置HandlerMapping組件 20 public ModelAndView student(){ 21 tc.Studentfirst(); 22 tc.Studentsecond(); 23 tc.Studentthird(); 24 25 ModelAndView mac = new ModelAndView(); 26 mac.setViewName("index"); 27 mac.getModel(); 28 29 //此處用於異常報錯 30 String str = null; 31 str.length(); 32 return mac; 33 } 34 }
第八步:測試運行
3.SpringAOP原理
在使用AOP以後,Spring容器返回的是組件對象,是採用動態代理技術生成一個動態代理對象(是原有組件對象的子類),
對原有組件的方法進行重寫,在重寫方法中調用切面組件追加功能和原有組件的功能。
動態代理:JDK動態代理:依賴於接口
CGLIB動態代理:不依賴於接口
CGLIB技術:採用子類模式生成動態代理對象,默認目標組件沒有接口採用
在使用中,能夠追加<aop:aspectj-autoproxy proxy-target-class="true"/> 強制採用CGLIB模式生成動態代理對象,建議使用,由於更安全
Proxy技術:採用接口模式生成動態代理對象,默認目標組件有接口採用