Liuqichun's Blog

Spring|@Autowired、@Resource、@RequiredArgsConstructor 使用

Spring 的核心是 IoC 容器和 DI(Dependency Injection)思想,这些提供了 Java 对象的管理以及它们之间依赖的管理。Bean 的管理是 Spring 自动完成的,而我们只需要使用一些注解(Annotation)。

常用的是 @Resource 和 @Autowired,以及 Lombok 的构造器进行注入。

使用

注入前对象必须由 Spring 管理。通过 @Component、@Service、@Repository 标识,Spring Boot 会自动扫描并初始化 Bean。

@Autowired

1    @Autowired
2    private UserMapper userMapper;

如果 Bean 出现了重复,在项目中通常使用 @Qualifier 单独指定 Bean。注意:@Qualifier 不能单独使用,只能配合 @Autowired 使用。

1    @Autowired
2    @Qualifier(value = "userMapper")
3    private UserMapper userMapper;

@Autowired 可以放在成员变量(field)、setter、类构造器上。它只有一个属性 —— required,Boolean 类型,取值为 false 时不依赖 Bean,也就是说被注入的 field 可以为 null,否则当依赖 Bean 不存在时报错。

 1// Autowired 构造器注入
 2class UserServiceImpl{
 3    private UserMapper userMapper;
 4    private DeptMapper deptMapper;
 5
 6    @Autowired
 7    public UserServiceImpl(UserMapper userMapper, DeptMapper deptMapper){
 8        this.userMapper = userMapper;
 9        this.deptMapper = deptMapper;
10    }
11}
12
13// Autowired Setter 注入
14class UserServiceImpl{
15    private UserMapper userMapper;
16
17    @Autowired
18    public void setUserMapper(UserMapper userMapper){
19        this.userMapper = userMapper;
20    }
21}

@Resource

@Resource 是 Java 规范的一个注解,Spring 也支持它,因此在 Spring 项目中可以用它。

@Resource 默认按照名字注入 Bean,这个名字通常是类名(其第一个字母小写);它也可以通过类型匹配。通过哪种方式匹配取决于 name 和 type 两个属性,两个属性不同时出现;若同时出现则按默认规则处理。

当 @Resource 匹配不到 Bean 时就不再注入,此时 field 为 null。

1    // 按名字注入bean
2    @Resource(name = "userMapper")
3    private UserMapper userMapper;
4
5    // 按类型注入 Bean
6    @Resource(type = UserMapper.class)
7    private UserMapper userMapper;

在项目中 @Autowired 已经不推荐使用了,通常推荐使用 JSR-250 的 @Resource,这样可以减少对 Spring 的依赖,让代码更加规范统一。

另外,和 @Resource 相关的还有 @PostConstruct、@PreDestroy。

@Autowired 和 @Resource 的区别

@RequiredArgsConstructor 构造器注入

Lombok 的构造器注入需要使用 @RequiredArgsConstructor 注解,放在类上。

简单使用示例

1@RequiredArgsConstructor
2@Service
3public class UserServiceImpl{
4    private final UserMapper userMapper;
5}

注意事项

1@RequiredArgsConstructor
2public class UserServiceImpl{
3    // 注入
4    private final UserMapper userMapper;
5    // 不注入
6    private static String USER_SIGN = "admin"
7    // 不注入
8    private final static String USER_SIGN2 = "user"
9}

经常遇到的问题

常见 Spring 注入 Bean 为 null

  1. 首先排查 Bean 是否被扫描到。Spring Boot 项目中与启动类同级及以下的 Bean 才能被扫描到;引入 MyBatis 或 MyBatis-Plus 后,所有模块的 Mapper 都需要使用 @MapperScan 进行扫描,否则需要使用 XML 配置扫描位置(不推荐这种方法)。
  2. 如果 Bean 装配没有问题,那可能这个对象是 new 出来的,这时不会对 new 出来的对象中 @Autowired、@Resource 标识的字段进行注入。
  3. Bean 出现重复,这种情况控制台会报错,此时在注入时通过 @Qualifier 或 @Resource(name = “xxx”) 指明具体的 Bean 即可
  4. 另一种解决办法,通过 SpringContext 拿,方法如下
 1@Slf4j
 2@Configuration
 3public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
 4
 5    private static ApplicationContext applicationContext = null;
 6
 7    /**
 8     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
 9     */
10    @SuppressWarnings("unchecked")
11    public static <T> T getBean(String name) {
12        assertContextInjected();
13        return (T) applicationContext.getBean(name);
14    }
15
16    /**
17     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
18     */
19    public static <T> T getBean(Class<T> requiredType) {
20        assertContextInjected();
21        return applicationContext.getBean(requiredType);
22    }
23
24    @Override
25    public void destroy() {
26        SpringContextHolder.clearHolder();
27    }
28
29    @Override
30    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
31        if (SpringContextHolder.applicationContext != null) {
32            log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
33        }
34        SpringContextHolder.applicationContext = applicationContext;
35    }
36}

另一种注入对象为 null 的情况

在静态代码块/普通代码块中使用被注入对象,被注入的对象为 null。

这种情况代码:

 1@Component("functionExecutor")
 2public class FunctionExecutor {
 3
 4    @Resource(name = "whereGreaterThenFunction")
 5    private  WhereGreaterThenFunction whereGreaterThenFunction;
 6
 7    @Resource(name = "countFunction")
 8    private  CountFunction countFunction;
 9
10    // 静态/普通代码
11   {
12        AviatorEvaluator.addFunction(whereGreaterThenFunction);
13        AviatorEvaluator.addFunction(countFunction);
14    }
15}

这里注入不成功是因为,在初始化 FunctionExecutor 时,成员变量还没有被注入,此时对象为 null。

解决办法是使用 @PostConstruct,使用这个注解的方法会在 Bean 初始化完成后被调用,这时所有的成员变量都已经注入。

最佳实践

  1. 在使用 @Component、@Service、@Controller、@Repository 时通常不指定 Bean 的名字,除非有重名的
  2. 注入 Bean 时推荐使用 Lombok 的构造器注入,更加简洁
  3. 单独注入 Bean 推荐使用 @Resource 而不是 @Autowired

<< Previous Post

|

Next Post >>

#Spring #SpringMVC