zoukankan      html  css  js  c++  java
  • Spring ioc的核心源码及拓展

    Spring ioc

    问题

    • javaSE获取一个类的Class有几种方式?
    • ioc的自动装载有几种方式?
    • ioc的底层通过哪些原理机制实现的?
    • 实现类ClassPathXmlApplicationContext的底层AbstractApplicationContext的源码核心方法是什么?

    Spring的两大核心机制 IoC(控制反转)和 AOP(面向切面编程),从开发者的角度来讲,我们使用Spring框架就是用它的IoC和AOP。IoC是典型的工厂模式,通过工厂来注入对象,AOP是代理模式。

    IoC是Spring框架的基石,IoC也叫控制反转,传统的开发方式中,需要调用对象时,需要手动来创建对象,即对象是由调用者主动创建出来的。

    通过IoC容器来创建对象

    • 搭建Spring环境
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.1.2.RELEASE</version>
      </dependency>
    
    • 创建IoC配置文件,可以自定义名称,spring.xml。
    <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:p="http://www.springframework.org/schema/p"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
      	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
      ">
      
        <bean id="student" class="com.m.entity.Student">
            <property name="id" value="1"></property>
            <property name="name">
                <value><![CDATA[<杨三>]]></value>
            </property>
            <property name="age" value="21"></property>
        </bean>
          
          
        <bean id="student3" class="com.southwind.entity.Student">
            <constructor-arg name="id" value="2" ></constructor-arg>
            <constructor-arg name="name" value="小王"></constructor-arg>
            <constructor-arg name="age" value="18"></constructor-arg>
            
            <constructor-arg index="0" value="3"></constructor-arg>
            <constructor-arg index="1" value="小明"></constructor-arg>
            <constructor-arg index="2" value="19"></constructor-arg>
        </bean>
         <!--p命名空间--> 
        <bean id="clazz" class="com.m.entity.Classes" p:id="1" p:name="一班" p:teacher-ref="teacher"></bean>
      
      </beans>
    
    • 调用API获取IoC创建好的对象
    
    //创建Class的方式
    //1
    Class clazz1 = Student.class;
    //2
    Class clazz2 = null;
    try {
        clazz2 = Class.forName("com.m.ioc.entity.Student");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    System.out.println(clazz1 == clazz2);   //true
    //3
    Student student = new Student();
    Class clazz3 = student.getClass();
    System.out.println(clazz2 == clazz3);   //true
    
    
    //加载spring.xml配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    //获取IoC中的对象
      Student student = (Student) applicationContext.getBean("student");
    //Student student2 = (Student) applicationContext.getBean(Student.class);
      System.out.println(student);
    

    AbstractApplicationContext是ioc-bean的核心类

    这个抽象类的子类就是 ClassPathXmlApplicationContext

    这个抽象类乃至ioc的核心方法就是实例方法refresh()

    这个抽象类有三个抽象方法 getBeanFactory(),closeBeanFactory(),refreshBeanFactory()

    obtainFreshBeanFactory()调用了refreshBeanFactory()
    下图的refresh()则调用了obtainFreshBeanFactory();
    

    注意,通过IoC容器来管理对象时,对象对应的实体类必须有无参构造函数,否则IoC无法实例化对象,因为IoC底层是通过反射机制来创建对象的,反射机制默认调用实体类的无参构造函数。

    再通过调用每个属性的setter方法来完成属性赋值的,所以实体类必须有setter方法,否则无法完成属性的赋值。

    IoC容器管理多个对象,并且对象之间有及联关系,如何实现?

    <!-- 创建Classes对象 -->
    <bean id="classes" class="com.m.entity.Classes">
        <property name="id" value="1"></property>
        <property name="name" value="一班"></property>
    </bean>
    
    <!-- 创建学生对象 -->
    <bean id="stu" class="com.m.entity.Student">
        <property name="id" value="1"></property>
        <property name="name" value="小红"></property>
        <property name="age" value="16"></property>
        <!-- 将IoC容器中的classes对象赋给stu对象的classes -->
        <property name="classes" ref="classes"></property>
    </bean>
    
    <bean id="classes" class="com.southwind.entity.Classes">
        <constructor-arg name="id" value="1"></constructor-arg>
        <constructor-arg name="name" value="一班"></constructor-arg>
        <constructor-arg name="students">
            <list>
                <ref bean="student"></ref>
                <ref bean="student2"></ref>
                <ref bean="student3"></ref>
            </list>
        </constructor-arg>
    </bean>
    

    集合属性通过list标签和ref标签完成注入,ref的bean属性指向需要注入的bean对象。

    Spring中bean是根据scope来生成的,scope表示bean的作用域。

    scope有4种类型:

    • singleton:单例,表示通过IoC容器获取的对象是唯一的。

    • prototype:原型,表示通过IoC容器获取的对象是不同的。

    • request:请求,表示通过IoC容器获取的对象在一次HTTP请求内有效。

    • session:会话,表示通过IoC容器获取的对象在一个用户会话内有效。

    request和session只适用于web项目,大多数情况下,我们只会使用singleton和prototype两种scope,并且scope的默认值是singleton。

    scope=singleton时,一旦加载spring.xml配置文件就会创建bean对象,并且只创建一次,单例模式。

    scope=prototype时,加载spring.xml配置文件时不会创建bean对象,每次获取对象时再来创建,获取多少次就创建多少个对象,原型模式。

    Spring的继承

    Spring的继承,与Java中的继承不一样,但是思想是类似的,Spring中的继承表示子bean可以继承父bean中的属性,对象层面的继承,Java中的继承表示子类可以继承父类的结构,类层面的继承。

    子bean在继承父bean的属性值的基础之上,可以对属性值进行覆盖,两个不同类型的对象之间也可以实现继承,前提是两个对象的属性完全一致,User中有int id和String name,Student中有int id和String name,同样可以完成继承,读取父bean的属性值,赋给对应的子bean的属性。

    Spring的依赖-depends-on="student"

    与Spring的继承类似,依赖也是bean与bean之间的一种关联方式,配置依赖关系之后,被依赖的bean一定优先创建,再创建依赖的bean,A依赖于B,先创建B,再创建A。

    IoC容器创建bean的顺序是由spring.xml中的顺序来决定的,从上向下执行创建。

    Spring IoC工厂方法创建对象

    IoC是典型的工厂模式,IoC通过工厂模式创建bean有两种方式:

    • 静态工厂方法
    public class StaticCarFactory {
        private static Map<Integer, Car> carMap;
        static {
            carMap = new HashMap<>();
            carMap.put(1,new Car(1,"奥迪"));
            carMap.put(2,new Car(2,"宝马"));
        }
    
        public static Car getCar(int num){
            return carMap.get(num);
        }
    }
    
    <!-- 配置静态工厂创建Car对象 -->
    <bean id="car" class="com.southwind.factory.StaticCarFactory" factory-method="getCar">
        <constructor-arg value="2"></constructor-arg>
    </bean>
    
    • 实例工厂方法
    public class InstanceCarFactory {
        private Map<Integer, Car> carMap;
        public InstanceCarFactory(){
            carMap = new HashMap<>();
            carMap.put(1,new Car(1,"奔驰"));
            carMap.put(2,new Car(2,"宝马"));
        }
    
        public Car getCar(int num){
            return carMap.get(num);
        }
    }
    
    <!-- 配置实例工厂对象 -->
    <bean id="instanceCarFactory" class="com.southwind.factory.InstanceCarFactory"></bean>
    
    <!-- 通过实例工厂对象获取Car对象 -->
    <bean id="car" factory-bean="instanceCarFactory" factory-method="getCar" >
        <constructor-arg value="1"></constructor-arg>
    </bean>
    

    IoC的自动装载(autowire)

    IocDI可以通过配置property的ref属性将bean进行依赖注入,同时Spring还提供了另外一种更加简便的方式:自动装载,不需要手动配置property,IoC容器会自动选择bean完成依赖注入。

    自动装载有两种方式:

    • byName:通过属性名完成自动装载。

    • byType:通过属性的数据类型完成自动装载。

        <bean  id="testDao" class="com.m.ioc.autowire.impl.TestDaoImpl">
            <property name="id" value="001"></property>
            <property name="name" value="TestDaoImpl"></property>
        </bean>
    
        <bean id="service" class="com.m.ioc.autowire.TestService" autowire="byType">
            <property name="id" value="1"></property>
        </bean>
    

    如果IoC容器同时存在多个数据类型相同的bean,会抛出异常。






    备注:最近来手感,写了个类似Tomcat服务

    github地址:https://github.com/cnamep001/my_tomcat.git






  • 相关阅读:
    apt常用命令(安装,更新,删除)
    记录一次坑爹的VM连接主机的路程
    VM安装centos
    初窥DB2之insert语句
    关于虚拟机的linux不能使用shell连接时的处理方法
    linux命令之查看字符集
    趣图:学JavaScript
    PHP搭建大文件切割分块上传功能示例
    判断变量是否不为空,函数isset()、!empty()与!is_null()的比较
    Javascript 中 null、NaN和undefined的区别
  • 原文地址:https://www.cnblogs.com/k-class/p/13659481.html
Copyright © 2011-2022 走看看