基於xml的spring-aop配置
- 把通知bean也交給spring管理
- 使用
aop:config
標籤表明開始AOP的配置 - 使用
aop:aspect
標籤表明開始配置切面
id:給切面提供唯一標識
ref:指定通知類bean的id - 在
aop:aspect
標籤的內部使用對應的標籤來配置通知的類型
讓printLog方法在切入點方法執行之前執行,前置通知 aop:before
method屬性:指定Logger類中哪個方法是前置通知。
pointcut屬性:指定切入點表達式,該表達式的含義指的是對業務層中哪些方法增強。
切入點表達式
-
寫法:execution(訪問修飾符 返回值 包名.包名……類名.方法名(參數列表))
-
例:
execution(public void com.smday.service.impl.AccountServiceImpl.saveAccount())
-
訪問修飾符可以省略,返回值可以使用通配符*匹配。
-
包名也可以使用
*
匹配,數量代表包的層級,當前包可以使用..
標識,例如* *..AccountServiceImpl.saveAccount()
-
類名和方法名也都可以使用
*
匹配:* *..*.*()
-
參數列表使用
..
可以標識有無參數均可,且參數可爲任意類型。
全通配寫法:* ….*(…)
通常情況下,切入點應當設置再業務層實現類下的所有方法:* com.smday.service.impl.*.*(..)
。
配置通知
<!--spring-ioc配置-->
<bean id="accountService" class="com.smday.service.impl.AccountServiceImpl"></bean>
<!--spring-aop配置-->
<!--配置Logger類-->
<bean id="logger" class="com.smday.utils.Logger"></bean>
<!--配置aop-->
<aop:config>
<!--配置切入點表達式 id 指定表達式的唯一標誌- expression屬性指定表達式內容
(此標籤寫在aop:aspect標籤內部只能當前切面使用) 還可以寫在aop:aspect,此時所有切面可用
-->
<aop:pointcut id="pt1" expression="execution(* com.smday.service.impl.*.*(..))"></aop:pointcut>
<aop:aspect id="logAdvise" ref="logger">
<!--配置通知的類型並且通知方法和切入點方法的關聯-->
<aop:before method="printLog" pointcut="execution(* com.smday.service.impl.*.*(..))"></aop:before>
<!--前置通知:在切入點方法執行之前執行-->
<aop:before method="beforePrintLog" pointcut-ref="pt1" ></aop:before>
<!--後置通知:在切入點方法正常執行之後執行,他和異常通知永遠只能執行一個-->
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1" ></aop:after-returning>
<!--異常通知:在切入點方法執行產生異常之後執行-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
<!--最終通知:無論切入點方法是否正常執行它都會在其後面執行-->
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
<!--環繞通知-->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
</aop:aspect>
</aop:config>
環繞通知
<!--環繞通知--><aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
/**
* 環繞通知
*
* 問題:配置環繞通知後,切入點的方法沒有執行,而通知方法執行而來.
* 分析:通過對比動態代理中的環繞通知代碼,發現動態代理的環繞通知有明確的切入點方法調用,而我們代碼中沒有.
* 解決:spring框架提供了ProceedingJoinPoint接口,接口中的proceed()方法,相當於明確調用切入點方法.
* 該接口可以作爲環繞通知的方法參數,在程序執行時,spring框架我會爲我們提供該接口的實現類供我們調用
*
* spring中的環繞通知:
* spring框架爲我們提供的一種可以在代碼中手動控制增強方法何時執行的方式
*/
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
//得到方法執行的所需參數
Object[] args = pjp.getArgs();
System.out.println("前置");
//明確調用業務層的方法(切入點方法)
rtValue = pjp.proceed(args);
System.out.println("後置");
}catch (Throwable t){
System.out.println("異常");
throw new RuntimeException(t);
}finally {
System.out.println("最終");
}
return rtValue;
}
基於註解+xml的spring-aop配置
<?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
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置spring創建容器時要掃描的包-->
<context:component-scan base-package="com.smday"></context:component-scan>
<!--配置spring開啓註解aop的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* com.smday.service.impl.*.*(..))")
private void pt1(){}
@Before("pt1()")
public void beforePrintLog() {
System.out.println("Logger.beforePrintLog");
}
@AfterReturning("pt1()")
public void afterReturningPrintLog() {
System.out.println("Logger.afterReturningPrintLog");
}
@AfterThrowing("pt1()")
public void afterThrowingPrintLog() {
System.out.println("Logger.afterThrowingPrintLog");
}
@After("pt1()")
public void afterPrintLog() {
System.out.println("Logger.afterPrintLog");
}
}
基於註解的方式,最終通知的調用將會出現在後置或者異常通知前,建議使用環繞通知。
環繞通知
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* com.smday.service.impl.*.*(..))")
private void pt1(){}
@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
//得到方法執行的所需參數
Object[] args = pjp.getArgs();
System.out.println("前置");
//明確調用業務層的方法(切入點方法)
rtValue = pjp.proceed(args);
System.out.println("後置");
} catch (Throwable t) {
System.out.println("異常");
throw new RuntimeException(t);
} finally {
System.out.println("最終");
}
return rtValue;
}
}
基於純註解的spring-aop配置
創建配置類
@Configuration
@ComponentScan(basePackages = "com.smday")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
獲取容器
public class aopTest {
public static void main(String[] args) {
//獲取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//獲取對象
AccountService as = ac.getBean("accountService", AccountService.class);
//執行方法
as.saveAccount();
}
}