手动模拟实现Spring-AOP

Spring中AOP设计思路是这样的.

在BeaFactory.getBean()时,调用方法initBean();
在该方法中判断当前Bean是否含有满足SpEL表达式的Method, 没有的话, 返回原始的Bean,
有的话, 会对当前Bean做动态代理,
当代理对象的方法被调用时,会发送通知到Aop拦截器,进而实现Aop的功能.

本篇文章思路接着上篇文章,
(手动模拟实现Spring-MVC)
https://blog.csdn.net/yangsnow_rain_wind/article/details/80223158


本次手动模拟Spring-Aop思路如下;
1.添加类FLAopProxy, 该类实现接口InvocationHandler, 用来实现Bean的动态代理功能
2.添加类FLAopConfig, 记录每个被代理Bean下所有满足条件的Method,并记录下其对应的aspject通知
3.修改类FLBeanWrapper, 为该包装类添加新属性FLAopProxy,用来记录Bean的代理配置

本次模拟类的包含关系如下图:
手动模拟实现Spring-AOP_第1张图片

整个关系如下;
每个FLBeanWrapper包含属性_originalBean(存储原始bean对象), _wrapperedBean(存储动态代理后的对象), FLAopProxy对象(用来生成动态代理).
每个FLAopProxy对象包含一个target属性, ()用来保存原始bean对象引用),包含一个FLAopConfig对象(保存Bean符合SpEl条件的method,及该method被调用时的aspect通知.)

下面是源码:

1. FLAopProxy

public class FLAopProxy implements InvocationHandler {

  private FLAopConfig config = new FLAopConfig();
   private Object target;

   //对原始对象生成动态代理,
   public Object getProxy(Object instance) {
       this.target = instance;
       Class clazz = instance.getClass();
       return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

       //当前的Method是代理后的method
       Method originalMethod=this.target.getClass().getMethod(method.getName(),method.getParameterTypes());

       //如果动态代理对象的方法属于config中配置的方法, 则调用前置通知
       if (config.containMethod(originalMethod)) {
           FLAopConfig.FLAspectRealHandler handler = this.config.getPoints(originalMethod);
           handler.getHanders()[0].invoke(handler.getApsHanderInstance());
       }
       Object result = method.invoke(target, args);

       //如果动态代理对象的方法属于config中配置的方法, 则调用后置通知
       if (config.containMethod(originalMethod)) {
           FLAopConfig.FLAspectRealHandler handler = this.config.getPoints(originalMethod);
           handler.getHanders()[1].invoke(handler.getApsHanderInstance());
       }
       return result;
   }

}

2. FLAopConfig

public class FLAopConfig {

    private Map points = new HashMap<>();

    public FLAspectRealHandler getPoints(Method m) {
        return this.points.get(m);
    }

    public void AddMethod(Method method, Object aspect, Method[] points) {
        this.points.put(method, new FLAspectRealHandler(aspect, points));
    }

    public boolean containMethod(Method m) {
        return this.points.containsKey(m);
    }

//内部对象, 用来存储method对象及需要通知的aspject
    public class FLAspectRealHandler {
        private Object apsHanderInstance;
        private Method[] handers;

        public FLAspectRealHandler(Object apsHanderInstance, Method[] handers) {
            this.apsHanderInstance = apsHanderInstance;
            this.handers = handers;
        }

        public Object getApsHanderInstance() {
            return apsHanderInstance;
        }

        public Method[] getHanders() {
            return handers;
        }
    }

}

3.修改FLBeanWrapper

添加属性FLAopProxy

public class FLBeanWrapper {
    private FLAopProxy aopProxy = new FLAopProxy();
    private Object _originalBean;
    private Object _wrapperedBean;

    public FLBeanWrapper(Object originalBean) {
        _originalBean = originalBean;
        this._wrapperedBean = aopProxy.getProxy(originalBean);
    }

    public void setAopConfig(FLAopConfig config) {
        this.aopProxy.setConfig(config);
    }

}

4.修改getBean()

getBean()方法中在bean实例化后, 对Bean进行包装, 并生成Bean对应的Aop配置信息

//com.FL.springlean.framework.context.FLWebApplicationContext#getBean

//getBean时, 全部验证下当前的Bean是否满足Expression的条件
public Object getBean(String beanName) {
     //...省略次要代码
     FLBeanWrapper beanWrapper = new FLBeanWrapper(beanInstance);

     //生成aop配置信息
     beanWrapper.setAopConfig(instantAopConfigByBean(beanDefinition));
}

private FLAopConfig instantAopConfigByBean(FLBeanDefinition beanDefinition) throws Exception {
    FLAopConfig config = new FLAopConfig();

    String expression = reader.getProperties().getProperty("pointCut");
    String[] before = reader.getProperties().getProperty("aspectBefore").split("\\s");
    String[] after = reader.getProperties().getProperty("aspectAfter").split("\\s");

    //此处获得Bean的原始类信息
    String className = beanDefinition.getBeanClassName();
    Class clazz = Class.forName(className);
    Class aspectClass = Class.forName(before[0]);

    Pattern pattern = Pattern.compile(expression);

    //遍历当前class下所有方法, 判断该方法是否满足条件.
    for (Method m : clazz.getMethods()) {
        Matcher matcher = pattern.matcher(m.toString());
        if (matcher.matches()) {

        //满足条件的记录下来
         config.AddMethod(m, aspectClass.newInstance(), new Method[]{aspectClass.getMethod(before[1]), aspectClass.getMethod(after[1])});
        }
    }
    return config;
}

5.修改initHandlerMappings()

在方法com.FL.springlean.framework.webmvc.servlet.FLDispatcherServlet#initHandlerMappings中,由于我们使用方法com.FL.springlean.framework.context.FLWebApplicationContext#getBean返回的Bean是动态代理对象,所以在initHandlerMappings需要获取原始的Controller对象, 为此,我们要修改下该方法;

    private void initHandlerMappings(FLWebApplicationContext context) {
        String[] beanNames = context.getBeanDefinitions();
        for (String beanName : beanNames) {
            Object proxy = context.getBean(beanName);
            Object controller = null;
            try {
                controller = FLAopProxyUtils.getTargetObject(proxy);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //忽略掉非主要代码
    }

//com.FL.springlean.framework.aop.FLAopProxyUtils
public class FLAopProxyUtils {
    public static  Object getTargetObject(Object proxy) throws Exception{
        //先判断一下,这个传进来的这个对象是不是一个代理过的对象
        //如果不是一个代理对象,就直接返回
        if(!isAopProxy(proxy)){ return proxy; }
        return getProxyTargetObject(proxy);
    }

    private static boolean isAopProxy(Object object){
        return Proxy.isProxyClass(object.getClass());
    }

    private static Object getProxyTargetObject(Object proxy) throws Exception{
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        FLAopProxy aopProxy = (FLAopProxy) h.get(proxy);
        Field target = aopProxy.getClass().getDeclaredField("target");
        target.setAccessible(true);
        return  target.get(aopProxy);
    }
}

注释: FLAopProxyUtils 为一个工具类用来判断一个对象是否为动态代理对象,并返回原始的对象.

6.添加测试Aspject类型

public class LogAspect {
    public void before(){
        //这个是前置通知
        System.out.println("Invoker Before Method!!!");
    }

        //这个是后置通知
    public void after(){
        System.out.println("Invoker After Method!!!");
    }

}

7.添加配置SpEl

pointCut=public .* com\.FL\.springlean\.userapi\.services\..*Service\..*\(.*\)
aspectBefore=com.FL.springlean.userapi.aspect.LogAspect before
aspectAfter=com.FL.springlean.userapi.aspect.LogAspect after

注释: 为了方便, 前置通知类及方法用空格隔开
如:属性aspectBefore, aop的类是”com.FL.springlean.userapi.aspect.LogAspect“,前置通知方法是before

———-完:———————

项目源码:
https://gitee.com/yangxulong/gitee-projects

你可能感兴趣的:(Spring)