当前位置: 首页>后端>正文

Spring的info泄露 spring information


文章目录

  • 夯实Spring系列|第十五章:Spring 配置元信息
  • 本章说明
  • 1.项目环境
  • 2.Spring 配置元信息
  • 3.Spring Bean 配置元信息
  • 4.Spring Bean 属性元信息
  • 4.1 PropertyValues
  • 4.2 AttributeAccessor
  • 4.3 BeanMetadataElement
  • 5.Spring 容器配置元信息
  • 6.基于 XML 文件装载 Spring Bean 配置元信息
  • 7.基于 Properties 文件装载 Spring Bean 配置元信息
  • 8.基于 Java 注解装载 Spring Bean 配置元信息
  • 9.Spring Bean 配置元信息底层实现
  • 9.1 Spring XML 资源 BeanDefinition 解析与注册
  • 9.2 Spring Properties 资源 BeanDefintion 解析与注册
  • 9.3 Spring Java 注册 BeanDefinition 解析与注册
  • 10.基于 XML 文件装载 Spring IOC 容器配置元信息
  • 11.基于 Java 注解装载 Spring IOC 容器配置元信息
  • 11.1 Spring IoC 容器装配注解
  • 示例
  • 11.2 Spring IoC 配置属性注解
  • 示例
  • 12.基于 Extensible XML authoring 扩展 Spring XML 元素
  • 12.1 编写 XML Schema 文件
  • 12.2 自定义 NamespaceHandler 实现 & 自定义 BeanDefinitionParser 实现
  • 12.3 注册 XML 扩展
  • 12.4 示例
  • 13.Extensible XML authoring 扩展原理
  • 13.1 源码调试
  • 14.基于 Properties 文件装载外部化配置
  • 14.1 示例
  • 15.基于 YAML 文件装载外部化配置
  • 15.1 基于 XML 示例
  • 15.2 基于 Java 注解示例
  • 16.面试题
  • 16.1 Spring 内建 XML 有哪些?
  • 16.2 Spring 配置元信息具体有哪些?
  • 16.3 Extensible XML authoring 的缺点?
  • 17.参考


夯实Spring系列|第十五章:Spring 配置元信息

本章说明

本章将详细的介绍 Spring 中的各类配置元信息,比较重要或者是前面章节没有讨论过的内容,会有简单的演示代码;这部分内容虽然在国内讨论的热度不高,但实际上在很多分布式应用的源码中处处可见,包括 dubbo、nacos 都有用到其中相关的技术,简而言之,这一章是学习 Spring 相关分布式应用技术栈源码的基础。

1.项目环境

  • jdk 1.8
  • spring 5.2.2.RELEASE
  • github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
  • 本章模块:configuration-metadata

2.Spring 配置元信息

配置元信息

  • Spring Bean 配置元信息 - BeanDefinition
  • Spring Bean 属性元信息 - PropertiesValues
  • Spring 容器配置元信息
  • Spring 外部化配置元信息 - PropertySource
  • Spring Profile 元信息 - @Profile

3.Spring Bean 配置元信息

Bean 配置元信息 - BeanDefinition

  • GenericBeanDefinition : 通用性 BeanDefinition
  • RootBeanDefinition : 无 Parent 的 BeanDefinition 或者是 合并后 BeanDefinition
  • AnnotatedBeanDefinition : 注解标注的 BeanDefinition

4.Spring Bean 属性元信息

Bean 属性元信息 - PropertyValues

  • 可修改实现 - MutablePropertyValues
  • 元素成员 - PropertyValue

Bean 属性上下文存储 - AttributeAccessor

Bean 元信息元素 - BeanMetadataElement

4.1 PropertyValues

第五章:Spring Bean 定义 4.BeanDefinition 构建 小节有相关的例子

4.2 AttributeAccessor

beanDefinition.setAttribute(key, value);

这个属性是附加属性,并不会影响我们 Spring Bean 的实例化,初始化阶段。

public class BeanConfigurationMetadataDemo {
    public static void main(String[] args) {
        // BeanDefinition 定义
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("name", "xwf");
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        // 附加属性(不影响 Bean 实例化、属性赋值)
        beanDefinition.setAttribute("name", "小仙");

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        beanFactory.registerBeanDefinition("user", beanDefinition);

        User user = beanFactory.getBean("user", User.class);
        Object name = beanDefinition.getAttribute("name");
        System.out.println(name);
        System.out.println(user);
    }
}

执行结果:

小仙
User{beanName='user', id=null, name='xwf', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}

可以看到从 beanDefinition 获取 name 属性确实是 小仙,但是 user 对象的 name 并没有改变。

我们可以通过 postProcessAfterInitialization 在初始化之后利用 beanDefinition 的 attribute 信息来自己进行替换。

public class BeanConfigurationMetadataDemo {
    public static void main(String[] args) {
        // BeanDefinition 定义
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("name", "xwf");
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        // 附加属性(不影响 Bean 实例化、属性赋值)
        beanDefinition.setAttribute("name", "小仙");
        //当前 BeanDefinition 来自于何方(辅助作用)
        beanDefinition.setSource(BeanConfigurationMetadataDemo.class);

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (ObjectUtils.nullSafeEquals("user", beanName) && User.class.equals(bean.getClass())) {
                    BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
                    if (BeanConfigurationMetadataDemo.class.equals(bd.getSource())) {
                        String name = String.valueOf(bd.getAttribute("name"));
                        User user = (User) bean;
                        user.setName(name);
                        return user;
                    }
                }
                return bean;
            }
        });
        beanFactory.registerBeanDefinition("user", beanDefinition);

        User user = beanFactory.getBean("user", User.class);
        Object name = beanDefinition.getAttribute("name");
        System.out.println(name);
        System.out.println(user);
    }
}

执行结果:

小仙
User{beanName='user', id=null, name='小仙', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}

4.3 BeanMetadataElement

org.springframework.beans.BeanMetadataElement

public interface BeanMetadataElement {

	/**
	 * Return the configuration source {@code Object} for this metadata element
	 * (may be {@code null}).
	 */
	@Nullable
	default Object getSource() {
		return null;
	}

}

可以用表示 BeanDefinition 来自于何方(辅助作用);比如上面的例子中 BeanConfigurationMetadataDemo.class.equals(bd.getSource()) 用来判断 BeanDefinition 来源,当然前提是提前设置了来源 beanDefinition.setSource(BeanConfigurationMetadataDemo.class)

5.Spring 容器配置元信息

Spring XML 配置元信息 - beans 元素相关

beans 元素属性

默认值

使用场景

profile

null(留空)

Spring Profiles 配置值

default-lazy-init

default

当 outter beans “default-lazy-init” 属性存在是,继承该值,否则为"false"

default-merge

default

当 outter beans “default-merge” 属性存在是,继承该值,否则为"false"

default-autowire

default

当 outter beans “default-autowire” 属性存在是,继承该值,否则为"no"

default-autowire-candidates

null(留空)

默认 Spring Beans 名称 pattern

default-init-method

null(留空)

默认 Spring Beans 自定义初始化方法

default-destroy-method

null(留空)

默认 Spring Beans 自定义销毁方法

Spring XML 配置元信息-应用上下文相关

XML 元素

使用场景

<context:annotation-config />

激活 Spring 注解驱动

<context:annotation-scan />

Spring @Component 以及自定义注解扫描

<context:load-time-weaver />

激活 Spring LoadTimeWeaver

<context:mbean-export />

暴露 Spring Beans 作为 JMX Beans

<context:mbean-server />

将当前平台作为 MBeanServer

<context:property-placeholder />

加载外部化配置资源作为 Spring 属性配置

<context:property-override />

利用外部化配置资源覆盖 Spring 属性值

相关的重要源码

  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate
  • populateDefaults

6.基于 XML 文件装载 Spring Bean 配置元信息

Spring bean 配置元信息

XML 元素

使用场景

<beans:beans />

单 XML 资源下的多个 Spring Beans 配置

<beans:bean />

单个 Spring Bean 定义(BeanDefinition)配置

<beans:alias />

为 Spring Bean 定义(BeanDefinition)映射别名

<beans:import />

加载外部 Spring XML 配置资源

底层实现 org.springframework.beans.factory.xml.XmlBeanDefinitionReader

7.基于 Properties 文件装载 Spring Bean 配置元信息

Spring bean 配置元信息

Properites 属性名

使用场景

(class)

Bean 类全程限定名

(abstract)

是否为抽象的 BeanDefinition

(parent)

指定 parent BeanDefinition

(lazy-init)

是否延迟初始化化

(ref)

引用其他 Bean 的名称

(scope)

设置 Bean 作用域

${n}

n 表示第 n+1 个构造器参数

底层实现 org.springframework.beans.factory.support.PropertiesBeanDefinitionReader

8.基于 Java 注解装载 Spring Bean 配置元信息

Spring 模式注解

Spring 注解

场景说明

起始版本

@Repository

数据仓库模式注解

2.0

@Component

通用组件模式注解

2.5

@Service

服务模式注解

2.5

@Controller

Web 控制器模式注解

2.5

@Configuration

配置类模式注解

3.0

Spring Bean 依赖注入注解

注解

类型

场景说明

起始版本

@Autowired

Spring 注解

Bean 依赖注入

2.5

@Qualifier

Spring 注解

限定注入

2.5

@Resource

Java 注解

类似 @Autowired

2.5

@Inject

Java 注解

类似 @Autowired

2.5

Spring Bean 条件装配注解

Spring 注解

场景说明

起始版本

@Profile

配置化条件装配

3.1

@Conditional

编程条件装配

4.0

Spring Bean 生命周期回调注解

Spring 注解

场景说明

起始版本

@PostConstruct

代替 XML 元素 或者 InitializingBean

2.5

@PreDestroy

代替 XML 元素 或者 DisposableBean

2.5

9.Spring Bean 配置元信息底层实现

Spring BeanDefintion 解析与注册

实现场景

实现类

起始版本

XML 资源

XmlBeanDefinitionReader

1.0

Properties 资源

PropertiesBeanDefinitionReader

1.0

java 注解

AnnotatedBeanDefinitionReader

3.0

9.1 Spring XML 资源 BeanDefinition 解析与注册

核心 API - XMLBeanDefintionReader

  • 资源 - Resource
  • 底层- BeanDefintionDocumentReader
  • XML 解析 - Java DOM Level 3 API
  • BeanDefintion 解析 - BeanDefinitionParserDelegate
  • BeanDefintion 注册 - BeanDefinitionRegistry

9.2 Spring Properties 资源 BeanDefintion 解析与注册

  • 核心 API - PropertiesBeanDefinitionReader
  • 资源
  • 字节流 - Resource
  • 字符流 - EncodedResource
  • 底层
  • 存储 - java.util.Properties
  • BeanDefinition 解析 - API 内部实现
  • BeanDefinition 注册 - BeanDefinitionRegistry

9.3 Spring Java 注册 BeanDefinition 解析与注册

  • 核心 API - AnnotatedBeanDefinitionReader
  • 资源
  • 类对象 - java.lang.Class
  • 底层
  • 条件评估 - ConditionEvaluator
  • Bean 范围解析 - ScopeMetadataResolver
  • BeanDefinition 解析 - 内部 API 实现
  • BeanDefinition 处理 - AnnotationConfigUtils#processCommonDefinitionAnnotations
  • BeanDefintion 注册 - BeanDefinitionRegistry

10.基于 XML 文件装载 Spring IOC 容器配置元信息

Spring IoC 容器相关 XML 配置

命名空间

所属模块

Schema 资源 URL

beans

spring-beans

https://www.springframework.org/schema/beans/spring-beans.xsd

context

spring-context

https://www.springframework.org/schema/context/spring-context.xsd

aop

spring-aop

https://www.springframework.org/schema/aop/spring-aop.xsd

tx

spring-tx

https://www.springframework.org/schema/tx/spring-tx.xsd

util

spring-beans

https://www.springframework.org/schema/util/spring-util.xsd

tool

spring-beans

https://www.springframework.org/schema/tool/spring-tool.xsd

11.基于 Java 注解装载 Spring IOC 容器配置元信息

11.1 Spring IoC 容器装配注解

Spring 注解

场景说明

起始版本

@ImportResource

替换 XML 元素

3.0

@Import

导入 Configuration Class

3.0

@ComponentScan

扫描指定 package

3.1

示例
/**
 * 基于 Java 注解装载 Spring IOC 容器配置元信息
 */
//将当前类作为 Configuration Class
@ImportResource("classpath:/META-INF/dependency-lookup-context.xml")
@Import(User.class)
public class AnnotatedSpringIoCContainerMetaConfigurationDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册当前类作为 Configuration Class
        context.register(AnnotatedSpringIoCContainerMetaConfigurationDemo.class);
        // 启动
        context.refresh();
        Map<String, User> beansOfType = context.getBeansOfType(User.class);
        printfEach(beansOfType);
        // 关闭
        context.close();
    }

    public static void printfEach(Map<String, User> map) {
        for (Map.Entry<String, User> entry : map.entrySet()) {
            System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
        }
    }
}

执行结果:

user用户对象初始化...
superUser用户对象初始化...
User Bean name : com.huajie.thinking.in.spring.ioc.overview.domain.User , content : User{beanName='com.huajie.thinking.in.spring.ioc.overview.domain.User', id=null, name='null', age=null, configFileReource=null, city=null, cities=null, lifeCities=null} 
User Bean name : user , content : User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]} 
User Bean name : superUser , content : SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]} 
superUser用户对象销毁...
user用户对象销毁...
com.huajie.thinking.in.spring.ioc.overview.domain.User用户对象销毁...

11.2 Spring IoC 配置属性注解

Spring 注解

场景说明

其实版本

@PropertySource

配置属性抽象 PropertySource 注解

3.1

@PropertySources

@PropertySource 集合注解

4.0

示例
/**
 * 基于 Java 注解装载 Spring IOC 容器配置元信息
 */
//将当前类作为 Configuration Class
@ImportResource("classpath:/META-INF/dependency-lookup-context.xml")
@Import(User.class)
@PropertySource(value = "classpath:/META-INF/user-bean-definitions.properties", encoding = "gbk")
@PropertySource(value = "classpath:/META-INF/user-bean-definitions.properties", encoding = "gbk")//可以写多个 jdk8 @Repeatable
public class AnnotatedSpringIoCContainerMetaConfigurationDemo {

    @Bean
    public User configuredUser(@Value(value = "${usr.name}") String name) {
        return User.createUser(name);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册当前类作为 Configuration Class
        context.register(AnnotatedSpringIoCContainerMetaConfigurationDemo.class);
        // 启动
        context.refresh();
        Map<String, User> beansOfType = context.getBeansOfType(User.class);
        printfEach(beansOfType);
        // 关闭
        context.close();
    }

    public static void printfEach(Map<String, User> map) {
        for (Map.Entry<String, User> entry : map.entrySet()) {
            System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
        }
    }
}

执行结果:

User Bean name : com.huajie.thinking.in.spring.ioc.overview.domain.User , content : User{beanName='com.huajie.thinking.in.spring.ioc.overview.domain.User', id=null, name='null', age=null, configFileReource=null, city=null, cities=null, lifeCities=null} 
User Bean name : configuredUser , content : User{beanName='configuredUser', id=null, name='小仙', age=null, configFileReource=null, city=null, cities=null, lifeCities=null} 
User Bean name : user , content : User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]} 
User Bean name : superUser , content : SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}

12.基于 Extensible XML authoring 扩展 Spring XML 元素

Spring XML 扩展

  • 编写 XML Schema 文件:定义 XML 结构
  • 自定义 NamespaceHandler 实现:命名空间绑定
  • 自定义 BeanDefinitionParser 实现:XML 元素与 BeanDefinition 解析
  • 注册 XML 扩展:命名空间与 XML Schema 映射

12.1 编写 XML Schema 文件

在 resource 目录下面建相同的包路径 com.huajie.thinking.in.spring.configuration.metadata

新建 users.xsd 文件

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://com.huajie/schema/users"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://com.huajie/schema/users">

    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>

    <!-- 定义 User 类型(定义复杂类型) -->
    <xsd:complexType name="User">
        <xsd:attribute name="id" type="xsd:integer" use="required"/>
        <xsd:attribute name="name" type="xsd:string" use="required"/>
        <xsd:attribute name="city" type="City"/>
    </xsd:complexType>

    <!-- 定义 City 类型(简单类型,枚举) -->
    <xsd:simpleType name="City">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="WUHAN"/>
            <xsd:enumeration value="BEIJING"/>
            <xsd:enumeration value="SHANGHAI"/>
        </xsd:restriction>
    </xsd:simpleType>

    <!-- 定义 user 元素 -->
    <xsd:element name="user" type="User"/>

</xsd:schema>

新建 Spring xml 配置文件 users-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:users="http://com.huajie/schema/users"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://com.huajie/schema/users
        http://com.huajie/schema/users.xsd">

       <users:user id="1" name="小仙" city="WUHAN"/>

</beans>

12.2 自定义 NamespaceHandler 实现 & 自定义 BeanDefinitionParser 实现

新建 UsersNamespaceHandler

public class UsersNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("user", new UsersBeanDefinitionParser());
    }

    private static class UsersBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {

        @Override
        protected Class<?> getBeanClass(Element element) {
            return User.class;
        }

        @Override
        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
            setPropertyValue("id", element, builder);
            setPropertyValue("name", element, builder);
            setPropertyValue("city", element, builder);
        }

        private void setPropertyValue(String attributeName, Element element, BeanDefinitionBuilder builder) {
            String value = element.getAttribute(attributeName);
            if (StringUtils.hasText(value)) {
                builder.addPropertyValue(attributeName, value);
            }
        }

    }

}

在 resource/META-INF 目录下面新建 spring.handlers 文件

http\://com.huajie/schema/users=com.huajie.thinking.in.spring.configuration.metadata.UsersNamespaceHandler

12.3 注册 XML 扩展

在 resource/META-INF 目录下面新建 spring.schemas 文件

http\://com.huajie/schema/users.xsd=com/huajie/thinking/in/spring/configuration/metadata/users.xsd

12.4 示例

/**
 * Spring xml 元素扩展示例
 */
public class ExtensibleXmlAuthoringDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        String location = "classpath:/META-INF/users-context.xml";
        xmlBeanDefinitionReader.loadBeanDefinitions(location);

        User bean = beanFactory.getBean(User.class);

        System.out.println(bean);
    }
}

执行结果:

User{beanName='1', id=1, name='小仙', age=null, configFileReource=null, city=WUHAN, cities=null, lifeCities=null}

13.Extensible XML authoring 扩展原理

触发时机在启动应用上下文时,Application.refresh() 方法

  • org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
  • AbstractRefreshableApplicationContext#refreshBeanFactory
  • AbstractXmlApplicationContext#loadBeanDefinitions
  • AbstractBeanDefinitionReader#loadBeanDefinitions
  • BeanDefinitionParserDelegate#parseCustomElement

BeanDefinitionParserDelegate#parseCustomElement 核心流程

  • 获取 namespace
  • 通过 namespace 解析 NamespaceHandler
  • 构造 ParserContext
  • 解析元素,获取 BeanDefinition

13.1 源码调试

使用 12.基于 Extensible XML authoring 扩展 Spring XML 元素 小节中的示例

调试我们将断点打在 BeanDefinitionParserDelegate#parseCustomElement 1383 行

1.获取 namespace

Spring的info泄露 spring information,Spring的info泄露 spring information_User,第1张

2.通过 namespace 解析 NamespaceHandler 获取到我们定义的 handler

Spring的info泄露 spring information,Spring的info泄露 spring information_spring_02,第2张

3.构造 ParserContext

Spring的info泄露 spring information,Spring的info泄露 spring information_Spring的info泄露_03,第3张

4.调用我们 handler#parse 方法,将 element 解析成 BeanDefinition

Spring的info泄露 spring information,Spring的info泄露 spring information_XML_04,第4张

14.基于 Properties 文件装载外部化配置

注解驱动

  • org.springframework.context.annotation.PropertySource
  • org.springframework.context.annotation.PropertySources

API 编程

  • org.springframework.core.env.PropertySource
  • org.springframework.core.env.PropertySources

14.1 示例

在本章 11.2 Spring IoC 配置属性注解 中已经有相关的示例

我们对示例做一定的改造,在外部化配置加载之前,新建一个 PropertySource 去替换其中的 usr.name 属性;PropertySources 有一定的顺序,先添加的 PropertySource 可以覆盖后添加的。

/**
 * 外部化配置示例
 */
@PropertySource(value = "classpath:/META-INF/user-bean-definitions.properties", encoding = "gbk")
public class PropertySourceDemo {

    @Bean
    public User user(@Value(value = "${usr.name}") String name) {
        return User.createUser(name);
    }


    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 扩展 Environment 中的 propertySources
        // 这个操作必须在 context.refresh() 之前完成
        Map<String,Object> propertySource = new HashMap<>();
        propertySource.put("usr.name","new-小仙");
        MapPropertySource ms = new MapPropertySource("first-property-source",propertySource);
        context.getEnvironment().getPropertySources().addFirst(ms);
        // 注册当前类作为 Configuration Class
        context.register(PropertySourceDemo.class);
        // 启动
        context.refresh();
        Map<String, User> beansOfType = context.getBeansOfType(User.class);
        printfEach(beansOfType);
        MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
        System.out.println(propertySources);

        // 关闭
        context.close();
    }

    public static void printfEach(Map<String, User> map) {
        for (Map.Entry<String, User> entry : map.entrySet()) {
            System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
        }
    }
}

执行结果:

User Bean name : user , content : User{beanName='user', id=null, name='new-小仙', age=null, configFileReource=null, city=null, cities=null, lifeCities=null} 
[MapPropertySource {name='first-property-source'}, PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}, ResourcePropertySource {name='class path resource [META-INF/user-bean-definitions.properties]'}]

可以看到 user 对象的 name = ‘new-小仙’

MapPropertySource 顺序:

1.first-property-source 我们自定义的 PropertySource

2.systemEnvironment Java 系统环境变量

3.通过 properties 文件加载的 PropertySource

15.基于 YAML 文件装载外部化配置

API 编程

  • org.springframework.beans.factory.config.YamlProcessor
  • org.springframework.beans.factory.config.YamlMapFactoryBean
  • org.springframework.beans.factory.config.YamlPropertiesFactoryBean

15.1 基于 XML 示例

新建 user.yaml 文件;user.name 和 Java 系统环境变量相同,所以我们用 usr 表示

usr:
  id: 1
  name: 小仙-yaml

新建 yaml-property-source-context.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="yamlMap" class="org.springframework.beans.factory.config.YamlMapFactoryBean">
        <property name="resources" value="classpath:/META-INF/user.yaml"/>
    </bean>

</beans>

调用示例

/**
 * 基于 xml 的 yaml 外部化配置示例
 */
public class XmlBasedYamlPropertySourceDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        String location = "classpath:/META-INF/yaml-property-source-context.xml";
        xmlBeanDefinitionReader.loadBeanDefinitions(location);

        Map<String, Object> users = beanFactory.getBean("yamlMap", Map.class);

        System.out.println(users);
    }
}

执行结果:

{usr={id=1, name=小仙-yaml}}

15.2 基于 Java 注解示例

新建 YamlPropertySourceFactory

public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
        yamlPropertiesFactoryBean.setResources(resource.getResource());
        Properties properties = yamlPropertiesFactoryBean.getObject();
        return new PropertiesPropertySource(name,properties);
    }
}

示例

/**
 * 基于 Java 注解的 yaml 外部化配置示例
 */
@PropertySource(name="yamlPropertySource",value = "classpath:/META-INF/user.yaml",factory = YamlPropertySourceFactory.class)
public class AnnotatedYamlPropertySourceDemo {

    @Bean
    public User user(@Value(value = "${usr.name}") String name) {
        return User.createUser(name);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册当前类作为 Configuration Class
        context.register(AnnotatedYamlPropertySourceDemo.class);
        // 启动
        context.refresh();

        User user = context.getBean("user", User.class);

        System.out.println(user);
        // 关闭
        context.close();
    }
}

执行结果:

User{beanName='user', id=null, name='小仙-yaml', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}

16.面试题

16.1 Spring 内建 XML 有哪些?

命名空间

所属模块

Schema 资源 URL

beans

spring-beans

https://www.springframework.org/schema/beans/spring-beans.xsd

context

spring-context

https://www.springframework.org/schema/context/spring-context.xsd

aop

spring-aop

https://www.springframework.org/schema/aop/spring-aop.xsd

tx

spring-tx

https://www.springframework.org/schema/tx/spring-tx.xsd

util

spring-beans

https://www.springframework.org/schema/util/spring-util.xsd

tool

spring-beans

https://www.springframework.org/schema/tool/spring-tool.xsd

16.2 Spring 配置元信息具体有哪些?

  • Bean 配置元信息:通过媒介(如 XML、Properties等),解析 BeanDefinition
  • IoC 容器配置元信息:通过媒介(如 XML、Properties等),控制 IoC 容器行为,比如注解驱动,AOP等
  • 外部化配置:通过资源抽象(如 Properties、YAML等),控制 PropertySource
  • Spring Profile:通过外部化配置,提供条件分支流程、

16.3 Extensible XML authoring 的缺点?

  • 高复杂度:开发人员需要书写 XML Schema,spring.handlers,spring.schemas 以及 Spring API。
  • 嵌套元素支持比较弱:通常需要使用方法递归或者其嵌套解析方式处理嵌套子元素
  • XML 处理性能较差:Spring XML 基于 DOM Level 3 API 实现,该 API 便于理解,然而性能较差。
  • XML 框架移植性差:很难适配高性能和便利性的 XML 框架,如 JAXB。

17.参考


https://www.xamrdz.com/backend/3zn1937730.html

相关文章: