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






原文地址:https://www.cnblogs.com/k-class/p/13659481.html