Spring系列之AOP實(shí)現(xiàn)的兩種方式

AOP常用的實(shí)現(xiàn)方式有兩種,一種是采用聲明的方式來(lái)實(shí)現(xiàn)(基于XML),一種是采用注解的方式來(lái)實(shí)現(xiàn)(基于AspectJ)。
首先復(fù)習(xí)下AOP中一些比較重要的概念:
Joinpoint(連接點(diǎn)):程序執(zhí)行時(shí)的某個(gè)特定的點(diǎn),在Spring中就是某一個(gè)方法的執(zhí)行 。
Pointcut(切點(diǎn)):說(shuō)的通俗點(diǎn),spring中AOP的切點(diǎn)就是指一些方法的集合,而這些方法是需要被增強(qiáng)、被代理的。一般都是按照一定的約定規(guī)則來(lái)表示的,如正則表達(dá)式等。切點(diǎn)是由一類連接點(diǎn)組成。
Advice(通知):還是說(shuō)的通俗點(diǎn),就是在指定切點(diǎn)上要干些什么。
Advisor(通知器):其實(shí)就是切點(diǎn)和通知的結(jié)合 。
一、基于XML配置的Spring AOP
采用聲明的方式實(shí)現(xiàn)(在XML文件中配置),大致步驟為:配置文件中配置pointcut, 在java中用編寫實(shí)際的aspect 類, 針對(duì)對(duì)切入點(diǎn)進(jìn)行相關(guān)的業(yè)務(wù)處理。
業(yè)務(wù)接口:
package?com.spring.service;
public?interface?IUserManagerService?{
????//查找用戶
????public?String findUser();
????
????//添加用戶
????public?void?addUser();
}業(yè)務(wù)實(shí)現(xiàn):
package?com.spring.service.impl;
import?com.spring.service.IUserManagerService;
public?class?UserManagerServiceImpl?implements?IUserManagerService{
????
private?String name;
????
????public?void?setName(String name){
????????this.name=name;
????}
????
????public?String getName(){
????????return?this.name;
????}
????
????public?String findUser(){
????????System.out.println("============執(zhí)行業(yè)務(wù)方法findUser,查找的用戶是:"+name+"=============");
????????return?name;
????}
????
????public?void?addUser(){
????????System.out.println("============執(zhí)行業(yè)務(wù)方法addUser=============");
????????//throw new RuntimeException();
????}
}切面類:
package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public?class?AopAspect?{
????
????/**
?????* 前置通知:目標(biāo)方法調(diào)用之前執(zhí)行的代碼
??????* @param jp
?????*/
????public?void?doBefore(JoinPoint jp){
????????System.out.println("===========執(zhí)行前置通知============");
????}
????
????/**
?????* 后置返回通知:目標(biāo)方法正常結(jié)束后執(zhí)行的代碼
??????* 返回通知是可以訪問(wèn)到目標(biāo)方法的返回值的
??????* @param jp
?????* @param result
?????*/
????public?void?doAfterReturning(JoinPoint jp,String result){
????????System.out.println("===========執(zhí)行后置通知============");
????????System.out.println("返回值result==================="+result);
????}
????
????/**
?????* 最終通知:目標(biāo)方法調(diào)用之后執(zhí)行的代碼(無(wú)論目標(biāo)方法是否出現(xiàn)異常均執(zhí)行)
??????* 因?yàn)榉椒赡軙?huì)出現(xiàn)異常,所以不能返回方法的返回值
??????* @param jp
?????*/
????public?void?doAfter(JoinPoint jp){
????????System.out.println("===========執(zhí)行最終通知============");
????}
????
????/**
?????*
?????* 異常通知:目標(biāo)方法拋出異常時(shí)執(zhí)行的代碼
??????* 可以訪問(wèn)到異常對(duì)象
??????* @param jp
?????* @param ex
?????*/
????public?void?doAfterThrowing(JoinPoint jp,Exception ex){
????????System.out.println("===========執(zhí)行異常通知============");
????}
????
????/**
?????* 環(huán)繞通知:目標(biāo)方法調(diào)用前后執(zhí)行的代碼,可以在方法調(diào)用前后完成自定義的行為。
??????* 包圍一個(gè)連接點(diǎn)(join point)的通知。它會(huì)在切入點(diǎn)方法執(zhí)行前執(zhí)行同時(shí)方法結(jié)束也會(huì)執(zhí)行對(duì)應(yīng)的部分。
??????* 主要是調(diào)用proceed()方法來(lái)執(zhí)行切入點(diǎn)方法,來(lái)作為環(huán)繞通知前后方法的分水嶺。
??????*
?????* 環(huán)繞通知類似于動(dòng)態(tài)代理的全過(guò)程:ProceedingJoinPoint類型的參數(shù)可以決定是否執(zhí)行目標(biāo)方法。
??????* 而且環(huán)繞通知必須有返回值,返回值即為目標(biāo)方法的返回值
??????* @param pjp
?????* @return
?????* @throws Throwable
?????*/
????public?Object doAround(ProceedingJoinPoint pjp) throws Throwable{
????????System.out.println("======執(zhí)行環(huán)繞通知開(kāi)始=========");
?????????// 調(diào)用方法的參數(shù)
????????Object[] args = pjp.getArgs();
????????// 調(diào)用的方法名
????????String method = pjp.getSignature().getName();
????????// 獲取目標(biāo)對(duì)象
????????Object target = pjp.getTarget();
????????// 執(zhí)行完方法的返回值
????????// 調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
????????Object result=pjp.proceed();
????????System.out.println("輸出,方法名:"?+ method + ";目標(biāo)對(duì)象:"?+ target + ";返回值:"?+ result);
????????System.out.println("======執(zhí)行環(huán)繞通知結(jié)束=========");
????????return?result;
????}
}Spring配置:
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:aop="http://www.springframework.org/schema/aop"
????xmlns:p="http://www.springframework.org/schema/p"
????xsi:schemaLocation="http://www.springframework.org/schema/beans
????http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
????http://www.springframework.org/schema/tx
????http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
????http://www.springframework.org/schema/aop
????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
????
????
????<bean?id="userManager"?class="com.spring.service.impl.UserManagerServiceImpl">
????????<property?name="name"?value="lixiaoxi">property>
????bean>??
????
?????
????<bean?id="aspectBean"?class="com.spring.aop.AopAspect"?/>
????<aop:config>
?????<aop:aspect?ref="aspectBean">
????????<aop:pointcut?id="pointcut"?expression="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))"/>
????????
????????<aop:before?method="doBefore"?pointcut-ref="pointcut"/>?
????????<aop:after-returning?method="doAfterReturning"?pointcut-ref="pointcut"?returning="result"/>
????????<aop:after?method="doAfter"?pointcut-ref="pointcut"?/>?
????????<aop:around?method="doAround"?pointcut-ref="pointcut"/>?
????????<aop:after-throwing?method="doAfterThrowing"?pointcut-ref="pointcut"?throwing="ex"/>
??????aop:aspect>
???aop:config>
beans>測(cè)試類:
package?com.spring.test;
import?org.springframework.context.ApplicationContext;
import?org.springframework.context.support.ClassPathXmlApplicationContext;
import?com.spring.service.IUserManagerService;
public?class?TestAop?{
????public?static?void?main(String[] args)?throws?Exception{
????????
????????ApplicationContext act = new?ClassPathXmlApplicationContext("applicationContext3.xml");
?????????IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");
?????????userManager.findUser();
?????????System.out.println("\n");
?????????userManager.addUser();
????}
}測(cè)試結(jié)果:

?二、使用注解配置AOP
采用注解來(lái)做aop, 主要是將寫在spring 配置文件中的連接點(diǎn)寫到注解里面。
業(yè)務(wù)接口和業(yè)務(wù)實(shí)現(xiàn)與上邊一樣,具體切面類如下:
package?com.spring.aop;
import?org.aspectj.lang.JoinPoint;
import?org.aspectj.lang.ProceedingJoinPoint;
import?org.aspectj.lang.annotation.After;
import?org.aspectj.lang.annotation.AfterReturning;
import?org.aspectj.lang.annotation.AfterThrowing;
import?org.aspectj.lang.annotation.Around;
import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.Before;
@Aspect
public?class?AopAspectJ?{
????
????/**
?????* 必須為final String類型的,注解里要使用的變量只能是靜態(tài)常量類型的
?????*/??
????public?static?final?String EDP="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))";
????
????/**
?????* 切面的前置方法 即方法執(zhí)行前攔截到的方法
??????* 在目標(biāo)方法執(zhí)行之前的通知
??????* @param?jp
?????*/
????@Before(EDP)
????public?void?doBefore(JoinPoint jp){
????????
????????System.out.println("=========執(zhí)行前置通知==========");
????}
????
????
????/**
?????* 在方法正常執(zhí)行通過(guò)之后執(zhí)行的通知叫做返回通知
??????* 可以返回到方法的返回值 在注解后加入returning
?????* @param?jp
?????* @param?result
?????*/
????@AfterReturning(value=EDP,returning="result")
????public?void?doAfterReturning(JoinPoint jp,String result){
????????System.out.println("===========執(zhí)行后置通知============");
????}
????
????/**
?????* 最終通知:目標(biāo)方法調(diào)用之后執(zhí)行的通知(無(wú)論目標(biāo)方法是否出現(xiàn)異常均執(zhí)行)
??????* @param?jp
?????*/
????@After(value=EDP)
????public?void?doAfter(JoinPoint jp){
????????System.out.println("===========執(zhí)行最終通知============");
????}
????
????/**
?????* 環(huán)繞通知:目標(biāo)方法調(diào)用前后執(zhí)行的通知,可以在方法調(diào)用前后完成自定義的行為。
??????* @param?pjp
?????* @return
?????* @throws?Throwable
?????*/
????@Around(EDP)
????public?Object doAround(ProceedingJoinPoint pjp)?throws?Throwable{
????????System.out.println("======執(zhí)行環(huán)繞通知開(kāi)始=========");
????????// 調(diào)用方法的參數(shù)
????????Object[] args = pjp.getArgs();
????????// 調(diào)用的方法名
????????String method = pjp.getSignature().getName();
????????// 獲取目標(biāo)對(duì)象
????????Object target = pjp.getTarget();
????????// 執(zhí)行完方法的返回值
????????// 調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
????????Object result=pjp.proceed();
????????System.out.println("輸出,方法名:"?+ method + ";目標(biāo)對(duì)象:"?+ target + ";返回值:"?+ result);
????????System.out.println("======執(zhí)行環(huán)繞通知結(jié)束=========");
????????return?result;
????}
????
????/**
?????* 在目標(biāo)方法非正常執(zhí)行完成, 拋出異常的時(shí)候會(huì)走此方法
??????* @param?jp
?????* @param?ex
?????*/
????@AfterThrowing(value=EDP,throwing="ex")
????public?void?doAfterThrowing(JoinPoint jp,Exception ex)?{
????????System.out.println("===========執(zhí)行異常通知============");
????}
}spring的配置:
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:aop="http://www.springframework.org/schema/aop"
????xmlns:p="http://www.springframework.org/schema/p"
????xsi:schemaLocation="http://www.springframework.org/schema/beans
????http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
????http://www.springframework.org/schema/tx
????http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
????http://www.springframework.org/schema/aop
????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
????
????
????<aop:aspectj-autoproxy/>????
????
????
????<bean?id="userManager"?class="com.spring.service.impl.UserManagerServiceImpl">
????????<property?name="name"?value="lixiaoxi">property>
????bean>???
????
?????
????<bean?id="aspectBean"?class="com.spring.aop.AopAspectJ"?/>
beans>測(cè)試類:
package?com.spring.test;
import?org.springframework.context.ApplicationContext;
import?org.springframework.context.support.ClassPathXmlApplicationContext;
import?com.spring.service.IUserManagerService;
public?class?TestAop1?{
????public?static?void?main(String[] args)?throws?Exception{
????????
????????ApplicationContext act = new?ClassPathXmlApplicationContext("applicationContext4.xml");
?????????IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");
?????????userManager.findUser();
?????????System.out.println("\n");
?????????userManager.addUser();
????}
}測(cè)試結(jié)果與上面相同。
注意:
1.環(huán)繞方法通知,環(huán)繞方法通知要注意必須給出調(diào)用之后的返回值,否則被代理的方法會(huì)停止調(diào)用并返回null,除非你真的打算這么做。??????????
2.只有環(huán)繞通知才可以使用JoinPoint的子類ProceedingJoinPoint,各連接點(diǎn)類型可以調(diào)用代理的方法,并獲取、改變返回值。
補(bǔ)充:
1.
.
.
.
例如定義切入點(diǎn)表達(dá)式 execution(* com.sample.service.impl..*.*(..))
execution()是最常用的切點(diǎn)函數(shù),其語(yǔ)法如下所示:
整個(gè)表達(dá)式可以分為五個(gè)部分:
(1)、execution(): 表達(dá)式主體。
(2)、第一個(gè)*號(hào):表示返回類型,*號(hào)表示所有的類型。
(3)、包名:表示需要攔截的包名,后面的兩個(gè)句點(diǎn)表示當(dāng)前包和當(dāng)前包的所有子包,com.sample.service.impl包、子孫包下所有類的方法。
(4)、第二個(gè)*號(hào):表示類名,*號(hào)表示所有的類。
(5)、*(..):最后這個(gè)星號(hào)表示方法名,*號(hào)表示所有的方法,后面括弧里面表示方法的參數(shù),兩個(gè)句點(diǎn)表示任何參數(shù)。
原文鏈接:cnblogs.com/xiaoxi/p/5981514.html
