作者:小傅哥
博客:https://bugstack.cn
?沉淀、分享、成長,讓自己和他人都能有所收獲!??
?
一、前言
二、目標(biāo)
三、方案
四、實(shí)現(xiàn)
1. 工程結(jié)構(gòu)
2. 定義Advice攔截器鏈
3. 定義 Advisor 訪問者
4. 方法攔截器
5. 代理工廠
6. 融入Bean生命周期的自動代理創(chuàng)建者
五、測試
1. 事先準(zhǔn)備
2. 自定義攔截方法
3. spring.xml 配置 AOP
4. 單元測試
六、總結(jié)
七、系列推薦
嘎小子,這片代碼水太深你把握不住!
在電視劇《楚漢傳奇》中有這么一段劉邦與韓信的飲酒對話,劉邦問韓信我那個(gè)曹參
讀過書見過世面能帶多少兵,韓信說能帶一萬五,又補(bǔ)充說一萬五都吃力。劉邦又一一說出樊噲
、盧綰
、周勃
,韓信笑著說不足2萬,腦子不行。這時(shí)候劉邦有點(diǎn)掛不住臉了,問:那我呢,我能帶多少兵。韓信說,你能帶十萬。劉邦一看比他們都多,啊,還行。轉(zhuǎn)頭一想就問韓信那你呢,你能帶多少兵。韓信喝多了,說啊,我,我多多益善。這時(shí)候劉邦惱了領(lǐng)導(dǎo)勁上來了,問:那我為什么能管著你,你給我說,說呀!
這像不像你領(lǐng)導(dǎo)問你,你能寫多少代碼、搭多少框架、接多少項(xiàng)目。可能很大一部分沒經(jīng)歷太多的新人碼農(nóng),僅僅是能完成一些簡單的功能模塊開發(fā),而沒有辦法駕馭整個(gè)項(xiàng)目的涉及到的所有工程,也不能為項(xiàng)目提煉出一些可復(fù)用的通用性組件模塊。在初級碼農(nóng)的心里,接一點(diǎn)需求還好,但沒有人帶的時(shí)候完全接一個(gè)較大型項(xiàng)目就會比較慌了,不知道這里有沒有坑,自己也把握住不。這些代碼一塊塊的帶著能寫,但是都弄到一塊,就太難了!
在代碼開發(fā)成長的這條路上,要經(jīng)歷CRUD、ERP查數(shù)據(jù)、接口包裝、功能開發(fā)、服務(wù)整合、系統(tǒng)建設(shè)等,一直到獨(dú)立帶人承擔(dān)較大型項(xiàng)目的搭建。這一過程需要你能有大量的編寫代碼經(jīng)驗(yàn)積累和復(fù)雜問題的處理手段,之后才能一段段的把看似獨(dú)立的模塊后者代碼片段組裝成一個(gè)較大型能跑起來的項(xiàng)目。就像 Spring 的開發(fā)過程一樣,我們總是不斷在添加新的功能片段,最后又把技術(shù)實(shí)現(xiàn)與Spring 容器整合,讓使用方可以更簡單的運(yùn)用 Spring 提供的能力。
在上一章節(jié)我們通過基于 Proxy.newProxyInstance 代理操作中處理方法匹配和方法攔截,對匹配的對象進(jìn)行自定義的處理操作。并把這樣的技術(shù)核心內(nèi)容拆解到 Spring 中,用于實(shí)現(xiàn) AOP 部分,通過拆分后基本可以明確各個(gè)類的職責(zé),包括你的代理目標(biāo)對象屬性、攔截器屬性、方法匹配屬性,以及兩種不同的代理操作 JDK 和 CGlib 的方式。
再有了一個(gè) AOP 核心功能的實(shí)現(xiàn)后,我們可以通過單元測試的方式進(jìn)行驗(yàn)證切面功能對方法進(jìn)行攔截,但如果這是一個(gè)面向用戶使用的功能,就不太可能讓用戶這么復(fù)雜且沒有與 Spring 結(jié)合的方式單獨(dú)使用 AOP,雖然可以滿足需求,但使用上還是過去分散。
因此我們需要在本章節(jié)完成 AOP 核心功能與 Spring 框架的整合,最終能通過在 Spring 配置的方式完成切面的操作。
其實(shí)在有了AOP的核心功能實(shí)現(xiàn)后,把這部分功能服務(wù)融入到 Spring 其實(shí)也不難,只不過要解決幾個(gè)問題,包括:怎么借著 BeanPostProcessor 把動態(tài)代理融入到 Bean 的生命周期中,以及如何組裝各項(xiàng)切點(diǎn)、攔截、前置的功能和適配對應(yīng)的代理器。整體設(shè)計(jì)結(jié)構(gòu)如下圖:
small-spring-step-12
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── aop
│ │ ├── aspectj
│ │ │ └── AspectJExpressionPointcut.java
│ │ │ └── AspectJExpressionPointcutAdvisor.java
│ │ ├── framework
│ │ │ ├── adapter
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── autoproxy
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── AopProxy.java
│ │ │ ├── Cglib2AopProxy.java
│ │ │ ├── JdkDynamicAopProxy.java
│ │ │ ├── ProxyFactory.java
│ │ │ └── ReflectiveMethodInvocation.java
│ │ ├── AdvisedSupport.java
│ │ ├── Advisor.java
│ │ ├── BeforeAdvice.java
│ │ ├── ClassFilter.java
│ │ ├── MethodBeforeAdvice.java
│ │ ├── MethodMatcher.java
│ │ ├── Pointcut.java
│ │ ├── PointcutAdvisor.java
│ │ └── TargetSource.java
│ ├── beans
│ │ ├── factory
│ │ │ ├── config
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ ├── InstantiationAwareBeanPostProcessor.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── DisposableBeanAdapter.java
│ │ │ │ ├── FactoryBeanRegistrySupport.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── Aware.java
│ │ │ ├── BeanClassLoaderAware.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── BeanFactoryAware.java
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── FactoryBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java
│ │ │ └── ListableBeanFactory.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── event
│ │ │ ├── AbstractApplicationEventMulticaster.java
│ │ │ ├── ApplicationContextEvent.java
│ │ │ ├── ApplicationEventMulticaster.java
│ │ │ ├── ContextClosedEvent.java
│ │ │ ├── ContextRefreshedEvent.java
│ │ │ └── SimpleApplicationEventMulticaster.java
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ ├── ApplicationContextAware.java
│ │ ├── ApplicationEvent.java
│ │ ├── ApplicationEventPublisher.java
│ │ ├── ApplicationListener.java
│ │ └── ConfigurableApplicationContext.java
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ └── utils
│ └── ClassUtils.java
└── test
└── java
└── cn.bugstack.springframework.test
├── bean
│ ├── IUserService.java
│ ├── UserService.java
│ └── UserServiceInterceptor.java
└── ApiTest.java
工程源碼:公眾號「bugstack蟲洞?!?,回復(fù):Spring 專欄,獲取完整源碼
AOP 動態(tài)代理融入到Bean的生命周期中類關(guān)系,如圖 13-2
cn.bugstack.springframework.aop.BeforeAdvice
public interface BeforeAdvice extends Advice {
}
cn.bugstack.springframework.aop.MethodBeforeAdvice
public interface MethodBeforeAdvice extends BeforeAdvice {
/**
* Callback before a given method is invoked.
*
* @param method method being invoked
* @param args arguments to the method
* @param target target of the method invocation. May be <code>null</code>.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
void before(Method method, Object[] args, Object target) throws Throwable;
}
cn.bugstack.springframework.aop.Advisor
public interface Advisor {
/**
* Return the advice part of this aspect. An advice may be an
* interceptor, a before advice, a throws advice, etc.
* @return the advice that should apply if the pointcut matches
* @see org.aopalliance.intercept.MethodInterceptor
* @see BeforeAdvice
*/
Advice getAdvice();
}
cn.bugstack.springframework.aop.PointcutAdvisor
public interface PointcutAdvisor extends Advisor {
/**
* Get the Pointcut that drives this advisor.
*/
Pointcut getPointcut();
}
cn.bugstack.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor
public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {
// 切面
private AspectJExpressionPointcut pointcut;
// 具體的攔截方法
private Advice advice;
// 表達(dá)式
private String expression;
public void setExpression(String expression){
this.expression = expression;
}
@Override
public Pointcut getPointcut() {
if (null == pointcut) {
pointcut = new AspectJExpressionPointcut(expression);
}
return pointcut;
}
@Override
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice){
this.advice = advice;
}
}
cn.bugstack.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor
public class MethodBeforeAdviceInterceptor implements MethodInterceptor {
private MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
this.advice.before(methodInvocation.getMethod(), methodInvocation.getArguments(), methodInvocation.getThis());
return methodInvocation.proceed();
}
}
cn.bugstack.springframework.aop.framework.ProxyFactory
public class ProxyFactory {
private AdvisedSupport advisedSupport;
public ProxyFactory(AdvisedSupport advisedSupport) {
this.advisedSupport = advisedSupport;
}
public Object getProxy() {
return createAopProxy().getProxy();
}
private AopProxy createAopProxy() {
if (advisedSupport.isProxyTargetClass()) {
return new Cglib2AopProxy(advisedSupport);
}
return new JdkDynamicAopProxy(advisedSupport);
}
}
cn.bugstack.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (isInfrastructureClass(beanClass)) return null;
Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
for (AspectJExpressionPointcutAdvisor advisor : advisors) {
ClassFilter classFilter = advisor.getPointcut().getClassFilter();
if (!classFilter.matches(beanClass)) continue;
AdvisedSupport advisedSupport = new AdvisedSupport();
TargetSource targetSource = null;
try {
targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
advisedSupport.setProxyTargetClass(false);
return new ProxyFactory(advisedSupport).getProxy();
}
return null;
}
}
public class UserService implements IUserService {
public String queryUserInfo() {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "小傅哥,100001,深圳";
}
public String register(String userName) {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "注冊用戶:" + userName + " success!";
}
}
public class UserServiceBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("攔截方法:" + method.getName());
}
}
<beans>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService"/>
<bean class="cn.bugstack.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean id="beforeAdvice" class="cn.bugstack.springframework.test.bean.UserServiceBeforeAdvice"/>
<bean id="methodInterceptor" class="cn.bugstack.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="pointcutAdvisor" class="cn.bugstack.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))"/>
<property name="advice" ref="methodInterceptor"/>
</bean>
</beans>
@Test
public void test_aop() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
IUserService userService = applicationContext.getBean("userService", IUserService.class);
System.out.println("測試結(jié)果:" + userService.queryUserInfo());
}
測試結(jié)果
攔截方法:queryUserInfo
測試結(jié)果:小傅哥,100001,深圳
Process finished with exit code 0