依赖注入的环节发生在:Spring初始化Bean的时候,对该Bean实例的具体字段通过反射的方式进行赋值的操作
以下是我对依赖注入的理解。
依赖注入,可以通俗的理解为属性填充。但与简单属性填充有些不同。我们知道Spring实现了IoC,也就是控制反转,即将对象实例的控制权进行了反转,Spring替我们创建对象,而初始化对象的过程就称为依赖注入,这个依赖可以基础类型,也可以是引用类型。
Spring是通过setter方法注入、构造器注入、自动绑定注入的形式实现依赖注入的,下面先看下是如何使用的,以及这些方式在底层API是如何被实现的!
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="user" name="user,user2" class="com.markus.spring.ioc.container.domain.User">
<property name="name" value="markus"/>
<property name="age" value="23"/>
<property name="city" value="BEIJING"/>
<property name="workCities" value="BEIJING,HEZE"/>
<property name="lifeCities" value="BEIJING,HEZE"/>
bean>
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder">
<property name="user" ref="user"/>
bean>
beans>
UserHolder类
package com.markus.spring.ioc.container.domain;
import com.markus.spring.ioc.container.eunms.City;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.io.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.List;
public class User implements BeanNameAware {
private Long id;
private Integer age;
private String name;
private City city; // 枚举类型 ∈ 基础类型(标量类型)
private City[] workCities;// 数组类型
private List<City> lifeCities;// 集合类型
private Resource resource;// Spring类型 ∈ 基础类型
private transient String beanName;
public User(){
// System.out.println("我被初始化了...");
}
public User(int age,String name){
this.age = age;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public City[] getWorkCities() {
return workCities;
}
public void setWorkCities(City[] workCities) {
this.workCities = workCities;
}
public List<City> getLifeCities() {
return lifeCities;
}
public void setLifeCities(List<City> lifeCities) {
this.lifeCities = lifeCities;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
", city=" + city +
", workCities=" + Arrays.toString(workCities) +
", lifeCities=" + lifeCities +
", resource=" + resource +
'}';
}
public static User createUser(){
System.out.println("我被创建了...");
User user = new User();
user.setAge(23);
user.setName("Markus");
return user;
}
public static User createUser(int age,String name){
User user = new User();
user.setAge(age);
user.setName(name);
return user;
}
@PostConstruct
public void init(){
System.out.println("["+beanName+"] Bean 正在初始化...");
}
@PreDestroy
public void destroy(){
System.out.println("["+beanName+"] Bean 正在销毁...");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
}
package com.markus.spring.spring.dependency.injection;
import com.markus.spring.ioc.container.domain.User;
import java.util.Collection;
/**
* @author: markus
* @date: 2022/3/19 11:27 下午
* @Description: 管理User对象的Bean
* @Blog: http://markuszhang.com/
*/
public class UserHolder {
private User user;
private Collection<User> users;
public UserHolder(){
}
public UserHolder(User user){
this.user = user;
}
public UserHolder(User user, Collection<User> users) {
this.user = user;
this.users = users;
}
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "UserHolder{" +
"user=" + user +
", users=" + users +
'}';
}
}
接下来看下主程序
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author: markus
* @date: 2022/8/29 10:38 PM
* @Description: xml方式 setter方法注入 示例
* @Blog: http://markuszhang.com/doc-blog/
* It's my honor to share what I've learned with you!
*/
public class XmlDependencySetterInjectionDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-setter-injection.xml");
UserHolder userHolder = context.getBean(UserHolder.class);
System.out.println(userHolder);
}
}
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.ioc.container.domain.User;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
/**
* @author: markus
* @date: 2022/9/2 10:13 PM
* @Description: 注解驱动 setter依赖注入
* @Blog: http://markuszhang.com
* It's my honor to share what I've learned with you!
*/
public class AnnotationDependencySetterInjectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotationDependencySetterInjectionDemo.class);
// 加载BeanDefinition
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(context);
beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-lookup.xml");
// 启动容器
context.refresh();
// @Bean已经将UserHolder注册到Spring中,我们只需要获取它,Spring内部去创建
UserHolder userHolder = context.getBean(UserHolder.class);
System.out.println(userHolder);
// 关闭容器
context.close();
}
@Bean
public UserHolder userHolder(User user) {
UserHolder userHolder = new UserHolder();
// setter注入
userHolder.setUser(user);
return userHolder;
}
}
依赖的User Bean配置
<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" name="user,user2" class="com.markus.spring.ioc.container.domain.User">
<property name="name" value="markus"/>
<property name="age" value="23"/>
<property name="city" value="BEIJING"/>
<property name="workCities" value="BEIJING,HEZE"/>
<property name="lifeCities" value="BEIJING,HEZE"/>
<property name="resource" value="classpath:/META-INF/user-config.properties"/>
</bean>
</beans>
接口回调这里,就是Spring内嵌接口,我们可以通过回调的方式将这些接口实例注入到我们自己的实例中;
其中能实现接口回调注入的Bean依次为:
- BeanFactory
- ApplicationContext
- MessageResource
- Environment
- EmbeddedValueResolver
- ApplicationEventPublisher
下面用BeanFactory举下例子:
先实现BeanFactoryAware接口
package com.markus.spring.spring.dependency.injection;
import com.markus.spring.ioc.container.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import java.util.Collection;
/**
* @author: markus
* @date: 2022/3/19 11:27 下午
* @Description: 管理User对象的Bean
* @Blog: http://markuszhang.com/
*/
public class UserHolder implements BeanFactoryAware {
private BeanFactory beanFactory;
// other filed
public BeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
// other field getter setter...
}
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author: markus
* @date: 2022/9/2 10:28 PM
* @Description: 接口回调接口 setter方法注入 示例
* @Blog: http://markuszhang.com
* It's my honor to share what I've learned with you!
*/
public class AwareCallbackDependencySetterInjectionDemo {
public static void main(String[] args) {
// 我们借用ClassPathXmlApplicationContext 将UserHolder Bean定义到xml中
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-setter-injection.xml");
UserHolder userHolder = context.getBean(UserHolder.class);
// 这里我们看下UserHolder中注入的beanFactory是否和Spring内部BeanFactory一致
System.out.println(userHolder.getBeanFactory() == context.getBeanFactory());
// 主程序输出
// true
// Process finished with exit code 0
}
}
我们看到上面又是通过xml实现setter方法注入,又是通过注解的方式实现setter方法注入,那他们最终转到底层都是通过BeanDefinitionBuilder.addPropertyReference(String,String)实现的,我们来看下代码实现:
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author: markus
* @date: 2022/9/2 10:39 PM
* @Description: 底层Api实现 setter方法依赖注入
* @Blog: http://markuszhang.com
* It's my honor to share what I've learned with you!
*/
public class ApiDependencySetterInjectionDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");
// 创建BeanDefinition
BeanDefinition beanDefinition = createBeanDefinition();
// 注册BeanDefinition
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
beanFactory.registerBeanDefinition("userHolder", beanDefinition);
UserHolder userHolder = applicationContext.getBean(UserHolder.class);
System.out.println(userHolder);
applicationContext.close();
// 控制台输出
// UserHolder{user=User{id=null, age=23, name='markus', city=BEIJING, workCities=[BEIJING, HEZE], lifeCities=[BEIJING, HEZE], resource=class path resource [META-INF/user-config.properties]}, users=null}
// Process finished with exit code 0
}
private static BeanDefinition createBeanDefinition() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
builder.addPropertyReference("user", "user");
return builder.getBeanDefinition();
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<import resource="dependency-lookup.xml"/>
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder">
<constructor-arg name="user" ref="user"/>
bean>
beans>
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author: markus
* @date: 2022/9/2 10:07 PM
* @Description: 构造器 依赖注入
* @Blog: http://markuszhang.com
* It's my honor to share what I've learned with you!
*/
public class XmlDependencyConstructorInjectionDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-constructor-injection.xml");
UserHolder userHolder = applicationContext.getBean(UserHolder.class);
System.out.println(userHolder);
applicationContext.close();
}
}
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.ioc.container.domain.User;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
/**
* @author: markus
* @date: 2022/9/4 11:28 AM
* @Description: 注解驱动 构造器注入 示例
* @Blog: http://markuszhang.com
* It's my honor to share what I've learned with you!
*/
public class AnnotationDependencyConstructorInjectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotationDependencySetterInjectionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(context);
beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-lookup.xml");
context.refresh();
UserHolder userHolder = context.getBean(UserHolder.class);
System.out.println(userHolder);
context.close();
}
@Bean
public UserHolder userHolder(User user) {
// 构造器注入
UserHolder userHolder = new UserHolder(user);
return userHolder;
}
}
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.spring.dependency.injection.UserHolder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author: markus
* @date: 2022/9/4 12:02 PM
* @Description: 底层api 实现依赖构造器注入 示例
* @Blog: http://markuszhang.com
* It's my honor to share what I've learned with you!
*/
public class ApiDependencyConstructorInjectionDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");
// 构建BeanDefinition
BeanDefinition userHolderBeanDefinition = createBeanDefinition();
// 注册BeanDefinition
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
registry.registerBeanDefinition("userHolder", userHolderBeanDefinition);
// 依赖查找 UserHolder
UserHolder userHolder = context.getBean("userHolder", UserHolder.class);
System.out.println(userHolder);
// 关闭应用上下文
context.close();
}
private static BeanDefinition createBeanDefinition() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
// 底层API实现构造器注入
builder.addConstructorArgReference("user");
return builder.getBeanDefinition();
}
}
自动绑定注入有以下三种:
- byType: 通过类型注入
- byName: 通过名称注入
- constructor: 构造器注入
下面我们看下例子
ps: 自动注入可以通过xml、注解形式实现。xml通过bean标签的autowire指定实现,注解则是通过@Autowired、@Resource等注解方式实现。
xml
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder" autowire="byType"/>
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder" autowire="byName"/>
<bean id="userHolder" name="userHolder" class="com.markus.spring.spring.dependency.injection.UserHolder" autowire="constructor"/>
注解
package com.markus.spring.spring.dependency.injectionv2;
import com.markus.spring.ioc.container.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
/**
* @author: markus
* @date: 2022/9/4 2:18 PM
* @Description: 自动绑定注入 字段、方法注入
* @Blog: http://markuszhang.com
* It's my honor to share what I've learned with you!
*/
public class AutowiringDependencyInjectionDemo {
@Autowired
private User user; // 注入的是 superUser (@Autowired 首先按照类型注入,如果找到多个就选被primary标注的bean注入,如果没标注,就按照名称选择)
@Resource
private User user1; // 注入的是 superUser
@Autowired
private Collection<User> users; // 注入的是 user superUser
@Autowired
private Map<String,User> userMap; // 注入的是 user superUser
@Autowired
private Optional<User> userOptional; // 注入的是 superUser
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AutowiringDependencyInjectionDemo.class);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) context.getBeanFactory());
reader.loadBeanDefinitions("classpath:/META-INF/dependency-lookup.xml");
context.refresh();
AutowiringDependencyInjectionDemo demo = context.getBean(AutowiringDependencyInjectionDemo.class);
System.out.println(demo.user);
System.out.println(demo.user1);
System.out.println(demo.users);
System.out.println(demo.userMap);
System.out.println(demo.userOptional);
context.close();
}
}
其实自动绑定底层最终也是通过setter方法或者构造器实现依赖注入的。
前面说了什么是依赖注入以及怎么使用这一特性,接下来这一章节就来说说它的内部原理是怎样的。
先来看下整体关于Bean初始化的概览图
通过上图来看,依赖注入的功能点表现在populateBean()方法内,它表现在拿到依赖对象并赋值到当前实例的字段中。
对于依赖注入的原理,有几个比较重要的类以及方法罗列如下:
根据上面的描述,大家应该对这两个底层原理有了大致的了解,只不过在细节上会有一些不同:
这两个BenaPostProcessor后置处理器有两个生命周期需要提及一下:
以上就是我对依赖注入的学习记录,其实流程很简单,多读几遍源码就能理顺清楚。主要记住几个关键类以及他们之间的调用关系即可