AOP 基於@AspectJ的AOP

在前面,咱們分別使用PointcutAdviceAdvisor接口來描述切點、加強、切面。而如今咱們使用@AdpectJ註解來描述。java

在下面的例子中,咱們是使用Spring自動掃描和管理Bean。spring

http://blog.csdn.net/p_3er/article/details/9239605app


3.6.1一個簡單的例子

a、目標類ide

@Repository public class UserDaoImpl implements UserDao { public void save() { System.out.println("保存用戶..."); } public void delete() { System.out.println("刪除用戶..."); } }


b、經過一個 POJO使用 @AspectJ管理切面函數

/*  * 經過@Aspect把這個類標識管理一些切面  */ @Aspect @Component public class FirstAspect { /*  * 定義切點及加強類型  */ @Before("execution(* save(..))") public void before(){ System.out.println("我是前置加強..."); } }


c、配置測試

        配置的時候要引入aop命名空間及打開@AspectJ切面驅動器this

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <!— 驅動器自動爲Spring容器中那些匹配@AspectJ切面的Bean建立代理,完成切面積入  --> <aop:aspectj-autoproxy/>


d、測試spa

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserDao userDao = (UserDao) context.getBean("userDaoImpl"); userDao.save(); userDao.delete();


e、結果.net

我是前置加強... 保存用戶... 刪除用戶...


3.6.2加強類型及其使用


@Before前置加強

          @Before(value= "切入點表達式或命名切入點",argNames = "指定命名切入點方法參數列表參數名字,能夠有多個用「,」分隔")代理

          3.6.1中的加強使用:@Before(value="execution(*save(..))")是缺省了value,直接鍵入值。


a、目標類

 

@Service public class UserService { public void say(String name){ System.out.println("service say:"+name); } public void run(String way){ System.out.println("service run:"+way); } }



 

 


b、經過一個 POJO使用 @AspectJ管理切面

@Aspect @Component public class BeforAspectj { /*  *單獨配置切點。給切點命名。 */ @Pointcut(value="execution(* say(..)) && args(param)",argNames="param") public void beforePointcut(String param){ } @Before(value="beforePointcut(s)",argNames="s") public void beforeAdvice(String s){ System.out.println("beforeAdvice:"+s); } }

或者把前面的切點定義那一塊直接定義到@Before加強的value中: 

@Aspect @Component public class BeforAspectj { @Before(value="execution(* say(..)) && args(s)",argNames="s") public void beforeAdvice(String s){ System.out.println("beforeAdvice:"+s); } }




c、配置和3.6.1中同樣

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>


d、測試

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService service = (UserService) context.getBean("userService"); service.say("zhangsan"); service.run("street");



e、結果

beforeAdvice:zhangsan service say:zhangsan service run:street



@AfterReturning後置加強

 

@AfterReturning(

value="切入點表達式或命名切入點",

pointcut="切入點表達式或命名切入點",

argNames="參數列表參數名",

returning="目標對象的返回值")

pointcutvalue是同樣的。若是使用pointcut來聲明,那麼前面聲明的value就沒用了。


a、目標類 

 

@Service public class UserService { public void say(String name){ System.out.println("service say:"+name); } public void run(String way){ System.out.println("service run:"+way); } public String getName(String name){ System.out.println("service getName:"+name); return "MR"+name; } }


 

 


b、經過一個 POJO使用 @AspectJ管理切面

@Aspect @Component public class AfterReturningAspectj { /*  * 即獲取返回值,又獲取傳入的參數  */ @AfterReturning( value="execution(* cn.framelife.spring..*.getName(..)) && args(sname)", returning="name", argNames="name,sname") public void afterGetNameAdvice(Object object,String sname){ System.out.println("afterGetNameAdvice:"+ (String)object+"--"+sname); } /*  * 只要加強,返回值和參數都不理會  */ @AfterReturning(value="execution(* cn.framelife.spring..*.run(..))") public void afterRunAdvice(){ System.out.println("afterRunAdvice"); } }



c、配置和3.6.1中同樣

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>


d、測試

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService service = (UserService) context.getBean("userService "); service.getName("zhangsan"); service.run("street");




e、結果

servicegetName:zhangsan afterGetNameAdvice:MRzhangsan--zhangsan servicerun:steet afterRunAdvice



 

 

@AfterThrowing異常拋出加強、@After Final加強

 

@AfterThrowing(

value="切入點表達式或命名切入點",

pointcut="切入點表達式或命名切入點",

argNames="參數列表參數名",

throwing="異常對應參數名")


@After( value="切入點表達式或命名切入點",argNames="參數列表參數名")


@After無論是拋出異常或者是正常退出,該加強都會執行。至關於try-catch-final裏的final代碼塊。


a、目標類

 

@Service public class UserService { public void tryThrow(){ System.out.println("service tryThrow"); throw new RuntimeException("i am a runtime exception"); } }

 


 


b、經過一個 POJO使用 @AspectJ管理切面

@Aspect @Component public class ThrowAspectj { @AfterThrowing(value="execution(* cn.framelife.spring..tryThrow(..))", throwing="exception", argNames="exception") public void afterThrowAdvisor(Exception exception){ System.out.println("afterThrowAdvisor:"+exception.getMessage()); } @After(value="execution(* cn.framelife.spring..tryThrow(..))") public void finalThrowAdvisor(){ System.out.println("finalThrowAdvisor"); } }




c、配置和3.6.1中同樣

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>


d、測試

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService service = (UserService) context.getBean("userService "); service.tryThrow();




e、結果

 

servicetryThrow afterThrowAdvisor:iam a runtime exception finalThrowAdvisor Exceptionin thread "main" java.lang.RuntimeException:i am a runtime exception


 


 

@Around環繞加強

 

@Around( value="切入點表達式或命名切入點",argNames="參數列表參數名")


 

a、目標類 

 

@Service public class UserService { public void round(){ System.out.println("service round"); } public void run(String way){ System.out.println("service run:"+way); } public String getName(String name){ System.out.println("service getName:"+name); return "MR"+name; } }


 

 


b、經過一個 POJO使用 @AspectJ管理切面

@Aspect @Component public class AroundAspect { @Around(value="execution(* cn.framelife.spring..round(..))") public Object aroundAdvisor(ProceedingJoinPoint point) throws Throwable{ System.out.println("before method"); Object target = point.proceed(); System.out.println("after method"); return target; } }




c、配置和3.6.1中同樣

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>


d、測試

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService service = (UserService) context.getBean("userService"); service.round(); service.run("street");






e、結果

 

beforemethod serviceround aftermethod servicerun:street afterRunAdvice


 


 

@DeclareParents引介加強

 

@DeclareParents(

value="AspectJ語法類型表達式",

defaultImpl=引入接口的默認實現類)


下面的例子是Waiter爲目標類,而後讓目標類擁有ISeller接口的功能:



 

a、兩個接口與兩個類


 

目標類與其接口:

 

public interface IWaiter { public void service(); }


@Component public class Waiter implements IWaiter { @Override public void service() { System.out.println("service"); } }


 

運行期織入到目標類的功能類與其接口:

public interface ISeller { public void sell(); }


public class Seller implements ISeller { @Override public void sell() { System.out.println("sell"); } }



b、經過一個 POJO使用 @AspectJ管理切面

@Aspect @Component public class DeclareAspect { /*  * value裏面配置目標類  * defaultImpl是功能類的實現類 */ @DeclareParents( value="cn.framelife.spring.aspectj.Waiter", defaultImpl=Seller.class) private ISeller seller; //使用功能類接口聲明一個對象 }



c、配置和3.6.1中同樣

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>


d、測試

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); IWaiter waiter = (IWaiter) context.getBean("waiter"); waiter.service(); ISeller seller = (ISeller)waiter; seller.sell();


e、結果

 

service sell



 

3.6.3切點函數


@開頭的函數都是針對註解類的。而不帶@的函數是針對普通類的。


execution()

execution(<修飾符模式>?<返回類型模式><方法名模式>(<參數模式>)<異常模式>?)

除了返回類型模式、方法名模式和參數模式外,其它都是可選的。

1)經過方法簽名定義切點

execution(public* *(..))

匹配全部目標類的public方法,但不匹配SmartSellerprotectedvoid showGoods()方法。第一個*表明返回類型,第二個*表明方法名,而..表明任意入參的方法;


execution(* *To(..))

匹配目標類全部以To爲後綴的方法。它匹配NaiveWaiterNaughtyWaitergreetTo()serveTo()方法。第一個*表明返回類型,而*To表明任意以To爲後綴的方法;


2)經過類定義切點

execution(*com.baobaotao.Waiter.*(..))

匹配Waiter接口的全部方法,它匹配NaiveWaiterNaughtyWaiter類的greetTo()serveTo()方法。第一個*表明返回任意類型,com.baobaotao.Waiter.*表明Waiter接口中的全部方法;


execution(*com.baobaotao.Waiter+.*(..))

匹配Waiter接口及其全部實現類的方法,它不但匹配NaiveWaiterNaughtyWaiter類的greetTo()serveTo()這兩個Waiter接口定義的方法,同時還匹配NaiveWaiter#smile()NaughtyWaiter#joke()這兩個不在Waiter接口中定義的方法。


3)經過類包定義切點

在類名模式串中,「.*」表示包下的全部類,而「..*」表示包、子孫包下的全部類。


execution(*com.baobaotao.*(..))

匹配com.baobaotao包下全部類的全部方法;


execution(*com.baobaotao..*(..))

匹配com.baobaotao包、子孫包下全部類的全部方法,如com.baobaotao.daocom.baobaotao.servier以及com.baobaotao.dao.user包下的全部類的全部方法都匹配。「..」出如今類名中時,後面必須跟「*」,表示包、子孫包下的全部類;


execution(*com..*.*Dao.find*(..))

匹配包名前綴爲com的任何包下類名後綴爲Dao的方法,方法名必須以find爲前綴。如com.baobaotao.UserDao#findByUserId()com.baobaotao.dao.ForumDao#findById()的方法都匹配切點。


4)經過方法入參定義切點

切點表達式中方法入參部分比較複雜,可使用「*」和「..」通配符,其中「*」表示任意類型的參數,而「..」表示任意類型參數且參數個數不限。


execution(*joke(String,int)))

匹配joke(String,int)方法,且joke()方法的第一個入參是String,第二個入參是int。它匹配NaughtyWaiter#joke(String,int)方法。若是方法中的入參類型是java.lang包下的類,能夠直接使用類名,不然必須使用全限定類名,如joke(java.util.List,int)


execution(*joke(String,*)))

匹配目標類中的joke()方法,該方法第一個入參爲String,第二個入參能夠是任意類型,如joke(Strings1,String s2)joke(Strings1,double d2)都匹配,但joke(Strings1,double d2,String s3)則不匹配;


execution(*joke(String,..)))

匹配目標類中的joke()方法,該方法第一個入參爲String,後面能夠有任意個入參且入參類型不限,如joke(Strings1)joke(Strings1,String s2)joke(Strings1,double d2,String s3)都匹配。


execution(*joke(Object+)))

匹配目標類中的joke()方法,方法擁有一個入參,且入參是Object類型或該類的子類。它匹配joke(Strings1)joke(Clientc)。若是咱們定義的切點是execution(*joke(Object)),則只匹配joke(Objectobject)而不匹配joke(Stringcc)joke(Clientc)

args()

該函數的入參是一個類名,表示目標類的方法入參對象是指定的類(包含子類),切點匹配。它容許類後後使用+通配符後綴,但添加也不添加+效果是同樣的。



例子:a、一個實體類

public class User implements Serializable { }


b、目標類

@Service public class UserService { public void say(User user){ System.out.println("say"); } }



c、經過一個 POJO使用 @AspectJ管理切面

@Aspect @Component public class BeforAspectj { @Before(value="args(cn.framelife.spring.aspectj.User)") public void beforeAdvice(){ System.out.println("beforeAdvice"); } }




d、配置和3.6.1中同樣

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>


e、測試

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService service = (UserService) context.getBean("userService"); service.say(new User());




f、結果

beforeAdvice say



within()

經過類的匹配模式串聲明切點。該函數定義的鏈接點是針對目標類(不能是接口)而言,而不是針對運行時的對象,這與execution()函數相同。Execution()所指定的鏈接點能夠是小到參數,而within()指定的鏈接點最小隻能是類。

within(cn.framelife.spring.aspectj.UserService)

匹配UserService類下全部的方法。

within(cn.framelife.spring.aspectj.*)

匹配cn.framelife.spring.aspectj包下所的的類,但不包括子孫包。cn.framelife.spring.aspectj.abc.AbcService是不匹配的。

within(cn.framelife.spring.aspectj..*)

匹配cn.framelife.spring.aspectj包及其子孫包下所的的類。

target()this()

target()函數是經過判斷目標類是否按類型匹配指定類決定鏈接點是否匹配。

target(cn.framelife.spring.aspectj.UserService)

匹配這UserService類及其子孫類的全部方法。若是UserService是一個接口,那麼會匹配UserService的實現類及實現類的子孫類中的全部的方法。

 

this()函數是經過判斷代理類是否按類型匹配指定類決定鏈接點是否匹配。

通常狀況下,使用this()target()定義切點,二者是等效的:

target(cn.framelife.spring.aspectj.UserService)this(cn.framelife.spring.aspectj.UserService)是同樣的。不管UserService是一個類仍是一個接口。

二者區別體如今經過引介切面產生代理對象時。


@annotation()

表示標註了某個自定義註解的方法,使用切面。


例子:a、自定義一個方法級別的註解

 

@Retention(value=RetentionPolicy.RUNTIME) @Target(value=ElementType.METHOD) public @interface BeforeAdvisor { boolean value() default false; }


 

b、經過一個 POJO使用 @AspectJ管理切面

@Aspect @Component public class BeforAspectj { @Before(value="@annotation(cn.framelife.spring.aspectj.annotation.BeforeAdvisor)") public void beforeAdvice(){ System.out.println("beforeAdvice"); } }


c、配置和3.6.1中同樣

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>


d、目標類

@Service public class UserService { @BeforeAdvisor public void annotation(){ System.out.println("annotation"); } }



e、測試

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService service = (UserService) context.getBean("userService"); service.annotation();


f、結果

beforeAdvice annotation


@args()

該函數的入參是一個註解類的類名,表示運行時目標類方法的入參對象的類標註了指定的註解。


例子:a、自定義註解類

 

 

@Retention(value=RetentionPolicy.RUNTIME) @Target(value=ElementType.TYPE) public @interface UserAnnotation { }


 

b、切面管理

 

@Aspect @Component public class BeforAspectj { @Before(value="@args(cn.framelife.spring.aspectj.annotation.UserAnnotation)") public void beforeAdvice(){ System.out.println("beforeAdvice"); } }


c、實體類(使用上面定義的註解)

 

 

@UserAnnotation public class User implements Serializable { }


d、目標類

 

 

@Service public class UserService { public void annotation(){ System.out.println("annotation"); } public void say(User user){ System.out.println("say"); } }


e、配置和3.6.1中同樣

 

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <aop:aspectj-autoproxy/>

 

f、測試

 

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService service = (UserService) context.getBean("userService"); service.say(new User());


g、結果

 

 

beforeAdvice say



 

 

@within@target()

 

@winthin(A)匹配任意標註了@A的目標類。@target(A)匹配@A的類及子孫類。


@annotation是標註在目標類的方法

@args是標註目標類方法的入參對象的類

@winthin@target是標註目標類


注意

上面的函數(特別是args())除了能夠指定類名外,還能夠指定參數名,將目標對象鏈接點上的方法入參綁定到加強的方法中。如:@Before中的例子。


3.6.4通配符與邏輯運算符

@Aspectj支持3種通配符:

*匹配任意字符,但它只能匹配上下文中的一個元素

..匹配任意字符,能夠匹配上下文中的多個元素,但在表示類時,必須和*聯合使用,而在表示入參時則單獨使用。

+表示按類型匹配指定類及其子孫類,必須跟在類名後面。如cn.framelife.spring.UserService+表示UserService類及其子類。


函數支持:

支持全部的通配符的函數:execution()within()

僅支持+通配符的函數:args()this()targ()。雖然這三個函數能夠支持+通配符,但對於這些函數來講使用和不使用+都是同樣的。

不支持通配符的函數:@args@within@target()@annotation()。也就是全部用於註解上的函數都不支持通配符。


@Aspectj支持的邏輯運算符:

&&

||

!

相關文章
相關標籤/搜索