老项目迁移问题:@ImportResource导入的xml配置里的Bean能够使用@PropertySource导入的属性值吗?【享学Spring】(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 老项目迁移问题:@ImportResource导入的xml配置里的Bean能够使用@PropertySource导入的属性值吗?【享学Spring】(上)

前言


写这篇文章的原动力是由于昨晚深夜一个小伙伴咨询我的一个问题(这位小伙伴这么晚了还在折腾,也是给个大大的赞),涉及到了如题方面的知识。

当然促使我书写本文最重要原因的是:这种从传统Spring项目向SpringBoot迁移进阶的case,我个人认为在现阶段的环境下还是有较大概率出现的,因此推荐收藏本文,对你后续或许有所帮助~


情景描述


为了更直观的说明问题所在,截图部分聊天记录如下:


image.png


这位小伙伴描述的问题还是蛮清晰,所以我还是很愿意跟他一起探讨的~


勾起兴趣还有一个原因:Spring对占位符提供了非常强大的支持,但基本上新手都还不能好好利用它和利用好它,更区分不清使用的规范和区别,本文也希望做点努力,能够起到稍微一点的作用~

对此部分内容若需要热场,推荐可以先浏览一下这篇文章:【小家Spring】Spring中@PropertySource和@ImportResource的区别,以及各自的实现原理解析 可以看到,早在我这篇文章里我就说了这么一句话:

image.png


而刚好这个小伙伴的场景(其实我自己还并没有遇到过此场景),就类属于老项目到SpringBoot新项目的一个迁移case,这时不结合分析,更待何时呢。


看到聊天记录,部分小伙伴可能会想:把Bean拿出来配置不就行了吗?或者key就写在原来的属性文件里呗?

其实对于职场老兵都能对此种现象给与理解和接受,毕竟有种叫理想化,有种叫是叫实际化~


因为我不可能贴出该小伙伴的源码(毕竟人家是生产环境的代码,咋可能贴出给大伙看呢?)so,接下来旨在说明这个问题,我就只好采用我的模拟大法喽:


传统Spring工程下使用


本处以一个传统的Spring工程为例,模拟这种使用case。classpath下有如下两个文件:

spring-beans.xml:


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


可以看到此xml配置Bean中使用了占位符:${diy.name}来引用下面属性文件的属性值~


my.properties:

diy.name = fsx-fsx

使用@ImportResource和@PropertySource分别把它哥俩导入:


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


运行如下测试用例:


@RunWith(SpringJUnit4ClassRunner.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"));
    }
}


打印结果为:


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


从结果中可以至少知道如下两点:

  1. 环境变量里是存在diy.name这个属性k-v的。并且此处我附上截图如下:

image.png


   2.xml中的占位符并没有被解析


若你对技术有敏感性的话,你会疑问为何占位符没被解析但并没有报错呢?

这个问题我在这篇文章:【小家Spring】Spring中@Value注解有多强大?从原理层面去剖析为何它有如此大的“能耐“ 里有过解释,有兴趣的可以点开看看(没兴趣的可以略过)


存在但又没被解析,看似有点矛盾,难道Spring工程不支持这么用,作为职场老兵的你,答案肯定是否定的,那如何破呢?

其实解决起来非常简单,我们只需要配置上一个PropertyResourceConfigurer即可:

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


再次运行,打印如下:

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

完美~


关于xml配置Bean处理占位符问题,为了加深理解,亦可参考:【小家Spring】Spring IoC是如何使用BeanWrapper和Java内省结合起来给Bean属性赋值的


我想说:此处介绍的是注解版怎么处理占位符问题,若你仍旧是传统的xml配置项目,至于具体使用哪个标签,小伙伴自行寻找咯~


我们知道PropertyResourceConfigurer它是个抽象类,它的三大实现子类除了上例使用的,还有其余两大实现类:PropertyOverrideConfigurer和PropertyPlaceholderConfigurer,若注册它哥俩可行吗??? 行不行试试呗

使用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的占位符已被成功解析)。从我的这个截图中也能看出来环境里已经没它了:


image.png


至于这深处到底是什么原因,有兴趣的可以轻点这里:【小家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加载进去才行,否则是读取不到滴~



相关文章
|
19天前
|
安全 Java API
深入解析 Spring Security 配置中的 CSRF 启用与 requestMatchers 报错问题
本文深入解析了Spring Security配置中CSRF启用与`requestMatchers`报错的常见问题。针对CSRF,指出默认已启用,无需调用`enable()`,只需移除`disable()`即可恢复。对于`requestMatchers`多路径匹配报错,分析了Spring Security 6.x中方法签名的变化,并提供了三种解决方案:分次调用、自定义匹配器及降级使用`antMatchers()`。最后提醒开发者关注版本兼容性,确保升级平稳过渡。
88 2
|
19天前
|
前端开发 安全 Java
Spring Boot 便利店销售系统项目分包设计解析
本文深入解析了基于Spring Boot的便利店销售系统分包设计,通过清晰的分层架构(表现层、业务逻辑层、数据访问层等)和模块化设计,提升了代码的可维护性、复用性和扩展性。具体分包结构包括`controller`、`service`、`repository`、`entity`、`dto`、`config`和`util`等模块,职责分明,便于团队协作与功能迭代。该设计为复杂企业级应用开发提供了实践参考。
52 0
|
1月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
101 0
|
19天前
|
SQL 前端开发 Java
深入理解 Spring Boot 项目中的分页与排序功能
本文深入讲解了在Spring Boot项目中实现分页与排序功能的完整流程。通过实际案例,从Service层接口设计到Mapper层SQL动态生成,再到Controller层参数传递及前端页面交互,逐一剖析每个环节的核心逻辑与实现细节。重点包括分页计算、排序参数校验、动态SQL处理以及前后端联动,确保数据展示高效且安全。适合希望掌握分页排序实现原理的开发者参考学习。
45 4
|
21天前
|
Java Spring 容器
两种Spring Boot 项目启动自动执行方法的实现方式
在Spring Boot项目启动后执行特定代码的实际应用场景中,可通过实现`ApplicationRunner`或`CommandLineRunner`接口完成初始化操作,如系统常量或配置加载。两者均支持通过`@Order`注解控制执行顺序,值越小优先级越高。区别在于参数接收方式:`CommandLineRunner`使用字符串数组,而`ApplicationRunner`采用`ApplicationArguments`对象。注意,`@Order`仅影响Bean执行顺序,不影响加载顺序。
|
19天前
|
安全 前端开发 Java
Spring Boot 项目中触发 Circular View Path 错误的原理与解决方案
在Spring Boot开发中,**Circular View Path**错误常因视图解析与Controller路径重名引发。当视图名称(如`login`)与请求路径相同,Spring MVC无法区分,导致无限循环调用。解决方法包括:1) 明确指定视图路径,避免重名;2) 将视图文件移至子目录;3) 确保Spring Security配置与Controller路径一致。通过合理设定视图和路径,可有效避免该问题,确保系统稳定运行。
67 0
|
1月前
|
Java 数据库连接 数据库
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——MyBatis 介绍和配置
本文介绍了Spring Boot集成MyBatis的方法,重点讲解基于注解的方式。首先简述MyBatis作为持久层框架的特点,接着说明集成时的依赖导入,包括`mybatis-spring-boot-starter`和MySQL连接器。随后详细展示了`properties.yml`配置文件的内容,涵盖数据库连接、驼峰命名规范及Mapper文件路径等关键设置,帮助开发者快速上手Spring Boot与MyBatis的整合开发。
118 0
|
1月前
|
缓存 Java 应用服务中间件
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——依赖导入和Thymeleaf相关配置
在Spring Boot中使用Thymeleaf模板,需引入依赖`spring-boot-starter-thymeleaf`,并在HTML页面标签中声明`xmlns:th=&quot;http://www.thymeleaf.org&quot;`。此外,Thymeleaf默认开启页面缓存,开发时建议关闭缓存以实时查看更新效果,配置方式为`spring.thymeleaf.cache: false`。这可避免因缓存导致页面未及时刷新的问题。
50 0
|
10月前
|
Java 开发者 Spring
解析Spring中Bean的生命周期
解析Spring中Bean的生命周期
89 2
|
10月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
118 0
下一篇
oss创建bucket