如何优雅的在普通类中使用Bean?

762 阅读3分钟

src=http___img.it610.com_image_product_eb2b2217af864820902cf7a476e6218d.jpg&refer=http___img.it610.webp
本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

       经过对上一篇文章《怎样在普通类中调用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方法。

后言

       既然看到这里了,感觉有所收获的朋友,不妨来个大大的点赞吧~~~