懒汉单例工具类中注入Spring对象

PunkLu 2020年03月11日 222次浏览
懒汉单例工具类中注入Spring对象

需求

最近工作里有个需求,在技术上大概就是做一个工具类,这个工具类中可以调用一个接口的三个实现类的方法来根据传入的不同的文件名进行类似的处理。然后在定时任务中调用这个工具类的处理方法即可。实现是在这个工具类中声明一个Map对象,用来保存要处理的文件名与实现类的关系。如下所示

private final Map<String, InterfaceName<?>> zipFileHandleMap  = new ConcurrentHashMap<String, InterfaceName<?>>();

作为Spring容器

最开始的时候没有考虑清楚,还是将这个类作为Spring Bean处理,这个时候为了保证这个类在加载的时候,就将对应的三个被Spring管理的接口实现类注入上述的Map对象,就有两种方式。

Spring原生支持的@Autowired

Spring原生的@Autowired注解不仅可以注入对象,还可以将对象注入事先定义好的List、Map中,所以只需要在上述的Map上加入@Autowired注解即可。

@PostConstruct方式

除了使用Spring自带的@Autowired注解,还可以使用@PostConstruct注解来实现。

@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。

通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:

Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

所以使用这种方式,只需要在类中分别使用@Autowired注解引入三个接口的实现类,然后再定义一个初始化方法,在这个方法里把@Autowired注入的三个实现类放入上述的Map中,并在方法上加上@PostConstruct注解即可。

实现SpringContextAware接口

可以让这个类实现SpringContextAware接口,并实现void setApplicationContext(ApplicationContext applicationContext)方法。在方法内从方法传入的applicationContext中取得接口的三个实现类并放入上述声明的Map中。这样当Spring启动完成的时候,这个方法会被执行以完成此类中Map对象的初始化赋值。

作为工具类

后来考虑到这个类只需要被定时任务调用,没有必要交给Spring管理。可以将此类改造为单例懒汉类,并在构造方法中完成对Map初始化赋值的操作。如下代码所示:

private static class SingletonHolder {
        private static final RdiZipEngineer INSTANCE = new RdiZipEngineer();
    }

    public static final RdiZipEngineer getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private final Map<String, InterfaceName<?>> zipFileHandleMap  = new ConcurrentHashMap<String, InterfaceName<?>>();

    private RdiZipEngineer(){
        Map<String, InterfaceName> map = SpringContextHolder.getBeansOfType(InterfaceName.class);
        for(InterfaceName<?> rdi : map.values()){
        	// 将模板名称与对应的接口实现类(模板对应的文件处理类)初始化放入Map中
            zipFileHandleMap.put(rdi.getTemplate(), rdi);
        }

    }