前面提到過動態代理的有兩種簡單的實現方式,就是JDK動態代理和CGLIB。
不懂先看前文:SpringAOP的實現之代理模式
自己寫嘛當然是乞丐版了,爲了實現簡單 但是又具有一定的通用性(拒絕對被代理類有實現接口的強硬要求)。所以採用CGLIB的方式實現AOP。
工程目錄結構。
首先要實現註解一樣的 AOP,要定義與AOP相關的註解。
定義2個AOP相關的註解標籤。
//作用在類上
@Target(ElementType.TYPE)
//運行時有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
//初期版本僅僅支持按照註解分類AOP
//比如只切Controller Controller.class
Class<? extends Annotation> value();
}
如果對一個代理類有多個AOP,那麼需要用戶規定代理執行的順序,那麼就需要Order註解。
/**
* 定義切面順序
*/
//作用在類上
@Target(ElementType.TYPE)
//運行時有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
//越小優先級別越高
int value();
}
定義好相關注解,需要定義AOP的基本骨架。這裏用到了類似於模板模式。 這個骨架定義了究竟支持哪幾種通知方式(前置,後置等等)。
模板模式的妙用:Spring源碼(3)-Spring中的ApplicationContext
通過此骨架的定義,可以讓子類按需實現自己想要實現的通知。
public abstract class DefaultAspect {
/**
* @param targetClass 被代理的目標類型
* @param method 被代理的方法
* @param args 方法的參數
* @throws Throwable
*/
public void before(Class<?> targetClass, Method method,Object[] args) throws Throwable {}
/**
*
* @param targetClass 被代理的目標類型
* @param method 被代理的方法
* @param args 方法的參數
* @param returnValue 返回值
* @return 修改後返回值
* @throws Throwable
*/
public Object afterReturning(Class<?> targetClass,Method method
,Object[] args,Object returnValue) throws Throwable{
return returnValue;
}
/**
*
* @param targetClass 被代理的目標類型
* @param method 被代理的方法
* @param args 方法的參數
* @param e 異常
* @throws Throwable 拋出被代理方法的 異常
*/
public void afterThrowing(Class<?> targetClass,
Method method,Object[] args,Throwable e) throws Throwable{}
}
只支持了前置通知,方法返回後通知,異常時通知。Aroud可以將這三個聯合起來就行。
因爲三個方法在抽象類裏面都是默認的空實現,不會讓用戶強制實現,可以按需實現。
在SpringAOP的實現之代理模式提到,使用CGLIB就需要實現MethodInterceptor去複用自己的增強邏輯,而在這裏也是一樣的。但是又有點不一樣,因爲我們可能(一定)會遇到一個被代理類有多個代理,所以這裏是一對多的關係。因此不能照搬原來的代碼。需要做出額外的操作來輔助完成多個AOP增強一個被代理類。
所以這裏需要聲明兩個屬性。
//被代理的class
private Class<?> targetClass;
//切面信息集合 照顧多個AOP的 情況
private List<AspectInfo> sortedAspectInfoList;
而且,多個AOP的執行順序是類似於同心圓的穿梭操作。所以多個before和多個after執行的順序不盡相同。需要做額外的工作。
上面出現的切面信息類應該定義如下。
package com.framework.aop;
/**
* @Desc
* @Author FuYouJ
* @date 2020/5/31 21:55
*/
import com.framework.aop.aspect.DefaultAspect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 接受註解的值
*/
@Data
public class AspectInfo {
private int orderIndex;
private DefaultAspect aspectObject;
public AspectInfo() {
}
public AspectInfo(int orderIndex, DefaultAspect aspectObject) {
this.orderIndex = orderIndex;
this.aspectObject = aspectObject;
}
}
創建一個AspectListExecutor類繼承於MethodInterceptor,該類主要的工作就是往被代理的對象添加橫切邏輯。
@Data
@NoArgsConstructor
public class AspectListExecutor implements MethodInterceptor {
//被代理的class
private Class<?> targetClass;
// targetCalss切面信息集合 照顧多個AOP的 情況
private List<AspectInfo> sortedAspectInfoList;
public AspectListExecutor(Class<?> targetClass,List<AspectInfo> aspectInfos){
this.targetClass = targetClass;
//直接先排序再賦值
this.sortedAspectInfoList = sort(aspectInfos);
}
//因爲需要按照order的順序執行aop,所以需要對 List<AspectInfo>排序。
private List<AspectInfo> sort(List<AspectInfo> aspectInfos) {
//升序排列
Collections.sort(aspectInfos, new Comparator<AspectInfo>() {
@Override
public int compare(AspectInfo o1, AspectInfo o2) {
return o1.getOrderIndex() - o2.getOrderIndex();
}
});
return aspectInfos;
}
/**
* 按照oder的順序升序執行
* @param method
* @param args
*/
private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
for (AspectInfo info : sortedAspectInfoList) {
info.getAspectObject().before(targetClass,method,args);
}
}
/**
* 發生異常的時候執行 降序
* @param method
* @param args
* @param e
*/
private void invokeAfterThrowingAdvices(Method method, Object[] args, Exception e) throws Throwable {
for (int i = sortedAspectInfoList.size() -1; i >=0 ; i--) {
sortedAspectInfoList.get(i).getAspectObject()
.afterThrowing(targetClass,method,args,e);
}
}
/**
* 如果代理方法正常返回,降序執行afterAdvice
* @param method
* @param args
* @param returnValue
* @return
*/
private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
Object res = null;
for (int i = sortedAspectInfoList.size()-1; i >=0 ; i--) {
res = sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue);
}
return res;
}
//織入邏輯的方法
/**
*
* @param o 被增強的對象
* @param method 需要攔截的方法
* @param args 方法參數
* @param methodProxy 代理方法
* @return 返回值
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
if (ValidationUtil.isEmpty(sortedAspectInfoList)){
//就算爲空也要執行原來的方法啊
return methodProxy.invokeSuper(o,args);
}
//1.按照order的順序執行完畢所有切面的before方法
invokeBeforeAdvices(method,args);
try {
//2。 執行被代理的方法
returnValue = methodProxy.invokeSuper(o,args);
//3. 如果被代理方法正常返回,按照order的順序 逆序執行afterReturning
returnValue = invokeAfterReturningAdvices(method,args,returnValue);
}catch (Exception e){
//執行異常時
invokeAfterThrowingAdvices(method,args,e);
}
return returnValue;
}
有了一個能往被代理對象裏面織入邏輯的武器,還需要使用這個武器的使用者纔可以。在CGLIB已經有這樣的使用者。就是Enhancer,它可以根據MethodInterceptor和目標類生成代理對象。
創建一個工具類,利用Enhancer創建一個代理現象。
public class ProxyCreator {
/**
*創建代理 對象並返回
* @param targetClass
* @param interceptor
* @return
*/
public static Object createProxy(Class<?> targetClass, MethodInterceptor interceptor){
Object o = Enhancer.create(targetClass, interceptor);
return o;
}
}
有了以上工作,下面只需要從容器中篩選所有對象,用代理對象替換容器裏面的被代理對象。 容器的實現
這裏不贅述。後面我會新開文章專門寫實現容器。這裏 直接貼出 容器的實現代碼 。
容器必須是單例,單例模式我以前寫過一個不是很好的文章,有心的可以看看幫助理解。
JAVA中N種單例模式簡單概括(防反射,克隆,序列化,實例化,多線程安全)
容器實現代碼
/**
* 容器類 單例模式 (線程安全,防止反射 ,序列化)
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public class BeanContainer {
//判斷實例是否已經被加載過
private boolean loaded = false;
private final ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<>();
/**
* 加載 bean的註解列表
* 當一個類被一下幾個註解修飾的時候,那麼就歸容器管理
*/
private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
= Arrays.asList(Component.class, Controller.class
,Service.class, Repository.class
,Aspect.class);
/**
* 獲取容器實例
* @return
*/
public static BeanContainer getInstance(){
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder{
//一個實例
HOLDER;
//一個成員
private BeanContainer instance;
private ContainerHolder(){
instance = new BeanContainer();
}
}
/**
* 掃描加載所有的bean
*避免 多線程同時加載
*/
public synchronized void loadBeans(String packageName){
if (isLoaded()){
log.warn("容器已經被加載過了");
return;
}
Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
if (ValidationUtil.isEmpty(classSet)) {
log.warn("沒有提取到任何的類"+packageName);
return;
}
for (Class<?> clazz : classSet) {
for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
//如果有這個註解
if (clazz.isAnnotationPresent(annotation)) {
//將m目標類本省作爲建 ,目標類的實例作爲 值,放在容器中
beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
}
}
}
loaded = true;
}
public boolean isLoaded() {
return loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public int size(){
return beanMap.size();
}
//增加一個實例
public Object addBean(Class<?>clazz, Object bean){
return beanMap.put(clazz,bean);
}
//刪除
public Object remove(Class<?>clazz){
return beanMap.remove(clazz);
}
/**
* 類獲取實例
* @param clazz
* @return
*/
public Object getBean(Class<?>clazz){
return beanMap.get(clazz);
}
/**
* 獲取所有的 beanclss集合
* @return
*/
public Set<Class<?>> getClasses(){
return beanMap.keySet();
}
/**
* 獲取所有實例
* @return
*/
public Set<Object> getBeans(){
return new HashSet<>(beanMap.values());
}
/**
* 獲取某註解 的class
* @param annotation
* @return
*/
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
//獲取所有class
Set<Class<?>> keySet = getClasses();
if (ValidationUtil.isEmpty(keySet)){
log.warn("容器爲空");
return null;
}
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> clazz : keySet) {
if (clazz.isAnnotationPresent(annotation)){
classSet.add(clazz);
}
}
return classSet.size() > 0?classSet:null;
}
public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
//獲取所有class
Set<Class<?>> keySet = getClasses();
if (ValidationUtil.isEmpty(keySet)){
log.warn("容器爲空");
return null;
}
//通過接口篩選
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> clazz : keySet) {
//判斷是否是我的子類
if (interfaceOrClass.isAssignableFrom(clazz)){
classSet.add(clazz);
//TODO
}
}
return classSet.size()>0?classSet:null;
}
}
依賴注入代碼
/**
* 依賴注入的服務
*/
@Slf4j
public class DependencyInjector {
private BeanContainer beanContainer;
public DependencyInjector(){
beanContainer = BeanContainer.getInstance();
}
/**
* 執行IOC
*/
public void doIoc(){
//遍歷容器class對象
if (ValidationUtil.isEmpty(beanContainer.getClasses())){
log.warn("容器爲空");
return;
}
for (Class<?> clazz : beanContainer.getClasses()) {
//遍歷每個對象的 成員變量
Field[] fields = clazz.getDeclaredFields();
if (ValidationUtil.isEmpty(fields)){
continue;
}
//找到自動注入的註解
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
Autowired autowired = field.getAnnotation(Autowired.class);
String autowiredValue = autowired.value();
//獲取類型
Class<?> fieldClass = field.getType();
//再容器中找到
Object fieldValue = getFiledInstance(fieldClass,autowiredValue);
if (fieldValue == null){
throw new RuntimeException("注入失敗,容器中不存在該類型,名字:"+autowiredValue);
}else {
//注入
Object targetBean = beanContainer.getBean(clazz);
//通過反射注入
ClassUtil.setField(field,targetBean,fieldValue,true);
}
}
}
}
}
/**
* 根據class對象 獲取容器管理的類或者實現類
* @param fieldClass
* @param autowired
* @return
*/
private Object getFiledInstance(Class<?> fieldClass, String autowired) {
Object fieldValue = beanContainer.getBean(fieldClass);
//入如果直接獲取到了就返回
if (fieldValue != null) {
return fieldValue;
}else {
//找實現類
Class<?> implClass = getImplementClass(fieldClass,autowired);
if (implClass != null){
return beanContainer.getBean(implClass);
}else {
return null;
}
}
}
/**
* 獲取接口的實現類
* @param fieldClass
* @param autowired
* @return
*/
private Class<?> getImplementClass(Class<?> fieldClass, String autowired) {
Set<Class<?>> classsSet = beanContainer.getClassesBySuper(fieldClass);
if (ValidationUtil.isEmpty(classsSet) == false){
//如果有多個實現類呢? 通過 Qualifier 設置名稱
if (ValidationUtil.isEmpty(autowired)){
//說明沒有精確指定
if (classsSet.size() == 1){
return classsSet.iterator().next();
}else {
//拋出異常
throw new RuntimeException("發現多個繼承類!卻又沒有指定名字"+fieldClass.getName());
}
}else {
for (Class<?> clazz : classsSet) {
//如果類的類名等於指定的名字
if (autowired.equals(clazz.getSimpleName())){
return clazz;
}
}
}
}
return null;
}
}
需要從容器裏面篩選出符合要被代理的對象,對其替換。
public class AspectWeaver {
private BeanContainer beanContainer;
//獲取容器的單例
public AspectWeaver(){
beanContainer = BeanContainer.getInstance();
}
//AOP主要方法
public void doAop(){
//獲取所有的切面類
//1.獲取所有的切面類
Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
//沒有需要代理的類,也就是沒有切面類。直接返回 此方法是非空驗證,可自行實現。
if (ValidationUtil.isEmpty(aspectSet)){ return; }
//2.將切面類按照不同的織入目標進行切分 比如切Controller的在一邊,切Service的 在一邊。用Map保存 比如 controller 有多個切面類要去增強他。所以用List保存
Map<Class<? extends Annotation>, List<AspectInfo>> categorizedMap = new HashMap<>();
//遍歷 所有的切面類
for (Class<?> aspectClass : aspectSet) {
//對切面類進行校驗,校驗切面的一般規範。比如 不能切自己(死循環)
boolean b = verifyAspect(aspectClass);
if (b){
//對於校驗通過的根據要切的目標去歸類保存
//比如Service的 切面增強 有一個列表
//比如Controller 切面增強 有一個列表
//列表是因爲 往往 不止一個切面類要去增強某被代理類
categorizeAspect(categorizedMap,aspectClass);
}else {
//對於不符合切面規範的,拋出異常
throw new RuntimeException("當前切面類必須持有@Aspect 和 @Order 和繼承自DefaultAspect");
}
}
//Map都是空的 也沒啥做的 直接返回 無事可做
if (ValidationUtil.isEmpty(categorizedMap)){return;}
//注意注意 這裏map百年裏的是KEY 假設 整個應用程序所有的切面只對service和controller進行增強,所以此時的key就是service和controller
//然後根據key獲取符合條件的切面類列表,全部進行織入。
for (Class<? extends Annotation> aClass : categorizedMap.keySet()) {
//執行織入 代理對象替換了容器的被代理對象
//這樣字從 容器取得對象就是已經增強後的對象了
List<AspectInfo> aspectInfoList = categorizedMap.get(aClass);
//對controller 和service織入
weaveByCategory(aClass, aspectInfoList);
}
}
/**
* 驗證切面 類是否滿足要求
* 必須要有兩個切面標籤
* 必須繼承自DefaultAspect.class 必須按照這個骨架執行啊
* Aspect的屬性不能是他本身
* @param aspectClass
* @return
*/
private boolean verifyAspect(Class<?> aspectClass) {
return aspectClass.isAnnotationPresent(Aspect.class)
&& aspectClass.isAnnotationPresent(Order.class)
&& DefaultAspect.class.isAssignableFrom(aspectClass)
&& aspectClass.getAnnotation(Aspect.class).value() != Aspect.class;
}
/**
*將切面類 按照不同的切面目標進行分類
* @param categorizedMap
* @param aspectClass
*/
private void categorizeAspect(Map<Class<? extends Annotation>, List<AspectInfo>> categorizedMap, Class<?> aspectClass) {
Order orderTag = aspectClass.getAnnotation(Order.class);
Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
DefaultAspect aspect = (DefaultAspect) beanContainer.getBean(aspectClass);
AspectInfo aspectInfo = new AspectInfo(orderTag.value(),aspect);
if (categorizedMap.containsKey(aspectTag.value()) == false) {
//如果第一次出現
List<AspectInfo> aspectInfoList = new ArrayList<>();
aspectInfoList.add(aspectInfo);
categorizedMap.put(aspectTag.value(),aspectInfoList);
}else {
//已有列表
categorizedMap.get(aspectTag.value())
.add(aspectInfo);
}
}
/**
*
* @param aspectAnnotationClass 要被切的註解所對應的類 比如@service的xxxxService
* @param aspectInfos 他所對應的集合
*/
private void weaveByCategory(Class<? extends Annotation> aspectAnnotationClass, List<AspectInfo> aspectInfos) {
//獲取被例如 @service標記的所有類
Set<Class<?>> aspectClassSet = beanContainer.getClassesByAnnotation(aspectAnnotationClass);
if (ValidationUtil.isEmpty(aspectClassSet)){return;}
//遍歷 爲每個被代理的類生成動態代理對象
for (Class<?> targetClass : aspectClassSet) {
AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass,aspectInfos);
Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
//將代理對象放到容器 替換被代理的實例
beanContainer.addBean(targetClass,proxyBean);
}
}
}
以上就是一個簡單版本的根據註解來分類切面的實現。
測試
編寫一個測試的controller,具有返回值。
@Controller
public class TestAopController {
public int say(){
System.out.println("我是controller的本地方法");
return 1;
}
}
爲他編寫兩個AOP類。順序分別是0和1
package com.fuyouj.aspect.test;
import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
/**
* @Desc
* @Author FuYouJ
* @date 2020/6/2 0:40
*/
@Aspect(value = Controller.class)
@Order(0)
@Slf4j
public class TestAopOne extends DefaultAspect {
@Override
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
log.info("我是切面邏輯before order0,我增強的目標類是{},我增強的目標方法是,方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
}
@Override
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
Integer res = (Integer) returnValue;
log.info("我是切面邏輯after order0,我增強的目標類是{},我增強的目標方法是,方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
res++;
return res;
}
}
package com.fuyouj.aspect.test;
import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
/**
* @Desc
* @Author FuYouJ
* @date 2020/6/2 0:40
*/
@Aspect(value = Controller.class)
@Order(1)
@Slf4j
public class TestAopTwo extends DefaultAspect {
@Override
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
log.info("我是切面邏輯before order1,我增強的目標類是{},我增強的目標方法是{},方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
}
@Override
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
Integer res = (Integer) returnValue;
log.info("我是切面邏輯after order1,我增強的目標類是{},我增強的目標方法是{},方法參數{}"
,targetClass.getSimpleName(),method.getName(),args);
res++;
return res;
}
}
編寫測試類
public class TestAop {
@Test
public void testAopOne(){
BeanContainer beanContainer = BeanContainer.getInstance();
//加載了所有類
beanContainer.loadBeans("com.fuyouj");
//AOP
new AspectWeaver().doAop();
// ioc
//這裏的細節 先AOP,再自動注入。
new DependencyInjector().doIoc();
TestAopController controller = (TestAopController) beanContainer.getBean(TestAopController.class);
int say = controller.say();
System.out.println(say+"======");
}
}
按照邏輯,倆個切面類的順序分別是0和1,所以 執行順序因該是
0的beore,1的before,1的after,0的after.並且原來額返回結果經過兩次修改,應該是2。
附上Controller註解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
後序預告
AOP2.0: 支持execution表達式
自己手寫一個Spring框架