基於 XML 配置文件的 AOP
一、創建切入點類
public class PointCutBean {
public void sayHello() {
System.out.println("Hello World!");
}
}
二、創建通知類
public class AdviceBean {
public void writeLine() {
System.out.println("------------------------------");
}
}
三、創建 Spring 配置文件
<?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="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的類型,並且建立通知方法和切入點方法的關聯 -->
<aop:before method="writeLine" pointcut="execution(* chu.yi.bo.PointCutBean.*(..))"/>
</aop:aspect>
</aop:config>
</beans>
四、測試
public void testAOP() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Application.xml");
PointCutBean pointCutBean = (PointCutBean)applicationContext.getBean("pointCutBean");
pointCutBean.sayHello();
}
五、切入點表達式
切入點表達式用來進行切入點(方法)的匹配,語法結構是:execution([修飾符] 返回值類型 包名.類名.方法名(參數))。修飾符是非必選項,返回值類型、包名的任意一級、類名、方法名、參數,都可以用通配符 * 來代替。包名、參數名也可以用通配符 . . 來代替,例子如下:
- 無省略模式
execution(public void chu.yi.bo.PointCutBean.sayHello()) - 省略修飾符
execution(void chu.yi.bo.PointCutBean.sayHello()) - 使用 * 號代替返回值,表示任意返回類型
execution(* chu.yi.bo.PointCutBean.sayHello()) - 使用 * 號替代任意一級包
execution(* *.*.*.PointCutBean.sayHello()) - 使用 .. 表示當前包以及子包
execution(* chu..PointCutBean.sayHello()) - 使用 * 號代替類名,表示任意類
execution(* chu..*.sayHello()) - 使用 * 號代替方法名,表示任意方法
execution(* chu..*.*()) - 方法有參數時,可以使用 * 替代任意類型參數
execution(* chu..*.*(*)) - 無論方法有無參數都可以使用 .. 代替任意類型參數
execution(* chu..*.*(..)) - 全使用通配符
execution(* *..*.*(..))
配置 AOP 相關標籤
標籤 | 說明 | 屬性 |
---|---|---|
aop:config | 用於聲明開始 AOP 的配置 | |
aop:aspect | 用於配置切面 | id:給切面提供唯一標識 ref:引用配置好的通知 bean 的 id |
aop:pointcut | 指定對哪些類的哪些方法進行增強,配置切入點表達式 | expression:用於定義切入點表達式 id:給切入點表達式提供唯一標識 |
aop:before | 配置前置通知。指定增強的方法在切入點方法之前執行 | method:用於指定通知類中的增強方法名稱 ponitcut-ref:用於指定切入點的表達式的引用 poinitcut:用於指定切入點表達式 |
aop:after-returning | 配置後置通知,切入點方法正常執行之後。它和異常通知只能有一個執行 | 同上 |
aop:after-throwing | 配置異常通知,切入點方法執行產生異常後執行。它和後置通知只能執行一個 | 同上 |
aop:after | 配置最終通知,無論切入點方法執行時是否有異常,它都會在其後面執行 | 同上 |
aop:around | 配置環繞通知 | 同上 |
一、aop:pointcut 標籤使用
<?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="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入點 -->
<aop:pointcut id="pt" expression="execution(* chu.yi.bo.PointCutBean.*(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的類型,並且建立通知方法和切入點方法的關聯 -->
<aop:before method="writeLine" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
aop:pointcut 標籤用於配置切入點,該標籤寫在 aop:aspect 標籤內部只能在當前切面使用,寫在 aop:aspect 標籤外部可以在所有切面使用。
通知類型
一、前置通知
1、切入點類
public class PointCutBean {
public void beforePointCut() {
System.out.println("beforePointCut");
}
}
2、通知類
public class AdviceBean {
public void beforeAdvice() {
System.out.println("beforeAdvice");
}
}
3、Spring 配置文件
<?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="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入點 -->
<aop:pointcut id="beforePt" expression="execution(* chu.yi.bo.PointCutBean.beforePointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的類型,並且建立通知方法和切入點方法的關聯 -->
<aop:before method="beforeAdvice" pointcut-ref="beforePt"/>
</aop:aspect>
</aop:config>
</beans>
二、後置通知
1、切入點類
public class PointCutBean {
public void afterReturningPointCut() {
System.out.println("afterReturningPointCut");
}
}
2、通知類
public class AdviceBean {
public void afterReturningAdvice() {
System.out.println("afterReturningAdvice");
}
}
3、Spring 配置文件
<?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="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入點 -->
<aop:pointcut id="afterReturningPt" expression="execution(* chu.yi.bo.PointCutBean.afterReturningPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的類型,並且建立通知方法和切入點方法的關聯 -->
<aop:after-returning method="afterReturningAdvice" pointcut-ref="afterReturningPt"/>
</aop:aspect>
</aop:config>
</beans>
三、異常通知
1、切入點類
public class PointCutBean {
public void afterThrowingPointCut() {
System.out.println("afterThrowingPointCut");
throw new RuntimeException();
}
}
2、通知類
public class AdviceBean {
public void afterThrowingAdvice() {
System.out.println("afterThrowingAdvice");
}
}
3、Spring 配置文件
<?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="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入點 -->
<aop:pointcut id="afterThrowingPt" expression="execution(* chu.yi.bo.PointCutBean.afterThrowingPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的類型,並且建立通知方法和切入點方法的關聯 -->
<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="afterThrowingPt"/>
</aop:aspect>
</aop:config>
</beans>
四、最終通知
1、切入點類
public class PointCutBean {
public void afterPointCut() {
System.out.println("afterPointCut");
}
}
2、通知類
public class AdviceBean {
public void afterAdvice() {
System.out.println("afterAdvice");
}
}
3、Spring 配置文件
<?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="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入點 -->
<aop:pointcut id="afterPt" expression="execution(* chu.yi.bo.PointCutBean.afterPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的類型,並且建立通知方法和切入點方法的關聯 -->
<aop:after method="afterAdvice" pointcut-ref="afterPt"/>
</aop:aspect>
</aop:config>
</beans>
五、環繞通知
1、切入點類
public class PointCutBean {
public void aroundPointCut() {
System.out.println("aroundPointCut");
}
}
2、通知類
public class AdviceBean {
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
Object[] args = pjp.getArgs();// 得到方法執行所需的參數
System.out.println("beforeAdvice 前置");
rtValue = pjp.proceed(args);// 明確調用業務層方法(切入點方法)
System.out.println("afterReturningAdvice 後置");
return rtValue;
} catch (Throwable t) {
System.out.println("afterThrowingAdvice 異常");
throw new RuntimeException(t);
} finally {
System.out.println("afterAdvice 最終");
}
}
}
Spring 框架提供了 ProceedingJoinPoint 接口,程序運行時,Spring 框架會創建該接口的實例,並作爲參數傳遞給通知方法。調用該實例的 proceed() 方法可以執行切入點方法,調用該實例的 getArgs() 方法可以獲取切入點方法的參數列表。
3、Spring 配置文件
<?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="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入點 -->
<aop:pointcut id="aroundPt" expression="execution(* chu.yi.bo.PointCutBean.aroundPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的類型,並且建立通知方法和切入點方法的關聯 -->
<aop:around method="aroundAdvice" pointcut-ref="aroundPt"/>
</aop:aspect>
</aop:config>
</beans>
AOP 相關術語
術語 | 說明 |
---|---|
Joinpoint(連接點) | 連接點是指那些被攔截到的點。在 spring 中,這些點指的是方法,因爲 spring 只支持方法類型的連接點。 |
Pointcut(切入點) | 所謂切入點是指要對哪些 Joinpoint 進行攔截的定義 |
Advice(通知/ 增強) | 通知是指攔截到 Joinpoint 之後所要做的事情就是通知。通知的類型:前置通知,後置通知,異常通知,最終通知,環繞通知。 |
Target(目標對象) | 代理的目標對象 |
Weaving(織入) | 把增強應用到目標對象來創建新的代理對象的過程 |
Proxy (代理) | 一個類被 AOP 織入增強後,就產生一個結果代理類 |
Aspect(切面) | 是切入點和通知的結合 |
基於註解實現 AOP
一、Spring 配置文件
<?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="chu.yi.bo"/>
<!-- 配置 spring 開啓註解 AOP 的支持 -->
<aop:aspectj-autoproxy/>
</beans>
二、前置通知
1、切入點類
@Component("pointCutBean")
public class PointCutBean {
public void beforePointCut() {
System.out.println("beforePointCut");
}
}
2、通知類
@Component("adviceBean")
@Aspect
public class AdviceBean {
@Before("execution(* chu.yi.bo.PointCutBean.beforePointCut(..))")
public void beforeAdvice() {
System.out.println("beforeAdvice");
}
}
三、後置通知
1、切入點類
@Component("pointCutBean")
public class PointCutBean {
public void afterReturningPointCut() {
System.out.println("afterReturningPointCut");
}
}
2、通知類
@Component("adviceBean")
@Aspect
public class AdviceBean {
@AfterReturning("execution(* chu.yi.bo.PointCutBean.afterReturningPointCut(..))")
public void afterReturningAdvice() {
System.out.println("afterReturningAdvice");
}
}
四、異常通知
1、切入點類
@Component("pointCutBean")
public class PointCutBean {
public void afterThrowingPointCut() {
System.out.println("afterThrowingPointCut");
throw new RuntimeException();
}
}
2、通知類
@Component("adviceBean")
@Aspect
public class AdviceBean {
@AfterThrowing("execution(* chu.yi.bo.PointCutBean.afterThrowingPointCut(..))")
public void afterThrowingAdvice() {
System.out.println("afterThrowingAdvice");
}
}
五、最終通知
1、切入點類
@Component("pointCutBean")
public class PointCutBean {
public void afterPointCut() {
System.out.println("afterPointCut");
}
}
2、通知類
@Component("adviceBean")
@Aspect
public class AdviceBean {
@After("execution(* chu.yi.bo.PointCutBean.afterPointCut(..))")
public void afterAdvice() {
System.out.println("afterAdvice");
}
}
六、環繞通知
1、切入點類
@Component("pointCutBean")
public class PointCutBean {
public void aroundPointCut() {
System.out.println("aroundPointCut");
}
}
2、通知類
@Component("adviceBean")
@Aspect
public class AdviceBean {
@Around("execution(* chu.yi.bo.PointCutBean.aroundPointCut(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object rtValue = null;
try{
Object[] args = pjp.getArgs();// 得到方法執行所需的參數
System.out.println("beforeAdvice 前置");
rtValue = pjp.proceed(args);// 明確調用業務層方法(切入點方法)
System.out.println("afterReturningAdvice 後置");
return rtValue;
}catch (Throwable t){
System.out.println("afterThrowingAdvice 異常");
throw new RuntimeException(t);
}finally {
System.out.println("afterAdvice 最終");
}
}
}
七、@Pointcut 註解配置切入點
1、切入點類
@Component("pointCutBean")
public class PointCutBean {
public void beforePointCut() {
System.out.println("beforePointCut");
}
}
2、通知類
@Component("adviceBean")
@Aspect
public class AdviceBean {
@Pointcut("execution(* chu.yi.bo.PointCutBean.beforePointCut(..))")
private void pt(){}
@Before("pt()")
public void beforeAdvice() {
System.out.println("beforeAdvice");
}
}
動態代理
Spring 面向切面編程,底層的實現原理是動態代理。
一、接口
public interface HelloInterface {
void sayHello();
}
二、被代理類
public class Hello implements HelloInterface {
public void sayHello() {
System.out.println("Hello World!");
}
}
三、Java 官方動態代理
public void proxy() {
final HelloInterface hello = new Hello();
HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName());
Object rtValue = method.invoke(hello, args);
System.out.println("After invoke " + method.getName());
return rtValue;
}
}
);
proxyHello.sayHello();
}
三、使用 CGLib 實現動態代理
public void proxy() {
final HelloInterface hello = new Hello();
HelloInterface proxyHello = (HelloInterface) Enhancer.create(hello.getClass(),
new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before invoke " + method.getName());
Object rtValue = method.invoke(hello, objects);
System.out.println("After invoke " + method.getName());
return rtValue;
}
});
proxyHello.sayHello();
}