问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

@Autowired注解在构造器上的使用规则

创作时间:
作者:
@小白创作中心

@Autowired注解在构造器上的使用规则

引用
CSDN
1.
https://blog.csdn.net/weixin_37477009/article/details/146285800

本文详细探讨了Spring框架中@Autowired注解在构造器上的使用规则,并通过代码示例详细解释了相关概念。文章内容涉及Spring框架的技术细节,具有一定的专业性和实用性。

背景

在Spring Framework官方文档中,有一段关于@Autowired注解在构造器上使用规则的描述:

As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean defines only one constructor to begin with. However, if several constructors are available and there is no primary/default constructor, at least one of the constructors must be annotated with @Autowired in order to instruct the container which one to use. See the discussion on constructor resolution for details.

为了更好地理解这段话,我们通过一些实践来验证这些规则。

实践

第一句

“As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean defines only one constructor to begin with.”

  • 含义:从 Spring Framework 4.3 版本开始,如果一个目标 Bean 只定义了一个构造器,那么在该构造器上标注 @Autowired 注解就不再是必须的。
@RestController
public class HelloController {
    private HelloService helloService;
    // 只有一个构造器,可以不写@Autowired
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }
    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}

第二句

“However, if several constructors are available and there is no primary/default constructor, at least one of the constructors must be annotated with @Autowired in order to instruct the container which one to use.”

  • 含义:然而,如果一个类有多个构造器,并且没有一个被标记为“主要构造器”或默认构造器,那么至少需要在一个构造器上标注 @Autowired,以便告诉 Spring 容器应该使用哪一个构造器。
@RestController
public class HelloController {
    private HelloService helloService;
//    @Autowired 不写@Autowired,helloSService会为null
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }
    public HelloController() {
    }
    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}
// Cannot invoke "com.forrest.learn.spring.autowired.example1.HelloService.sayHello()" because "this.helloService" is null
@RestController
public class HelloController {
    private HelloService helloService;
    @Autowired
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }
    public HelloController() {
    }
    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}

想法

在实际开发中,很少会采用构造器注入,因为写起来太麻烦了……

那我们需要搞清楚,为什么不推荐使用注入?

Why field injection is not recommended?

  • (1)不方便写单测(不赞同,挺方便的)
  • (2)不能被final修饰(确实有风险,但完全属于人为造成的风险……)
  • (3)字段注入隐藏了类的依赖项(不赞同,根据字段注入,也可以知道当前类依赖了哪些类)
  • (4)字段注入依赖于 Spring 框架在构建对象后填充依赖项。(下文解释)
  • (5)字段注入使类依赖于用于注入依赖项的框架。(不赞同,@Autowired是Spring的注解,确实依赖了框架,但我们可以用Java的@Resource进行依赖注入)

不能被final修饰(确实有风险,但helloService被恶意修改了,在运行时可以被测出来,因为运行结果不符合预期了)

@RestController
public class HelloController {
    @Autowired
    private HelloService helloService;
    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}
  • 由于helloService不是final的,因此有被修改的风险:
@RestController
public class HelloController {
    @Autowired
    private HelloService helloService;
    @Autowired
    private ApplicationContext applicationContext;
    @GetMapping("/hello")
    public String hello() {
        HelloController helloController = applicationContext.getBean(HelloController.class);
        Field field = null;
        try {
            field = HelloController.class.getDeclaredField("helloService");
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
        field.setAccessible(true);
        try {
            field.set(helloController, null);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return helloService.sayHello();
    }
}

helloService注入时不为null,但被修改成了null,导致了npe……

不方便写单测(挺方便的)

public class HelloControllerTest {
    private MockMvc mockMvc;
    @Mock
    private HelloService helloService;
    @InjectMocks
    private HelloController helloController;
    @BeforeEach
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(helloController).build();
    }
    @Test
    public void hello_WhenCalled_ReturnsHelloMessage() throws Exception {
        String expectedMessage = "Hello, World!";
        when(helloService.sayHello()).thenReturn(expectedMessage);
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(expectedMessage));
![](https://wy-static.wenxiaobai.com/chat-rag-image/3532264452953879054)
    }
}

字段注入依赖于 Spring 框架在构建对象后填充依赖项。

@Service
public class HelloServiceImpl implements HelloService {
    private final HelloRepository helloRepository;
//
//    public HelloServiceImpl() {
//
//    }
    @Autowired
    public HelloServiceImpl(HelloRepository helloRepository) {
        this.helloRepository = helloRepository;
        helloRepository.initialize();
    }
    @Override
    public String sayHello() {
        return "Hello World";
    }
}
  • 但完全可以先字段注入,然后通过@PostContruct再调用依赖的方法:

总结

通过@Autowired字段注入,并未有不可接受的副作用,而且代码简洁。

IDEA的警告挺烦的,怎么关了?

或者采用@Resource


© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号