专栏首页技术进阶之路Spring 中实现自动注入的几种方式

Spring 中实现自动注入的几种方式

Spring 中实现自动装配的注解有以下几个:

  • @Autowired@Qualifier@Primary@Resource@Inject

一、@Autowired

Spring 中最常用的一个注解,当一个组件需要另一个组件作为属性的时候,我们可以通过两种方式对属性进行赋值,一种是通过构造方法,一种是通过 set 方法(类比),而这个注解使用的方法就是后者。

下面介绍该注解的特点:

  • 首先是 按照类型 自动注入,适用于容器中只有一种该类型的组件;
  • 如果存在多个相同类型的组件,则将属性名作为 id 查询容器中的组件并注入;
  • 默认属性对应的组件在容器中必须是存在的,如果想无论存在与否都注入可以令属性 required = false
  • 可以在该注解的基础之上使用 @Qualifier("bookDao") 注解指定要注入组件的 id,这时属性名的 id 已失效;
  • 如果不使用上述注解指定 id ,存在多个相同类型的组件时也可以使用 @Primary 注解设置 Bean 的优先级为最优。
@Autowired(required = false)
@Qualifier("bookDao2")
private BookDao bookDao;

上面注入的组件的 id 值为 bookDao2

二、@Resource

@Autowired 不同,@Resource 注解是 按照属性名 自动注入,它属于 JSR250 规范;

该注解不支持 @Qualifier@Primary 的使用,但是可以使用它的 name 属性指定要注入组件的 id 值。

@Resource(name = "bookDao3")
private BookDao bookDao;

上面注入的组件的 id 值为 bookDao3

三、@Inject

要使用 @Inject 注解必须要先导包,它属于 JSR330 规范 :

<!--    inject 注解    -->
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

导包之后就可以使用了,该注解的效果和 @Autowired 的效果一样,只不过没有任何属性,所以功能有些欠缺,但是可以和另外两个注解配合使用。

@Inject
private BookDao bookDao;

上面注入的组件的 id 值为 bookDao

四、@Autowired 的使用方式

该注解主要是通过 BeanPostProcessor 的实现类 AutowiredAnnotationBeanPostProcessor 实现的。

该类及其父类重写了 postProcessBeforeInitialization 方法,在初始化 Bean 之前,先对属性进行赋值,从而实现自动注入。

1、Set 方法

该注解除了可以放在属性上面,还可以放在方法上面:

@Component
public class Boss {

    private Car car;

    public Car getCar() {
        return car;
    }

    @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
}

可以放在 set 方法上面,他会自动的去 IOC 容器中找方法中的参数,这里的参数是 car ,所以他会去容器中找 car 这个类,然后创建一个对象完成赋值。

官方 3.X 建议使用该方式注入。

2、构造器

对于加在 IOC 容器中的组件,容器启动后会调用 无参构造器 创建对象进行初始化赋值操作。

我们也可以不使用默认的,我们提供一个有参构造器:

@Autowired
public Boss(Car car) {
    this.car = car;
}

构造器要使用的组件,也都是从容器中获取。

所以也可以这么写:

public Boss(@Autowired Car car) {
    this.car = car;
}

同时如果该类只有一个有参构造器,那么 @Autowired 注解 可以省略

官方 4.X 开始建议使用该方式注入。

3、@Bean + 方法参数

我们可以不改变 Boss 这个类,即不在 Boss 中注入 Car,而是在将 Boss 放入容器的时候注入它需要的参数 Car

@Bean
public Boss boss(@Autowired Car car) {
    return new Boss();
}

这里的 @Autowired 可以省略,也是用的最多的一种方式。

五、使用 Spring 底层的组件

如果自己写的组件想要使用 Spring 底层的组件可以使用另一种方式 :比如想要使用 SpringApplicationContext

Spring 为我们提供了相关的接口,他们都是 xxxAware,比如 ApplicationContextAware

每一个接口都对应一个方法,我们可以在方法中获取 Spring 底层的组件,然后给成员变量赋值以获取相关组件。

public class Red implements ApplicationContextAware {

    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}

关于 Aware 的原理:

其实它还是使用了我们之前说过的 BeanPostProcessor ,每一个 Aware 都对应一个 AwareProcessor,这个 processor 正是BeanPostProcessor 的实现类,所以肯定会有一个 postProcessBeforeInitialization 方法,我们重点来看一下这个方法。

@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
	AccessControlContext acc = null;
	if (System.getSecurityManager() != null && (bean instanceof ApplicationContextAware)) {
		acc = this.applicationContext.getBeanFactory().getAccessControlContext();
	}
	if (acc != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareInterfaces(bean);
			return null;
		}, acc);
	}
	else {
		invokeAwareInterfaces(bean);
	}
	return bean;
}

invokeAwareInterfaces(bean); 方法中主要是下面的逻辑:

if (bean instanceof ApplicationContextAware) {
	((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}

其实就是先判断是不是那几个 Aware 中的一个,如果是就赋值,我们能看到的就是在 初始化 的时候利用 后置处理器 完成赋值。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring 中的 @Import 注解及向容器中添加 Bean 的几种方式

    这次介绍一下 Spring 中的一个重要的注解 @Import 以及向容器中添加 Bean 的几种方式 ,该注解在 SpringBoot 自动转配中起到重要的作...

    wsuo
  • IP 数据报格式详解

    ❔ 给出一数据报的总长度为 3820 字节,其数据部分的长度为 3800 字节(使用固定首部),需要分片为长度不超过 1420 字节的数据报片。

    wsuo
  • Ubuntu19.10 中安装 JDK

    然后就是上传到服务器上面去,如果你用的是虚拟机就更方便了,如果不是请使用工具传输,这里推荐使用远程连接工具,也就是 Xftp6 。

    wsuo
  • React.js实战之React 生命周期1 组件的生命周期

    JavaEdge
  • 知乎 Android 客户端组件化实践

    知乎 Android 客户端最早使用的是最常见的单工程 MVC 架构,所有业务逻辑都放在了主工程 Module 里,网络层和一些公共代码分别被抽成了一个 Mod...

    用户1269200
  • 前端面试宝典(五)—— Vue

    MVVM是Model-View-ViewModel的简写,Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI...

    萌兔IT
  • 【PMP】项目进度考点的说明

    【单选】资源平衡是一种常用的进度网络分析方法。在以下哪种情况下,必须进行资源平衡?( )

    心跳包
  • 38. Vue组件切换 使用v-if、v-else结合flag进行切换

    在登陆页面的需求中,一般都会有登陆、注册两个按钮,然后点击不同的按钮显示不同的页面。在这里对应的页面可以是一个组件。

    Devops海洋的渔夫
  • Vue 组件(二):父子组件通信

    子组件是不能直接访问父组件中的数据的,但有时候父子组件之间需要进行数据交互,这就涉及到了父子组件通信的问题。简单来说,父组件向子组件通信是通过 props 进行...

    Chor
  • Tomcat的源码分析

    总结 : 由上面摘抄的主要源码可知 ,在catalina.bat 启动时 ,主要是加载了一个叫bootstrap.jar 的jar包 . 在加载这个jar...

    时间静止不是简史

扫码关注云+社区

领取腾讯云代金券