25-Java-Spring框架(三)

我只是一個碼農發表於2020-05-06

Spring框架的瞭解SpringIOC的部分內容請閱讀23-Java-Spring框架(一)

SpringwebMVC的瞭解、請求流程、運用等請閱讀24-Java-Spring框架(二)

四、Spring AOP(Aspect Oriented Programming)

  1.SpringAOP瞭解

    SpringAOP將通用業務和傳統業務隔離,通過AOP配置進行結合實現功能疊加的效果(可以利用低耦合模式實現通用業務的呼叫)

         三個相關術語:1.Aspect(方面/切面):指封裝了通用業務邏輯,可以切入到其他目標上元件上的方法

                2.Pointcut(切入點):指表示式,用於指定被切的目標元件和方法

              (1)方法限定表示式:execution(修飾符  返回型別  方法名(引數列表)  丟擲異常)

                execution(* query*())  表示所有以query開頭的方法會被切入功能(第一個*表示方法返回型別,第二個*表示任意)

                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技術:採用介面模式生成動態代理物件,預設目標元件有介面採用

相關文章