Spring@Autowired注解与省去get和set方法,对注解Autowired放在setter方法上的情况

本文深入探讨Spring框架中的依赖注入机制,包括@Autowired注解的使用、不同注入方式的区别及其实现原理,同时介绍了如何利用Spring自动扫描特性简化依赖注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:c="http://www.springframework.org/schema/c"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd
		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.3.xsd">


         <bean id="autotest" class="autotest" autowire="constructor">  
            
         </bean>
         
         <!-- 
         当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去
          -->
         <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
        
          <bean id="person" class="Person">  
            <property name="age" value="21"></property>  
            <property name="name" value="李跃"></property>  
             <property name="sex" value="男"></property>  
         </bean>  

  
</beans>

 

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class autotest {  
          
     @Autowired//这里用了这个注释就省去了构造方法或者get,set方法来设置person属性  
    private Person person;
     
  

	public void fun(){  
           ConfigurableApplicationContext cf=new ClassPathXmlApplicationContext("spring-mvc.xml");      
           autotest autotest=(autotest)cf.getBean("autotest");   
           System.out.println(autotest);  
           System.out.println(autotest.person);  
     }  
       
        public static void main(String args[]){  
            new autotest().fun();  
        }  
}  


否则如下

 

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:c="http://www.springframework.org/schema/c"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd
		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.3.xsd">


         <bean id="autotest" class="autotest" >  
            <property name="person" ref="person"></property>  
         </bean>
         
      
          <bean id="person" class="Person">  
            <property name="age" value="21"></property>  
            <property name="name" value="李跃"></property>  
             <property name="sex" value="男"></property>  
         </bean>  

  
</beans>

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class autotest {  
          
    
    private Person person;
     
    public Person getPerson() {
		return person;
	}

	public void setPerson(Person person) {
		this.person = person;
	}

	public void fun(){  
           ConfigurableApplicationContext cf=new ClassPathXmlApplicationContext("spring-mvc.xml");      
           autotest autotest=(autotest)cf.getBean("autotest");   
           System.out.println(autotest);  
           System.out.println(autotest.person);  
     }  
       
        public static void main(String args[]){  
            new autotest().fun();  
        }  
}  

 

 

 

 

 

2.对注解Autowired放在setter方法上的情况,这种情况要注意几种情况,先看代码

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:c="http://www.springframework.org/schema/c"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd
		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.3.xsd">


         <bean id="autotest" class="autotest"/>  
          
         <!-- 
                            当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去
          -->
        <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
         
         <bean id="person" class="Person">  
             <property name="age" value="21"></property>  
             <property name="name" value="李跃"></property>  
             <property name="sex" value="男"></property>  
         </bean>  

  
</beans>

 

 

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

//A:
public class Autotest{  
//C:    
private Person person;
	
	public void fun(){  
           ConfigurableApplicationContext cf=new ClassPathXmlApplicationContext("spring-mvc.xml");      
           Autotest autotest=(Autotest)cf.getBean("autotest");   
          
           System.out.println(autotest.person); //Person@4f970963
           System.out.println(this); //autotest@2f92e0f4
           System.out.println(autotest); //autotest@7b49cea0
           System.out.println(this.person);//null,
           
     }  
       
    public Person getPerson() {
    	System.out.println(person); 
		return person;
	}
    
    @Autowired
	public void setPerson(Person person) {
		this.person = person;
	}

   public static void main(String args[]){  
	       new Autotest().fun(); // (new Autotest()==B)调用A==b.a.fun() 
        }  
}  

 

 

 

上面要注意区分几个对象this,autotest,是有区别的,对this和autotest来说person都是他们的属性,但是真正通过注解和配置文件配合工作的却是autotest,所以对于注解在setter方法上的情况,只有autotest.person不是空,单独的this.person或者new 一个对象.person是空的。这里我是来自http://blog.youkuaiyun.com/badboy_qiao/article/details/52713257最后总结的启发。

 

 

结论:注入的对象,只会调用空参构造函数,且这个对象的所有属性都是默认值,自己手动赋予的值不会被使用,

 

所以在A中的C类,在B中调用A的时候,创建的A对象的属性都是默认值,所以A对象虽然有了,但是A的属性C却是null,所以在B中直接this.a.c.method()是会报null指针异常的,且是c是null的发生原因。

解决方式:C在A中添加get方法,然后B中使用a.getC()即可获得c的对象,且c的对象也是spring注入的。

 

再顺便谈一个问题做下记录,为了更好理解上面的几句话看下面对象的输出,this对象始终和第一次真正new出来的对象(Autotest test=new Autotest();不是new Autotest();)是同一个东西

 

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

//A:
public class autotest{  
//C:    
private Person person;
	
	public void fun(){  
           ConfigurableApplicationContext cf=new ClassPathXmlApplicationContext("spring-mvc.xml");      
           autotest autotest=(autotest)cf.getBean("autotest");   
           autotest test=new autotest();
           System.out.println(test);//D autotest@7b49cea0 
           System.out.println(this); //C autotest@28a418fc
           System.out.println(this.person);//null
           System.out.println(test.person);//null
           System.out.println(autotest.person);//Person@887af79
     }  
       
    public Person getPerson() {
    	System.out.println(person); 
		return person;
	}
    
    @Autowired
	public void setPerson(Person person) {
		this.person = person;
	}

   public static void main(String args[]){ 
	      System.out.println(new autotest());//B autotest@2f92e0f4
	      autotest test=new autotest();
	      System.out.println(test.person);//null
	      System.out.println(test);//A autotest@28a418fc
	      test.fun(); 
        }  
}  

 

 

 

 

 

还有一个地方就是如果所有bean在容器中已经存在,那么我们某个Java类中如果用到其它对象,我们可以省去在当前这个Java类的bean配置文件中用property来配置它包含的属性,它会自动注入这些属性对象,直接使用就行。

如下面这里使用到这个对象

 

这个类的bean中可以直接

<bean name="shopManagerController" class="com.xxxx.ShopManagerController"">

</bean>不用在controller的bean的property配置shopManagerService,可以省去,用了resource注解settter方法也可以省去,但是要在接口实现类或者类的上面使用@Service("resource右边的name或者接口或者类的首字母小写")

 

<!-- 把标记了@Controller等注解的类转换为bean -->
<context:component-scan base-package="com.XXX" />

XML中也不需要配置controller的bean

 

这就是spring中依赖注入的思想,通过添加自动扫描包和对应类上添加注解,那么对象的创建和依赖都由spring去帮助我们完成。

 

1.bean的懒加载,个人理解

1.通过自动扫包,注解方式配置bean,不用繁琐的在xml文件中进行配置很多bean,
这种bean当tomcat启动项目的时候,bean对象是null,当我调用相应方法时,
它会首先被实例化然后被调用,如下面只有当我我调用selectpage方法时,handle通过afterPropertiesSet方法调用子类的
的initService方法被实例化,然后handle对象不再为空


protected abstract void initService();


@Override
public void afterPropertiesSet() throws Exception {
initService();
System.out.println("初始化handle"+handle);
if (handle == null) {
logger.info("Generic handle is not set. Please set it in the initService method if handle access is desired!");
}


}




public SearchCriteria<T> selectPage(SearchCriteria<T> sc) {
return handle.selectPage(sc);
}




2.xml中配置bean,这种bean当tomcat启动项目的时候,bean对象就被实例化放入到一个容器中,当我调用相应方法时,
它会被直接取出来,如上面当我我调用selectpage方法时,不需要再进行afterPropertiesSet方法调用子类的
的initService方法对handle进行实例化,然后handle对象不会为空。

<think>我们面对的问题是如何为使用了`@Autowired`注入的`privateMap<String,WorkFlowCheckUserAllService>workflowService`成员变量编写单元测试,特别是要覆盖`workflowService.get("1")`的调用场景。根据引用[4],我们知道Spring支持将接口的多个实现类注入到一个Map中,其中key为bean的名字(默认是类名首字母小写,或者通过`@Component`、`@Service`等注解的value属性指定),value为对应的实现类实例。在单元测试中,我们需要模拟这个Map的行为。步骤:1.使用Mockito框架来模拟依赖。2.在测试类中,我们需要手动创建或模拟`WorkFlowCheckUserAllService`接口的不同实现类的实例(或者模拟这些实例),并将它们放入一个Map中,然后将这个Map设置到被测试类的`workflowService`字段中。3.然后调用被测试类的方法,该方法内部会调用`workflowService.get("1")`,我们通过预先设置Map中的key为"1"的条目来模拟这个行为,并验证其调用。具体操作如下:假设我们有如下的被测试类:```java@ServicepublicclassMyService{@AutowiredprivateMap<String,WorkFlowCheckUserAllService>workflowService;publicvoiddoSomething(Stringkey){WorkFlowCheckUserAllServiceservice=workflowService.get(key);if(service!=null){service.execute();//假设接口有一个execute方法}}}```我们想要测试`doSomething`方法,特别是当传入的key为"1"时的情况。单元测试类可以这样写(使用JUnit5Mockito):```javaimportorg.junit.jupiter.api.Test;importorg.junit.jupiter.api.extension.ExtendWith;importorg.mockito.InjectMocks;importorg.mockito.Mock;importorg.mockito.junit.jupiter.MockitoExtension;importjava.util.HashMap;importjava.util.Map;importstaticorg.mockito.Mockito.verify;@ExtendWith(MockitoExtension.class)classMyServiceTest{@MockprivateWorkFlowCheckUserAllServicemockService1;//模拟key为"1"的服务@InjectMocksprivateMyServicemyService;//被测试的类实例@TestvoidwhenKeyIs1_thenCallService1(){//手动设置workflowService这个Map,因为@InjectMocks不会自动注入MapMap<String,WorkFlowCheckUserAllService>mockMap=newHashMap<>();mockMap.put("1",mockService1);//使用反射将mockMap设置到myService的workflowService字段中ReflectionTestUtils.setField(myService,"workflowService",mockMap);//调用被测试方法myService.doSomething("1");//验证mockService1的execute方法被调用verify(mockService1).execute();}}```注意:这里使用了Spring的`ReflectionTestUtils`来设置私有字段。如果你使用的是Spring的测试框架,那么它已经包含在内。否则,需要添加依赖。另一种方式是在测试类中不使用`@InjectMocks`,而是直接创建被测试类的实例,并手动设置其字段:```java@TestvoidwhenKeyIs1_thenCallService1(){MyServicemyService=newMyService();Map<String,WorkFlowCheckUserAllService>mockMap=newHashMap<>();mockMap.put("1",mockService1);myService.setWorkflowService(mockMap);//需要提供setter方法,或者继续使用反射myService.doSomething("1");verify(mockService1).execute();}```如果不想提供setter方法,可以继续使用反射工具。但是,通常我们更倾向于使用setter方法或者构造器来注入依赖,这样便于测试。因此,如果条件允许,可以考虑为`workflowService`提供一个setter方法,或者使用构造器注入(推荐)而不是字段注入。如果使用构造器注入:```java@ServicepublicclassMyService{privatefinalMap<String,WorkFlowCheckUserAllService>workflowService;publicMyService(Map<String,WorkFlowCheckUserAllService>workflowService){this.workflowService=workflowService;}//...其他方法}```这样在测试中就可以直接通过构造器注入模拟的Map:```java@TestvoidwhenKeyIs1_thenCallService1(){Map<String,WorkFlowCheckUserAllService>mockMap=newHashMap<>();mockMap.put("1",mockService1);MyServicemyService=newMyService(mockMap);myService.doSomething("1");verify(mockService1).execute();}```这样就不需要反射工具了,而且代码更清晰。总结:为了测试`@Autowired`注入的Map,我们可以在测试中手动构建一个Map,并设置到被测试类中(通过反射或setter或构造器)。然后模拟Map中特定key对应的服务,并验证调用。相关问题:1.在Spring中,如何为接口的多个实现类注入到Map编写集成测试?2.除了使用Map注入,Spring还有哪些方式可以注入一个接口的多个实现类?3.使用构造器注入字段注入在单元测试中有什么优缺点?4.如何在没有setter方法且使用字段注入的情况下,通过反射工具设置私有字段的值?</think>### 如何为`@Autowired`注入的Map成员变量编写单元测试 针对您的问题,以下是编写单元测试的完整方案,重点覆盖`workflowService.get("1")`场景: #### 1. 测试环境搭建(使用JUnit + Mockito) ```java @ExtendWith(MockitoExtension.class) class WorkflowServiceTest { @InjectMocks private YourTargetClass target; // 包含@Autowired Map的类 @Mock private WorkFlowCheckUserAllService service1; // key="1"的实现 @Mock private WorkFlowCheckUserAllService service2; // 其他实现 private Map<String, WorkFlowCheckUserAllService> serviceMap; @BeforeEach void setUp() { serviceMap = new HashMap<>(); serviceMap.put("1", service1); serviceMap.put("otherKey", service2); // 通过反射注入模拟Map ReflectionTestUtils.setField(target, "workflowService", serviceMap); } } ``` #### 2. 测试`workflowService.get("1")`的调用 ```java @Test void whenGetServiceByKey1_thenCorrectServiceReturned() { // 调用目标方法(内部使用workflowService.get("1")) target.executeWorkflow("1"); // 验证具体实现类的方法调用 verify(service1).specificMethod(any()); // 验证接口方法调用 } ``` #### 3. 覆盖边界场景的测试 ```java @Test void whenKeyNotExists_thenHandleGracefully() { // 调用不存在key target.executeWorkflow("invalidKey"); // 验证未调用具体服务 verify(service1, never()).specificMethod(any()); verify(service2, never()).specificMethod(any()); // 可验证异常处理或默认行为 } @Test void whenKeyIsNull_thenHandleNullPointer() { assertThrows(NullPointerException.class, () -> target.executeWorkflow(null)); } ``` #### 4. 关键实现说明 1. **反射注入Map** 使用`ReflectionTestUtils.setField()`直接注入模拟的Map,避免Spring容器依赖 2. **接口实现类模拟** ```java @Mock private WorkFlowCheckUserAllService service1; ``` 为每个需要测试的实现类创建Mock对象 3. **验证特定方法调用** ```java verify(service1).checkUser(any()); // 验证接口方法调用 ``` 精确验证被调用服务的业务方法 4. **覆盖异常场景** ```java when(service1.specificMethod(any())).thenThrow(new RuntimeException()); ``` #### 5. 完整测试示例 ```java @Test void testWorkflowServiceGet1Scenario() { // 准备测试数据 User user = new User("testUser"); // 模拟service1的行为 when(service1.checkUser(user)).thenReturn(true); // 执行测试方法 boolean result = target.processWorkflow("1", user); // 验证结果调用链 assertTrue(result); verify(service1).checkUser(user); verify(service2, never()).checkUser(any()); } ``` ### 最佳实践建议 1. **避免真实Spring容器** 单元测试中应避免使用`@SpringBootTest`,用模拟代替真实依赖注入[^4] 2. **使用构造器注入** 重构为构造器注入更易测试: ```java public class YourTargetClass { private final Map<String, WorkFlowCheckUserAllService> workflowService; public YourTargetClass(Map<String, WorkFlowCheckUserAllService> workflowService) { this.workflowService = workflowService; } } ``` 3. **接口命名规范** 确保实现类有明确的`@Component`命名: ```java @Component("serviceImpl1") public class WorkflowServiceImpl1 implements WorkFlowCheckUserAllService {...} ``` ### 相关问题 1. 如何测试Spring中一个接口多个实现类的注入场景? 2. 使用Mockito测试Map注入时有哪些常见陷阱? 3. 如何为`@Autowired`注入的List类型成员编写单元测试? 4. Spring框架中构造器注入字段注入在可测试性上有何区别? 5. 当Map中的实现类有交叉依赖时如何设计单元测试? [^1]: 通过反射工具可实现私有字段的依赖注入 [^4]: 策略模式实现的接口多实现类适合用Map注入测试
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值