Spring解决的核心问题

Spring解决的核心问题: 把对象之间的关系用配置来管理

  • 依赖注入: 依赖关系在Spring的IOC容器中管理
  • 通过把对象包装在Bean中, 以达到管理对象和进行额外操作的目的

Bean与BeanDefinition

Bean 是Spring的一等公民

  • Bean 的本质就是Java对象, 这个对象的生命周期由Spring容器来进行管理
  • 不需要为了创建Spring的Bean 而在Java类上做额外的限制, 体现了Spring的低侵入.
  • 对于Java对象的控制体现在配置上(配置文件或注解)

在Spring中, 是根据配置, 生成用来描述Bean的BeanDefinition. 类似于Java中描述类的Class.
BeanDefinition的几个重要属性:

  1. Bean 的作用范围 @Scope: singleton prototype session globalsession request
  2. 懒加载lazy-init(@Lazy) : 决定Bean实例是否延迟加载
  3. 首选primary(@Primary) : 如果一个接口有多个实现类, 那么设置为true的bean,会被选择优先的实现类
  4. factory-bean(工厂bean的名称) 和factory-method(工厂 方法的名称) (@Configuration和@Bean)

代码验证BeanDefinition

创建一个User类

package com.demo.entity;
public class User {
}

静态工厂类

package com.demo.entity.factory;
import com.demo.entity.User;
//静态工厂调用
public class StaticFactory {
	// 静态的方法,返回User对象
	public static User getUser(){
		return new User();
	}
}

实例工厂类

package com.demo.entity.factory;

import com.demo.entity.User;

//实例工厂调用
public class UserFactory {
	//普通的方法,返回User对象
	//不能通过类名调用,需要通过对象调用
	public User getUser(){
		return new User();
	}
}

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"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--  使用类的无参构造函数创建对象
	singleton 单例
	lazy-init="true" 首次使用该bean 的时候,才会创建出实例
	-->
	<bean id="user1" class="com.demo.entity.User" scope="singleton" lazy-init="true" primary="true"></bean>

	<bean id="user2" class="com.demo.entity.factory.StaticFactory" factory-method="getUser" scope="singleton"></bean>

	<bean id="userFactory" class="com.demo.entity.factory.UserFactory" ></bean>

<!--
   factory-bean 其实也是一种bean 来自于上面定义的id为userFactory的Bean实例
 -->
	<bean id="user3" factory-bean="userFactory" factory-method="getUser" scope="singleton"></bean>
</beans>

配置文件的解读

  1. bean的id为user1的, 配置了Scope为单例, 延迟加载, 并且是首选的bean
  2. id为user2的bean , 定义了一个factory-method 即工厂方法. 之所以能够定义一个工厂方法, 是因为StaticFactory类中, 有public static User getUser()静态方法, 可以从该方法中, 通过类名静态的获取Bean的实例.
  3. id为userFactory的bean, 定义了UserFactory这个类, 该类的方法为public User getUser(), 由于不是静态的, 因此不能定义工厂方法
  4. id为user3的, 定义了一个factory-bean, 即工厂bean, 这个bean取自id为userFactory的bean , factory-method工厂方法为UserFactory这个类的getUser方法.
代码运行测试

如下的main方法加载了配置文件, 并从中通过getBean方法来获取Bean的实例.

public static void main(String[] args) {
		// 加载配置
		String xmlPath = "D:\\mycode\\spring_study\\spring-framework-5.2.0.RELEASE\\springdemo\\src\\main\\resources\\spring\\spring-config.xml";
		ApplicationContext applicationContext = new FileSystemXmlApplicationContext(xmlPath);
		// 获得无参构造函数创建的对象
		User user1a = (User) applicationContext.getBean("user1");
		User user1b = (User) applicationContext.getBean("user1");

		// 静态工厂创建的对象
		User user2a = (User) applicationContext.getBean("user2");
		User user2b = (User) applicationContext.getBean("user2");

		// 实例工厂创建的对象
		User user3a = (User) applicationContext.getBean("user3");
		User user3b = (User) applicationContext.getBean("user3");
		
		System.out.println("无参构造函数创建的对象user1 "+user1a);
		System.out.println("无参构造函数创建的对象user1 "+user1b);

		System.out.println("静态工厂创建的对象user2 "+user2a);
		System.out.println("静态工厂创建的对象user2 "+user2b);

		System.out.println("实例工厂创建的对象user3 "+user3a);
		System.out.println("实例工厂创建的对象user3 "+user3b);

	}

控制台打印如下 . 可以看到无参构造, 静态工厂, 实例工厂都能够获取到bean , 并且都是单例的. 即多次调用getBean方法, 都是获取的同一个对象.

将配置文件中scope="singleton" 去除, 即配置文件改成如下的

<bean id="user1" class="com.demo.entity.User"  lazy-init="true" primary="true"></bean>

	<bean id="user2" class="com.demo.entity.factory.StaticFactory" factory-method="getUser" ></bean>

	<bean id="userFactory" class="com.demo.entity.factory.UserFactory" ></bean>

<!--
   factory-bean 其实也是一种bean 来自于上面定义的id为userFactory的Bean实例
 -->
	<bean id="user3" factory-bean="userFactory" factory-method="getUser" ></bean>

再次运行main方法, 可以看到通过同样的bean id多次调用getbean方法, 获取到的对象也是一样的.
说明创建Bean默认是单例模式.

将Scope改成scope="prototype"

再次运行main方法, 可以看到同一个bean id调用getBean方法获取的实例是不同的.

Spring容器主要流程

  1. 读取配置(xml或者注解)到内存中 (解析配置)
  2. 在内存中, 这些配置会被转化为Resource对象
  3. Resource对象被解析为BeanDefinition实例
  4. 把BeanDefinition实例注册到容器中 (通过配置定位对象, 把定位到的对象, 注册到容器中)

与BeanDefinition相关的类

BeanDefinition的主要类关系图如下

BeanDefinition 是在spring-beans模块中的

spring-beans模块存放的是Bean和简单容器相关的接口和类.
BeanDefinition 继承了AttributeAccessor, BeanMetadataElement 这两个接口 .
在Spring中, 某个类继承或实现了某个接口, 也就代表了这个类或者接口有哪些功能.

AttributeAccessor

AttributeAccessor : 该接口的描述如下, 该接口定义了对任意数据元数据的修改或获取方式.

* Interface defining a generic contract for attaching and accessing metadata
 * to/from arbitrary objects.
BeanMetadataElement

BeanMetadataElement 接口, 主要是提供了getSource() 方法. 该方法是用于传输一个可配置的元对象, 主要是返回BeanDefinition 这个对象本身.

Return the configuration source Object for this metadata element (may be null).
AbstractBeanDefinition

AbstractBeanDefinition 抽象类, 实现了BeanDefinition 接口, 里面的方法用于给通用的属性赋值.
其一些方法如下.

AbstractBeanDefinition 抽象类基础上, 衍生出了如下的一些子类.

RootBeanDefinition

RootBeanDefinition 类中有如下的方法. 该方法说明了RootBeanDefinition 类不能作为其他BeanDefinition类的子类 , 但可以作为父类.

	@Override
	public void setParentName(@Nullable String parentName) {
		if (parentName != null) {
			throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
		}
	}

DefaultListableBeanFactory 类中有private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) 方法, 使用到了RootBeanDefinition.
可以看到是调用了getMergedLocalBeanDefinition 方法用于合并bean 的.

ChildBeanDefinition

ChildBeanDefinition是一个子类, 不能单独存在, 必须要依赖于一个父的BeanDefinition. ChildBeanDefinition目前已经完全被GenericBeanDefinition 取代.

GenericBeanDefinition

GenericBeanDefinition 是通用的BeanDefinition实现. 是一个Bean文件属性定义类,
GenericBeanDefinition 类有如下的setParentName方法, 该方法可以设置parentName, 不会像RootBeanDefinition去抛异常.

	@Override
	public void setParentName(@Nullable String parentName) {
		this.parentName = parentName;
	}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐