重新认识IOC一文中详细介绍了Ioc的概念,作用,以及传统Java的相关实现。本文开始学习Spring Ioc的实现方式
Spring Ioc的实现方式
Spring Ioc的实现方式也是基于依赖查找和依赖注入。
Spring 依赖查找,可以根据Bean名称或者Bean类型进行依赖查找,也可以混合查找(Bean名称+Bean类型)。spring3.0开始,支持根据注解依赖查找。
Spring 依赖注入,可以根据Bean名称或者Bean类型进行依赖注入,注入Spring内建的Bean对象(比如Environment,),也可以实现Aware接口注入非Bean对象。
同时Spring可以根据应用场景,灵活使用延迟/实时依赖查找/依赖注入
Spring Ioc依赖查找
Java Bean代码如下:
public class User {
private String id;
private String name;
private int age;
private String address;
private Company company;
private Properties context;
private String contextAsText;
public User() {}
public User(String id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Properties getContext() {
return context;
}
public void setContext(Properties context) {
this.context = context;
}
public String getContextAsText() {
return contextAsText;
}
public void setContextAsText(String contextAsText) {
this.contextAsText = contextAsText;
}
@Override
public String toString() {
return "User{"
+ "id='"
+ id
+ '''
+ ", name='"
+ name
+ '''
+ ", age="
+ age
+ ", address='"
+ address
+ '''
+ ", company="
+ company
+ ", context="
+ context
+ ", contextAsText='"
+ contextAsText
+ '''
+ '}';
}
@PostConstruct
public void init() {
System.out.println("用户 id:" + id + " 正在初始化中...");
}
@PreDestroy
public void destroy() {
System.out.println("用户 id: " + id + " 正在销毁中...");
}
public static User getInstance() {
User user = new User();
user.setId("1");
user.setName("Shawn");
user.setAge(26);
user.setAddress("SHANGHAI");
return user;
}
}
根据Bean名称依赖查找
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.xxx.User">
<property name="id" value="1"/>
<property name="name" value="shawn"/>
<property name="age" value="25"/>
<property name="address" value="shanghai"/>
</bean>
</beans>
java代码如下:
private final ApplicationContext applicationContext;
public DependencyLookUpDemo() {
applicationContext = new ClassPathXmlApplicationContext("classpath:/dependency-lookup.xml");
}
/** 根据BeanName:单一类型依赖查找 */
public User lookupByBeanName() {
return (User) applicationContext.getBean("user");
}
根据Bean类型依赖查找
/** 根据BeanType:单一类型依赖查找 */
public User lookupByBeanType() {
return applicationContext.getBean(User.class);
}
根据Bean类型+Bean名称依赖查找
/** 根据BeanName & BeanType:单一类型依赖查找 */
public User lookupByBeanNameAndType() {
return applicationContext.getBean("user", User.class);
}
返回集合类型
/** 集合类型依赖查找 */
public Map<String, User> lookupCollectionType() {
return applicationContext.getBeansOfType(User.class);
}
根据java注解依赖查找
注解如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Super {}
注解使用情况:
@Super
public class SuperUser extends User {
private String idCard;
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "SuperUser{"
+ "id='"
+ getId()
+ '''
+ ", name='"
+ getName()
+ '''
+ ", age="
+ getAge()
+ ", address='"
+ getAddress()
+ '''
+ ", idCard='"
+ getIdCard()
+ '}';
}
}
xml配置如下:
<!-- primary="true" 用于解决org.springframework.beans.factory.NoUniqueBeanDefinitionException expected single matching bean but found 2-->
<bean id="superUser" class="com.xxx.SuperUser" parent="user"
primary="true">
<property name="idCard" value="1234567890"/>
</bean>
根据java annotation依赖查找 代码如下:
/** 根据java annotation依赖查找 */
public Map<String, Object> lookupBeansByAnnotation() {
return applicationContext.getBeansWithAnnotation(Super.class);
}
延迟依赖查找
需要用到ObjectFactory类进行延迟依赖查找
xml配置如下:
<bean id="objectFactory"
class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="user"/>
</bean>
延迟依赖查找代码如下:
public User lookupInLazy() {
ObjectFactory<User> objectFactory =
(ObjectFactory<User>) applicationContext.getBean("objectFactory");
return objectFactory.getObject();
}
Spring Ioc 依赖注入
前置Bean定义如下:
<bean id="user" class="com.shawn.study.deep.in.spring.core.ioc.domain.User">
<property name="id" value="1"/>
<property name="name" value="shawn"/>
<property name="age" value="25"/>
<property name="address" value="shanghai"/>
</bean>
<!-- primary="true" 用于解决org.springframework.beans.factory.NoUniqueBeanDefinitionException expected single matching bean but found 2-->
<bean id="superUser" class="com.shawn.study.deep.in.spring.core.ioc.domain.SuperUser" parent="user"
primary="true">
<property name="idCard" value="1234567890"/>
</bean>
<bean id="objectFactory"
class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="user"/>
</bean>
根据Bean名称依赖注入
<bean id="userRepository"
class="com.xxx.UserRepository">
<property name="users">
<util:list>
<ref bean="user"/>
<ref bean="superUser"/>
</util:list>
</property>
</bean>
根据Bean类型依赖注入
<bean id="auto-user-repository"
class="com.xxx.UserRepository" autowire="byType"/>
依赖注入内建依赖和非bean对象
- 手动依赖注入,需要手动引入内建的依赖,如果不手动引入,则不会被注入进来
- 自动依赖注入,可以自动注入内建依赖
@Test
public void testInjectionWithCode() {
UserRepository userRepository = injection.injectWithCode();
assertNotNull(userRepository);
User[] users = userRepository.getUsers();
assertNotNull(users);
for (User user : users) {
assertUser(user);
}
BeanFactory beanFactory = userRepository.getBeanFactory();
assertNull(beanFactory);
Environment environment = userRepository.getEnvironment();
assertNull(environment);
ObjectFactory<ApplicationContext> applicationContextObjectFactory =
userRepository.getObjectFactory();
assertNull(applicationContextObjectFactory);
ObjectFactory<User> userObjectFactory = userRepository.getUserObjectFactory();
assertNull(userObjectFactory);
}
@Test
public void testInjectionWithAutoWiring() {
UserRepository userRepository = injection.injectWithAutoWiring();
assertNotNull(userRepository);
User[] users = userRepository.getUsers();
assertNotNull(users);
for (User user : users) {
assertUser(user);
}
BeanFactory beanFactory = userRepository.getBeanFactory();
assertNotNull(beanFactory);
Environment environment = userRepository.getEnvironment();
assertNotNull(environment);
ObjectFactory<ApplicationContext> applicationContextObjectFactory =
userRepository.getObjectFactory();
assertNotNull(applicationContextObjectFactory);
ObjectFactory<User> userObjectFactory = userRepository.getUserObjectFactory();
assertNotNull(userObjectFactory);
}
依赖注入内建Bean
比如Environment
UserRepository beanAutoWiring = source.getBeanAutoWiring();
BeanFactory beanFactory = beanAutoWiring.getBeanFactory();
if (beanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory defaultListableBeanFactory =
(DefaultListableBeanFactory) beanFactory;
String[] beanDefinitionNames = defaultListableBeanFactory.getBeanDefinitionNames();
Arrays.stream(beanDefinitionNames).forEach(System.out::println);
}
Environment env1 = beanFactory.getBean("environment", Environment.class);
Environment env2 = beanAutoWiring.getEnvironment();
assertEquals(env1, env2);
UserRepository userRepository = source.getBeanFromXMl();
assertNull(userRepository.getEnvironment());
Spring Ioc容器
BeanFactory
BeanFactory是Spring Ioc的基本容器,是一个容器接口,BeanFactory负责配置、创建、管理Bean。
- BeanFactory是Spring Ioc最底层的容器接口,提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean别名的方法。它最主要的方法就是getBean(String beanName)。
- BeanFactory最直接的三个子接口
-
- HierarchicalBeanFactory:提供父容器的访问功能。
- ListableBeanFactory:提供了根据Bean Type获取集合Bean的方法
- AutowireCapableBeanFactory:
-
-
- 自动注入Bean。可根据Bean Name,Type,Constructor等方式自动装配Bean,详情看autowire, autowireBeanProperties两个方法
- 创建Bean。通过CreateBean方法,创建Bean实例,并通过initializeBean进行初始化Bean,通过applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization方法进行初始化回调处理,最后通过destroyBean销毁Bean,实现Bean的生命周期管理以及更灵活的Bean管理和配置。
- 解析依赖,resolveDependency可以解析指定的依赖关系,支持字段、方法、构造函数等各种依赖注入方式。
-
- ConfigurableBeanFactory继承了HierarchicalBeanFactory和SingletonBeanRegistry
-
- 注册单例Bean
- 定制化BeanFactory,可以设置父级BeanFactory、类加载器、表达式解析器、类型转换服务等,以满足特定应用程序的需求。
- ConfigurableListableBeanFactory扩展了ConfigurableBeanFactory,ListableBeanFactory, AutowireCapableBeanFactory三个基本接口。并扩展了注册内建依赖,获取BeanDefinition,冻结配置等方法。
- DefaultListableBeanFactory是Ioc容器底层实现类。
ApplicationContext
ApplicationContext是BeanFactory的超集,为Spring提供了企业级的扩展功能
- 面向切面(AOP)
- 配置元信息(Configuration Metadata)
- 资源管理(Resources)
- 事件(Events)
- 国际化(i18n)
- 注解(Annotations)
- Environment 抽象(Environment Abstraction)
- 从UML中可以看到ApplicationContext也是一个接口,继承了HierarchicalBeanFactory和ListableBeanFactory,所以ApplicationContext也是一个BeanFactory。
- ApplicationContext同时还继承了EnvironmentCapable,MessageSource,ResourcePatternResolver,所以ApplicationContext除了是Ioc容器外,也提供了Environment 抽象,国际化,资源管理相关能力。
- ConfigurationApplicationContext继承了ApplicationContext和LifeCycle两个接口,LifeCycle管理着ApplicationContext的生命周期,ConfigurationApplication还扩展了事件监听机制和配置元信息(除了Bean相关配置之外,还有Environment加载多个配置来源)。
- ConfigurationApplicationContext,addBeanFactoryPostProcessor(BeanFactoryPostProcessor)方法为ApplicationContext提供了可扩展性,例如Spring AOP面向切面编程就是通过BeanFactoryPostProcessor来实现的,还有Spring Cache等。
- BeanDefinitionRegistry负责注册Bean相关元信息。
- GenericApplicationContext内部BeanFactory就是DefaultListableBeanFactory。
ApplicationContext常用的实现类
| 类名 | 作用 |
|---|---|
| AnnotationConfigApplicationContext | 从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式。 |
| ClassPathXmlApplicationContext | 从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式。 |
| FileSystemXmlApplicationContext | 从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。 |
| AnnotationConfigWebApplicationContext | 专门为web应用准备的,适用于注解方式。 |
| XmlWebApplicationContext | 从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。 |
BeanFactory和ApplicationContext区别
- BeanFactory是Spring Ioc最底层的容器,而ApplicationContext也是Ioc容器,继承自BeanFactory,在其基础上添加很多特性,例如事件机制,国际化,资源管理,AOP,Environment抽象等。
- BeanFactory在初始化容器时,并未实例化Bean,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至首次调用getBean方法才会抛出异常;而ApplicationContext则在初始化应用上下文时初始化所有Singleton Bean,这样有利于检查所依赖属性是否已经注入。且由于ApplicationContext会预先初始化所有的Singleton Bean,于是在系统创建前期会有较大的系统开销,后续程序再获取Singleton Bean的时候将会有就好的性能。
- BeanFactory负责处理BeanDefinition,管理Bean的生命周期,维护Bean之间的依赖关系