[Spring] old small family project migration issues: xml configuration @ImportResource introduced in the Bean can make

Each one

Masters are paranoid, paranoia can produce power, compromise is no power. You compromise to the world you're the air. So if there is no prejudice, but where's the master of it

Related Reading

[Spring] small home Detailed load and use PropertyPlaceholderConfigurer, PropertyOverrideConfigurer and other attributes of the profile Properties of
[small] Spring home Spring difference in @PropertySource and @ImportResource, as well as their resolve to achieve the principle of

[Spring] Spring in a small house @Value annotation How strong? In principle level to analyze why it has so much "ability"


<center> interested Spring scannable code added wx group: Java高工、架构师3群(the end the two-dimensional code) </ center>


Foreword

The driving force behind writing this article is because late last night a little consult my partner a question (the junior partner so late and still toss, but also give a big praise), such as knowledge related to the problem area.
Of course, prompted me to write this article is the most important reason: This traditional Springitems to SpringBootmigrate advanced case, I personally think that at this stage of the environment or have a greater probability of appearing, it is recommended that the collection of this article, you may have follow-up the help ~

Scenario Description

For more intuitive description of the problem, part chat screenshot as follows:
Here Insert Picture Description
The problem described is still pretty small partner clear, so I am willing to explore together with him -

Arouse interest for another reason: Springto placeholder provides a very strong support, but basically novice still can not take advantage of it and make good use of it, but can not tell the difference between area codes and use, the paper also hopes to make an effort , can play a role - a little bit of
this part, if necessary 热场, recommend may want to read this article: [Spring] difference between a small home in Spring @PropertySource and @ImportResource, as well as their resolve to achieve the principle can be seen, early in my article where I said such a sentence:
Here Insert Picture Description
and just this little scene partner (in fact, I still have not encountered this scene), it belongs to the class 老项目to SpringBoot新项目a migration case, then is not binding assay now, when yet.

See chat history, some small partners might think: the Bean out configuration is not on line yet? Or write the key attributes of the original file chant?
In fact, for workplace veterans can give understanding and acceptance of this phenomenon, after all, he called a kind of idealistic , kind called is called the actual of ~




Because I can not put the junior partner of the source (? After all, people are a production environment code, ye may be posted for everyone to see it) so, the following is intended to illustrate this problem, I had to use my analog Dafa myself:

Using the traditional Spring project

This imposed a traditional Spring project, for example, to simulate this use case. classpathThe following two documents:
Spring-the beans.xml:

<bean id="myPerson" class="com.fsx.bean.Person">
    <property name="name" value="${diy.name}"/>
    <property name="age" value="18"/>
</bean>

This can be seen that use xml configuration Bean placeholders: ${diy.name}to reference the file attribute property value below ~

my.properties:

diy.name = fsx-fsx

Use @ImportResourceand @PropertySourcewere introduced into it brothers:

@ImportResource(locations = "classpath:spring-beans.xml")
@PropertySource("classpath:my.properties")
@Configuration
public class RootConfig {
}

Run the following test:

@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration(classes = {RootConfig.class})
public class TestSpringBean {

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private Environment environment;

    @Test
    public void test1() {
        Person bean = (Person) applicationContext.getBean("myPerson");
        System.out.println(bean);

        System.out.println(environment.getProperty("diy.name"));
    }

}

Print results:

Person{name='${diy.name}', age=18}
fsx-fsx

From the results it can be known at least the following two points:

  1. Environment variable is the presence of diy.namethis property kv of. And here I am attaching the screenshot below:
    Here Insert Picture Description
  2. xml placeholder not be parsed

If you have a sensitivity to technology, you may wonder why the placeholder has not been resolved yet and there is no error it?
The problem I have in this article: [Spring] Spring in a small home @Value annotation How strong? In principle level to analyze why it has so much "ability" where there have been explained, are interested can point to open to see (not interested can skip)

But there is not any to be resolved, it seems a bit contradictory, does not support such a project with Spring as a career veteran of you, the answer is definitely no, then how to break it?
In fact, solving very simple, we only need to configure on a PropertyResourceConfigurercan:

    @Bean
    public PropertyResourceConfigurer propertyResourceConfigurer() {
        PropertyResourceConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        // 使用的PropertySourcesPlaceholderConfigurer,不用自己再手动指定亦可处理占位符~~~
        // configurer.setLocation(new ClassPathResource("my.properties")); // 加载指定的属性文件
        return configurer;
    }

Run again, is printed as follows:

Person{name='fsx-fsx', age=18}
fsx-fsx

Perfect ~

About xml configuration Bean deal with placeholders issues, in order to deepen understanding, can also refer to: [Spring] Spring IoC small home is how to use BeanWrapper and Java Bean introspection combine to property assignment

I want to say: annotated version presented here is how to deal with the problem placeholder, if you are still traditional xml configuration items, as to which specific label use, small partners to find yourself slightly ~

We know that PropertyResourceConfigurerit is an abstract class, its implementation subclasses in addition to the three cases of use, as well as the remaining two implementation classes: PropertyOverrideConfigurerand PropertyPlaceholderConfigurer, if it registered two brothers feasible? ? ? Okay try chanting

使用PropertyOverrideConfigurer

PropertyOverrideConfigurer 利用属性文件的相关信息,覆盖XML 配置文件中Bean定义。它要求配置的属性文件第一个.前面是beanName来匹配,所以这个子类我看都不用看,它肯定不行(因为它改变了k-v的结构)。

其实上面说配置PropertyResourceConfigurer的实现类来处理是不太准确的。
准确的说应该是配置PlaceholderConfigurerSupport的实现子类来处理Placeholder占位符更精确,特此纠正哈~

使用PropertyPlaceholderConfigurer

其实大多数小伙伴对PropertyPlaceholderConfigurer比对PropertySourcesPlaceholderConfigurer熟悉多了,毕竟前者的年纪可大多了~
它哥俩都是PlaceholderConfigurerSupport的实现子类有能力能够处理占位符
PropertySourcesPlaceholderConfigurer是Spring 3.1后提供的,希望用来取代PropertyPlaceholderConfigurer

    @Bean
    public PropertyResourceConfigurer propertyResourceConfigurer() {
        PropertyResourceConfigurer configurer = new PropertyPlaceholderConfigurer();
        //configurer.setLocation(new ClassPathResource("my.properties")); // 加载指定的属性文件
        return configurer;
    }

运行上面用例就报错了:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'diy.name' in value "${diy.name}"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172)
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124)

what?看打印结果,明明environment.getProperty("diy.name")从环境里能拿到这个key呀,怎么会报错呢???
这就是为何Spring3.1要提供一个PropertySourcesPlaceholderConfigurer旨在想代替掉此类的原因了。

但是,但是,但是把上配置中注掉的那行代码放开(也就是说自己设置location从而把属性文件加载进来),就能正常work了。
关于使用这种方式我还有必要再说明一点:若自己设置了location加载属性文件,@PropertySource("classpath:my.properties")这句代码对此种场景就没有必要了,xml配置的占位符也是能够读取到的。但是但是但是,若注掉这句@PropertySource...,此时运行输出如下:

Person{name='fsx-fsx', age=18}
null

会发现environment.getProperty("diy.name")为null,也就是说该属性值并不会存在应用的环境内了(但是xml的占位符已被成功解析)。从我的这个截图中也能看出来环境里已经没它了:
Here Insert Picture Description
至于这深处到底是什么原因,有兴趣的可以轻点这里:【小家Spring】详解PropertyPlaceholderConfigurer、PropertyOverrideConfigurer等对属性配置文件Properties的加载和使用

==只new一个PropertyPlaceholderConfigurer报错原因分析:==
其实从源代码处一眼就能看出来原因:

public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport {
    ...
    // 是否能被解析到值,重点在于入参的这个Properties props是否有这个key,而这个参数需要追溯它的父类~
    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
        StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
        doProcessProperties(beanFactoryToProcess, valueResolver);
    }
    ...
}

// 从父类中看看props的传值是啥~~~
public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport implements BeanFactoryPostProcessor, PriorityOrdered {
    ...
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            Properties mergedProps = mergeProperties();
            // Convert the merged properties, if necessary.
            convertProperties(mergedProps);
            // Let the subclass process the properties.
            // 抽象方法,交给子类~~~这里传入的mergedProps,全部来自location~~~
            processProperties(beanFactory, mergedProps);
        } catch (IOException ex) {
            throw new BeanInitializationException("Could not load properties", ex);
        }
    }

    protected Properties mergeProperties() throws IOException {
        ...
        loadProperties(result);
        ...
    }
    // 从配置里的location里把属性值都读出来~~~~~
    protected void loadProperties(Properties props) throws IOException {
        if (this.locations != null) {
            for (Resource location : this.locations) {
                ...
                PropertiesLoaderUtils.fillProperties(props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
                ...
            }
        }
    }
    ...
}

由此可见,若上述@Bean配置使用的是PropertyPlaceholderConfigurer,那必须手动的把属性文件设置location加载进去才行,否则是读取不到滴~

==那么问题来了,为何使用PropertySourcesPlaceholderConfigurer,只需要简单的new一个就成了勒(并不需要手动设置location)?==
一样的,从源码处一看便知,非常非常简单:

// @since 3.1  直接实现了EnvironmentAware,说明此Bean可以拿到当前环境Environment
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
    ...
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ...
        // 把环境属性都放进来~
        if (this.environment != null) {
            this.propertySources.addLast(
                new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
                    @Override
                    @Nullable
                    public String getProperty(String key) {
                        return this.source.getProperty(key);
                    }
                }
            );
        }
        // 把配置的属性放进来~~~
        PropertySource<?> localPropertySource = new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
        this.propertySources.addFirst(localPropertySource);
        ...
    }   
}

相信不用我做过多的解释,就知道为何不用自己设置location,直接使用注解@PropertySource("classpath:my.properties")就好使了吧。这个时候环境截图如下(注意:此处我截图是基于已经set了location的截图哦):
Here Insert Picture Description
what?虽然配置时候set了location去加载属性文件,但是上面代码中add进去的属性源environmentPropertieslocalProperties

public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties";
public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties";

两个PropertySource都并没有出现?
关于此,我这里就不再解释了,哈哈。还是那句话,留给小伙伴们自己思考,若思考不明白的亦可扫码入群探讨哦~(当然参照上面文章也是可行的)


SpringBoot工程下使用

关于在SpringBoot中使用,简单到令人发指,毕竟SpringBoot的使命也是让你使用它能够简单到木有朋友。
so,在SpringBoot工程下使用@ImportResource@PropertySource啥都不用配,它是能够天然的直接work的~

==原因分析如下:==
一切得益于SpringBoot强悍的自动化配置能力,它提供了这样一个配置类:

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
public class PropertyPlaceholderAutoConfiguration {

    // 注意此处使用的是PropertySourcesPlaceholderConfigurer
    // 并且你可以在本容器内覆盖它的默认行为哟~~~~
    @Bean
    @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

}

PropertyPlaceholderAutoConfiguration它被配置在了自动配置包下的spring.factories文件里:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
...

因此它会随着工程启动而自动生效。有了上面对Spring工程下的使用分析,此处就不用再花笔墨解释了~

另外附加说明一点:哪怕你的属性不使用@PropertySource导入,而是写在SB自带的application.properties文件里,依旧是没有问题的。
==原因分析如下:==
其实这个涉及到的是SpringBootapplication.properties的加载时机问题,因为本文对SB的介绍并不是重点,因此此处我直接简单的说出结论即止:

  • SpringBoot通过事件监听机制加载很多东西,加载此属性文件用的是ConfigFileApplicationListener这个监听器
  • 它监听到ApplicationEnvironmentPreparedEvent(环境准备好后)事件后开始加载application.properties等文件
  • ApplicationEnvironmentPreparedEvent的事件发出是发生在createApplicationContext()之前~~~ 部分代码如下:

    public class SpringApplication {
    ...
    public ConfigurableApplicationContext run(String... args) {
        ...
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 此步骤 会发出ApplicationEnvironmentPreparedEvent事件
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        Banner printedBanner = printBanner(environment);
    
        // 开始创建,初始化容器~
        context = createApplicationContext();
        ...
    }
    ...
    }

    so,在SB环境下已经早早把属性都放进环境内了,借助它默认配置好的PropertySourcesPlaceholderConfigurer来处理的,那可不能正常work吗。一切都是这么自然,这或许就是SB的魅力所在吧~

关于小伙伴问题的解决

开头提出了问题,肯定得解决问题嘛~~~如下图
Here Insert Picture Description
哈哈,虽然最终我并没有直接的帮助解决问题,但是此问题给了我写本文的动力,总体还是不错的~

总结

本文通过一个小伙伴咨询的小问题(真是小问题吗?)引申比较详细的说了Spring在处理占位符这块的内容(其实本并没打算写这么多的,尴尬~)
写本文的目的开头也说了,我认为在SpringBoot还并非100%***的当下,肯定有人会遇到从传统Spring项目向SpringBoot过度的一个阶段,而本文的描述或许能给你的迁移提供一种新的思路(特别是时间紧、任务重的时候),希望小伙伴们能有所收获,peace~

知识交流

若文章格式混乱,可点击原文链接-原文链接-原文链接-原文链接-原文链接

==The last:如果觉得本文对你有帮助,不妨点个赞呗。当然分享到你的朋友圈让更多小伙伴看到也是被作者本人许可的~==

If interested in technical content can be added wx ××× stream: Java高工、架构师3群.
If the group fails two-dimensional code, please add wx number: fsx641385712(or two-dimensional code is scanned beneath wx). And Note: "java入群"the word, will be invited into the group manually

[insert here the picture description] (! Https://img-blog.csdnimg.cn/20190703175020107.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Y2NDEzODU3MTI =, size_16, color_FFFFFF, t_70, pic_center = 300X)

Guess you like

Origin blog.51cto.com/3631118/2421289