为什么使用弹簧构造函数依赖注入而不是@Autowired
#java #springboot #dependencyinjection

自动

我见过的大多数春季开发人员仍在使用@Autowired语法将依赖项注入他们的类。不再推荐这一点,当您尝试使用它时,Intellij甚至会给您警告。
Image description

今天,我们将讨论为什么不以这种方式使用依赖注入的原因。我还曾在此事件之前也看到过这一事件。在场上@AUTOWIEL可以使该场可能具有可能的变异性和难以测试。将注入的字段变为可变可能会干扰通过使用CGLIB库来处理这些注入类别的弹簧代理。

设置DTO

在测试项目中,我们正在设置一个应该向用户进行会话的DTO。因此,每个用户只能查看bean的实例与他们的会话有关。

userdatadto.java类:

import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.SessionScope;

@Component
@SessionScope
public class UserDataDto {
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

设置春季安全配置

我们还设置了一些春季安全性,以初始化我们在此处测试的两个用户实例。

SecurityConfig.class:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user1 = User.withUsername("user1")
                .password(passwordEncoder.encode("password"))
                .roles("ADMIN")
                .build();
        UserDetails user2 = User.withUsername("user2")
                .password(passwordEncoder.encode("password"))
                .roles("ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user1, user2);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .httpBasic()
                .and()
                .authorizeRequests()
                .antMatchers("/**")
                .hasRole("ADMIN")
                .anyRequest()
                .authenticated();
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
}

设置控制器类

最后是控制器类。
usercontroller.class:

@RestController("/")
public class UserController {
    @Autowired
    UserDataDto userDataDto;

    @PostMapping("/saveUserName")
    public void saveUserName(@RequestBody String userName){
        userDataDto.setUserName(userName);
    }

    @PostMapping("/saveUserNameReplaceInstance")
    public void saveUserNameReplaceInstance(@RequestBody String userName){
        UserDataDto userDataDto = new UserDataDto();
        userDataDto.setUserName(userName);
        this.userDataDto = userDataDto;
    }

    @GetMapping("/getUserName")
    public String getUserName(){
        return userDataDto.getUserName();
    }
}

测试预期效果

好吧,现在我们运行春季启动应用程序并开始测试!
我将使用Postman使用User1 < /p>发布到 /saveUsername端点。

Image description

Image description
现在,使用一个单独的Chrome隐身窗口尝试使用User2从 /getUsername端点获得。什么都没有显示,好!

Image description

Image description

覆盖会话范围bean进入辛格尔顿

现在让我们有意地引起新的端点 /saveusernamereplaceinstance,该问题将替换自动bean。

    @PostMapping("/saveUserNameReplaceInstance")
    public void saveUserNameReplaceInstance(@RequestBody String userName){
        UserDataDto userDataDto = new UserDataDto();
        userDataDto.setUserName(userName);
        this.userDataDto = userDataDto;
    }

这次我们将使用user1

的新端点通过邮递员发送相同的东西

Image description
在使用User2的Chrome中,我现在看到User1的数据!

Image description

哦,不!这里的大问题,我们从本质上将一个sessionscope bean变成了所有实例之间共享的单身人士!

构造函数注入

现在如何防止构造函数注入? Intellij将在单击@Autowired注释时自动重构自动代码,在Windows上点击Alt-Enter,然后单击Create constructor。

Image description

但是为了方便起见,我在另一堂课上做到了。
constructorInctionController.java:

@RestController
public class ConstructorInjectionController {
    final
    UserDataDto userDataDto;

    public ConstructorInjectionController(UserDataDto userDataDto) {
        this.userDataDto = userDataDto;
    }

    @PostMapping("/saveUserNameCi")
    public void saveUserName(@RequestBody String userName){
        userDataDto.setUserName(userName);
    }

    @PostMapping("/saveUserNameReplaceInstanceCi")
    public void saveUserNameReplaceInstance(@RequestBody String userName){
        UserDataDto userDataDto = new UserDataDto();
        userDataDto.setUserName(userName);
        //this.userDataDto = userDataDto;
    }

    @GetMapping("/getUserNameCi")
    public String getUserName(){
        return userDataDto.getUserName();
    }
}

尝试使用映射的saveusernamereplaceinstanceci绘制线路,而IDE将阻止您造成巨大的错误。这使您可以将注入的字段声明为最终和在控制器类实例化后不可变的字段。

Image description

一如既往的代码在Github