Spring創(chuàng)建AOP代理并非只有@Aspect一種方式
環(huán)境:Spring6.1.2
1. 簡(jiǎn)介
在Spring項(xiàng)目中,使用@Aspect
注解定義切面(Aspect)并創(chuàng)建AOP(面向切面編程)代理是一種常見(jiàn)的做法,它主要用于實(shí)現(xiàn)跨多個(gè)類和方法的橫切關(guān)注點(diǎn)(Cross-cutting Concerns)的模塊化。下面是對(duì)使用@Aspect
定義切面以及創(chuàng)建AOP代理的示例:
@Aspect
public class LogAspect {
// 定義切點(diǎn)
@Pointcut("execution(* com.pack..*.*(..))")
public void log() {
}
// 前置通知
@Before("log()")
public void beforeLog() {
System.out.println("記錄日志Before...");
}
// 后置通知
@After("log()")
public void afterLog() {
System.out.println("記錄日志After");
}
// 異常通知
@AfterThrowing(pointcut = "log()", throwing = "tx")
public void ex(Throwable tx) {
System.err.println("發(fā)生異常: " + tx.getMessage()) ;
}
// 環(huán)繞通知
@Around("log() && args(name)")
public Object around(ProceedingJoinPoint pjp, String name) throws Throwable {
System.out.println("log before...") ;
System.out.println("name = " + name) ;
Object ret = pjp.proceed() ;
System.out.println("log after...") ;
return ret ;
}
}
以上是一個(gè)簡(jiǎn)單的異常通知切面定義。在實(shí)際工作中絕大多數(shù)情況下都是通過(guò)上面的方式操作。
但是在某些場(chǎng)景下,你可能需要更細(xì)粒度的控制來(lái)創(chuàng)建代理對(duì)象,比如根據(jù)特定條件動(dòng)態(tài)決定是否創(chuàng)建代理、自定義代理的創(chuàng)建過(guò)程或調(diào)整代理的行為。這時(shí),使用ProxyFactoryBean或ProxyFactory可以提供更大的靈活性。ProxyFactoryBean主要用于在Spring容器中配置和創(chuàng)建代理對(duì)象,而ProxyFactory則提供了編程式創(chuàng)建代理對(duì)象的能力。如果你需要在代碼中動(dòng)態(tài)地創(chuàng)建代理對(duì)象,而不是通過(guò)Spring容器來(lái)管理,那么使用ProxyFactory可能更合適。
接下來(lái)將詳細(xì)介紹通過(guò)ProxyFactoryBean和ProxyFactory創(chuàng)建AOP代理對(duì)象。
2. 代理對(duì)象創(chuàng)建
2.1 ProxyFactoryBean創(chuàng)建代理
該類提供了對(duì)切入點(diǎn)、任何適用的建議及其順序的完全控制。然而,如果您不需要這樣的控制,也可以選擇更簡(jiǎn)單的選項(xiàng)。
ProxyFactoryBean與其他Spring FactoryBean實(shí)現(xiàn)一樣,引入了一個(gè)間層。簡(jiǎn)單說(shuō)如果你定義了一個(gè)名為foo的ProxyFactoryBean,那么引用foo的對(duì)象看不到ProxyFactoryBean實(shí)例本身,而是由ProxyFactoryBean中的getObject()方法實(shí)現(xiàn)創(chuàng)建的對(duì)象。此方法創(chuàng)建一個(gè)AOP代理,用于包裝目標(biāo)對(duì)象。
ProxyFactoryBean很多關(guān)鍵的屬性繼承自ProxyConfig(Spring中所有aop代理工廠的超類)。這些關(guān)鍵屬性結(jié)束如下:
ProxyFactoryBean proxy = new ProxyFactoryBean() ;
// 如果要代理的是目標(biāo)類,而不是目標(biāo)類的接口,則為T(mén)rue。如果該屬性值設(shè)置為true,則創(chuàng)建CGLIB代理
proxy.setProxyTargetClass(false) ;
// 控制是否對(duì)通過(guò)CGLIB創(chuàng)建的代理應(yīng)用積極優(yōu)化。除非您完全理解相關(guān)AOP代理如何處理優(yōu)化,否則不應(yīng)該輕松地使用此設(shè)置。目前僅用于CGLIB代理。它對(duì)JDK動(dòng)態(tài)代理沒(méi)有影響。
proxy.setOptimize(false) ;
// 如果代理配置被凍結(jié),則不再允許更改配置。無(wú)論是作為輕微的優(yōu)化,還是當(dāng)您不希望調(diào)用者在創(chuàng)建代理后能夠操作代理(通過(guò)建議的接口)時(shí),這都是有用的。此屬性的默認(rèn)值為false,因此允許更改(例如添加額外的通知)。
proxy.setFrozen(false) ;
// 確定是否應(yīng)該在ThreadLocal中暴露當(dāng)前代理,以便目標(biāo)可以訪問(wèn)它。如果目標(biāo)需要獲取代理,而exposeProxy屬性被設(shè)置為true,那么可以使用AopContext.currentProxy()方法。
proxy.setExposeProxy(false) ;
// 接口名稱的字符串?dāng)?shù)組。如果沒(méi)有提供,則使用目標(biāo)類的CGLIB代理
proxy.setProxyInterfaces(new Class<?>[] {}) ;
// 要應(yīng)用的Advisor、攔截器或其他Advice名稱的字符串?dāng)?shù)組。點(diǎn)菜很重要,先到先得。也就是說(shuō),列表中的第一個(gè)攔截器是第一個(gè)能夠攔截調(diào)用的。
proxy.setInterceptorNames("interceptor01") ;
// 不管getObject()方法被調(diào)用的頻率如何,工廠是否應(yīng)該返回一個(gè)對(duì)象。有幾個(gè)FactoryBean實(shí)現(xiàn)提供了這樣的方法。默認(rèn)值為true
proxy.setSingleton(true) ;
以上是對(duì)ProxyFactoryBean創(chuàng)建代理對(duì)象時(shí)的核心配置說(shuō)明。
完整使用案例如下:
public interface CommonDAO {}
public class PersonService {
public void save() {
System.out.println("save method invoke...") ;
}
}
@Configuration
public class AppConfig {
@Bean
public MethodInterceptor logInterceptor() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("日志記錄...") ;
return invocation.proceed() ;
}
};
}
@Bean
public ProxyFactoryBean personService() throws Exception {
ProxyFactoryBean proxy = new ProxyFactoryBean() ;
proxy.setProxyTargetClass(true) ;
proxy.setTargetSource(new SingletonTargetSource(new PersonService())) ;
proxy.setProxyInterfaces(new Class<?>[] {CommonDAO.class}) ;
proxy.setInterceptorNames("logInterceptor") ;
return proxy ;
}
}
2.2 ProxyFactory創(chuàng)建代理
用Spring很容易通過(guò)編程創(chuàng)建AOP代理。這讓你可以在不依賴Spring IoC的情況下使用Spring AOP。由目標(biāo)對(duì)象實(shí)現(xiàn)的接口會(huì)自動(dòng)被代理。如下示例:
public interface CommonDAO {}
public class PersonService {
public void save() {
System.out.println("save method invoke...") ;
}
}
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new PersonService()) ;
factory.setProxyTargetClass(true) ;
// 設(shè)置通知類(內(nèi)部會(huì)自動(dòng)的包裝為Advisor)
factory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("權(quán)限控制...") ;
return invocation.proceed() ;
}
});
factory.addAdvisor(new PointcutAdvisor() {
@Override
public Advice getAdvice() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("日志記錄...") ;
return invocation.proceed() ;
}
} ;
}
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("save") ;
}
} ;
}
}) ;
PersonService ps = (PersonService) factory.getProxy() ;
ps.save() ;
}
以上是本篇文章的全部?jī)?nèi)容,希望對(duì)你有幫助。