项目中有个需求,根据传进来的参数判断,去调用哪个地址。
首先配置文件
xiahui:
oms:
interfaceAddress:
cw: 1
ck: 2
dc: 3
最初的想法是在静态代码块中,去读取配置文件,比如这样
@Configuration
public class InterfaceSendConfig {
public static volatile InterfaceSendConfig interfaceSendConfig;
public static InterfaceSendConfig getInstance(){
if (interfaceSendConfig==null) {
synchronized (InterfaceSendConfig.class){
if (interfaceSendConfig==null) {
interfaceSendConfig = new InterfaceSendConfig();
}
}
}
return interfaceSendConfig;
}
private static Map<String,String> MAP;
public static String cw;
public static String ck;
public static String dc;
@Value("${xiahui.oms.interfaceAddress.cw}")
public void setCw(String cw){
InterfaceSendConfig.cw = cw;
}
public static String getCw(){
return cw;
}
@Value("${xiahui.oms.interfaceAddress.ck}")
public void setCk(String ck){
InterfaceSendConfig.ck = ck;
}
public static String getCk(){
return ck;
}
@Value("${xiahui.oms.interfaceAddress.dc}")
public void setDc(String dc){
InterfaceSendConfig.dc = dc;
}
public static String getDc(){
return dc;
}
public static String getInterfaceAddress(String source){
return MAP.get(source);
}
static {
MAP = ImmutableMap.of(
OmsInterfaceSource.CW.getCode(),getCw(),
OmsInterfaceSource.CK.getCode(),getCk(),
OmsInterfaceSource.DC.getCode(),getDc()
);
}
}
但是运行的时候,会报这个错误
java.lang.IllegalStateException: Unable to load cache item
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:79)
at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:569)
at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:416)
at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:137)
at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:109)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:423)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:257)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:286)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:130)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
at com.xiahui.XiahuiMsInterfaceApp.main(XiahuiMsInterfaceApp.java:19)
Caused by: java.lang.ExceptionInInitializerError: null
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:571)
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)
at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:582)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108)
at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
... 20 common frames omitted
Caused by: java.lang.NullPointerException: null value in entry: CW=null
at com.google.common.collect.CollectPreconditions.checkEntryNotNull(CollectPreconditions.java:32)
at com.google.common.collect.ImmutableMap.entryOf(ImmutableMap.java:176)
at com.google.common.collect.ImmutableMap.of(ImmutableMap.java:142)
at com.xiahui.interfacebusiness.interfaceHander.InterfaceSendConfig.<clinit>(InterfaceSendConfig.java:70)
... 30 common frames omitted
Process finished with exit code 1
大家都知道,静态方法、变量和代码块,是初始化类的时候就会加载,这个时候还没有读到配置文件中的值,但是又在静态代码块中去初始化参数值,所以会报空指针的错误。
后来我想了下,那是不是不在最初的时候去创建map呢。改成了这样
@Configuration
public class InterfaceSendConfig {
public static volatile InterfaceSendConfig interfaceSendConfig;
public static InterfaceSendConfig getInstance(){
if (interfaceSendConfig==null) {
synchronized (InterfaceSendConfig.class){
if (interfaceSendConfig==null) {
interfaceSendConfig = new InterfaceSendConfig();
}
}
}
return interfaceSendConfig;
}
private static Map<String,String> MAP;
public static String cw;
public static String ck;
public static String dc;
@Value("${xiahui.oms.interfaceAddress.cw}")
public void setCw(String cw){
InterfaceSendConfig.cw = cw;
}
public static String getCw(){
return cw;
}
@Value("${xiahui.oms.interfaceAddress.ck}")
public void setCk(String ck){
InterfaceSendConfig.ck = ck;
}
public static String getCk(){
return ck;
}
@Value("${xiahui.oms.interfaceAddress.dc}")
public void setDc(String dc){
InterfaceSendConfig.dc = dc;
}
public static String getDc(){
return dc;
}
public String getInterfaceAddress(String source){
InterfaceSendConfig.getInstance().getMap();
return MAP.get(source);
}
public void getMap(){
MAP = ImmutableMap.of(
OmsInterfaceSource.CW.getCode(),getCw(),
OmsInterfaceSource.CK.getCode(),getCk(),
OmsInterfaceSource.DC.getCode(),getDc()
);
}
}
启动发现,这次没报错了啊,去测试的时候也可以获取到配置文件中的值。但是仔细看看又觉得不对,这样写不是请求接口的时候,都会去初始化一遍map吗,所以这么写也不合适。
突然想到@Bean注解的定义
@Bean:产生一个Bean对象,然后将Bean对象交给Spring管理,被注解的方法是会被AnnotationConfigApplicationContext或者AnnotationConfgWebApplicationContext扫描,用于构建bean定义,从而初始化Spring容器。产生这个对象的方法Spring只会调用一次,之后Spring就会将这个Bean对象放入自己的Ioc容器中。
那是不是说 可以在@Bean中,初始化我的Map,说干就干
@Configuration
public class InterfaceSendConfig{
public static volatile InterfaceSendConfig interfaceSendConfig;
public static InterfaceSendConfig getInstance(){
if (interfaceSendConfig==null) {
synchronized (InterfaceSendConfig.class){
if (interfaceSendConfig==null) {
interfaceSendConfig = new InterfaceSendConfig();
}
}
}
return interfaceSendConfig;
}
private static Map<String,String> MAP;
@Value("${xiahui.oms.interfaceAddress.cw}")
public String cw;
@Value("${xiahui.oms.interfaceAddress.ck}")
public String ck;
@Value("${xiahui.oms.interfaceAddress.dc}")
public String dc;
@Bean
public void init(){
System.out.println(cw);
System.out.println(ck);
System.out.println(dc);
MAP = ImmutableMap.of(
OmsInterfaceSource.CW.getCode(),cw,
OmsInterfaceSource.CK.getCode(),ck,
OmsInterfaceSource.DC.getCode(),dc
);
}
public String getInterfaceAddress(String source){
return MAP.get(source);
}
}
经测试发现 ,果然在项目加载的时候,就已经读取到了配置文件
有个延伸点,如果想在@Bean初始化之后,获取值怎么办?可以用实现CommandLineRunner接口
该接口中的Component会在所有Spring的Beans都初始化之后,在SpringApplication的run()之前执行。
多个类需要有顺序的初始化资源时,我们还可以通过类注解@Order(n)进行优先级控制。
至此结束。