Spring源码------手写体验AOP

Spring源码------手写体验AOP

目录

Spring源码------手写体验AOP

1、前言

2、AOP的实现原理

3、核心代码

4、总结


1、前言

本篇博客并非是对Spring源码的深入研究。过程中主要是体验Spring AOP的过程。那么这篇博客涉及到的知识点大致有以下几点:

  • 如何自定义注解,如何通过反射机制去赋予注解强大的功能(说白了,就是体验在反射机制下,注解功能是多么的强大)
  • Spring Ioc容器的实现原理
  • Spring DI 注解注入
  • SpringMVC AOP
  • Java反射机制
  • Java I/O流(加载配置文件,读取配置文件信息) 
  • 正则表达式

2、AOP的实现原理

相信大家或多或少的了解过AOP,都知道它是面向切面编程,在网上搜索可以找到很多的解释。这里一句话来总结j就是:AOP是能够让我们在不影响原有功能的前提下,为软件横向扩展功能。 那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具体的某一层就是我们所说的横向。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。

我们在来看一下AOP和OOP的区别:AOP是OOP的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。

接下来介绍一下提到AOP就必须要了解的知识点:

  • 切面(Aspect):拦截器类,其中会定义切点以及通知
  • 切点(PointCut):具体拦截的某个业务点。
  • 通知(Advice):切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:
  • 前置通知:@Before 在目标业务方法执行之前执行
  • 后置通知:@After 在目标业务方法执行之后执行
  • 返回通知:@AfterReturning 在目标业务方法返回结果之后执行
  • 异常通知:@AfterThrowing 在目标业务方法抛出异常之后
  • 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行
     

其中SpringAop 的实现主要是通过动态代理思想实现的。主要有两种实现的方式:jdk动态代理和Cglib字节码代理

具体可以阅读我之前写的文章:《设计模式------代理模式详解》

3、核心代码

主要类结构图:

  

.......
//创建真正的实例对象
    private Object instantiateBean(String beanName,ZPSBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            if(this.factoryBeanObjectCache.containsKey(beanName)){
                instance = this.factoryBeanObjectCache.get(beanName);
            }else {
                Class<?> clazz = Class.forName(className);
                //默认的类名首字母小写
                instance = clazz.newInstance();

                //AOP是在这里开始进行的
                //首先需要读取配置文件,然后进行解析
                ZPSAdviceSupport config = instantionAopConfig(beanDefinition);
                config.setTargetClass(clazz);
                config.setTarget(instance);

                //判断是否需要生成代理类,如果需要就覆盖原生对象
                //如果不需要,就不做任何处理
                if(config.pointCutMatch()){
                    instance = new ZPSJdkDynamicAopProxy(config).getProxy();
                }

                this.factoryBeanObjectCache.put(beanName, instance);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return instance;
    }
.......
/**
 * @description: 封装通知Advice信息
 * @author: zps
 * @create: 2020-05-09 22:51
 **/
@Data
public class ZPSAdvice {

    private Object aspect;
    private Method adviceMethod;
    private String throwName;

    public ZPSAdvice(Object aspect , Method adviceMethod){
        this.aspect = aspect;
        this.adviceMethod = adviceMethod;
    }

}

/**
 * @description: 封装读取到的配置文件信息
 * @author: zps
 * @create: 2020-05-09 22:54
 **/
@Data
public class ZPSAopConfig {
    private String pointCut;
    private String aspectClass;
    private String aspectBefore;
    private String aspectAfter;
    private String aspectAfterThrow;
    private String aspectAfterThrowingName;
}

/**
 * @description: AOP配置文件解析工具类
 *
 * @author: zps
 * @create: 2020-05-09 23:02
 **/
public class ZPSAdviceSupport {

    //从配置文件获取的AOP相关信息
    private ZPSAopConfig config;
    private Object target;
    private Class targetClass;
    private Pattern pointCutClassPattern;

    //保存被代理方法的通知
    //Mehhod:被代理的方法   Map<String , ZPSAdvice> String : 需要织入的通知类型,如before , ZSPAdvice:通知对象
    private Map<Method , Map<String , ZPSAdvice>> methodCache;

    public ZPSAdviceSupport(ZPSAopConfig config){
        this.config = config;
    }


    private void parse(){
        //把Spring的Excpress变成Java能够识别的正则表达式
        String pointCut = config.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");


        //保存专门匹配Class的正则
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));


        //享元的共享池
        methodCache = new HashMap<Method, Map<String, ZPSAdvice>>();
        //保存专门匹配方法的正则
        Pattern pointCutPattern = Pattern.compile(pointCut);

        try {
            //获取切面类
            Class aspectClass = Class.forName(this.config.getAspectClass());
            //保存切面的方法
            Map<String , Method> aspectMethods = new HashMap<String, Method>();
            for(Method method : aspectClass.getMethods()){
                aspectMethods.put(method.getName() , method);
            }

            for(Method method : this.targetClass.getMethods()){
                //将方法化为String
                String methodString = method.toString();
                if(methodString.contains("throws")){
                    methodString = methodString.substring(0 , methodString.lastIndexOf("throws")).trim();
                }
                Matcher matcher = pointCutPattern.matcher(methodString);
                if(matcher.matches()){ //若匹配,保存一对一的映射关系
                    Map<String , ZPSAdvice> advices = new HashMap<String, ZPSAdvice>();

                    if(!(config.getAspectBefore() == null ||
                            config.getAspectBefore().equals(""))){
                        advices.put("before" , new ZPSAdvice(aspectClass.newInstance() ,
                                aspectMethods.get(config.getAspectBefore())));
                    }
                    if(!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))){
                        advices.put("after",new ZPSAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfter())));
                    }
                    if(!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))){
                        ZPSAdvice advice = new ZPSAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfterThrow()));
                        advice.setThrowName(config.getAspectAfterThrowingName());
                        advices.put("afterThrow",advice);
                    }
                    //跟目标代理类的业务方法和Advices建立一对多个关联关系,以便在Porxy类中获得
                    methodCache.put(method , advices);
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //根据一个目标代理类的方法,获得其对应的通知
    public Map<String,ZPSAdvice> getAdvices(Method method, Object o) throws Exception {
        //享元设计模式的应用
        Map<String,ZPSAdvice> cache = methodCache.get(method);
        if(null == cache){
            Method m = targetClass.getMethod(method.getName(),method.getParameterTypes());
            cache = methodCache.get(m);
            this.methodCache.put(m,cache);
        }
        return cache;
    }

    //判断是否需要生成代理类
    public boolean pointCutMatch() {
         return pointCutClassPattern.matcher(this.targetClass.toString())
         .matches();
    }

    public void setTargetClass(Class<?> clazz) {
        this.targetClass = clazz;
        parse();
    }

    public void setTarget(Object instance) {
        this.target = instance;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public Object getTarget() {
        return target;
    }

}
**
 * @description: AOP工具类:主要是解析配置文件
 * @author: zps
 * @create: 2020-05-09 22:59
 **/
public class ZPSJdkDynamicAopProxy implements InvocationHandler {

    private ZPSAdviceSupport config;

    public ZPSJdkDynamicAopProxy(ZPSAdviceSupport config) {
        this.config = config;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader() , this.config
        .getTargetClass().getInterfaces() , this);
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        Map<String, ZPSAdvice> advices = config.getAdvices(method,null);

        Object returnValue;
        try {
            invokeAdivce(advices.get("before"));

            returnValue = method.invoke(this.config.getTarget(),objects);

            invokeAdivce(advices.get("after"));
        }catch (Exception e){
            invokeAdivce(advices.get("afterThrow"));
            throw e;
        }

        return returnValue;
    }

    private void invokeAdivce(ZPSAdvice advice) {
        try {

            advice.getAdviceMethod().invoke(advice.getAspect());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

4、总结

手写体验Spring总结如下:

Spring初始化的大致流程:IOC—>AOP—>DI—>MVC

  • IOC(控制反转)

         1、创建整个容器的主体ApplicationContext。
         2、BeanDefinitionReader读取配置文件(properties、xml、yml),扫描相关的类,将配置信息存储到BeanDefinition。
         3、初始化IOC容器,将实例化对象存储到BeanWrapper。
         4、ApplicationContext getBean()采用的单例bean,容器式(Lazy)延时加载。

  • AOP(面向切面编程)

         1、getBean()—>instantiateBean() 判断是否要生成Proxy代理对象。
            (1)加载AOP的配置文件,调用instantionAopConfig(beanDefinition),生成AdvisedSupport。
   AdvisedSupport Map<Method,Map<String,GPAdvice>> methodCache存储了目标代理类的业务方法和Advices建立的                       一对多的关联关系。
   AdvisedSupport getAdvices()可根据一个目标代理类的方法,获得其对应的通知。
            (2)判断规则,要不要生成代理类GPJdkDynamicAopProxy,如果要就覆盖原生对象,如果不要就不做任何处理,返回原      生对象。

  • DI(依赖注入)

     1、getBean()—>instantiiateBean()用反射初始化Bean对象。
     2、populateBean()完成依赖注入,用反射注入。
     3、循环依赖注入:用缓存去解决依赖注入的问题。

  • MVC

     1、DisparcherServlet init()初始化9大组件。
     2、initStrategers()初始化HandlerMapping,HandlerAdapter,ViewResolver。
           (1)一个HandlerMapping对应一个HandlerAdapter。
           (2)ViewResolver涉及模板,一个ModelAndView关联一个ViewResolver。
     3、根据HandlerAdapter得到一个ModelAndView。
          (1)如不返回页面则直接responser.getWriter().write()输出到浏览器上。
          (2)如返回页面,启用模板引擎生成一个View,通过View render()渲染(即:读取模板文件内容,用正则替换占位符),最后输出到浏览器上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值