springaop之拙劣實現

springaop之拙劣實現(基於註解配置的aop)

簡單使用

1.定義註解

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog {
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog2 {
}

2.業務類

@Service
public class ARoleService2 {
    public ARoleService2(){
        System.out.println("正在創建ARoleService2");
    }
    //可以多個註解修飾同一個方法
    @MyLog2
    @MyLog
    public List<String> getNameList(String... name){
        List<String> list = new ArrayList<>();
        for (String s : name) {
            list.add(s);
        }
        return list;
    }
}

3.aop增強方法

@Component
@EabelMyAop
public class MySpringAop {

    private static final Logger log = LoggerFactory.getLogger(MySpringAop.class);

    public MySpringAop() {
        log.info("正在創建MySpringAop對象");
    }

    /**
     * @param joinPoint
     */
    @MyBefore(pointCut = MyLog.class)
    public Object Around(MyJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getMethodName();
        Object[] args = joinPoint.getArgs();
        log.info("\r\n方法名稱:{}\r\n方法參數:{}",methodName,JSON.toJSONString(args));
        return null;
    }

    @MyAfter(pointCut = MyLog.class)
    public Object after(MyJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getMethodName();
        Object result = joinPoint.getResult();
        log.info("\r\n後置方法方法名稱:{}\r\n返回結果:{}",methodName,JSON.toJSONString(result));
        return result;
    }

    @MyAround(pointCut = MyLog2.class,order = 8)
    public Object Around2(AroundJointPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getMethodName();
        Object[] args = joinPoint.getArgs();
        log.info("============環繞通知log2,執行前============");
        Object proceed = joinPoint.proceed();
        log.info("============環繞通知log2,執行後============");
        return proceed;
    }


    @MyAround(pointCut = MyLog.class,order = 2)
    public Object Around3(AroundJointPoint joinPoint) throws Throwable {
        log.info("============環繞通知log1,執行前============");
        Object proceed = joinPoint.proceed();
        log.info("============環繞通知log1,執行後============");
        return proceed;
    }

    @MyAfterThrowing(pointCut = MyLog.class)
    public Object after(AroundJointPoint joinPoint) throws Throwable {
        return "發生cuowu 信息";
    }
}

4.測試

@SpringBootTest(classes = MaAopRun.class)
public class MyAopTest {
    @Autowired
    private ARoleService2 aRoleService2;
    @Test
    public void tsst1(){
        List<String> nameList = aRoleService2.getNameList("張三", "李四", "王五");
        System.out.println(nameList);
    }
}

//運行結果
正在創建ARoleService2
2020-06-15 18:25:20.516  INFO 1020 --- [           main] com.git.proxy.ProxyUtil                  : 正在爲com.git.maaop.test.service.RoleService創建cgLib動態代理對象
2020-06-15 18:25:20.578  INFO 1020 --- [           main] com.git.myaop.MyAopTest                  : Started MyAopTest in 1.1 seconds (JVM running for 2.492)

2020-06-15 18:25:20.915  INFO 1020 --- [           main] com.git.maaop.test.config.MySpringAop    : 
方法名稱:getNameList
方法參數:[["張三","李四","王五"]]
2020-06-15 18:25:20.915  INFO 1020 --- [           main] com.git.maaop.test.config.MySpringAop    : ============環繞通知log1,執行前============
2020-06-15 18:25:20.915  INFO 1020 --- [           main] com.git.maaop.test.config.MySpringAop    : ============環繞通知log2,執行前============
2020-06-15 18:25:20.926  INFO 1020 --- [           main] com.git.maaop.test.config.MySpringAop    : ============環繞通知log2,執行後============
2020-06-15 18:25:20.927  INFO 1020 --- [           main] com.git.maaop.test.config.MySpringAop    : ============環繞通知log1,執行後============
2020-06-15 18:25:20.927  INFO 1020 --- [           main] com.git.maaop.test.config.MySpringAop    : 
後置方法方法名稱:getNameList
返回結果:["張三","李四","王五"]
[張三, 李四, 王五]

實現原理

必須對spring的後置處理器和cglib代理有所瞭解,不然看了也白看

實現思路

1.在spring啓動過程中使用beanFactoryPostProcessor對所有的BeanDefinition掃描,將bean裏面的所有加了@MyAround的方法存到一個list裏面(包括before和after等)
2.利用beanPostProcessor在bean初始化的時候判斷該bean要不要被增強,如果需要的話返回一個代理對象(cglib)

  • 爲什麼會在初始化的時候返回代理對象(因爲在實例化的時候返回對象,後面就不會初始化了),但是這麼做會創建兩次bean(一次spring調用的,一次cglib創建子類的時候自動的
  • 至於bean的創建順序問題,可以通過beanFactory來獲取,如果沒有spring會自動創建

原理

1.aop

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//通過import一個類實現開啓自定義aop的作用
@Import(MyAopProcessor.class)
public @interface EabelMyAop {
}

2.MyAopProcessor

/**
 * MyAopProcessor實現了
 * 1.BeanFactoryPostProcessor beanFactoryPostProcessor後置處理器用於篩選那些那些方法上面加了自定義註解,比如MyAround等
 * 2.InstantiationAwareBeanPostProcessor 用於在初始化方法之後返回一個代理對象,本文采用cglib代理
 * 3.BeanFactoryAware 爲了獲得beanFactory的功能
 * @author authorZhao
 * @date 2020年06月06日
 */

public class MyAopProcessor implements BeanFactoryPostProcessor,InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private static final Logger log = LoggerFactory.getLogger(MyAopProcessor.class);
    static {//開啓cglib生成的代理clazz輸出到文件
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
    }

    /**
     * 中間商的方法
     */
    private Map<Method,Class> methodClassHashMap = new HashMap<>();

    private BeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        Arrays.stream(beanDefinitionNames).forEach(n->{
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(n);
            String beanClassName = beanDefinition.getBeanClassName();
            if(StringUtils.isEmpty(beanClassName))return;
            try {
                Class beanClass = Class.forName(beanClassName);
                Arrays.stream(beanClass.getDeclaredMethods()).forEach(i->{
                    if(i.isAnnotationPresent(MyAround.class)||i.isAnnotationPresent(MyBefore.class)||i.isAnnotationPresent(MyAfter.class)||i.isAnnotationPresent(MyAfterThrowing.class))
                        //註解校驗
                        methodClassHashMap.put(i,beanClass);
                });
            } catch (Exception e) {
                log.error(beanClassName);
            }
        });
        AopUtil.setMap(methodClassHashMap);
    }

    /**
     * 在初始化之前返回一個對象,關於爲什麼不在這一步返回代理對象是因爲還想再後面繼續初始化的操作
     * @param beanClass
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
     * 初始化之前執行的方法,因爲要初始化,所以在這一步不做操作
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
     * 初始化之後的操作,這一步返回代理對象,本文采用cglib代理
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //.構建MyJoinPoint,cglib多重代理問題會有方法衝突的問題
        //1.判斷該對象需不需要被代理
        List<PointChain> pointChainList = AopUtil.getPointChainList(methodClassHashMap, bean.getClass(), beanFactory);
        if(ListUtil.isEmpety(pointChainList))return bean;
        AopMethodProxy aopMethodProxy = new AopMethodProxy(pointChainList);
        //2.生成代理對象
        Object object =  ProxyUtil.getObject(bean.getClass(),aopMethodProxy);
        //3.屬性賦值,之前的對象就沒用了
        BeanUtils.copyProperties(bean,object);
        return object;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

3.cglib代理的實現

思路,遍歷執行before方法、around方法、after方法,拋異常就會執行afterThrow

@Override
    public Object apply(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        cglibJoinPoint.clear();
        cglibJoinPoint.setArgs(params);
        cglibJoinPoint.setProxy(proxy);
        cglibJoinPoint.setMethodProxy(methodProxy);

        Object result = null;
        try {
            //1.不需要增強的方法直接返回
            if (!AopUtil.needEnhance(method)) return methodProxy.invokeSuper(proxy, params);

            //構建方法

            //2.before方法
            beforePointChains.forEach(i->{
                MyJoinPoint myJoinPoint = i.getMyJoinPoint();
                ((MyJoinPointAbstract) myJoinPoint).setArgs(params);
                i.intercept();
            });
            //3.around方法
            cglibJoinPoint.setArgs(params);
            //((AbstractPointChain) i).setParams(new Object[]{myJoinPoint});
            result = cglibJoinPoint.proceed();
            //4.after方法
            for (AfterPointChain afterPointChain : afterPointChains) {
                MyJoinPointAbstract myJoinPoint = ((MyJoinPointAbstract) afterPointChain.getMyJoinPoint());
                myJoinPoint.setArgs(params);
                myJoinPoint.setResult(result);
                if (((AbstractPointChain) afterPointChain).hasReturn()) {
                    return afterPointChain.intercept();
                }else{
                    afterPointChain.intercept();
                }
            }
            //5.afterThrowing

        }catch (Throwable e){
            cglibJoinPoint.setError(e);
            for (AfterThrowPointChain afterThrowPointChain : afterThrowPointChains) {
                if (((AbstractPointChain) afterThrowPointChain).hasReturn()) {
                    return afterThrowPointChain.intercept();
                }else{
                    afterThrowPointChain.intercept();
                }
            }
        }
        cglibJoinPoint.restIndex();
        return result;
    }

本文的難點在於執行arond方法,我這裏所有方法都只有一個固定的參數joinPoint,執行
proceed方法就是執行目標方法,之前想到了多個around的時候採用多重代理,但是結果cglib的多重代理報錯(查看字節碼反編譯發現是生成了重複的方法),所以採用這種責任鏈模式,在所有得方法裏面的joinPoint其實是一個對象

public Object Around3(AroundJointPoint joinPoint) throws Throwable {
        log.info("============環繞通知log1,執行前============");
        Object proceed = joinPoint.proceed();
        log.info("============環繞通知log1,執行後============");
        return proceed;
    }

所有的方法都是遍歷pointChain來執行

PointChain接口以及實現
public interface PointChain {

    /**
     * 執行方法
     * @return
     */
    Object intercept();

    /**
     * 序號,序號越小越優先執行
     * @return
     */
    default int getOrder(){
        return 0;
    }

    /**
     * 獲取切入點函數
     * @return
     */
    MyJoinPoint getMyJoinPoint();

}

一個具體的jointChain,AroundPointChain

/**
 * @author authorZhao
 * @date 2020年06月09日
 */
public interface AroundPointChain extends PointChain{
    /**
     * 執行方法,默認執行各自的方法
     * @return
     */
    @Override
    default Object intercept() {
        return doAround();
    }

    /**
     * 在原始方法前後執行
     * @return
     */
    Object doAround();

}
AroundPointChainImpl
public class AroundPointChainImpl extends AbstractPointChain implements AroundPointChain {

    public AroundPointChainImpl(Object obj, Method method, Object[] params) {
        super(obj, method, params);
    }

    public AroundPointChainImpl() {
    }

    @Override
    public Object doAround(){
        try{
            return getMethod().invoke(getObj(),getParams());
        }catch (Throwable e){
            throw new MyAopException(e);
        }

    }
}

AbstractPointChain

pointChain繼承的抽象類,這個類用於執行加了@MyAround註解方法

public abstract class AbstractPointChain {

    /**
     * aop配置的對象
     */
    private Object obj;

    /**
     * aop處的方法
     */
    private Method method;

    /**
     * 參數
     */
    private Object[] params;

    /**
     * 固定參數參數
     */
    private MyJoinPoint myJoinPoint;

    private MySignature mySignature;

    private int order=0;

    public AbstractPointChain(Object obj, Method method, Object[] params) {
        this.obj = obj;
        this.method = method;
        this.params = params;
    }

    public AbstractPointChain() {
    }



    /**
     * 方法是否返回結果
     * @return
     */
    public boolean hasReturn(){
        Class<?> returnType = method.getReturnType();
        if(returnType==Void.class)return false;
        return true;
    }

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Object[] getParams() {
        //return params;
        return new Object[]{getMyJoinPoint()};
    }

    public void setParams(Object[] params) {
        this.params = params;
    }

    public MyJoinPoint getMyJoinPoint() {
        return myJoinPoint;
    }

    public void setMyJoinPoint(MyJoinPoint myJoinPoint) {
        this.myJoinPoint = myJoinPoint;
    }

    public MySignature getMySignature() {
        return mySignature;
    }

    public void setMySignature(MySignature mySignature) {
        this.mySignature = mySignature;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}

每個pointchain裏面都有同個joinPoint,這個jointPoint的構建是關鍵,jointPoint既能執行原始方法,又能執行環繞通知裏面的其他通知方法。

JoinPoint實現
/**
 * @author authorZhao
 * @date 2020年06月06日
 */
public interface MyJoinPoint {

    /**
     * 獲取當前對象,增強之後的對象,暫時不對外開放
     * @return
     */
    default Object getThis(){
        return null;
    };

    /**
     * 獲取目標對象,可以在任何階段獲得,暫時不對外開放
     * @return
     */
    default Object getTarget(){
        return null;
    };

    /**
     * 獲取方法的參數,可以任意階段獲取
     * @return
     */
    Object[] getArgs();

    /**
     * 獲得返回結果,只有在後置方法裏面能夠獲得
     * @return 獲得返回結果
     */
    Object getResult();

    /**
     * 獲得方法簽名
     * @return 暫時返回簽名對象
     */
    MySignature getSignature();

}
/**
 * 環繞方法
 * @author authorZhao
 * @since 2020-06-14
 */
public interface AroundJointPoint extends MyJoinPoint{

    /**
     * 執行目標方法,如果有多重代理會執行下一層代理方法,
     * 直到所有代理執行完畢會執行目標方法
     * @return Object
     * @throws Throwable
     */
    Object proceed() throws Throwable;

    /**
     * 執行原始方法,這樣子會導致多重攔截失效
     * @return
     * @throws Throwable
     */
    Object invoke()throws Throwable;
}
public abstract class MyJoinPointAbstract implements AroundJointPoint, ErrorJointPoint {

    /**
     * 參數
     */
    private Object[] args;

    /**
     * 返回結果
     */
    private Object result;


    /**
     * 異常信息
     */
    private Throwable error;

    private MySignature signature;

    public MyJoinPointAbstract(Object[] args) {
        this.args = args;
    }

    public MyJoinPointAbstract() {
    }

    @Override
    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    @Override
    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    @Override
    public Throwable getError() {
        return error;
    }

    public void setError(Throwable error) {
        this.error = error;
    }

    @Override
    public MySignature getSignature() {
        return signature;
    }

    public void setSignature(MySignature signature) {
        this.signature = signature;
    }
}

GglibJoinPoint是具體的類,裏面包含了cglib的MethodProxy,目前這裏實現了功能,但是比較拙劣

/**
 * @author authorZhao
 * @date 2020年06月06日
 */
public class GglibJoinPoint extends MyJoinPointAbstract {

    //private AroundPointChain aroundPointChain;

    private Map<Integer,AroundPointChain> pointChainMap = new HashMap<>();
    /**
     * cglib的代理對象
     */
    private Object proxy;

    /**
     * cglib的代理方法
     */
    private MethodProxy methodProxy;

    private int index = 0;

    public GglibJoinPoint() {
    }

    public GglibJoinPoint(Object[] args, AroundPointChain aroundPointChain, Object proxy, MethodProxy methodProxy) {
        super(args);
        this.proxy = proxy;
        this.methodProxy = methodProxy;
    }

    public GglibJoinPoint(AroundPointChain aroundPointChain, Object proxy, MethodProxy methodProxy) {
        this.proxy = proxy;
        this.methodProxy = methodProxy;
    }

    public GglibJoinPoint(Map<Integer, AroundPointChain> pointChainMap) {
        this.pointChainMap = pointChainMap;
    }

    /**
     * 每個方法執行該方法應該是執行PointChain的方法,當PointChain全部執行完畢才執行原始的方法
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    @Override
    public Object proceed() throws Throwable {
        Object obj;
        if(ListUtil.isEmpety(pointChainMap)) {
            obj = invoke();
            setResult(obj);
            return obj;
        }
        int size = pointChainMap.size();

        if(index>=size){
            obj = invoke();
            setResult(obj);
            return obj;
        }else{
            index++;
            obj = pointChainMap.get(index-1).intercept();

        }
        setResult(obj);
        return obj;
    }

    public static Map<Integer,AroundPointChain> build(List<AroundPointChain> aroundPointChainList){
        if(ListUtil.isEmpety(aroundPointChainList))return null;
        Map<Integer,AroundPointChain> map = new HashMap<>();
        for (int i = 0; i < aroundPointChainList.size(); i++) {
            map.put(i,aroundPointChainList.get(i));
        }
        return map;
    }

    public static GglibJoinPoint buildJointPoint(List<AroundPointChain> aroundPointChainList){
        return new GglibJoinPoint(build(aroundPointChainList));
    }
    public void initMap(List<AroundPointChain> aroundPointChainList){
        this.pointChainMap=build(aroundPointChainList);
    }

    /**
     * 清空屬性,重複利用
     */
    public void clear(){
        /*setArgs(null);
        setError(null);
        setResult(null);
        setProxy(null);
        setMethodProxy(null);*/
    }

    @Override
    public Object invoke() throws Throwable{
        return this.methodProxy.invokeSuper(this.proxy,getArgs());
    }


    public Object getProxy() {
        return proxy;
    }

    public void setProxy(Object proxy) {
        this.proxy = proxy;
    }

    public MethodProxy getMethodProxy() {
        return methodProxy;
    }

    public void setMethodProxy(MethodProxy methodProxy) {
        this.methodProxy = methodProxy;
    }

    public void restIndex(){
        this.index=0;
    }

}

代碼上多了自己也越看越亂,再次詳細用文字描述一下。

  • pointchain就是加了@MyAround註解的方法
  • jopintPoint就是裏面的參數,但是他可以執行目標方法。
  • 如果只有一個環繞通知目標方法就是業務代碼裏面的,如果有多個通知目標方法就是下一個通知的方法

具體實現細節有興趣的可參考我的github拙劣實現springaop

1.非spring環境直接aspectj

2.個人分享,篇幅較長,有興趣的看github實現

3.本人原創,轉載請申明

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章