Spring自动装配之方法、构造器位置的自动注入操作
作者:金一白 发布时间:2021-11-30 23:28:40
Spring自动装配之方法、构造器位置的自动注入
1. 注解定义
@Autowired的定义信息如下:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
可以看出,@Autowired注解可以定义在构造器上,方法上,方法参数上,字段上,还有自定义注解上;
2. 注解使用
2.1 定义在造器上
@Autowired //定义在构造器方法上
public UserService(UserDao userDao) {
this.userDao = userDao;
}
或者
// 定义在构造器参数上
public UserService(@Autowired UserDao userDao) {
this.userDao = userDao;
}
特别注意,当一个类只有一个有参构造器,且该构造器不一定需要是public修饰的, 组件注入的时候不需要指定在构造器方法上或者构造器参数上指定@Autowired,只需要声明构造器即可;
2.2 定义在方法和参数上
定义在Set方法上
@Autowired //定义在set方法上
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
定义在配置Bean的方法上
package com.yibai.spring.annotation.main.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import com.yibai.spring.annotation.service.UserDao;
import com.yibai.spring.annotation.service.UserService;
//@ComponentScan("com.yibai.spring.annotation.service")
@Service
public class MainConfigForAutowired {
// Spring自动从IOC容器中找出UserDao作为方法参数传入,这里@Autowired可有可无
@Bean
public UserService userService(@Autowired UserDao userDao) {
UserService userService = new UserService();
userService.setUserDao(userDao);
return userService;
}
@Bean
public UserDao userDao() {
return new UserDao();
}
}
2.3 定义在字段上
package com.yibai.spring.annotation.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import lombok.Getter;
@Getter
@Service
public class UserService {
@Qualifier(value = "userDao")
@Autowired(required = true)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
3. 注入位置推荐
一般推荐注入位置放在构造器上,因为不管字段还是方法的方式注入,都是先创建组件,再注入依赖的组件,如果在构造方法上就需要使用依赖的组件,那么只有在构造器上注入才是可以实现的,因为执行顺序的问题;
package com.yibai.spring.annotation.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import lombok.Getter;
@Getter
@Service
public class UserService {
@Qualifier(value = "userDao")
@Autowired(required = true)
private UserDao userDao;
public UserService() {
//@Autowired定义在字段或者set方式上,在构造器上无法获取到依赖的组件
System.out.println(userDao); // null
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
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 底层的组件可以使用另一种方式 :比如想要使用 Spring 的 ApplicationContext。
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 中的一个,如果是就赋值,我们能看到的就是在 初始化 的时候利用 后置处理器 完成赋值。
来源:https://blog.csdn.net/luojinbai/article/details/86586512


猜你喜欢
- 说明:.NET Compact Framework 中不支持异步委托调用,也就是 BeginInvoke 和 EndInvoke 方法。Be
- 很不错的蓝牙通信demo实现发送和接受功能,就用了两个类就实现了,具体内容如下说下思路把 主要有两个类 主界面类 和 蓝牙聊天服务类&nbs
- 在网上看了比较多的关于Tab的教程,发现都很杂乱。比较多的用法是用TitlePagerTabStrip和ViewPaper。不过TitleP
- Java 用反射设置对象的属性值实例详解/** * 用反射设置对象的属性值 * @param obj 需要設置值的對象 * @param f
- 本篇介绍我们如何利用selenium 来操作各种页面元素阅读目录链接(link)输入框 textbox按钮(Button)下拉选择框(Sel
- 前言最近在项目中使用到定时任务,之前一直都是使用Quartz 来实现,最近看Spring 基础发现其实Spring 提供 Spring Sc
- 近日在工作中需要根据设备的HardwareID来获取设备的驱动程序信息,比如驱动程序版本等。经过摸索,得到了两种不同的解决办法,两种办法各有
- PagerBottomTabStrip 是一个基本按谷歌Material Design规范完成的安卓底部导航栏控件官方设计规范:https:
- maven项目install时忽略执行test在项目所在文件夹根目录使用maven命令打包时<!-- 不执行单元测试,也不编译测试类
- 为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。包的作用1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用
- 本文实例为大家分享了java斗地主发牌的具体代码,供大家参考,具体内容如下分析这是一个模仿斗地主发牌的例子;按照斗地主的规则,完成洗牌发牌的
- 1.简述描述:1、对输入的字符串进行加解密,并输出。2、加密方法为:当内容是英文字母时则用该英文字母的后一个字母替换,同时字母变换大小写,如
- C# CefSharp 过滤 RequestHandler 图片1、方式一ChromiumWebBrowser 实现 IRequestHan
- 疑问,确实像往常一样在service上添加了注解 @Transactional,为什么查询数据库时还是发现有数据不一致的情况,想想肯定是事务
- 一、前言近些年,随着微服务框架在越来越多的公司产品中实践落地,以Spring Cloud Alibaba为导向的一站式微服务解决方案也成为微
- 项目介绍springboot搭建的访客管理系统,针对高端基地做严格把控来访人员信息管理,用户后端可以设置多个管理员帐号,给予不同部门的管理层
- springboot中集成jpa需要再pom文件中添加jpa的jar包,使用springboot的话iju不用自己规定版本号了,自动管理依赖
- 先说结论:字段类型更改为 'keyword'elasticSearch官方文档中创建index代码如下PUT /my_sto
- 本文实例为大家分享了java实现五子棋游戏的具体代码,供大家参考,具体内容如下此游戏具有双人对战功能和人机对战功能一、游戏界面的实现一个游戏
- 现在,汽车的踪影无处不在,公路上疾驰,大街边临停,小区中停靠,车库里停泊。管理监控如此庞大数量的汽车是个头疼的问题。精明的人们把目光放在车牌