• 写一个简易的spring包含ioc和aop功能


      在前几篇文章中,分别介绍了实现pointcut表达式的功能从xml中获取信息创建一个简单的ioc容器,如何解析注解利用cglib生成新代理类该如何获取字段注解根据这些知识点,就可以实现一个简易版的ioc容器

    IOC

    创建bean过程

    在这里插入图片描述

      在扫描xml中的配置时可能会有通过注解配置bean,由于在配置bean时可能会有相互引用bean的情况,因此需要xml中配置的bean信息和注解bean的信息加载完之后再来创建bean;

      在创建bean时需要考虑到以下几个方面:

    • 1.单例,多例bean应该在什么时候创建
    • 2.bean的循环引用检测
    • 3.在什么时候填充bean的属性

      在创建bean的时候考虑到bean可能是单例或者是多例;那么在根据bean信息创建bean时多例bean就不需要在这个时候创建了,只需要在使用到多例bean时再创建;而单例bean则需要在这个阶段创建,因为只需要创建一次可供多次使用;

      在创建bean时,如果在构造方法中引用了其他bean就可能会造成循环引用,因此在创建bean时还需要检测是否构成了循环引用;那如何检测呢?其实也比较简单,只需要在创建bean时,将正在创建的beanName放入到一个set集合中,bean创建完成就将beanName从集合中删除;

    在这里插入图片描述

      在初始化bean之后,应该立即将bean的属性填充吗?

      答案是否定的,因为可能会有aop切面的配置;aop代理有2种方式:JDK动态代理,cglib代理;cglib代理会导致代理后的对象属性值消失;因而为了统一处理,应该在aop代理完成之后再填充bean的属性;

    在这里插入图片描述
      cglib代理会生成一个新class,而target只是提供class信息;新代理生成的cglibObject对象与原target对象没有关联;因而如果创建target对象时给target对象属性赋值经cglib代理之后,新生成的对象cglibObject的属性值仍然是null;

    在这里插入图片描述
      jdk动态代理的生成的类,proxyObject对原target对象保持引用;因此对target对象的属性赋值之后,经aop代理生成的类不会丢失原target的属性值;

    如何获取bean?

      在创建bean时只创建了单例bean,如果获取的bean是多例,那么就需要重新创建;
    在这里插入图片描述

    AOP

    流程:

    在这里插入图片描述

      从配置中获取到所有的advices,容器中的bean去匹配advices;这样bean就得到了匹配的advice,通过bean+advice由aop代理生成代理类对象;

    advice的类型

      advice一共有5中类型,其中after,around可以看作是复合类型,因此5种adviceType,可以分解为3种;
    在这里插入图片描述

    AOP代理

      由于同一个目标类,可能同时被多个切面类代理;如果每被一个切面代理都要生成一个新的类型,这样效率就比较低;因此要将这些作用到同一个类的切面整合到一起,一次代理整合多个不同切面的advice;

      切面类的整合实际是advice的整合,advice分为3类:before,afterReturning,afterThrowing;将所有切面配置的advice,按照类型分别存放到这3种类型中;

    在这里插入图片描述

    advice去重

      advice加入到MethodAdvice时,是根据type类型分别加入到对应的XXXadviceInterceptor中;before,afterReturning,afterThrowing这三种advice对应:BeforeAdviceInterceptor,AfterReturningAdviceInterceptor,AfterThrowingAdviceInterceptor;
      而advice,around与其他三种类型不同;

    • after要将其分别加入到:AfterReturningAdviceInterceptor,AfterThrowingAdviceInterceptor;
    • around要将其加入到:BeforeAdviceInterceptor,AfterReturningAdviceInterceptor,AfterThrowingAdviceInterceptor;

      每个advice可以分成三部分:adviceType + pointcut + method;假设现在有2个advice:

    • before:
    • around:

      这2个advice是同一个切面,那么before与around的:pointcut ,method,都相同;而around可以分解为:before + afterReturning + afterThrowing;around生成的advice会加入到这3个XXXadviceInterceptor中;在beforeAdviceInterceptor中就有2个advice对象,而这2个advice调用的切面方法相同都是before,就会造成调用相同切面的before方法2次;那如何去重也就简单了,只需要在XXXadviceInterceptor中判断:新加入的advice在该XXXadviceInterceptor中是否存在;advice是否存在由:pointcut ,method决定;

      这里需要注意的是,Advice是放入到Set中,需要重写Advice的hashcode和equals方法;而Advice包含Pointcut对象,因此还需要重写Pointcut的hashcode和equals方法;

    测试

    • AOP 的advice去重测试
    <beans>
        <bean  id="aspect" class="tt.AOPtest"/>
        <bean class ="tt.Aspected"/>
        <aopConfig>
            <aspect ref = "aspect">
                <before pointcut="execution (* * tt.inr.impl.* *())"  method="before" />
                
                <around pointcut="execution (* * tt.inr.impl.*  *())"  method="before" />
            aspect>
        aopConfig>
        <propertyPlaceholder load="test.properties"/>
        <componentScan />
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    test.properties

    t1=dfgdfg
    t2=ty
    t3=name1
    name=78
    fg=456
    
    • 1
    • 2
    • 3
    • 4
    • 5

    bean

    @Component
    public class QAZ implements QWE {
    
        @Value(" ${t1} ")
        String tt;
        @Value("${ fg }")
        String gh;
        @Value("${name}")
        String nb;
        @Value("my addr")
        String addr;
    
        @Override
        public String toString() {
            return "QAZ{" +
                    "tt='" + tt + '\'' +
                    ", gh='" + gh + '\'' +
                    ", nb='" + nb + '\'' +
                    ", addr='" + addr + '\'' +
                    '}';
        }
    
    
        @Override
        public void print() {
            System.out.println(toString());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    测试代码:

        @Test
        public void test() throws Throwable {
            Map<String , XmlNode> labelParse  = new HashMap<>();
            labelParse.put("aopConfig", AdvisorParse.getInstance());
            ApplicationContext context =
                    new XmlApplicationContext("aop.xml",labelParse,null);
            Aspected aspected = (Aspected) context.getBean(Aspected.class);
            QWE qaz = (QWE) context.getBean(QWE.class);
            qaz.print();
    
        }
    ==============================result===============================================================
    aspect before test
    QAZ{tt='dfgdfg', gh='456', nb='78', addr='my addr'}
    aspect before test
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 循环引用测试

    只有在构造方法中引用其他bean,才可能造成循环引用;

    public class A {
        B b;
        public A(B b){
            this.b = b;
        }
    }
    
    public class B {
    	public A a;
    	public B(A a){
    	    this.a = a;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    xml配置

    
    <beans >
        <bean id="test2" class="tt.B" >
            <constructor type="tt.A" ref="test1" />
        bean>
    
        <bean id="test1" class="tt.A">
            <constructor type="tt.B" ref="test2" />
        bean>
    
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果

    exception.CycleDependencyException:  tt.B->tt.A->tt.B
    	at Context.BeanFactory.creatBean(BeanFactory.java:75)
    	at Context.BeanFactory.getConstructorParamRefValue(BeanFactory.java:136)
    
    
    • 1
    • 2
    • 3
    • 4
    • @Autowired测试
    @Component
    public class QAZ implements QWE {
    
        @Autowired
        AutowiredTest autowiredTest;
        ....
        ..
    }
    
    
    @Component
    public class AutowiredTest {
        @Value("${autowired }")
        String q1;
        @Value("dgd")
        String q2;
        @Value("1")
        int yy;
        @Value("true")
        boolean rr;
        @Override
        public String toString() {
            return "AutowiredTest{" +
                    "q1='" + q1 + '\'' +
                    ", q2='" + q2 + '\'' +
                    ", yy=" + yy +
                    ", rr=" + rr +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    结果

    
    aspect before test
    QAZ{autowiredTest=AutowiredTest{q1='testAutowire', q2='dgd', yy=1, rr=true}, tt='dfgdfg', gh='456', nb='78', addr='my addr'}
    aspect before test
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码地址:ioc-aop

  • 相关阅读:
    C++--Linux基础使用
    flink数据类型和序列化-1.13
    机器学习笔记 - CRAFT(文本检测的字符区域感知)论文解读
    大数据Hadoop之——DorisDB核心概念介绍与简单使用(StarRocks)
    Java IO 之 BIO、NIO 和 AIO
    小笔记:03-jquery-对前端option的一些操作
    C++语言实现网络爬虫详细代码
    C# 发送邮件
    Kernel: module接口ABI相关问题分析的思路;__GENKSYMS__;
    .NET混合开发解决方案3 WebView2的进程模型
  • 原文地址:https://blog.csdn.net/m0_37550986/article/details/126216667