1、實現MethodBeforeAdvice等接口
pom.xml添加spring核心依賴:
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
在Spring中,org.springframework.aop包下有四個接口,分別是MethodBeforeAdvice(前置通知)、AfterReturningAdvice(后置通知)、MethodInterceptor(環繞通知)、ThrowsAdvice(異常通知),其中,前三個接口都有對應的實現方法,分別實現后就可以在對應的通知方法中添加功能,但是ThrowsAdvice異常通知沒有實現方法,所以需要自定義一個方法,不過對方法名有規定,必須寫成afterThrowing,代碼如下:
定義一個切面類UserServiceAdvice:
public class UserServiceAspect implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor, ThrowsAdvice { /** * 后置通知 * @param o * @param method * @param objects * @param o1 * @throws Throwable */ @Override public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("后置通知"); } /** * 前置通知 * @param method * @param objects * @param o * @throws Throwable */ @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("前置通知。。。"); } @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("環繞通知前。。。"); Object returnVal = methodInvocation.proceed(); System.out.println("環繞通知后。。。"); return returnVal; } /** * 異常通知,參照MethodBeforeAdvice, * 該方法的方法名必須叫做afterThrowing, * method,args,target這三個參數可以省略,要么全部聲明 * Exception必須保留 * @param e */ public void afterThrowing(Exception e){ System.out.println("產生了異常:"+e.getMessage()); } }
在resources目錄下創建一個Spring配置文件applicationContext.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 http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 目標對象 --> <bean id="userService" class="edu.nf.ch12.service.impl.UserServiceImpl" /> <!-- 切面 --> <bean id="userServiceAspect" class="edu.nf.ch12.service.aspect.UserServiceAspect" /> <!-- aop配置, --> <aop:config proxy-target-class="false"> <!-- 配置切入點,使用aspectJ的切入點表達式, 表達式語法: 1、execution(訪問修飾符 方法返回值 包名.類名.方法名(參數類型)) execution是切入到方法級別的 2、within(訪問修飾符 方法返回值 包名.類名) within是切入到類級別的 說明:訪問修飾符可以省略, 方法返回值、包名、類名、方法名、可以使用*號進行統配, 方法參數可以指定參數的類型,也可以使用".."來標識任意類型和個數的參數 例如:execution(* edu.nf.ch12.service.impl.*.*(..)) 表示edu.nf.ch12.service.impl包下的所有類,以及任意返回值類和任意參數類型和個數的方法都會匹配--> <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch12.service.impl.UserServiceImpl.say(..))"/> <!-- 配置通知器(Advisor),其實就是切面 advice-ref引用上面定義的切面的id pointcut-ref引用上面定義的切入點的id--> <aop:advisor advice-ref="userServiceAspect" pointcut-ref="myPointcut"/> <!-- 當有多個切面,但是又不想共用一個切入點表達式的時候,那么使用pointcut屬性來重新制定切入點表達式 --> <aop:advisor advice-ref="demoAspect" pointcut="execution(* edu.nf.ch12.service.impl.UserServiceImpl.run())"/> </aop:config> </beans>
2、使用AspectJ配置,不實現任何接口
(1)使用配置文件配置切面
當使用AspectJ配置時,不需要實現任何接口,因為對這些通知的方法都是在配置文件中配置和綁定的。
創建一個切面類:
public class UserServiceAspect { /** * 前置通知 * @param jp 連接點,通過這個連接點可以獲取目標方法 */ public void before(JoinPoint jp){ System.out.println("前置通知,目標方法參數:" + jp.getArgs()[0]); } /** * 環繞通知 * @param pjp 連接點,可以獲取目標方法參數以及方法信息以及調用目標方法等等 */ public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("環繞通知前。。。"); //獲取目標方法的的Method對象 MethodSignature ms = (MethodSignature)pjp.getSignature(); Method method = ms.getMethod(); System.out.println("當前調用的目標方法:" + method.getName()); //調用目標方法 Object returnVal = pjp.proceed(); System.out.println("環繞通知后。。。"); return returnVal; } /** * 后置通知 * @param returnVal 目標方法的返回值 */ public void afterReturning(String returnVal){ System.out.println("后置通知,返回參數:" + returnVal); } /** * 異常通知 * @param e 目標方法產生的異常對象 */ public void afterThrowing(Throwable e){ System.out.println("異常通知,異常信息:" + e.getMessage()); } /** * 最終通知 */ public void after(){ System.out.println("最終通知"); } }
在resources目錄中創建一個Spring配置文件applicationContext.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 http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userService" class="edu.nf.ch13.service.impl.UserServiceImpl"/> <!-- 定義切面 --> <bean id="userServiceAspect" class="edu.nf.ch13.service.aspect.UserServiceAspect"/> <!-- 配置AOP,基於AspectJ --> <aop:config> <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch13.service.impl.UserServiceImpl.*(..))"/> <!-- 裝配切面,ref引用上面配置的切面的id --> <aop:aspect ref="userServiceAspect"> <!-- 裝配通知,method對應通知的方法名,pointcut-ref引用上面定義的切入點的id 如果不同的通知想使用不同的切入點,那么使用pointcut屬性進行自定義 --> <!-- 前置通知 --> <aop:before method="before" pointcut-ref="myPointcut"/> <!-- 環繞通知 --> <aop:around method="around" pointcut-ref="myPointcut"/> <!-- 后置通知,returning屬性指定后置通知方法的參數名(參數名稱要一致) --> <aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/> <!-- 異常通知,throwing屬性指定異常通知方法的參數名(名稱要一致) --> <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/> <!-- 最終通知 --> <aop:after method="after" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config> </beans>
(2)使用注解和配置類配置切面
在上面的applicationContext.xml配置文件中,對基於AspectJ的aop配置完全可以使用注解來配置,
在切面類中:
/** * AspectJ提供了所有通知注解,這些注解都有一個value屬性, * 屬性指定的是@Pointcut注解所標注的方法名,即pointcut(), * 如:@Around("pointcut()") * 如果不同通知想使用不同的切入點那么可以直接在value屬性中自定義 * 如:@Before("execution(* edu.nf.ch14.service.impl.UserServiceImpl.say())") */ @Component /** * @Aspect注解標識當前類為一個切面類 */ @Aspect public class UserServiceAspect extends AbstractAsPect { /** * 聲明一個切入點,@Pointcut聲明在一個方法上,在這里將這個方法放在了AbstractAspect這個類中 */ // @Pointcut("execution(* edu.nf.ch15.service.impl.UserServiceImpl.*(..))") // public void pointcut(){ // // } /** * 前置通知 * @param jp 連接點,通過這個連接點可以獲取目標方法 * pointcut()指定的是切入點注解所標注的方法名 * @Before注解的value屬性指定的是切入點注解所標注的方法名, * 結果不同通知想使用不同的切入點 */ @Before("execution(* edu.nf.ch15.service.impl.UserServiceImpl.say())") public void before(JoinPoint jp){ System.out.println("前置通知,目標方法參數:" + jp.getArgs()[0]); } /** * 環繞通知 * @param pjp 連接點,可以獲取目標方法參數以及方法信息以及調用目標方法等等 */ @Around("pointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("環繞通知前。。。"); //獲取目標方法的的Method對象 MethodSignature ms = (MethodSignature)pjp.getSignature(); Method method = ms.getMethod(); System.out.println("當前調用的目標方法:" + method.getName()); //調用目標方法 Object returnVal = pjp.proceed(); System.out.println("環繞通知后。。。"); return returnVal; } /** * 后置通知 * @param returnVal 目標方法的返回值 */ @AfterReturning(value = "pointcut()", returning = "returnVal") public void afterReturning(String returnVal){ System.out.println("后置通知,返回參數:" + returnVal); } /** * 異常通知 * @param e 目標方法產生的異常對象 */ @AfterThrowing(value = "pointcut()", throwing = "e") public void afterThrowing(Throwable e){ System.out.println("異常通知,異常信息:" + e.getMessage()); } /** * 最終通知 */ @After("pointcut()") public void after(){ System.out.println("最終通知"); } }
創建一個配置類,可以做到0配置,
SpringConfig:
@Configuration @ComponentScan("edu.nf.ch15") /** * 啟用AspectJ注解處理器,等同於xml中的<aop:aspectj-autoproxy/> */ @EnableAspectJAutoProxy public class SpringConfig { }