(二)手写Spring项目之AOP

github: https://github.com/iamzhubaoliang/MySpring
博客不易,希望给个star,如果有问题可以评论,也可以github issue,我会耐心解答疑惑

1. 什么是AOP

AOP就是面向切面编程,将横切服务与核心业务逻辑分离,更好关注核心业务逻辑或者说将系统级服务与具体的商业逻辑分离。AOP的实现主要实现方法是代理,Spring中使用的是原生JDK的动态代理

2. 原生JDK动态代理

原生的JDK动态代理首先代理类要实现InvocationHandler接口,并重写invoke方法,创建代理的时候将原对象传入,这样代理对象持有原对象,可以调用原对象的方法,并对某些部分进行增强
例子
接口


public interface peopleInterface {
    public void runFunc();
}

实现类

public class people implements peopleInterface{
    @Override
    public void runFunc() {
        System.out.println("方法执行");
    }
}

代理类

public class JDKProxy implements InvocationHandler {
    private people p;

    public  Object bind(people target)
    {
        this.p=target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置");
        p.runFunc();
        System.out.println("后置");
        return null;
    }

    public static void main(String[] args) {
        people p=new people();
        JDKProxy proxy=new JDKProxy();
        peopleInterface peopleInterface= (test.peopleInterface) proxy.bind(p);
        peopleInterface.runFunc();
    }
}

注意:原生JDK必须有接口,因为原生JDK是基于接口实现的

3.实现思路

基于上述的原生JDK的例子,基本的实现方法就是这样,那么我们怎样去做增强?这个思路就是在遍历的时候将增强的方法存入代理中,当执行方法的时候判断这个方法是否有增强,具体做法就是用一个集合来进行存储
首先我们要识别增强类,方法就是使用@Aspect注解

  if (clazz.isAnnotationPresent(Aspect.class)) {
                for (Method method : clazz.getMethods()) {
                    String annotationPath = "";

                    if (method.isAnnotationPresent(PointCut.class)) {
                        String delegateString = method.getAnnotation(PointCut.class).value();
                        annotationPath=delegateString;
                        if (delegateString.charAt(delegateString.length() - 1) == ')') {

                            //说明是在某个方法上面的切面
                            annotationPath =annotationPath.replace("()","");
                            //切掉方法保留到类
                            String[] seg=cutName(annotationPath);
                            AddToAspects(clazz,seg[0],true,seg[1]);
                        }else
                        {//切面是某个包或者类
                            String annotationPathClone=new String(annotationPath);
                            URL resource=Container.classLoader.getResource(annotationPathClone.replace(".","/"));
                            if(resource==null)
                                resource=Container.classLoader.getResource(annotationPathClone.replace(".","/")+".class");
                            File file=new File(resource.getFile());
//                            System.out.println(file);
                            if(file.isDirectory()) {
                                ArrayList<File> fileArray=new ArrayList<>();
                                DFSgetCureentDir(file,fileArray);
                                for(File f:fileArray)
                                {
                                   String key= f.getAbsolutePath().replace(splitOP,".");
                                   key=key.substring(key.indexOf(annotationPath),key.indexOf(f.getName())+f.getName().length()-6);
                                   AddToAspects(clazz,key,false,"");
                                }

                            }else
                            {
                                String key= file.getAbsolutePath().replace(splitOP,".");
                                key=key.substring(key.indexOf(annotationPath),key.indexOf(file.getName())+file.getName().length()-6);
                                AddToAspects(clazz,key,false,"");
                            }
                        }

                    }


                }
            }

首先查看遍历的类是不是有Aspect注解,如果有那么遍历每一个方法寻找PointCut注解,这个注解上的value为要增强的类或者具体的方法,当有)的时候我们认为是方法,当没有的时候认为是某个包或者类,将要增强的类或者方法作为key,利用MethodNode包装方法作为value放入BeforeDelegatedSet,AfterDelegatedSet,MethodNode的作用是包装方法,里面有个boolean isFunction表明这个增强是在整个类上还是某个方法上面。

  private static void AddToAspects(Class<?> clazz,String key,boolean isMethod,String MethodName) {
        for (Method method : clazz.getMethods())
        {
            MethodNode methodNode=new MethodNode(method,isMethod);
            methodNode.setMethodName(MethodName);

            if(method.isAnnotationPresent(Before.class))
            {
                if(!BeforeDelegatedSet.containsKey(key))
                    BeforeDelegatedSet.put(key,new ArrayList<>());
               BeforeDelegatedSet.get(key).add(methodNode);
            }
            if(method.isAnnotationPresent(After.class))
            {
                if(!AfterDelegatedSet.containsKey(key))
                    AfterDelegatedSet.put(key,new ArrayList<>());
               AfterDelegatedSet.get(key).add(methodNode);
            }
        }

    }

然后在CreateBean的时候就要查找BeforeDelegatedSet,AfterDelegatedSet这两个set看是否有这个类的增强,如果有那么取出来放入设置放入动态工厂,当从动态工厂里面拿对象的时候将这些方法设置放入代理对象中

  if(BeforeDelegatedSet.containsKey(beanDefintion.getClazz().getName()))
                {
                    dynamicBeanFactory.setDelegated(true);
                    dynamicBeanFactory.setBeforemethodCache(BeforeDelegatedSet.get(beanDefintion.getClazz().getName()));
                }
                if(AfterDelegatedSet.containsKey(beanDefintion.getClazz().getName()))
                {
                    dynamicBeanFactory.setDelegated(true);
                    dynamicBeanFactory.setAftermethodCache(AfterDelegatedSet.get(beanDefintion.getClazz().getName()));
                }


    //此方法用于返回是不是动态代理对象
    public Object getTarget() {
        if(isDelegated)
        {
            BeanProxyUtil beanProxyUtil=new BeanProxyUtil();
            beanProxyUtil.setAftermethodCache(getAftermethodCache());
            beanProxyUtil.setBeforemethodCache(getBeforemethodCache());
            return beanProxyUtil.creatCarProxy(instance);
        }else
            return instance;
    }

这样当执行的时候就循环遍历放入代理中的方法,如果是类的每个类级别的增强,类中的每个方法都要执行,如果是方法的(有个bool值isFuntion表明是方法上的增强)则只有某个特定的方法执行

package com.baoliang.spring.ProxyUtils;


import com.baoliang.spring.Helper.MethodNode;
import com.baoliang.spring.Helper.Container;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;

public class BeanProxyUtil implements InvocationHandler {

    private Object tartget;
    private ArrayList<MethodNode> BeforemethodCache;
    private ArrayList<MethodNode> AftermethodCache;

    public ArrayList<MethodNode> getBeforemethodCache() {
        return BeforemethodCache;
    }

    public void setBeforemethodCache(ArrayList<MethodNode> beforemethodCache) {
        BeforemethodCache = beforemethodCache;
    }

    public ArrayList<MethodNode> getAftermethodCache() {
        return AftermethodCache;
    }

    public void setAftermethodCache(ArrayList<MethodNode> aftermethodCache) {
        AftermethodCache = aftermethodCache;
    }




    public Object creatCarProxy(Object target) {
        this.tartget=target;
        try {
            Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
            return proxy;
        }catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        Object result=null;
        //整个类级别的切面方法执行

        if(BeforemethodCache!=null)
        for(MethodNode beforMethod:BeforemethodCache)
        {
            if((!beforMethod.isFunction()))
            {
                String[] name=beforMethod.getMethod().getDeclaringClass().toString().split("\\.");
                beforMethod.getMethod().invoke(Container.singletonObjects.get(name[name.length-1]));
            }
        }


        //特定方法执行
        if(BeforemethodCache!=null)
        for(MethodNode beforMethod:BeforemethodCache)
        {
            if(beforMethod.isFunction()&&beforMethod.getMethodName().equals(method.getName()))
            {
                String[] name=beforMethod.getMethod().getDeclaringClass().toString().split("\\.");
                beforMethod.getMethod().invoke(Container.singletonObjects.get(name[name.length-1]));
            }
        }
        result=method.invoke(tartget,args);
        if(AftermethodCache!=null)
        for(MethodNode afterMethod:AftermethodCache)
        {
            if(afterMethod.isFunction()&&afterMethod.getMethodName().equals(method.getName()))
            {
                String[] name=afterMethod.getMethod().getDeclaringClass().toString().split("\\.");
                afterMethod.getMethod().invoke(Container.singletonObjects.get(name[name.length-1]));
            }
        }
        //类级别
        if(AftermethodCache!=null)
        for(MethodNode afterMethod:AftermethodCache)
        {
            if((!afterMethod.isFunction()))
            {
                String[] name=afterMethod.getMethod().getDeclaringClass().toString().split("\\.");
                afterMethod.getMethod().invoke(Container.singletonObjects.get(name[name.length-1]));
            }
        }

      return  result;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值