目錄
-
環境準備
-
核心代碼實現
-
測試
-
總結
環境準備
-
spring源碼版本5.0.2
-
jdk 1.8
-
ide idea
-
在源碼中創建子模塊
友情提示:
我們1.0版本只需要實現前置,後置通知即可。由於ioc源碼需要依賴spring 本身的,所以spring ioc源碼有些地方需要改造下,實現方式上可能稍微有些不同,不過核心思想是一致的。後續手寫ioc的時候將這塊整合到一起去
核心代碼實現
實現思路:
-
實現自己的註解 @Aspect @Before @After @EnableAopAspect
-
編寫AopBeanPostProcessor 用於增強邏輯,將原生對象轉換成代理對象返回給spring ioc
-
編寫AopAnntationRegisterBeanDefinition用於向spring添加aop的後置處理器的前期工作
-
編寫代理proxyFactory工廠,用於jdk和cglib不同的代理方式
-
編寫MethodInterceptor對切面進行封裝和執行
-
編寫Advice用於抽象各種通知
-
改造spring源碼context模塊註冊後置處理部分,我們自己的aop後置處理器由自己去實例化,即此PostProcessorRegistrationDelegate類registerBeanPostProcessors方法的改造
-
思路有了我們來做具體實現吧
在spring-context模塊中新建自己的包結構如圖:
- 來看下我們自己的@EnableAopAspect註解:
-
bd註冊類和spring實現相同,沒什麼可看的,我們重點看下自己的aop後置處理器的具體實現邏輯:
public class AopBeanPostProcessor implements BeanPostProcessor {
private DefaultListableBeanFactory beanFactory;
public AopBeanPostProcessor(ConfigurableListableBeanFactory beanFactory){
this.beanFactory=(DefaultListableBeanFactory)beanFactory;
}
//重寫postProcessAfterInitialization 在原生對象填充完屬性執行完初始化方法後執行
//對原生對象進行轉換操作
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//從beanDefinitionMap中通獲取加了Aspect註解的的beanName
String[] beanNamesForAnnotation = beanFactory.getBeanNamesForAnnotation(Aspect.class);
//如果存在切面類,就進行轉換,否則直接返回原生對象
if(beanNamesForAnnotation.length>0) {
//獲取切面類,此工廠是我們通過構造方法傳入的,我們基於spring ioc實現的aop
//所以有些地方需要改造.將DefaultListableBeanFactory傳入到自己的定義的後置處理器重用於獲取我們的切面類對象
Object target = beanFactory.getBean(beanNamesForAnnotation[0]);
Method[] declaredMethods = target.getClass().getDeclaredMethods();
//通過反射,循環切面類中的所有方法
for(Method method:declaredMethods){
//如果該方法上存在Before註解,獲取其註解中的切面表達式,並判斷當前原生對象是否需要進行增強
//即是否符合我們切面表達式
if(method.isAnnotationPresent(Before.class)) {
Before annotation = method.getAnnotation(Before.class);
String name = bean.getClass().getName();
String substring = annotation.value().substring(0, annotation.value().indexOf("("));
Pattern pattern = Pattern.compile(substring);
//如果符合切面表達式,將其轉換爲代理對象後返回給spring
if (pattern.matcher(name).matches()) {
MethodInterceptor methodInterceptor = new MethodInterceptor(method,
target, method.getParameters());
//3 實例化Advice
Advice advice = new MethodBeforeAdvice(methodInterceptor);
//將原生對象轉換爲代理對象
return ProxyFactory.getProxy(bean, advice).getProxy();
}
}
}
return bean;
}else {
return bean;
}
}
}
- 此處有個地方和spring不大一樣,就是增加了構造方法,用於傳遞DefaultListableBeanFactory 從而來獲取我們的切面類對象.還需改造的地方就是spring 註冊後置處理時的源碼,由於我們需要通過構造方法傳遞DefaultListableBeanFactory ,所以不能交由spring原生代碼去實例化,所以我們增加如下代碼:
注:上面都加了註釋大家可以仔細看下後置處理器中的邏輯.
- 代理工廠我們實現的比較簡單,不解析許多了,看下代碼吧
- 我們的前置處理器中邏輯如下:
幾處的核心代碼基本都編寫完了,下面我們來測試一把
測試
1,在Configuration類中引入自己的@EnableAopAspect註解即開啓aop代理增強功能.
2,創建aop切面類,編寫前置通知,以及橫切邏輯的表達式
3,在main函數中啓動spring容器,獲取twoService對象實例.執行其query方法.
4,在切面類的前置通知方法處,打debug斷點,執行看結果
執行完後,再看控制檯結果
哎呦臥槽嘞,完美!!!
總結
至此我們1.0v aop實現完成,1.0v版本粗略實現了aop增強功能,並且支持jdk的動態代理,由於沒有手工實現ioc部分,所以有些地方和spring實現方式不同,而且環繞通知並未實現,但是核心原理是一樣的,我們後續在對1.0v進行改造升級2.0v,敬請期待。
個人公衆號: