個人理解: spring Aop 是什麼:面向切麵編程,類似於自定義攔截操作,支持攔截之前操作@Before,攔截之後操作@After,攔截環繞操作@Around。 什麼情況下使用spring Aop:舉例如下 code案例: applicationContext.xml 配置文件 maven po ...
個人理解:
spring Aop 是什麼:面向切麵編程,類似於自定義攔截操作,支持攔截之前操作@Before,攔截之後操作@After,攔截環繞操作@Around。
什麼情況下使用spring Aop:舉例如下
- 當需要統計某些方法 or 指定xx開頭的方法名 or 指定xx結尾的方法名 or 某些類下的方法 or 某些包下的方法 or 所有的方法的耗時統計或添加日誌信息時,使用spring Aop 切麵編程可以不用修改任何需要統計或添加日誌的方法,只需很少一部分代碼實現需要做的操作。
- 某交易系統需要限制每個登陸用戶查詢次數時,spring Aop 切麵編程在不修改原有代碼上可以完美實現。
code案例:
applicationContext.xml 配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd "> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:propertiesConfig/test.properties</value> </property> </bean> <!-- 掃描@Controller註解 --> <context:component-scan base-package="com.maven.project"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 開啟AOP監聽 只對當前配置文件有效 --> <aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy> <task:executor id="executor" pool-size="5" /> <task:scheduler id="scheduler" pool-size="10" /> <task:annotation-driven executor="executor" scheduler="scheduler" /> <!-- 線程池 --> <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 線程池維護線程的最少數量 --> <property name="corePoolSize" value="10" /> <!-- 線程池維護線程所允許的空閑時間 --> <property name ="keepAliveSeconds" value ="300" /> <!-- 線程池維護線程的最大數量 --> <property name="maxPoolSize" value="100" /> <!-- 線程池所使用的緩衝隊列 --> <property name="queueCapacity" value="25" /> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass"> <value>org.springframework.web.servlet.view.JstlView</value> </property> <property name="prefix"> <value>/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans>
maven pom.xml spring Aop dependency
<!-- Spring AOP + AspectJ --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.11</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> <!-- end -->
spring Aop 切麵業務處理類
package com.maven.project.web.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class CustomAspect { //com.maven.project.services包下(包括子包下 的)for開頭的方法 @Pointcut("execution(* com.maven.project.services..for*(..))") public void query(){} //所有使用@QuerySystem自定義註解的方法 @Pointcut("@annotation(com.maven.project.web.customAnnotations.QuerySystem)") public void loginTimeOut(){} @Before("query()")//方法之前執行 public void queryBefore(JoinPoint joinPoint){ System.out.println("=======query start @Before ====方法名稱:"+joinPoint.getSignature().getName()); } @After("query()")//方法之後執行 public void queryAfter(JoinPoint joinPoint){ System.out.println("=======query end @After ====方法名稱:"+joinPoint.getSignature().getName()); } @Around("query()")//方法前後執行,如果需要返回值,則必須 return joinPoint.proceed(); 返回類型為Object public void queryAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("=======query start @Around ====方法名稱:"+joinPoint.getSignature().getName()); joinPoint.proceed(); System.out.println("=======query end @Around ====方法名稱:"+joinPoint.getSignature().getName()); } @Around("loginTimeOut()") public void times(ProceedingJoinPoint joinPoint) throws Throwable{ long start = System.currentTimeMillis(); joinPoint.proceed(); long end = System.currentTimeMillis(); System.out.println("======= 登陸耗時:"+(end-start)/1000+" ====方法名稱:"+joinPoint.getSignature().getName()); } }
自定義註解
package com.maven.project.web.customAnnotations; import java.lang.annotation.*; @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface QuerySystem { String description() default ""; }
引用自定義註解的方法
@RequestMapping("/login") @QuerySystem //自定義註解引用 public void login(HttpServletRequest request, HttpServletResponse response) { // 方法實現,,,, }
execution 表達式
1、execution(): 表達式主體。 2、第一個*號:表示返回類型,*號表示所有的類型。 3、包名:表示需要攔截的包名,後面的兩個句點表示當前包和當前包的所有子包,com.sample.service.impl包、子孫包下所有類的方法。 4、第二個*號:表示類名,*號表示所有的類。 5、*(..):最後這個星號表示方法名,*號表示所有的方法,後面括弧裡面表示方法的參數,兩個句點表示任何參數。 AspectJ的Execution表達式 execution()是最常用的切點函數,其語法如下所示: execution(<修飾符模式>? <返回類型模式> <方法名模式>(<參數模式>) <異常模式>?) 除了返回類型模式、方法名模式和參數模式外,其它項都是可選的。與其直接講解該方法的使用規則,還不如通過一個個具體的例子進行理解。下麵,我們給出各種使用execution()函數實例。 1)通過方法簽名定義切點 execution(public * *(..))l 匹配所有目標類的public方法,但不匹配SmartSeller和protected void showGoods()方法。第一個*代表返回類型,第二個*代表方法名,而..代表任意入參的方法; execution(* *To(..))l 匹配目標類所有以To為尾碼的方法。它匹配NaiveWaiter和NaughtyWaiter的greetTo()和serveTo()方法。第一個*代表返回類型,而*To代表任意以To為尾碼的方法; 2)通過類定義切點 execution(* com.baobaotao.Waiter.*(..))l 匹配Waiter介面的所有方法,它匹配NaiveWaiter和NaughtyWaiter類的greetTo()和serveTo()方法。第一個*代表返回任意類型,com.baobaotao.Waiter.*代表Waiter介面中的所有方法; execution(* com.baobaotao.Waiter+.*(..))l 匹 配Waiter介面及其所有實現類的方法,它不但匹配NaiveWaiter和NaughtyWaiter類的greetTo()和serveTo()這 兩個Waiter介面定義的方法,同時還匹配NaiveWaiter#smile()和NaughtyWaiter#joke()這兩個不在Waiter 介面中定義的方法。 3)通過類包定義切點 在類名模式串中,“.*”表示包下的所有類,而“..*”表示包、子孫包下的所有類。 execution(* com.baobaotao.*(..))l 匹配com.baobaotao包下所有類的所有方法; execution(* com.baobaotao..*(..))l 匹 配com.baobaotao包、子孫包下所有類的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及 com.baobaotao.dao.user包下的所有類的所有方法都匹配。“..”出現在類名中時,後面必須跟“*”,表示包、子孫包下的所有類; execution(* com..*.*Dao.find*(..))l 匹配包名首碼為com的任何包下類名尾碼為Dao的方法,方法名必須以find為首碼。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切點。 4)通過方法入參定義切點 切點表達式中方法入參部分比較複雜,可以使用“*”和“ ..”通配符,其中“*”表示任意類型的參數,而“..”表示任意類型參數且參數個數不限。 execution(* joke(String,int)))l 匹 配joke(String,int)方法,且joke()方法的第一個入參是String,第二個入參是int。它匹配 NaughtyWaiter#joke(String,int)方法。如果方法中的入參類型是java.lang包下的類,可以直接使用類名,否則必須使用全限定類名,如joke(java.util.List,int); execution(* joke(String,*)))l 匹 配目標類中的joke()方法,該方法第一個入參為String,第二個入參可以是任意類型,如joke(String s1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,double d2,String s3)則不匹配; execution(* joke(String,..)))l 匹配目標類中的joke()方法,該方法第 一個入參為String,後面可以有任意個入參且入參類型不限,如joke(String s1)、joke(String s1,String s2)和joke(String s1,double d2,String s3)都匹配。 execution(* joke(Object+)))l 匹 配目標類中的joke()方法,方法擁有一個入參,且入參是Object類型或該類的子類。它匹配joke(String s1)和joke(Client c)。如果我們定義的切點是execution(* joke(Object)),則只匹配joke(Object object)而不匹配joke(String cc)或joke(Client c)。