在spring中使用aspectj有2種方式:
- xml配置
- 注解配置(推薦)
這2種方式需要添加的jar包都一樣(待修改):
- spring-aop.RELEASE.jar
- aspectjweaver.jar
在spring中使用aspectj,不需要添加aspectjrt.jar,也不需要專門的ajc編譯器,使用javac編譯即可。
xml配置方式
(1)目標接口、目標類
新建包com.chy.dao,包下新建接口UserDao、實現類UserDaoImpl:
public interface UserDao { public void addUser(); public void deleteUser(); }
public class UserDaoImpl implements UserDao{ @Override public void addUser() { System.out.println("正在添加用戶..."); } @Override public void deleteUser() { System.out.println("正在刪除用戶..."); } }
(2)切面
新建包com.chy.aspect,包下新建類UserDaoAspect:
public class UserDaoAspect { //前置通知要調用的方法 public void before(){ System.out.println("正在執行前置通知..."); } //后置通知要調用的方法 public void after(){ System.out.println("正在執行后置通知..."); } //返回通知要調用的方法 public void afterReturning(Object obj){ System.out.println("正在執行返回通知..."); System.out.println("目標方法的返回值是:"+obj); } // 異常通知要調用的方法 public void afterThrowing(JoinPoint point,Exception e){ System.out.println("異常信息:"+e.getMessage()); } }
(3)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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目標對象--> <bean name="userDaoImpl" class="com.chy.dao.UserDaoImpl" /> <!--切面--> <bean name="userDaoAspect" class="com.chy.aspect.UserDaoAspect" /> <!--AOP配置--> <aop:config> <!--全局切入點,所有切面都可以引用。配置切入點只能用id,不能用name --> <!-- <aop:pointcut id="pointCut" expression="execution(* com.chy.dao.UserDaoImpl.*(..))" /> --> <!--一個<aop:aspect>配置一個切面--> <aop:aspect ref="userDaoAspect"> <!--局部切入點,只能在此切面中引用--> <aop:pointcut id="pointCut" expression="execution(* com.chy.dao.UserDao.*(..))" /> <!--前置通知--> <aop:before method="before" pointcut-ref="pointCut" /> <!--可以引用切入點,也可以現配--> <!-- <aop:before method="before" pointcut="execution(* com.chy.dao.UserDao.*(..))"/> --> <!--后置通知--> <aop:after method="after" pointcut-ref="pointCut" /> <!--返回通知--> <!--如果要使用目標方法的返回值,可以用returning要將目標方法的返回值傳遞給返回通知要調用的方法的哪個形參--> <aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="obj"/> <!--異常通知--> <!--如果要使用捕獲的異常對象,可以用throwing指定要將異常對象傳遞給哪個形參--> <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="e"/> </aop:aspect> </aop:config> </beans>
5種通知對應的方法都可以傳遞JoinPoint型的參數,不用傳遞實參,只有目標方法的返回值、捕獲的異常需要傳實參。
returning是返回通知的特有屬性,throwing是異常通知的特有屬性。
環繞通知
環繞通知與其它通知有重疊,通常不與其它通知一起使用。
// 環繞通知 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //前增強 System.out.println("正在執行前增強..."); //調用目標方法 Object object=proceedingJoinPoint.proceed(); //后增強 System.out.println("正在執行后增強..."); return object; }
<!--環繞通知--> <!--ProceedingJoinPoint是JoinPoint的子類,不必手動傳參--> <aop:around method="around" pointcut-ref="pointCut" />
注解配置方式
(1)目標接口、目標類
public interface UserDao { public void addUser(); public void deleteUser(); }
@Repository public class UserDaoImpl implements UserDao{ @Override public void addUser() { System.out.println("正在添加用戶..."); } @Override public void deleteUser() { System.out.println("正在刪除用戶..."); } }
(2)切面
@Component @Aspect public class UserDaoAspect { //配置切入點 @Pointcut("execution(* com.chy.dao.UserDao.*(..))") private void pointCut(){} /* 前置通知 @Before(value="pointCut()"),只有一個屬性時可以只寫屬性值 @Before("execution(* com.chy.dao.UserDao.*(..))"),屬性值可以引用切入點,也可以現配 */ @Before("pointCut()") public void before(){ System.out.println("正在執行前置通知..."); } //后置通知 @After("pointCut()") public void after(){ System.out.println("正在執行后置通知..."); } //返回通知。可傳遞目標方法的返回值。 @AfterReturning(value = "pointCut()",returning = "obj" ) public void afterReturning(Object obj){ System.out.println("正在執行返回通知..."); System.out.println("目標方法的返回值是:"+obj); } // 異常通知。可傳遞異常對象。 @AfterThrowing(value = "pointCut()",throwing = "e") public void afterThrowing(JoinPoint point,Exception e){ System.out.println("異常信息:"+e.getMessage()); } }
環繞通知:
// 環繞通知 @Around("pointCut()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //前增強 System.out.println("正在執行前增強..."); //調用目標方法 Object object=proceedingJoinPoint.proceed(); //后增強 System.out.println("正在執行后增強..."); return object; }
(3)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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--使用包掃描,要掃描多個包時用逗號分隔--> <context:component-scan base-package="com.chy.dao,com.chy.aspect" /> <!--啟用AspectJ的注解--> <aop:aspectj-autoproxy /> </beans>
使用注解十分方便,xml文件也更加簡潔,推薦。
使用
不管是xml方式,還是注解方式,使用時都是一樣的:
(1)如果目標類實現了接口,不管切入點配置為接口、還是實現類:
execution(* com.chy.dao.UserDao.*(..)) execution(* com.chy.dao.UserDaoImpl.*(..))
使用時都只能使用接口(代理的是接口,不是具體的實現類):
UserDao userDao=applicationContext.getBean("userDaoImpl", UserDao.class); userDao.addUser();
紅色標出的兩處都只能使用接口。
(2)如果目標類沒有實現接口,那切入點只能配置為目標類:
execution(* com.chy.dao.UserDaoImpl.*(..))
使用時自然只能使用目標類:
UserDaoImpl userDao=applicationContext.getBean("userDaoImpl", UserDaoImpl.class); userDao.addUser();
調用方法時會自動增強。
AOP常見術語
- Joinpoint (連接點):目標類中的所有方法
- Pointcut(切入點):目標類中被增強的方法。有時候我們只增強目標類的部分方法。
- Weaving(織入):把增強應用到目標對象創建代理對象的過程。spring aop采用動態代理織入,aspectj采用在編譯期、類裝載期織入。