本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
经过对上一篇文章《怎样在普通类中调用Bean?》的学习,我们可以通过实现了ApplicationContextAware接口的SpringContextHolder来获取Spring容器内的Bean。但是,使用这种方法是有一定隐患的。
使用ApplicationContextAware的隐患
我们知道,在Spring框架中,Bean之间除非有明确的依赖关系,否则其加载顺序是随机的。例如:如果bean1的构造器或者其成员变量(使用@Autowired或者@Resource修饰的)中包含bean2,则bean1依赖于bean2,那么bean2的加载顺序肯定优先于bean1;但是,如果bean1和bean2之间没有任何关系,则没办法控制其加载顺序。
从《怎样在普通类中调用Bean?》的学习中,我们知道SpringContextHolder是加了@Component注解的,它也是在Spring容器加载的时候才能获取到ApplicationContext。如果这个时候,由于业务要求,我们需要在应用初始化的时候,去调用SpringContextHolder获取Bean,就有可能出现空指针错误。
public class UserUtils {
// 从容器中获取Bean
private static final UserService userService = SpringContextHolder.getBean("userService");
// 根据用户编号获取用户信息
public static User getUser(Long id){
return userService.findById(id);
}
}
假如我们有UserUtils类,可以根据用户编号获取用户信息,代码如上。如果我们要在引用初始化的时候,根据超级管理员的用户编号打印出超级管理员姓名,则有可能出现空指针,调用代码如下。
@Slf4j
@Component
public class ApplicationIniter {
// 应用初始化
@PostConstruct
public void init(){
User user = UserUtils.getUser(1L);
log.info("super admin is {}", user.getName());
}
}
解决办法
ApplicationContextAware接口的问题,可以通过指定依赖关系的方法来解决。
@Order并不能指定Bean加载顺序,它的主要作用是,当注入的类型是集合或者列表时,用来指定写入集合或者列表的顺序。
1、使用@DependsOn来解决(不推荐)
@DependsOn可以指定当前Bean依赖于哪个Bean,但是@DependsOn的参数是被依赖Bean的名称,如果被依赖Bean名称发生改变,则会导致异常。
2、将普通类也注册到Spring容器中
将普通类注册到Spring容器后,就可以直接使用Spring框架的注入方式来进行Bean注入了。话不多说,直接上代码。
@Component
public class UserUtils {
// 自身静态实例
private static UserUtils instance;
// 从容器中获取Bean
@Resource
private UserService userService;
// 初始化
@PostConstruct
public void init(){
UserUtils.instance = this;
}
// 根据用户编号获取用户信息
public static User getUser(Long id){
return instance.userService.findById(id);
}
}
通过上面的方式,我们即解决了Bean的加载顺序问题,也实现了静态调用getUser方法。
后言
既然看到这里了,感觉有所收获的朋友,不妨来个大大的点赞吧~~~