Spring注入Bean的几种方式

12/2/2021 Spring

# Spring注入Bean的几种方式🧨

# 1、基于 field 注入(@Autowired注解)😑

@Autowired
IPersonnelService personnelService;
1
2

# 2、构造函数注入(推荐)👌

@Service
@Transactional(rollbackFor = Exception.class)
public class PersonnelServiceImpl extends ServiceImpl<PersonnelDao, PersonnelModel> implements IPersonnelService
{
    private final PersonnelDao personnelDao;
    
    /**
     * 构造函数注入
     */
    public PersonnelServiceImpl(PersonnelDao personnelDao) {
        this.personnelDao = personnelDao;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3、setter方法注入🎉

@Component
@Slf4j
public class TaskJob
{
    private IPersonnelService personnelService;

    @Autowired
    public void setPersonnelService(IPersonnelService personnelService)
    {
        this.personnelService = personnelService;
    }

    /**
     * 检测合同是否快到期任务
     */
    @Scheduled(cron = "0 28 14 * * ? ")
    public void contractNoticeJob()
    {
        log.info("---- 检测合同是否快到期任务 START ----");
        personnelService.getContractNoticeJob();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 4、自定义的工具类中如何注入Bean🌋

场景:

我们要在我们自己封装的Utils工具类中或者非controller普通类中使用@Autowired注解注入Service或者Mapper接口,直接注入是报错的

原因:

因为Utils使用了静态的方法,静态变量,静态方法不是对象的属性,属于class的;普通方法才是属于对象
spring注入是在容器中实例化对象,静态类,方法的加载优于@Autowired的加载

Java变量的初始化顺序为:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法–>@Autowired> @PostConstruct

解决方案✌️

// 解决方案1:使用@PostConstruct注解

@Component   
public class TestUtils {  

	@Autowired  
	private ItemService itemService;  
	
	private static TestUtils testUtils;  

	@PostConstruct
	public void init() {      
    	testUtils = this;
	}   
  
	public static void test()
    {
    	testUtils.itemService.insert();  
    	
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 解决方案2:使用setter注入

@Component
public class TestUtils
{
    private static StringRedisTemplate redisTemplate;

    @Autowired
    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        RedisHelper.redisTemplate = redisTemplate;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13

# Spring开发团队的建议🙌

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for 
mandatory dependencies and setter methods or configuration methods for optional dependencies.

简单来说,就是

- 强制依赖就用构造器方式

- 可选、可变的依赖就用 setter 注入当然你可以在同一个类中使用这两种方法。构造器注入更适合强制性的注入旨在不变性,Setter 注入更适合可变性的注入。

# 那么使用@Autowired注解(基于field注入的坏处)有哪些?

基于 field 注入虽然简单,但是却会引发很多的问题。这些问题在我平常开发阅读项目代码的时候就经常遇见。

  • 容易违背了单一职责原则 使用这种基于 field 注入的方式,添加依赖是很简单的,就算你的类中有十几个依赖你可能都觉得没有什么问题,普通的开发者很可能会无意识地给一个类添加很多的依赖。但是当使用构造器方式注入,到了某个特定的点,构造器中的参数变得太多以至于很明显地发现 something is wrong。拥有太多的依赖通常意味着你的类要承担更多的责任,明显违背了单一职责原则(SRP:Single responsibility principle)。

  • 依赖注入与容器本身耦合依赖注入框架的核心思想之一就是受容器管理的类不应该去依赖容器所使用的依赖。换句话说,这个类应该是一个简单的POJO(Plain Ordinary Java Object)能够被单独实例化并且你也能为它提供它所需的依赖。

  • 这个问题具体可以表现在:

    • 你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化,这更像是集成测试
    • 你的类和依赖容器强耦合,不能在容器外使用
    • 不能使用属性注入的方式构建不可变对象(final 修饰的变量)
十年
陈奕迅