关于autowire注解,在没有写set方法的的情况下,如何将值注入进去

Autowire注解在无set方法时的注入原理
本文通过一个例子展示了在没有set方法的情况下,如何使用Autowire注解将Spring容器中的DepartmentDao实例注入到EmployeeHandler对象中。通过分析UnsafeObjectFieldAccessorImpl类的结构图,揭示了注入的实现细节。

问题的代码详细演示


@Repository
public class EmployeeDao {
   
   

	private static Map<Integer, Employee> employees = null;
	
	@Autowired
	private DepartmentDao departmentDao;
	
	static{
   
   
		employees = new HashMap<Integer, Employee>();

		employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1, new Department(101, "D-AA")));
		employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1, new Department(102, "D-BB")));
		employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0, new Department(103, "D-CC")));
		employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0, new Department(104, "D-DD")));
		employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1, new Department(105, "D-EE")));
	}
	
	private static Integer initId = 1006;
	
	public void save(Employee employee){
   
   
		if(employee.getId() == null){
   
   
			employee.setId(initId++);
		}
		
		employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
		employees.put
<think>嗯,用户问的是关于Spring框架中禁止使用@Autowired注入,而应该用构造方法注入,这样便于单元测试。我需要先理解这两者的区别,以及为什么构造方法注入对单元测试更好。 首先,@Autowired通常用于字段注入,也就是直接在字段上加上注解Spring会自动注入依赖。但这样做的问题可能在于,当没有Spring容器的时候,比如单元测试,这样的字段无法被外部设置,导致测试困难。这时候可能需要用反射来设置私有字段,比较麻烦。 然后,构造方法注入是通过类的构造方法来传递依赖,这样在创建对象时必须传入所有需要的依赖。这样做的优点是,依赖关系明确,而且可以在不依赖Spring情况下手动注入依赖,方便测试。比如在单元测试中,可以直接new对象并传入mock的依赖,而不需要启动整个Spring上下文。 另外,构造方法注入还有助于保证依赖不可变,因为字段可以被声明为final,这样一旦注入后就不能修改,提高了代码的安全性。而字段注入则不能做到这一点,因为字段通常是非final的。 可能需要提到Spring从4.x版本开始推荐使用构造方法注入,特别是当只有一个构造方法时,@Autowired可以省略,Spring会自动使用该构造方法。这样代码更简洁。 同时,用户可能对单元测试的具体操作不太熟悉,可能需要举例说明,比如使用Mockito来创建mock对象,并通过构造方法传入,这样测试类就不需要依赖Spring的上下文加载,测试速度更快,也更符合单元测试的理念。 还要注意用户可能的背景,可能是刚开始学习Spring,或者在公司项目中遇到了相关规范,需要解释清楚为什么这样做更好,而不仅仅是怎么做。可能还需要提到其他注入方式,比如setter注入,但构造方法注入在依赖不可变和明确性上更有优势。 总结来说,回答需要涵盖以下几点: 1. @Autowired字段注入的问题:测试困难,依赖容器,无法final。 2. 构造方法注入的优点:明确依赖,便于手动注入,支持final字段。 3. 单元测试中的实际应用,如何通过构造方法传入mock对象。 4. Spring的版本支持和推荐做法。 5. 可能的其他好处,比如代码可维护性,不可变性等。 需要确保用简单的中文解释清楚,避免使用过多技术术语,让刚接触的人也能理解。</think>在 Spring 框架中,禁止使用 `@Autowired` **字段注入**(直接标注在成员变量上),而推荐使用**构造方法注入**,主要有以下原因和优势: --- ### 1️⃣ **为何禁止字段注入 (`@Autowired` 在变量上)?** - **依赖容器**:字段注入强制依赖 Spring 容器,对象必须由 Spring 创建(否则依赖无法注入)。 - **单元测试困难**:测试时无法直接通过构造方法传入依赖,必须用反射等复杂手段修改私有字段。 - **不可变性问题**:字段无法设为 `final`,对象可能在初始化后意外修改依赖。 - **隐藏依赖关系**:类没有明确声明自己需要哪些依赖,依赖关系不够透明。 --- ### 2️⃣ **为何推荐构造方法注入?** ```java // 正确做法:通过构造方法显式声明依赖 public class UserService { private final UserRepository userRepository; // final 保证不可变 // Spring 会自动调用此构造方法注入依赖(无需额外注解) public UserService(UserRepository userRepository) { this.userRepository = userRepository; } } ``` - **明确依赖关系**:构造方法强制要求调用者传入所有必要依赖,代码可读性更高。 - **天然支持单元测试**:测试时可直接 `new` 对象并传入 Mock 依赖(如 Mockito 的模拟对象)。 - **不可变性**:依赖字段可设为 `final`,避免运行时被修改。 - **脱离容器运行**:对象不再强制依赖 Spring,复用性和灵活性更高。 --- ### 3️⃣ **单元测试的便捷性对比** 假设要对 `UserService` 进行测试: #### **字段注入的测试困境** ```java public class UserService { @Autowired // ❌ 字段注入 private UserRepository userRepository; } // 测试时必须用反射暴力修改私有字段: @Test void test() throws Exception { UserService service = new UserService(); UserRepository mockRepo = Mockito.mock(UserRepository.class); ReflectionTestUtils.setField(service, "userRepository", mockRepo); // 复杂且不直观 } ``` #### **构造方法注入的测试优势** ```java @Test void test() { UserRepository mockRepo = Mockito.mock(UserRepository.class); UserService service = new UserService(mockRepo); // ✅ 直接传入 Mock 依赖 // 直接进行测试... } ``` --- ### 4️⃣ **Spring 官方推荐** - **Spring 4+ 默认支持**:如果类只有唯一一个构造方法,`@Autowired` 可省略。 - **依赖不可变原则**:通过 `final` 字段确保依赖在对象生命周期内不变。 - **代码健壮性**:强制要求依赖在初始化时全部就绪,避免部分依赖为 `null` 的风险。 --- ### 📌 总结 **构造方法注入**通过显式声明依赖关系,使代码更安全、更易测试,同时符合面向对象设计原则。而字段注入(`@Autowired` 在变量上)因其隐含的依赖和测试复杂性,已成为不推荐的做法。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值