夯实Spring系列|第九章:IoC 依赖注入(专题)-下

夯实Spring系列|第九章:IoC 依赖注入(专题)-下

本章说明

接着上一章 夯实Spring系列|第八章:IoC 依赖注入(专题)-上 的内容,我们继续讨论 Spring IoC 依赖注入的其他方式,以及大家比较关心的@Autowired原理分析。

1.项目环境

2.依赖注入类型选择

这么多的注入类型,我们平时该怎么选择和使用呢?

  • 低依赖:构造器注入

    • 强制依赖注入,依赖比较少,参数比较少的情况推荐使用(官方推荐)
  • 多依赖:Setter 方法注入

    • 多依赖,如果依赖之间存在前后依赖关系的话,尽量不要用这种方式注入
  • 便利性:字段注入

    • 仅关注与需要注入的字段,但是这种官方并不推荐,在后续的 SpringBoot 版本中都属于淘汰状态
  • 声明类:方法注入

    • 建议多使用@Bean这种方式进行注入,配合其他注入方式。

3.基础类型注入

此部分用 夯实Spring系列|第二章:IoC 依赖查找 里面的例子进行演示

github 相关源码模块 ioc-container-overview

  • com.huajie.thinking.in.spring.ioc.overview.dependency.lookup.DependencyLookUpDemo

基础类型

  • 原生类型(Primitive):boolean、byte、char、short、int、float、long、double
  • 标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Porperties、UUID
  • 常规类型(General):Object、String、TimeZone、Calendar、Optional 等等
  • Spring 类型:Resource、InputSource、Formatter 等等

3.1 Enum 类型

比如我们 User 类里面加一个 City 枚举类型的字段,表示这个用户是哪个城市的。

我们先建一个 Enum 类型的 City 类

public enum City {
    WUHAN,
    BEIJING,
    SHANGHAI
}

User 类更新

public class User{

    private Long id;

    private String name;

    private Integer age;

    private City city;

    private City[] cities;

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    public City[] getCities() {
        return cities;
    }

    public void setCities(City[] cities) {
        this.cities = cities;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public static User createUser(String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

    public static User createUser() {
        User user = new User();
        user.setName("static-user");
        return user;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", city=" + city +
                ", cities=" + Arrays.toString(cities) +
                '}';
    }

    @PostConstruct
    public void init() {
        System.out.println(beanName + "用户对象初始化...");
    }

    @PreDestroy
    public void destroy() {
        System.out.println(beanName + "用户对象销毁...");
    }
}

dependency-lookup-context.xml 文件 增加 city 和 cities

    <bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
        <property name="name" value="xwf"/>
        <property name="age" value="18"/>
        <property name="id" value="1"/>
        <property name="city" value="WUHAN"/>
        <property name="cities" value="WUHAN,BEIJING"/>
    </bean>

输出结果:

实时查找---User{id=1, name='xwf', age=18,  city=WUHAN, cities=[WUHAN, BEIJING]}

3.2 Resource 类型

在上面的基础之上

User 增加 private Resource configFileReource;和对应的 set/get 方法。

完整代码如下:

/**
 * 用户类
 */
public class User{

    private Long id;

    private String name;

    private Integer age;

    private Resource configFileReource;

    private City city;

    private City[] cities;

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    public City[] getCities() {
        return cities;
    }

    public void setCities(City[] cities) {
        this.cities = cities;
    }

    public Resource getConfigFileReource() {
        return configFileReource;
    }

    public void setConfigFileReource(Resource configFileReource) {
        this.configFileReource = configFileReource;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public static User createUser(String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

    public static User createUser() {
        User user = new User();
        user.setName("static-user");
        return user;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", configFileReource=" + configFileReource +
                ", city=" + city +
                ", cities=" + Arrays.toString(cities) +
                '}';
    }

    @PostConstruct
    public void init() {
        System.out.println(beanName + "用户对象初始化...");
    }

    @PreDestroy
    public void destroy() {
        System.out.println(beanName + "用户对象销毁...");
    }
}

在 resouces/META_INF目录下新建 user-config.properties 文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jX2OUryt-1587622795067)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200422175120208.png)]
dependency-lookup-context.xml 文件增加 configFileReource

    <bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
        <property name="name" value="xwf"/>
        <property name="age" value="18"/>
        <property name="id" value="1"/>
        <property name="configFileReource" value="classpath:/META-INF/user-config.properties"/>
        <property name="city" value="WUHAN"/>
        <property name="cities" value="WUHAN,BEIJING"/>
    </bean>

输出结果:
因为 toString() 的缘故看起来是字符串,实际上是 Reource 这个类型

实时查找---User{id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING]}

4.集合类型注入

集合类型

  • 数组类型(Array):原生类型、标量类型、常规类型、Spring类型
  • 集合类型(Collection):
    • Collection:List、Set(SortedSet、NavigableSet、EnumSet)
    • Map:Properties

4.1 数组类型

3.1 Enum类型 中 User 的 private City[] cities;

4.2 集合类型

3.1 Enum类型 中 User 新增字段 private List<City> lifeCities;

新增 set/get 方法

重写toString 方法

    @Override
    public String toString() {
        return "User{" +
                "beanName='" + beanName + '\'' +
                ", id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", configFileReource=" + configFileReource +
                ", city=" + city +
                ", cities=" + Arrays.toString(cities) +
                ", lifeCities=" + lifeCities +
                '}';
    }

xml 中 新增 lifeCities 属性

    <bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
        <property name="name" value="xwf"/>
        <property name="age" value="18"/>
        <property name="id" value="1"/>
        <property name="configFileReource" value="classpath:/META-INF/user-config.properties"/>
        <property name="city" value="WUHAN"/>
        <property name="cities" value="WUHAN,BEIJING"/>
        <property name="lifeCities" value="WUHAN,BEIJING"/>
    </bean>

debug 调试,说明注入的是一个 Arraylist 集合类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XE4xJ9YN-1587622795070)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200422193222659.png)]
执行结果

实时查找---User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}

5.限定注入

使用注解 @Qualifier 限定

  • 通过 Bean 名称限定
  • 通过分组限定

基于注解 @Qualifier 扩展限定

  • 自定义注解 - 如 Spring Cloud @LoadBalanced

5.1 通过 Bean 名称限定

/**
 * {@link Qualifier} 注解依赖注入
 */
public class QualifierNameAnnotationDependencyInjectionDemo {

    @Autowired
    @Qualifier("user")
    private User user;

    @Autowired
    private User user1;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(QualifierAnnotationDependencyInjectionDemo2.class);
        applicationContext.refresh();
        QualifierAnnotationDependencyInjectionDemo2 beanDemo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo2.class);
        System.out.println("Qualifier名称:" + beanDemo.user);
        System.out.println("普通:" + beanDemo.user1);
        applicationContext.close();
    }

    @Bean
    public User user() {
        return createUser("xwf-bean");
    }

    @Bean
    @Primary
    public User superUser() {
        return createUser("super-xwf-bean");
    }

    private static User createUser(String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

}

执行结果:

user用户对象初始化...
superUser用户对象初始化...
名称:User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
分组:User{beanName='superUser', id=null, name='super-xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
superUser用户对象销毁...
user用户对象销毁...

普通的 user1 对象通过类型注入的方式,注入了 superUser 对象。

而加了 @Qualifier("user")user 通过 user名称的限定,注入了 user 对象

5.2 分组

/**
 * {@link Qualifier} 注解依赖注入
 */
public class QualifierNameAnnotationDependencyInjectionDemo {

    @Autowired
    @Qualifier("user")
    private User user;

    @Autowired
    @Qualifier
    private User user_group;

    @Autowired
    private User user1;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(QualifierNameAnnotationDependencyInjectionDemo.class);
        applicationContext.refresh();
        QualifierNameAnnotationDependencyInjectionDemo beanDemo = applicationContext.getBean(QualifierNameAnnotationDependencyInjectionDemo.class);
        System.out.println("Qualifier名称:" + beanDemo.user);
        System.out.println("普通:" + beanDemo.user1);
        System.out.println("分组:" + beanDemo.user_group);
        applicationContext.close();
    }

    @Bean
    public User user() {
        return createUser("xwf-bean");
    }

    @Bean
    @Qualifier
    public User userGroup() {
        return createUser("xwf-bean-group");
    }

    @Bean
    @Primary
    public User superUser() {
        return createUser("super-xwf-bean");
    }

    private static User createUser(String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

}

执行结果

Qualifier名称:User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
普通:User{beanName='superUser', id=null, name='super-xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
分组:User{beanName='userGroup', id=null, name='xwf-bean-group', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}

标有 @Bean@QualifieruserGroup() 方法被注入到标有 @Qualifier 的 user_group 字段上。

5.3 集合分组

我们再加入一个 User 的定义 和 Collection<User>,都用 @Qualifier 进行标注

    @Autowired
    @Qualifier
    private Collection<User> allUsers_group;

    @Bean
    @Qualifier
    @Primary
    public User userGroup2() {
        return createUser("xwf-bean-group2");
    }

5.4 元标注分组

自定义一个注解@UserGroup,使用 @Qualifier 进行标注

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface UserGroup {

}

在原来类的基础上加入

    @Autowired
    @UserGroup
    private Collection<User> allUsers_group_annotation;

    @Bean
    @UserGroup
    public User userGroup3() {
        return createUser("xwf-bean-group-annotation1");
    }

    @Bean
    @UserGroup
    public User userGroup4() {
        return createUser("xwf-bean-group-annotation2");
    }

5.5 完整示例

/**
 * {@link Qualifier} 注解依赖注入
 */
public class QualifierAnnotationDependencyInjectionDemo {

    @Autowired
    @Qualifier("user")
    private User user;

    @Autowired
    @Qualifier
    private User user_group;

    @Autowired
    private Collection<User> allUsers;

    @Autowired
    @Qualifier
    private Collection<User> allUsers_group;

    @Autowired
    @UserGroup
    private Collection<User> allUsers_group_annotation;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(QualifierAnnotationDependencyInjectionDemo.class);
        applicationContext.refresh();
        QualifierAnnotationDependencyInjectionDemo beanDemo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo.class);
        System.out.println("名称:" + beanDemo.user);
        System.out.println("分组:" + beanDemo.user_group);
        System.out.println("集合:size-" + beanDemo.allUsers.size() + "=" + beanDemo.allUsers);
        System.out.println("集合-分组:size-" + beanDemo.allUsers_group.size() + "=" + beanDemo.allUsers_group);
        System.out.println("集合-分组-注解:size-" + beanDemo.allUsers_group_annotation.size() + "=" + beanDemo.allUsers_group_annotation);
        applicationContext.close();
    }

    @Bean
    public User user() {
        return createUser("xwf-bean");
    }

    @Bean
    @Qualifier
    public User userGroup() {
        return createUser("xwf-bean-group");
    }

    @Bean
    @Qualifier
    @Primary
    public User userGroup2() {
        return createUser("xwf-bean-group2");
    }

    @Bean
    @Primary
    public User superUser() {
        return createUser("super-xwf-bean");
    }

    @Bean
    @UserGroup
    public User userGroup3() {
        return createUser("xwf-bean-group-annotation1");
    }

    @Bean
    @UserGroup
    public User userGroup4() {
        return createUser("xwf-bean-group-annotation2");
    }

    private static User createUser(String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

}

执行结果

名称:User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
分组:User{beanName='userGroup2', id=null, name='xwf-bean-group2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
集合:size-6=[User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup', id=null, name='xwf-bean-group', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup2', id=null, name='xwf-bean-group2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='superUser', id=null, name='super-xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup3', id=null, name='xwf-bean-group-annotation1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup4', id=null, name='xwf-bean-group-annotation2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]
集合-分组:size-4=[User{beanName='userGroup', id=null, name='xwf-bean-group', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup2', id=null, name='xwf-bean-group2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup3', id=null, name='xwf-bean-group-annotation1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup4', id=null, name='xwf-bean-group-annotation2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]
集合-分组-注解:size-2=[User{beanName='userGroup3', id=null, name='xwf-bean-group-annotation1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup4', id=null, name='xwf-bean-group-annotation2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]

由结果可知

  • 通过名称user限定,注入了对应的 user 对象
  • 通过 @Qualifier 分组,注入了 标有@Qualifier对象,如果有多个,那么使用 @Primary 表示主要依赖
  • 通过集合的方式 Collection<User> allUsers,将所有的 User 类型注入,有 6 个对象
  • 通过 @Qualifier+集合的方式 Collection<User> allUsers_group ,将所有标注 @Qualifier 的User 类型注入,有 4 个对象,分别为两个 @Qualifier 标注,两个 @UserGroup 标注
  • 通过 @UserGroup+集合的方式 Collection<User> allUsers_group_annotation ,将所有标注 @UserGroup 的User 类型注入,有 2 个对象

6.延迟依赖注入

使用 API ObjectFactory 延迟注入

  • 单一类型
  • 集合类型

使用 API ObjectProvider 延迟注入(推荐)

  • 单一类型
  • 集合类型

6.1 ObjectProvider

ObjectProvider 安全性高,可以减少避免一些异常

/**
 * 延迟注解依赖注入
 */
public class LazyAnnotationDependencyInjectionDemo {

    @Autowired
    private User user;

    @Autowired
    private ObjectProvider<User> providerUser;

    @Autowired
    private ObjectProvider<Collection<User>> providerUsers;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(LazyAnnotationDependencyInjectionDemo.class);
        applicationContext.refresh();
        LazyAnnotationDependencyInjectionDemo beanDemo = applicationContext.getBean(LazyAnnotationDependencyInjectionDemo.class);

        System.out.println(beanDemo.user);
        System.out.println(beanDemo.providerUser.getIfAvailable());
        System.out.println(beanDemo.user == beanDemo.providerUser.getIfAvailable());
        beanDemo.providerUser.forEach(System.out::println);
        System.out.println("=======================");
        System.out.println(beanDemo.providerUsers.getObject());
        applicationContext.close();
    }

    @Bean
    @Primary
    public User user() {
        return createUser("xwf-bean");
    }

    @Bean
    public User user2() {
        return createUser("xwf-bean2");
    }

    private static User createUser(String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

}

执行结果

User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
true
User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
User{beanName='user2', id=null, name='xwf-bean2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
=======================
[User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='user2', id=null, name='xwf-bean2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]

7.依赖处理过程

基础知识

  • 入口 - DefaultListableBeanFactory#resolveDependency
  • 依赖描述符 - DependencyDescriptor
  • 自动绑定候选对象处理器 - AutowireCandidateResolver

7.1 入口 DefaultListableBeanFactory#resolveDependency

	@Override
	@Nullable
	public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
...

7.2 DependencyDescriptor 依赖处理元信息

  • org.springframework.beans.factory.config.DependencyDescriptor
public class DependencyDescriptor extends InjectionPoint implements Serializable {

	private final Class<?> declaringClass;

	@Nullable
	private String methodName;

	@Nullable
	private Class<?>[] parameterTypes;

	private int parameterIndex;

	@Nullable
	private String fieldName;

	private final boolean required;

	private final boolean eager;

	private int nestingLevel = 1;

	@Nullable
	private Class<?> containingClass;

	@Nullable
	private transient volatile ResolvableType resolvableType;

	@Nullable
	private transient volatile TypeDescriptor typeDescriptor;
    ...

7.3 debug 调试注入过程

新建测试类 AnnotationDependencyInjectionResolutionDemo

/**
 * 注解驱动依赖注入过程
 */
public class AnnotationDependencyInjectionResolutionDemo {

    @Autowired
    private User user;
    // DependencyDescriptor 元信息
    // 必须 (required = true)
    // 实时注入 (eager = true)
    // 通过类型 (User.class)
    // 字段名称 (user)
    // 是否首要 (Primary = true)

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AnnotationDependencyInjectionResolutionDemo.class);
        applicationContext.refresh();
        AnnotationDependencyInjectionResolutionDemo beanDemo = applicationContext.getBean(AnnotationDependencyInjectionResolutionDemo.class);

        System.out.println(beanDemo.user);
        applicationContext.close();
    }

    @Bean
    @Primary
    public User user() {
        return createUser("xwf-bean");
    }

    @Bean
    public User user2() {
        return createUser("xwf-bean2");
    }

    private static User createUser(String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

}

在入口方法resolveDependency中打上断点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6clPL7pU-1587622795073)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423075334678.png)]
元信息和我们猜测的一样

// DependencyDescriptor 元信息
// 必须 (required = true)
// 实时注入 (eager = true)
// 通过类型 (User.class)
// 字段名称 (user)
// 是否首要 (Primary = true)

继续向下,前面三个判断都不满足,分别是判断依赖类型

  • 依赖类型是否等于 Optional

  • 依赖类型是否等于 ObjectFactory

  • 依赖类型是否等于 javaxInjectProviderClass

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dcspIAvb-1587622795076)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423080644615.png)]
进入到 doResolveDependency 方法,我们关注重点 doResolveDependency 中的 1250 行 findAutowireCandidates 方法,方法名称翻译过来就是找到 Autowire 候选者,找到我们通过 @Bean 定义的两个 User(user 和 user2)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DxuJWzFx-1587622795078)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423092426888.png)]
1262 行 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);因为有多个 User.class 对象,通过此方法找到主要依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHzEyKXo-1587622795080)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423093626883.png)]
并通过 matchingBeans.get(autowiredBeanName); 获取 User.Class 对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbVd59Bd-1587622795081)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423093701754.png)]
接着 1287 行通过 instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); 最终通过 beanFactory.getBean(beanName) 依赖查找的方式获取到这个 user 对象

	public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {

		return beanFactory.getBean(beanName);
	}

最后返回 user 对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qQZNdGPe-1587622795082)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423094112874.png)]

8.@Autowired 注入原理

@Autowired 注入过程

  • 元信息解析
    • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
    • 包括合并 BeanDefinition
  • 依赖查找
    • 获取 Bean 的对象,在这个过程中触发依赖处理的过程 DefaultListableBeanFactory#resolveDependency
  • 依赖注入(字段、方法)
    • 通过反射设置字段属性

8.1 细节1

接着上面的源码调试过程,我们再看一些细节,重新打开调试,idea 左下角的堆栈信息就是本次方法的调用链。

我们点击第二个 inject ,就会进入到上一层的调用方法中 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2c4d3ECJ-1587622795083)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423095844612.png)]
重新打上断点,可以看到在 640行 beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); 获取到 user 对象,然后在 668 行 field.set(bean, value); 通过反射注入到字段属性中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Knd5FolA-1587622795085)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200423100226316.png)]

8.2 细节2

AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

这个方法也比较重要

合并 Bean 的定义,为什么要合并呢?如果当前的类继承另一个类

比如 SuperUser 继承 User,xml 定义中 parent="user"

    <bean primary="true" id="superUser" class="com.huajie.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user">
        <property name="address" value="wuhan"/>
    </bean>

那么,就需要先将 user 定义的相关属性合并到 superUser中,其实每个 Bean 的定义都会走这个方法,如果没有继承,那么合并的就是自己本身。

9.JSR-330 @Inject 注入原理

@Inject 注入过程

  • 如果 JSR-330 存在 ClassPath 中,复用 AutowiredAnnotationBeanPostProcessor 实现

AutowiredAnnotationBeanPostProcessor#AutowiredAnnotationBeanPostProcessor() 构造器

处理 AutowiredValueInject 三个注解

autowiredAnnotationTypes是一个LinkedHashSet

	private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
	
public AutowiredAnnotationBeanPostProcessor() {
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		try {
			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
			logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

在构建元数据的时候调用 findAutowiredAnnotation()获取注解信息 ,遍历 autowiredAnnotationTypes 只要找到一个就返回,换言之,不论是三个注解中的哪一个都可以。

	@Nullable
	private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
		MergedAnnotations annotations = MergedAnnotations.from(ao);
		for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
			MergedAnnotation<?> annotation = annotations.get(type);
			if (annotation.isPresent()) {
				return annotation;
			}
		}
		return null;
	}

如果我们自定义一个注解 ,将注解加入到 autowiredAnnotationTypes这个对象中,效果应该和 @Autowired一样,在本文 11 自定义依赖注入注解 中有相关的示例。

10.Java 通用注入原理

CommonAnnotationBeanPostProcessor

这个类和上面讲的的 AutowiredAnnotationBeanPostProcessor 都是处理依赖注入注解相关的类。AutowiredAnnotationBeanPostProcessor主要是处理 @AutowiredValueInject 这三个注解,而本类主要是处理通用型的,主要包括下面三个注解和两个生命周期回调:

  • 注入注解
    • javax.xml.ws.WebServiceRef
    • javax.ejb.EJB
    • javax.annotation.Resource
  • 生命周期注解
    • javax.annotation.PostConstruct
    • javax.annotation.PreDestroy

源码分析

10.1 区别1 实现细节

CommonAnnotationBeanPostProcessor 实现了 InstantiationAwareBeanPostProcessor 接口,而 AutowiredAnnotationBeanPostProcessor 则是继承了 InstantiationAwareBeanPostProcessorAdapter,原因在于 CommonAnnotationBeanPostProcessor 是Spring 5 基于 Java 8 的实现,该接口中都是 default 的方法。而原来的需要一个 Adapter 的适配器来达到这个效果。

10.2 区别2 生命周期

两个类都实现了 MergedBeanDefinitionPostProcessor 来处理 BeanDefinition 的合并操作,但是

CommonAnnotationBeanPostProcessor 中的方法多了

super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
		InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}

postProcessMergedBeanDefinition() 多了生命周期(Lifecycle)的处理,主要指的是 PostConstruct(初始化) 和 PreDestroy(销毁) 这两个阶段。

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		LifecycleMetadata metadata = findLifecycleMetadata(beanType);
		metadata.checkConfigMembers(beanDefinition);
	}

10.3 区别3 顺序 Order

BeanPostProcessor 都具有一定的顺序,本次讨论的两个继承了 InitDestroyAnnotationBeanPostProcessor

而 InitDestroyAnnotationBeanPostProcessor 实现了 PriorityOrdered 接口,CommonAnnotationBeanPostProcessor 相关源码如下:

构造方法 setOrder(Ordered.LOWEST_PRECEDENCE - 3);

   ...
   public CommonAnnotationBeanPostProcessor() {
		setOrder(Ordered.LOWEST_PRECEDENCE - 3);
		setInitAnnotationType(PostConstruct.class);
		setDestroyAnnotationType(PreDestroy.class);
		ignoreResourceType("javax.xml.ws.WebServiceContext");
	}
	...

    public void setOrder(int order) {
		this.order = order;
	}

	@Override
	public int getOrder() {
		return this.order;
	}

AutowiredAnnotationBeanPostProcessor 中

private int order = Ordered.LOWEST_PRECEDENCE - 2;

由此可见,CommonAnnotationBeanPostProcessor 会优先执行。

11.自定义依赖注入注解

基于 AutowiredAnnotationBeanPostProcessor 实现

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InjectUser {

}

我们在之前的例子 AnnotationDependencyFieldInjectDemo 中加一个字段

private User inject_user; 并标注上我们自定义的注解 @InjectUser

通过 autowiredAnnotationBeanPostProcessor()方法将我们自定义的注解加入到 autowiredAnnotationTypes对象中

/**
 * 基于注解实现字段注入示例
 */
public class AnnotationDependencyFieldInjectDemo {

    @Autowired
    private UserHolder userHolder;

    @Resource
    private UserHolder userHolder_resource;

    @InjectUser
    private User inject_user;

    @Bean(name = "injectUserAnnotationBeanPostProcessor")
    @Order(Ordered.LOWEST_PRECEDENCE - 3)
    public static AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
        AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
        beanPostProcessor.setAutowiredAnnotationType(InjectUser.class);
        return beanPostProcessor;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AnnotationDependencyFieldInjectDemo.class);

        applicationContext.refresh();

        // @Autowired 不支持 static 修饰的属性,所以还是采用依赖查找的方式
        AnnotationDependencyFieldInjectDemo demo = applicationContext.getBean(AnnotationDependencyFieldInjectDemo.class);
        System.out.println("userHolder:" + demo.userHolder);
        System.out.println("userHolder_resource:" + demo.userHolder_resource);
        System.out.println("userHolder 和 userHolder_resource 比较:" + (demo.userHolder == demo.userHolder_resource));
        System.out.println("inject_user:" + demo.inject_user);
        applicationContext.close();
    }


    @Bean
    public User user() {
        User user = new User();
        user.setName("xwf-bean-field");
        user.setAge(20);
        return user;
    }

    @Bean
    public UserHolder userHolder(User user) {
        return new UserHolder(user);
    }
}

执行结果:

userHolder:UserHolder{user=User{beanName='user', id=null, name='xwf-bean-field', age=20, configFileReource=null, city=null, cities=null, lifeCities=null}}
userHolder_resource:UserHolder{user=User{beanName='user', id=null, name='xwf-bean-field', age=20, configFileReource=null, city=null, cities=null, lifeCities=null}}
userHolder 和 userHolder_resource 比较:true
inject_user:User{beanName='user', id=null, name='xwf-bean-field', age=20, configFileReource=null, city=null, cities=null, lifeCities=null}

自定义实现(比较复杂,就不演示了)

  • 生命周期处理
    • InstantiationAwareBeanPostProcessor
    • MergedBeanDefinitionPostProcessor
  • 元数据
    • InjectElement
    • InjectionMetadata

12.面试题

1.有多少种依赖注入的方式?

  • 构造器注入
  • Setter 注入
  • 字段注入
  • 方法注入
    • @Bean
  • 接口回调注入
    • Aware

这里还可以结合本文 2. 依赖注入类型选择 来进行回答。

2.你偏好构造器注入还是Setter注入?

两种依赖注入的方式都可以使用,如果是必须依赖的话,那么推荐使用构造器注入,Setter 注入用于可选依赖。

如果参数比较少的情况下,还是使用构造器注入,这也是 Spring 官方推荐的注入方式。

3.Spring 依赖注入的来源有哪些?

答案将在下一章中详细讨论。

13.参考

  • 极客时间-小马哥《小马哥讲Spring核心编程思想》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值