一、概述
Spring在此只是使用了和AspectJ一樣的註解,但並沒有使用AspectJ的編譯器或者織入器,底層依然使用的是Spring AOP,依然是在運行時動態生成AOP代理,並不依賴AspectJ的編譯器或者織入器。
二、例子與註釋
1、定義Aspect與@Before增強處理
package com.aspect.service;
//定義一個接口
public interface Hello {
public void sayHello();
public void addUser(String name,String pass);
}
定義一個切面Bean
當啓動@Aspect支持後,只要在Spring容器配置一個帶@Aspect註解的Bean,Spring會自動識別該Bean,並將該Bean作爲切面處理
package com.aspect.service;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//定義一個切面類,用@Aspect修飾,表示這個爲切入類
@Aspect
public class AuthAspect {
//匹配包下所有的類的sayHello方法
//匹配的方法執行作爲切入點
@Pointcut("execution(* *.sayHello(..))")
/*
* 第一個*號爲返回類型,表示所有類型
* 第二個星號表示包,這裏表示所有的包
* (..)表示任何參數
*/
private void select(){
System.out.println("==============啓動===============");
}
//@Before增強處理,指定切入點
@Before("select()")
public void authority(){
System.out.println("模擬執行權限檢查");
}
@Before("select()")
public void init(){
System.out.println("啓動測試");
}
@After("select()")
public void destory(){
System.out.println("結束測試後");
}
}
目標方法:
package com.aspect.service.impl;
import org.springframework.stereotype.Component;
import com.aspect.service.Hello;
@Component("helloImpl") //注入bean
public class HelloImpl implements Hello{
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("執行Hello組件的sayHello()方法");
}
public void addUser(String name, String pass) {
// TODO Auto-generated method stub
System.out.println("執行Hello組件的addUser添加用戶:"+name);
}
}
配置文件:
<context:annotation-config/>
<!-- 自動掃描指定包及其子包下的所有bean類 -->
<context:component-scan base-package="com.aspect.service,com.aspect.service.impl,com.home.bean,com.home.model">
<!-- 只是以Axe結尾的類當成Spring容器中的bean -->
<!-- type:指定過濾器類型
1.annotation:Annotation過濾器,該過濾器需要指定一個Annotation名,如lee.AnnotationTest
2.assignable:類過濾器,該過濾器直接指定一個java類
3.regex,正則表達式過濾器,指定過濾規則,使用如下
4.aspectJ:AspectJ過濾器,如org.example.*Service+
-->
<!--<context:include-filter type="regex" expression=".*Axe"/> -->
<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
<context:exclude-filter type="regex" expression=".*TeacherConfig"/>
<context:exclude-filter type="assignable" expression="org.springframework.context.support.ResourceBundleMessageSource"/>
<context:exclude-filter type="regex" expression="com.home.model.C*"/>
</context:component-scan>
<!-- 啓動@AspectJ支持 -->
<aop:aspectj-autoproxy/>
測試類:
package com.aspect.service.impl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ImplHelloTest {
public static void main(String[] args) throws Exception{
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
HelloImpl helloImpl=context.getBean("helloImpl",HelloImpl.class);
helloImpl.sayHello();
helloImpl.addUser("xieyongxue", "1234");
}
}
輸出如下:
啓動測試
模擬執行權限檢查
執行Hello組件的sayHello()方法
結束測試後
執行Hello組件的addUser添加用戶:xieyongxue
模擬方法結束後釋放資源...
2.其他幾個增強處理具體示例
1.before:前置通知(應用:各種校驗)
在方法執行前執行,如果通知拋出異常,阻止方法運行
2.afterReturning:後置通知(應用:常規數據處理)
方法正常返回後執行,如果方法中拋出異常,通知無法執行
必須在方法執行後才執行,所以可以獲得方法的返回值。
3.around:環繞通知(應用:十分強大,可以做任何事情) 【掌握】
方法執行前後分別執行,可以阻止方法的執行。要求必須手動的執行目標方法。
4.afterThrowing:拋出異常通知(應用:包裝異常信息)
方法拋出異常後執行,如果方法沒有拋出異常,無法執行
5.after:最終通知(應用:清理現場)
方法執行完畢後執行,無論方法中是否出現異常
package com.aspect.service;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class LogAspect {
@AfterReturning(returning="rvt",pointcut="execution(* *.addUser(..))")
//聲明rvt時指定的類型會限制目標方法必須返回指定類型的值或者咩有返回值
//此處將rvt的類型聲明爲Object,意味着對目標方法的返回值不做限制
public void log(int rvt){
System.out.println("獲取目標方法返回值:"+rvt);
System.out.println("模擬記錄日誌功能...");
}
@AfterThrowing(throwing="ex",pointcut="execution(* *.addUser(..))")
public void throwMsg(Throwable ex){
System.out.println("目標方法拋出的異常爲:"+ex);
System.out.println("模擬Advice對異常的修復....");
}
@After("execution(* *.addUser(..))")
public void release(){
System.out.println("模擬方法結束後釋放資源...");
}
}
package com.aspect.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TxAspect {
@Around("execution(* *.addPersons(..))")
public Object txProcess(ProceedingJoinPoint jp) throws java.lang.Throwable{
System.out.println("執行目標方法之前開始模式事務....");
//獲取目標方法原始調用的參數
Object[]args=jp.getArgs();
if(args.length>0 && args!=null){
//修改目標方法調用參數的第一個參數
args[0]=(Integer)args[0]*2;
}
//以改變後的參數去執行目標方法,並保存目標方法執行後的參數
Object rvt=jp.proceed(args);
System.out.println("執行目標方法後,模擬結束事務。。。。");
//如果rvt的類型是Integer,將rvt改爲它的平方,用於改變目標方法的返回值
if(rvt!=null && rvt instanceof Integer){
rvt=(Integer)rvt*(Integer)rvt;
}
return rvt;
}
}
package com.aspect.service.impl;
import org.springframework.stereotype.Component;
import com.aspect.service.Hello;
@Component
public class World implements Hello{
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("執行Hello組件的sayHello方法");
}
public void addUser(String name, String pass) {
// TODO Auto-generated method stub
System.out.println("執行Hello組件的addUser的方法");
if(name.length()<3){
throw new IllegalArgumentException("傳入名稱的參數長度過短");
}
}
public int addUser(int rvt){
System.out.println("執行帶返回值的addUser方法:"+rvt);
if(rvt<10){
throw new IllegalArgumentException("傳入的參數過小");
}
return rvt;
}
public int addPersons(int rvt){
System.out.println("執行帶返回值的addPerson()方法:"+rvt);
return rvt;
}
}
測試類:
package com.aspect.service.impl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class WorldTest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
World world=context.getBean("world",World.class);
world.addUser(20);
world.addUser("xieyongxue", "1234");
world.addPersons(10);
System.out.println("addPersons的返回值爲:"+world.addPersons(10));
}
}
輸出:
執行帶返回值的addUser方法:20
獲取目標方法返回值:20
模擬記錄日誌功能...
模擬方法結束後釋放資源...
執行Hello組件的addUser的方法
模擬方法結束後釋放資源...
執行目標方法之前開始模式事務....
執行帶返回值的addPerson()方法:20
執行目標方法後,模擬結束事務。。。。
執行目標方法之前開始模式事務....
執行帶返回值的addPerson()方法:20
執行目標方法後,模擬結束事務。。。。
addPersons的返回值爲:400