在枚举类中注入bean

使用枚举类统一管理多个接口实现类

上次使用了模版模式,对策略的行为进行了约束。其中就抽象方法limitFilter()中要执行的方法进行了统一管理,因为这里的方法其实都是一些过滤器,他们的行为是类似的,所以我抽象了一个接口,让所有的过滤器实现这个接口:

1
2
3
public interface IRule {
void filter(List<String> list1, List<Integer> list2);
}

最简单的方法就是直接创建一个类,实现这个接口,目前的过滤器不多,只有4~5个,那还好说,如果后期实现的过滤器越来越多,实现类就越来越多,那就很不好管理了,于是我使用了枚举类统一管理所有的实现类:

1
2
3
4
5
6
7
8
9
10
11
12
public class Rules {
public enum Rule1 implements IRule{
INSTANCE;
@Override
public void filter(List<String> list1, List<Integer> list2) {}
}
public enum Rule2 implements IRule{
INSTANCE;
@Override
public void filter(List<String> list1, List<Integer> list2) {}
}
}

这样就可以把所有的实现类放在Rules类中了。

使用枚举类,在无形之中也使用了单例模式 4

使用静态内部类,为枚举类注入bean

一开始还没什么问题,后来我发现,如果枚举类中想使用@Autowired或@Resource注入对象时,它直接就空指针了,这是为啥啊?这个问题的原因见。在我查询了一些资料 1后有了思路,使用静态内部类注入对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public enum Rule1 implements IRule{
INSTANCE;

@Setter
private GaodeFeignClient gaodeClient;

// 在枚举类中注入bean
@Component
public static class EnumTypeInjector{
@Resource
private GaodeFeignClient gaodeClient;

@PostConstruct
private void postConstruct(){
INSTANCE.setGaodeClient(gaodeClient);
}
}
@Override
public void filter(List<String> list1, List<Integer> list2) {}
}

让我来解释一下这波操作。

Spring容器在扫描时,会扫描到被@Component注解的类EnumTypeInjectorEnumTypeInjector会被Spring容器认为是一个组件类Bean,Spring容器在启动时,就会将这个类实例化。在这个类中注入了gaodeClient,@PostConstruct注解的方法在Spring Bean的生命周期只会被执行一次,它调用枚举类对象INSTANCEsetGaodeClient()方法,将EnumTypeInjector内部类注入的gaodeClient对象赋值给了枚举类Rule1的类对象INSTANCE,使其完成了注入。

  1. 为什么使用静态内部类?使用普通内部类行不行?
    首先回答:使用普通内部类不行,必须使用静态内部类。因为普通内部类对象会持有一个指向外部类对象的隐式引用 3,这导致想要实例化内部类对象帮我们注入时,必须先有一个外部类对象,而现在的外部类是一个枚举类,枚举类对象是默认单例模式的,它无法由Spring容器管理。但是静态内部类是与外部类没有关联的,给静态内部类加上Spring注解,spring就可以将它实例化,并执行postConstruct()方法,将内部类中注入的bean对象set给外部的枚举类。

  2. 为什么静态内部类使用了@Component注解?
    使用@Component注解静态内部类,可以让Spring容器扫描到这个类,从而加载它,执行他的postConstruct()方法,从而完成注入。

  3. @PostConstruct的作用? 2
    官方文档

    如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么久无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
    @PostConstruct注解的方法将会在依赖注入完成后被自动调用。

为什么使用@Autowired无法为枚举类注入依赖

枚举类是天然的单例模式,它的构造器是私有的,就算给枚举类加上Spring的注解,Spring容器也无法管理枚举类对象,因为容器无法将它实例化。
枚举类对象是由JVM管理的。

Reference

1. java枚举类如何获得Spring管理的对象
2. @PostConstruct注解
3. 内部类与静态内部类
4. 单例模式