程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Spring AOP 注解和xml實現,springaop注解xml

Spring AOP 注解和xml實現,springaop注解xml

編輯:JAVA綜合教程

Spring AOP 注解和xml實現,springaop注解xml


 AOP是OOP的延續,是Aspect Oriented Programming的縮寫,意思是面向切面編程。可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。 AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP可以說也是這種目標的一種實現。

我們現在做的一些非業務,如:日志、事務、安全等都會寫在業務代碼中(也即是說,這些非業務類橫切於業務類),但這些代碼往往是重復,復制——粘貼式的代碼會給程序的維護帶來不便,AOP就實現了把這些業務需求與系統需求分開來做。這種解決的方式也稱代理機制。

先來了解一下AOP的相關概念,《Spring參考手冊》中定義了以下幾個AOP的重要概念,結合以上代碼分析如下:

  • 切面(Aspect):官 方的抽象定義為“一個關注點的模塊化,這個關注點可能會橫切多個對象”,在本例中,“切面”就是類TestAspect所關注的具體行為,例 如,AServiceImpl.barA()的調用就是切面TestAspect所關注的行為之一。“切面”在ApplicationContext 中<aop:aspect>來配置。
  • 連接點(Joinpoint) :程序執行過程中的某一行為,例如,UserService.get的調用或者UserService.delete拋出異常等行為。
  • 通知(Advice) :“切面”對於某個“連接點”所產生的動作,例如,TestAspect中對com.spring.service包下所有類的方法進行日志記錄的動作就是一個Advice。其中,一個“切面”可以包含多個“Advice”,例如ServiceAspect。
  • 切入點(Pointcut) :匹配連接點的斷言,在AOP中通知和一個切入點表達式關聯。例如,TestAspect中的所有通知所關注的連接點,都由切入點表達式execution(* com.spring.service.*.*(..))來決定。
  • 目標對象(Target Object) :被一個或者多個切面所通知的對象。例如,AServcieImpl和BServiceImpl,當然在實際運行時,Spring AOP采用代理實現,實際AOP操作的是TargetObject的代理對象。
  • AOP代理(AOP Proxy) : 在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。默認情況下,TargetObject實現了接口時,則采用JDK動態代理,例 如,AServiceImpl;反之,采用CGLIB代理,例如,BServiceImpl。強制使用CGLIB代理需要將 <aop:config>的 proxy-target-class屬性設為true。

通知(Advice)類型:

  • 前置通知(Before advice):在 某連接點(JoinPoint)之前執行的通知,但這個通知不能阻止連接點前的執行。ApplicationContext中 在<aop:aspect>裡面使用<aop:before>元素進行聲明。例如,TestAspect中的doBefore方 法。
  • 後置通知(After advice):當 某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>裡面使 用<aop:after>元素進行聲明。例如,ServiceAspect中的returnAfter方法,所以Teser中調用 UserService.delete拋出異常時,returnAfter方法仍然執行。
  • 返回後通知(After return advice):在某連接點正常完成後執行的通知,不包括拋出異常的情況。ApplicationContext中在<aop:aspect>裡面使用<after-returning>元素進行聲明。
  • 環繞通知(Around advice):包 圍一個連接點的通知,類似Web中Servlet規范中的Filter的doFilter方法。可以在方法的調用前後完成自定義的行為,也可以選擇不執 行。ApplicationContext中在<aop:aspect>裡面使用<aop:around>元素進行聲明。例 如,ServiceAspect中的around方法。
  • 拋出異常後通知(After throwing advice):在方法拋出異常退出時執行的通知。ApplicationContext中在<aop:aspect>裡面使用<aop:after-throwing>元素進行聲明。例如,ServiceAspect中的returnThrow方法。

注:可以將多個通知應用到一個目標對象上,即可以將多個切面織入到同一目標對象。

使用Spring AOP可以基於兩種方式,一種是比較方便和強大的注解方式,另一種則是中規中矩的xml配置方式。

先說注解,使用注解配置Spring AOP總體分為兩步,

第一步是在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"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- 激活組件掃描功能,在包cn.ysh.studio.spring.aop及其子包下面自動掃描通過注解配置的組件 -->
	<context:component-scan base-package="cn.ysh.studio.spring.aop"/>
	<!-- 激活自動代理功能 -->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	
	<!-- 用戶服務對象 -->
	<bean id="userService" class="cn.ysh.studio.spring.aop.service.UserService" />

</beans>
 
第二步是為Aspect切面類添加注解:
/**
 * 系統服務組件Aspect切面Bean
 * @author Shenghany
 * @date 2013-5-28
 */
//聲明這是一個組件
@Component
//聲明這是一個切面Bean
@Aspect
public class ServiceAspect {

	private final static Log log = LogFactory.getLog(ServiceAspect.class);
	
	//配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點
	@Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
	public void aspect(){	}
	
	/*
	 * 配置前置通知,使用在方法aspect()上注冊的切入點
	 * 同時接受JoinPoint切入點對象,可以沒有該參數
	 */
	@Before("aspect()")
	public void before(JoinPoint joinPoint){
		if(log.isInfoEnabled()){
			log.info("before " + joinPoint);
		}
	}
	
	//配置後置通知,使用在方法aspect()上注冊的切入點
	@After("aspect()")
	public void after(JoinPoint joinPoint){
		if(log.isInfoEnabled()){
			log.info("after " + joinPoint);
		}
	}
	
	//配置環繞通知,使用在方法aspect()上注冊的切入點
	@Around("aspect()")
	public void around(JoinPoint joinPoint){
		long start = System.currentTimeMillis();
		try {
			((ProceedingJoinPoint) joinPoint).proceed();
			long end = System.currentTimeMillis();
			if(log.isInfoEnabled()){
				log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
			}
		} catch (Throwable e) {
			long end = System.currentTimeMillis();
			if(log.isInfoEnabled()){
				log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
			}
		}
	}
	
	//配置後置返回通知,使用在方法aspect()上注冊的切入點
	@AfterReturning("aspect()")
	public void afterReturn(JoinPoint joinPoint){
		if(log.isInfoEnabled()){
			log.info("afterReturn " + joinPoint);
		}
	}
	
	//配置拋出異常後通知,使用在方法aspect()上注冊的切入點
	@AfterThrowing(pointcut="aspect()", throwing="ex")
	public void afterThrow(JoinPoint joinPoint, Exception ex){
		if(log.isInfoEnabled()){
			log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
		}
	}
	
}
測試代碼:
/**
 * Spring AOP測試
 * @author Shenghany
 * @date 2013-5-28
 */
public class Tester {

	private final static Log log = LogFactory.getLog(Tester.class);
	
	public static void main(String[] args) {
		//啟動Spring容器
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		//獲取service組件
		UserService service = (UserService) context.getBean("userService");
		//以普通的方式調用UserService對象的三個方法
		User user = service.get(1L);
		service.save(user);
		try {
			service.delete(1L);
		} catch (Exception e) {
			if(log.isWarnEnabled()){
				log.warn("Delete user : " + e.getMessage());
			}
		}
	}
}

控制台輸出如下:

 

 INFO [spring.aop.aspect.ServiceAspect:40] before execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
 INFO [spring.aop.service.UserService:19] getUser method . . .
 INFO [spring.aop.aspect.ServiceAspect:60] around execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))	Use time : 42 ms!
 INFO [spring.aop.aspect.ServiceAspect:48] after execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
 INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
 INFO [spring.aop.aspect.ServiceAspect:40] before execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
 INFO [spring.aop.service.UserService:26] saveUser method . . .
 INFO [spring.aop.aspect.ServiceAspect:60] around execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))	Use time : 2 ms!
 INFO [spring.aop.aspect.ServiceAspect:48] after execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
 INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
 INFO [spring.aop.aspect.ServiceAspect:40] before execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
 INFO [spring.aop.service.UserService:32] delete method . . .
 INFO [spring.aop.aspect.ServiceAspect:65] around execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))	Use time : 5 ms with exception : spring aop ThrowAdvice演示
 INFO [spring.aop.aspect.ServiceAspect:48] after execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
 INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
 WARN [studio.spring.aop.Tester:32] Delete user : Null return value from advice does not match primitive return type for: public boolean cn.ysh.studio.spring.aop.service.UserService.delete(long) throws java.lang.Exception
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"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


	<!-- 系統服務組件的切面Bean -->
	<bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>
	<!-- AOP配置 -->
	<aop:config>
		<!-- 聲明一個切面,並注入切面Bean,相當於@Aspect -->
		<aop:aspect id="simpleAspect" ref="serviceAspect">
			<!-- 配置一個切入點,相當於@Pointcut -->
			<aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
			<!-- 配置通知,相當於@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
			<aop:before pointcut-ref="simplePointcut" method="before"/>
			<aop:after pointcut-ref="simplePointcut" method="after"/>
			<aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
			<aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
		</aop:aspect>
	</aop:config>

</beans>

 

通常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:

1)execution(* *(..))

表示匹配所有方法

2)execution(public * com. savage.service.UserService.*(..))
表示匹配com.savage.server.UserService中所有的公有方法
3)execution(* com.savage.server..*.*(..))
表示匹配com.savage.server包及其子包下的所有方法
除了execution表示式外,還有within、this、target、args等Pointcut表示式。一個Pointcut定義由Pointcut表示式和Pointcut簽名組成

 

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 
  • modifiers-pattern:方法的操作權限
  • :返回值
  • :方法所在的包
  • :方法名
  • :參數名
  • :異常
ret-type-patterndeclaring-type-patternname-patternparm-patternthrows-pattern

其中,除ret-type-pattern和name-pattern之外,其他都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值為任意類型;方法名任意;參數不作限制的所有方法。

 

Pointcut定義時,還可以使用&&、||、! 運算,如

 

來源:http://ntzrj513.blog.163.com/blog/static/27945612201362232315/

 來源:http://www.eclipse.org/aspectj/doc/released/runtime-api/org/aspectj/lang/Signature.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved