跳至主要內容

属性源

wangdx大约 25 分钟

属性源

PropertySource 属性源

应用程序中的资源操作

  • 在项目开发中,为了保持项目设计的灵活性,:会利用属性资源文件来实现一些配置项的定义,这样应用程序就可以根据属性资源文件配置的不同,而达到不同的运行效果。后时在程序启动时有可能会进行一些初始化的环境数据的处理,而为了便于这些环境数据的后续操作方便,也会将其保存在内存之中,这样不同的属性数据就需要使用不同的类型来进行处理,如图所示,从而造成项目中会存在有若干个不同属性存储结构的问题从而造成使用的混乱。

PropertySource 继承结构

  • 也有可能是一个 Map 集合类型,所以在实际开发中,属性源可能是一个 PropertiesPropertySource 采用了抽象类的结构进行定义同时对于属性源的定义采用了泛型声明。在项目开发的过程中,开发者可以根据当前业务的需要,选择一个子类进行 PropertySource 类对象的实例化处理
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;

import java.util.Arrays;
import java.util.Map;

public class SpringSourceDemo { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) {
        Map<String, Object> data = Map.of("yootk", "www.yootk.com",
                "jixianit", "www.jixianit.com"); // Java层次上的集合管理
        PropertySource propertySource = new MapPropertySource("url", data); // 资源管理
        LOGGER.info("属性资源获取。yootk = {}", propertySource.getProperty("yootk")); // 获取KEY
    }
}

PropertySources 属性源管理

PropertySource 与 Propertysources

  • 在一个完整的项目应用中,有可能会存在有若干个不同的属性源(PropertySource 对象实例)为了便于应用程序资源的方便获取,在 Spring 中提供了 PropertySources 接口,所有的 PropertySource 对象实例向此接口注册后就可以根据指定的名称获取一个 PropertySource 对象实例,从而实现属性源的统一管理

PropertySources 接口

  • PropertySources 接口中定义了资源的获取操作方法,而具体的资源存储的处理是由 MutablePropertySources 子类实现的,该类的继承结构如图所示。在该类中会将所有注册的资源保存在 List 集合之中。同时该集合考虑到并发数据存储的安全问题,使用了 CopyOnWriteArrayList 实现子类
1、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.*;

import java.util.Arrays;
import java.util.Map;
import java.util.Properties;

public class SpringSourceDemo { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) {
        Map<String, Object> data = Map.of("yootk", "www.yootk.com",
                "jixianit", "www.jixianit.com"); // Java层次上的集合管理
        PropertySource mapSource = new MapPropertySource("url", data); // 资源管理
        Properties prop = new Properties();
        prop.setProperty("java", "Java进阶开发实战");
        prop.setProperty("spring", "Spring开发实战");
        PropertySource propSource = new PropertiesPropertySource("book", prop); // 属性源
        MutablePropertySources sources = new MutablePropertySources();
        sources.addLast(mapSource); // 追加属性源管理
        sources.addLast(propSource); // 追加属性源管理
        LOGGER.info("属性资源获取:java = {}", sources.get("book").getProperty("java"));
    }
}

PropertyResolver 属性解析

PropertyResolver 接口

  • Spring 开发框架在进行设计时,充分的考虑到了各种可能存在的资源读取状况,例如:用户配置资源、属性文件配置资源,以及动态表达式配置资源等操作,所以为了统一资源的读取处理,提供了 PropertyResolver 资源解析接口,该类可以根据当前传入的字符串文本的类型不同(可能是普通的文本,也可能是带有“${}”结构的表达式读取)来决定资源读取的形式,该接口提供的常用方法如表所示。

PropertySourcesPropertyResolver 继承结构

  • 由于项目中的属性可能包存在不同的属性源之中,所以在 PropertyResolver 接口中提供的方法,主要是根据属性 KEY 字符串或表达式实现属性内容的读取,这样一来就进步达到了属性资源读取操作的统一,而要想使用 PropertyResolver 接口,则可以借助 FPropertySourcesPropertyResolver 子类
1、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.*;

import java.util.Map;
import java.util.Properties;

public class SpringSourceDemo { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) {
        Map<String, Object> data = Map.of("yootk", "www.yootk.com",
                "jixianit", "www.jixianit.com"); // Java层次上的集合管理
        PropertySource mapSource = new MapPropertySource("url", data); // 资源管理
        Properties prop = new Properties();
        prop.setProperty("java", "Java进阶开发实战");
        prop.setProperty("spring", "Spring开发实战");
        PropertySource propSource = new PropertiesPropertySource("book", prop); // 属性源
        MutablePropertySources sources = new MutablePropertySources();
        sources.addLast(mapSource); // 追加属性源管理
        sources.addLast(propSource); // 追加属性源管理
        PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(sources);
        LOGGER.info("属性资源获取:java = {}", resolver.getProperty("java"));
    }
}


2、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.*;

import java.util.Map;
import java.util.Properties;

public class SpringSourceDemo { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) {
        Map<String, Object> data = Map.of("yootk", "www.yootk.com",
                "jixianit", "www.jixianit.com"); // Java层次上的集合管理
        PropertySource mapSource = new MapPropertySource("url", data); // 资源管理
        Properties prop = new Properties();
        prop.setProperty("java", "Java进阶开发实战");
        prop.setProperty("spring", "Spring开发实战");
        PropertySource propSource = new PropertiesPropertySource("book", prop); // 属性源
        MutablePropertySources sources = new MutablePropertySources();
        sources.addLast(mapSource); // 追加属性源管理
        sources.addLast(propSource); // 追加属性源管理
        PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(sources);
        LOGGER.info("资源表达式解析处理:yootk = {}", resolver.resolvePlaceholders("${java}"));
        LOGGER.info("资源表达式解析处理:yootk = {}", resolver.resolvePlaceholders("${lee}"));
        LOGGER.info("资源表达式解析处理:yootk = {}", resolver.resolvePlaceholders("yootk"));
    }
}

配置环境管理

ConfigurableEnvironment 配置环境管理

系统属性管理

  • Spring 是基于 JVM 的一种应用扩展,所以在整个的 Spring 框架的内部除了需要维护自身所需要的属性资源之外,还要兼顾到本地系统中 JVMM 的属性信息,而为了简化这一操作的管理,在 Spring 中提供了一个 ConfigurableEnvironment 环境属性管理接口,通过该接口可以直接获取系统属性项

Environment 接口继承结构

  • 继承关系如图所示。为便于后续的扩展 ConfigurableEnvironment 采用接口进行定义,这样在获取对应的系统属性信息时就需要通过该接口的实现子类"StandardEnvironment”来获取实例化对象
1、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;

public class SpringSourceDemo { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        StandardEnvironment environment = new StandardEnvironment();
        MutablePropertySources sources = environment.getPropertySources(); // 系统的环境属性
        for (PropertySource source : sources) {
            LOGGER.info("{}:{}", source.getName(), source.getSource());
        }
    }
}


2、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;

public class SpringSourceDemo { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        PropertyResolver resolver = new StandardEnvironment();
        LOGGER.info("【获取静态设置的JVM属性】JDK版本 = {}",
                resolver.getProperty("java.specification.version"));
        LOGGER.info("【获取动态设置的JVM属性】yootk = {}", resolver.getProperty("yootk"));
        LOGGER.info("【获取动态设置的JVM属性】edu = {}", resolver.getProperty("edu"));
    }
}

Environment 与 Profile 管理

Profile 配置

  • 在系统开发中为了便于不同运行环境的管理,必然会提供有不同的 profie 环境(例如:dev 环境、product 环境等),为了便于这些不同的 Profile 环境管理,在 Spring 中会通过“@Profile”注解提供相关的配置类,而后利用“@ActiveProfiles”注解选择默认激活的 Profile 环境
1、
package com.yootk.bean.vo;

public class Message { // 定义实现 Profile配置类结构
    private String path;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public String toString() {
        return "消息路径:" + this.path;
    }
}


2、
package com.yootk.bean;

import com.yootk.bean.vo.Message;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration // 配置Bean,也需要向Spring容器中注册
public class YootkProfileConfig {
    @Bean // Bean的注册
    @Profile("dev") // 定义Profile的信息
    public Message devMessage() {
        Message message = new Message();
        message.setPath("dev/yootk.com");
        return message;
    }
    @Bean // Bean的注册
    @Profile("test") // 定义Profile的信息
    public Message testMessage() {
        Message message = new Message();
        message.setPath("test/yootk.com");
        return message;
    }
    @Bean // Bean的注册
    @Profile("product") // 定义Profile的信息
    public Message productMessage() {
        Message message = new Message();
        message.setPath("product/yootk.com");
        return message;
    }
}



3、
package com.yootk.main;

import com.yootk.bean.YootkProfileConfig;
import com.yootk.bean.vo.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.StandardEnvironment;

public class SpringAnnotationProfile { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringAnnotationProfile.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(); // 注解方式启动Spring容器
        context.getEnvironment().setActiveProfiles("dev"); // 设置环境
        context.register(YootkProfileConfig.class); // 配置类
        context.refresh(); // 容器的刷新
        Message message = context.getBean(Message.class); // 获取指定类型的 Bean
        LOGGER.info("获取message.path属性:{}", message.getPath());
    }
}

ConversionService 数据转换服务

Spring 转换器

  • 在进行资源文件、Spring 配置文件以及注解定义时,所有设置的数据都会以字符串的形式存储,然而在 Spring 运行的过程中,这些字符串的数据都可以自动转为所需要的目标类型从而实现属性的赋值处理,而实现这一处理机制的核心就在于转换器的支持

Spring 转换器结构

  • Spring 在设计时充分的考虑到了所有常用转换处理形式,为了进行所有转换器的统一管理提供了一个 Converter 转换处理接口,而后根据此接口定义的标准提供了一系列的处理子类
1、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;

public class SpringSourceDemo { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        ConfigurableEnvironment environment = new StandardEnvironment();
        ConfigurableConversionService conversionService = environment.getConversionService();
        Double num = conversionService.convert("6.5", Double.class); // 转型
        LOGGER.info("数据转换后的数学计算:{}", num * 2);
    }
}

ApplicationContext 继承结构

Spring 容器与 Bean 注册

  • 在 Spring 之中所有要使用的 Bean 对象都需要在 XML 文件中进行注册,而后在 Spring 容器启动时,会对这些配置项进行解析,同时为了便于容器内的 Bean 对象使用会将这些对象存储在一个集合之中,这样当用户需要某些对象时就可以利用 getBean()方法实现对象的引用

ApplicationContext 接口继承结构

  • 在 Spring 中最为重要的就是其内置的 Spring 容器而为了便于用户进行容器的处理操作,提供了 ApplicationContext 应用上下文接口,该接口的继承结构如图所示,开发者可以利用该接口提供的方法进行容器的相关信息获取以及存储对象的访问。

加载磁盘上的 Spring 配置文件

  • Spring 容器的启动除了可以通过 ClassPathXmlApplicationContext 子类读取项目环境下的配置文件之外,也可以使用 FileSystemApplicationContext 子类实现本地磁盘上的 Spring 配置文件的读取。
1、
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>     <!-- 启用Annotation配置注解支持 -->
    <context:component-scan base-package="com.yootk"/>
    <context:property-placeholder location="classpath:config/*.properties"/>
</beans>

public class ApplicationContextTest {
    public static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextTest.class);

    public static void main(String[] args) {
        String xmlPath = "D:\\develop\\project\\images\\upload\\spring-base.xml";
        ApplicationContext cx = new FileSystemXmlApplicationContext(xmlPath);
        IDeptService deptService = cx.getBean("deptServiceImpl", IDeptService.class);
        LOGGER.info("调用业务接口:{}",deptService.get(1l));
    }
}

EnvironmentCapable

EnvironmentCapable 接定义

  • Spring 中为了便于不同应用环境的管理,提供了 Environment 等相关环境处理接口为了便于 ApplicationContext 管理环境实例,Spring 又提供了一个 EnvironmentCapable 接口,该接口的主要功能是获取 Environment 接口实例
1、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public class SpringSourceDemo { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringSourceDemo.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        ConfigurableApplicationContext context =
                new ClassPathXmlApplicationContext("spring/spring-base.xml");
        ConfigurableEnvironment environment = context.getEnvironment();
        LOGGER.info("当前使用的JDK版本:{}", environment.getProperty("java.specification.version"));
    }
}

事件发布器

ApplicationEventPublisher 事件发布器

Spring 事件处理模型

  • Java 为了便于程序结构的扩展,提供了事件处理的支持,开发者只需要通过 EventObject 类即可自定义事件类型,随后通过 EventListener 进行事件监听处理 Spring 沿用了此种处理模式,基于 EventObject 扩展了 ApplicationEvent 事件类,基于 EventListener 扩展 ApplicationListener 事件监听
1、
package com.yootk.event;

import org.springframework.context.ApplicationEvent;

public class YootkEvent extends ApplicationEvent { // 自定义事件
    public YootkEvent(Object source) { // 事件源随意定义
        super(source);
    }
}


2、
package com.yootk.event.listener;

import com.yootk.event.YootkEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;

public class YootkEventListener implements ApplicationListener<YootkEvent> { // 事件监听
    private static final Logger LOGGER = LoggerFactory.getLogger(YootkEventListener.class);
    @Override
    public void onApplicationEvent(YootkEvent event) {
        LOGGER.info("事件源:{}", event.getSource());
    }
}


3、
package com.yootk.event.config;

import com.yootk.event.listener.YootkEventListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class YootkEventConfig { // 配置类
    @Bean
    public YootkEventListener eventListener() {
        return new YootkEventListener();
    }
}


4、
package com.yootk.main;

import com.yootk.event.YootkEvent;
import com.yootk.event.config.YootkEventConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class StartApplicationEvent { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(StartApplicationEvent.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(); // 注解方式启动Spring容器
        context.register(YootkEventConfig.class); // 配置类
        context.refresh(); // 容器的刷新
        YootkEvent event = new YootkEvent("沐言科技:www.yootk.com"); // 设置事件类
        context.publishEvent(event); // 事件发布
    }
}

Messagesource 资源读取

国际化资源读取

  • 程序的设计与开发中,为了满足国际化项目运行的目的,就需要将应用中的全部文字信息提取为一组资源文件,在使用时根据 KEY 读取对应的数据项,并且会基于不同的区域设置返回不同的文本信息

Messagesource 接口实现结构

  • 传统的 Java 开发中使用 ResourceBundle 与 Local 的整合实现国际化资源读取处理,而在 Spring 中对这一基础功能进行了包装,提供了 MessageSource 接口,以便统一资源读取操作
1、
i18n/Message.properties:
	yootk.site=www.yootk.com
i18n/Message_zh_CN.properties:
	yootk.site=沐言优拓:www.yootk.com
i18n/Message_en_US.properties:
	yootk.site=YOOTK : www.yootk.com


2、
package com.yootk.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.env.ConfigurableEnvironment;

import java.util.Locale;

public class MessageSourceDemo { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageSourceDemo.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasenames("i18n.Message");
        LOGGER.info("【Message默认资源】yootk.site = {}",
                messageSource.getMessage("yootk.site", null, null));
        LOGGER.info("【Message中文资源】yootk.site = {}",
                messageSource.getMessage("yootk.site", null, new Locale("zh", "CN")));
        LOGGER.info("【Message英文资源】yootk.site = {}",
                messageSource.getMessage("yootk.site", null, new Locale("en", "US")));
    }
}

PropertyEditor 属性编辑器

PropertyEditor 接口作用

  • Spring 中为了便于所有 Bean 对象的管理,一般会使用资源文件来实现 Bean 属性内容的定义,随后在程序中可以利用“@Value(SpEL 表达式)”的注解进行指定 KEY 属性的注入,如图所示。如果此时要注入的只是普通的文本,那么直接注入即可,而如果此时要为属性注入的文本需要进行处理后并基于其他 Bean 的形式注入,那么就需要对当前的属性编辑后才可以成功注

自定义属性处理器

  • 现在假设在资源文件中定义了 Company 与 Address 两个 Bean 的属性内容,Company 类包含有 Addres 这样的组合结构。此时的数据是无类的引用,而在定义 Address 属性内容时采用了“国家-省份-城市'法直接注入的,需要开发人员手工实例化 Address 对象,随后对这一文本数据进行拆分处理与属性赋值后,才可以将 Address 对象实例注入到 Company 对象实例之中,这样就要对当前读取到的属性进行编辑处理,而 Spring 为了解决这一设计的需求,提供了 PropertyEditor 操作接口,实际应用时的类关联结构如图所示。开发者需要自定义属性编辑器处理类,随后利用 PropertyEditorRegistrar 进行属性编辑器的注入,才可以自动生效。为便于读者理解本操作下面将通过一个完整的案例进行说明,具体实现步骤如下所示。
1、
package com.yootk.bean.vo;

public class Address {
    private String country;
    private String provice;
    private String city;

    @Override
    public String toString() {
        return "【Address】国家:" + this.country + "、省份:" + this.provice + "、城市:" + this.city;
    }
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }

    public String getProvice() {
        return provice;
    }

    public void setProvice(String provice) {
        this.provice = provice;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}


2、
package com.yootk.bean.vo;

import org.springframework.beans.factory.annotation.Value;

public class Company {
    @Value("${company.name}")
    private String name;
    @Value("${company.homepage}")
    private String homepage;
    @Value("${company.edu}")
    private String edu;
    @Value("${company.address}")
    private Address address; // 其他Bean的引用

    @Override
    public String toString() {
        return "【Company】名称:" + this.name + "、主页:" + this.homepage + "、教育:" + this.edu;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHomepage() {
        return homepage;
    }

    public void setHomepage(String homepage) {
        this.homepage = homepage;
    }

    public String getEdu() {
        return edu;
    }

    public void setEdu(String edu) {
        this.edu = edu;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}


3、
company.name=沐言科技
company.homepage=www.yootk.com
company.edu=edu.yootk.com
company.address=中国-北京-北京

4、
package com.yootk.editor;

import com.yootk.bean.vo.Address;

import java.beans.PropertyEditorSupport;

public class AddressPropertyEdit extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        // 当资源设置之后,会触发此方法的执行
        String result[] = text.split("-"); // 字符串拆分
        Address address = new Address(); // 手工实例化对象
        address.setCountry(result[0]); // 属性设置
        address.setProvice(result[1]); // 属性设置
        address.setCity(result[2]); // 属性设置
        super.setValue(address); // 保存Bean对象
    }
}


5、
package com.yootk.editor;

import com.yootk.bean.vo.Address;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;

public class AddressPropertyEditorRegistrar implements PropertyEditorRegistrar {
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        // 根据Address类型匹配,如果发现要进行此类型对象的操作,则自动找到匹配的编辑器
        registry.registerCustomEditor(Address.class, new AddressPropertyEdit());
    }
}


6、
package com.yootk.config;

import com.yootk.bean.vo.Company;
import com.yootk.editor.AddressPropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:props/company.properties") // 资源引入
public class CompanyConfiguration {
    @Bean
    public Company getCompany() {
        return new Company();
    }
    @Bean
    public CustomEditorConfigurer customEditorConfigurer() {
        CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
        customEditorConfigurer.setPropertyEditorRegistrars(new PropertyEditorRegistrar[] {
                new AddressPropertyEditorRegistrar()}); // 注册属性编辑器
        return customEditorConfigurer;
    }
}


7、
package com.yootk.main;

import com.yootk.bean.vo.Company;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringAnnotationScanPackage { // 李兴华高薪就业编程训练营
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringAnnotationScanPackage.class);
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(); // 注解方式启动Spring容器
        context.scan("com.yootk.config"); // 扫描包配置
        context.refresh(); // 容器的刷新
        Company company = context.getBean(Company.class);
        LOGGER.info("{}", company);
        LOGGER.info("{}", company.getAddress());
    }
}


8、
company.name=\u6c90\u8a00\u79d1\u6280
company.homepage=www.yootk.com
company.edu=edu.yootk.com
company.address=\u4e2d\u56fd-\u5317\u4eac-\u5317\u4eac

生命周期

Bean 的初始化与销毁

Spring 初始化与回收管理

  • 在 Java 原生语言的设计结构之中,考虑到了类对象初始化已经对象回收时的资源释放问题,所以提供了构造方法以及回收操作供用户使用,如图所示,但是这些处理全部都属 于 JVM 管理范畴之中的,Sprina 框架在设计时,考虑到了 Bean 的管理机制,所以又进行了 Bean 生命周期处理形式的扩充,用户可以根据自己的需要定义 Spring 容器下的初始化与回收处理操作。
1、
package com.yootk.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MessageListener { // 自定义的消息监听方法
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageListener.class);
    public void onRecieve(String msg) { // 消息的接收处理
        LOGGER.info("【接收到新消息】{}", msg); // 日志输出消息内容
    }
    public void openChannel() { // 打开监听
        LOGGER.info("【通道连接】服务器消息通道建立成功。");
    }
    public void closeChannel() {
        LOGGER.info("【通道关闭】断开服务器连接,关闭消息监听服务。");
    }
}


2、
    <bean id="messageListener" class="com.yootk.listener.MessageListener"
        init-method="openChannel" destroy-method="closeChannel"/>

3、
package com.yootk.main;

import com.yootk.listener.MessageListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StartYootkSpringApplication { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(StartYootkSpringApplication.class);
    public static void main(String[] args) {
        // 容器的启动需要使用到特定的接口和类,此时的配置文件保存在CLASSPATH路径下,所以直接使用此类实例化
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring/spring-base.xml");
        MessageListener listener = context.getBean(MessageListener.class); // 获取Bean实例
        for (int x = 0; x < 3; x++) {   // 接收消息内容
            listener.onRecieve("沐言科技:www.yootk.com");
        }
        context.close(); // 关闭容器
    }
}


4、
package com.yootk.main;

import com.yootk.listener.MessageListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StartYootkSpringApplication { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(StartYootkSpringApplication.class);
    public static void main(String[] args) {
        // 容器的启动需要使用到特定的接口和类,此时的配置文件保存在CLASSPATH路径下,所以直接使用此类实例化
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring/spring-base.xml");
        MessageListener listener = context.getBean(MessageListener.class); // 获取Bean实例
        for (int x = 0; x < 3; x++) {   // 接收消息内容
            listener.onRecieve("沐言科技:www.yootk.com");
        }
        context.registerShutdownHook(); // 关闭的钩子处理
//        context.close(); // 关闭容器
    }
}

InitializingBean 和 DisposableBean

Spring 提供的初始化与销毁处理接口

  • Bean 的初始化与销毁处理操作方法,极大的丰富了 Bean 的生命周期控制,但是传统的实现方式是基于 XML 文件进行配置定义,这样的配置方式虽然灵活,但是降低了程序代码的可读性,同时使得代码的维护出现困难。
  • 在现代的 Spring 开发中已经不再提倡基于 XML 的配置方式处理了,而此时要想实现同样的生命周期控制操作,就需要使用 InitializingBean(Bean 初始化操作接口)与 DisposableBean(Bean 销毁操作接口)进行实现
1、
package com.yootk.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class MessageListener implements InitializingBean, DisposableBean { // 自定义的消息监听方法
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageListener.class);
    public void onRecieve(String msg) { // 消息的接收处理
        LOGGER.info("【接收到新消息】{}", msg); // 日志输出消息内容
    }
    @Override
    public void destroy() throws Exception {
        LOGGER.info("【通道关闭】断开服务器连接,关闭消息监听服务。");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("【通道连接】服务器消息通道建立成功。");
    }
}

2、
package com.yootk.main;

import com.yootk.listener.MessageListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class StartYootkSpringApplication { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(StartYootkSpringApplication.class);
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(); // 注解方式来启动Spring容器
        context.scan("com.yootk.listener"); // 定义扫描包
        context.refresh(); // 刷新操作
        MessageListener listener = context.getBean(MessageListener.class); // 获取Bean实例
        for (int x = 0; x < 3; x++) {   // 接收消息内容
            listener.onRecieve("沐言科技:www.yootk.com");
        }
        context.close(); // 关闭容器
    }
}

JSR-250 注解管理生命周期

1、
// https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api
implementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'


2、
package com.yootk.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class MessageListener { // 自定义的消息监听方法
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageListener.class);
    public void onRecieve(String msg) { // 消息的接收处理
        LOGGER.info("【接收到新消息】{}", msg); // 日志输出消息内容
    }
    @PreDestroy // 销毁方法
    public void closeChannel() {
        LOGGER.info("【通道关闭】断开服务器连接,关闭消息监听服务。");
    }

    @PostConstruct // 初始化方法
    public void openChannel() {
        LOGGER.info("【通道连接】服务器消息通道建立成功。");
    }
}


3、
package com.yootk.main;

import com.yootk.listener.MessageListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class StartYootkSpringApplication { // Spring容器的启动类
    private static final Logger LOGGER = LoggerFactory.getLogger(StartYootkSpringApplication.class);
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(); // 注解方式来启动Spring容器
        context.scan("com.yootk.listener"); // 定义扫描包
        context.refresh(); // 刷新操作
        MessageListener listener = context.getBean(MessageListener.class); // 获取Bean实例
        for (int x = 0; x < 3; x++) {   // 接收消息内容
            listener.onRecieve("沐言科技:www.yootk.com");
        }
        context.close(); // 关闭容器
    }
}


4、
package com.yootk.vo;

public interface ICompany {
    public String getTitle();
}


5、
package com.yootk.vo;
public class CompanyA implements ICompany {
    private String title;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}


6、
package com.yootk.vo;


public class CompanyB implements ICompany {
    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}


7、
package com.yootk.config;

import com.yootk.vo.CompanyA;
import com.yootk.vo.CompanyB;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class NewCompanyConfig {
    @Bean
    public CompanyA companyA() {
        CompanyA companyA = new CompanyA();
        companyA.setTitle("Hello Muyan");
        return companyA;
    }
    @Bean
    public CompanyB companyB() {
        CompanyB companyB = new CompanyB();
        companyB.setTitle("Hello Yootk");
        return companyB;
    }
}


8、
package com.yootk.test;

import com.yootk.config.NewCompanyConfig;
import com.yootk.vo.ICompany;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

// 表示要进行Spring配置文件的加载,后续也可能是进行配置类的加载
@ContextConfiguration(classes = {NewCompanyConfig.class}) // 定义配置类
@ExtendWith(SpringExtension.class) // 表示此时使用外部的测试工具(JUnit5)
public class TestCompany {
    private static final Logger LOGGER =
            LoggerFactory.getLogger(TestCompany.class) ;// 获取日志实例
    @Autowired // 自动注入接口实例
    @Qualifier("companyA")
    private ICompany company; // 注入接口实例
    @Test
    public void testPrint() {
        LOGGER.info("【ICompany接口实例】title = {}、class = {}",
                this.company.getTitle(), this.company.getClass().getName());
    }
}


9、
// https://mvnrepository.com/artifact/jakarta.annotation/jakarta.annotation-api
implementation group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '2.1.0'

10、
package com.yootk.test;

import com.yootk.config.NewCompanyConfig;
import com.yootk.vo.ICompany;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;



// 表示要进行Spring配置文件的加载,后续也可能是进行配置类的加载
@ContextConfiguration(classes = {NewCompanyConfig.class}) // 定义配置类
@ExtendWith(SpringExtension.class) // 表示此时使用外部的测试工具(JUnit5)
public class TestCompany {
    private static final Logger LOGGER =
            LoggerFactory.getLogger(TestCompany.class) ;// 获取日志实例
    @Resource(name = "companyA")
    private ICompany company; // 注入接口实例
    @Test
    public void testPrint() {

        LOGGER.info("【ICompany接口实例】title = {}、class = {}",
                this.company.getTitle(), this.company.getClass().getName());
    }
}

Lifecycle 生命周期处理规范

Lifecycle 生命周期控制接口

  • Bean 的生命周期受到容器的控制管理,所以为了进一步规范化这种容器管理的机制在 Spring2.0 版本中增加了 Lifecycle 生命周期控制接口,在该接口中除了定义有初始化与销毁的方法之外,还提供了 Bean 运行状态的判断,而后依据该状态来决定当前的 Bean 是否要执行初始化或销毁的处理操作
1、
package com.yootk.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.Lifecycle;
import org.springframework.stereotype.Component;


@Component
public class MessageListener implements Lifecycle { // 自定义的消息监听方法
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageListener.class);
    private boolean running = false; // 描述的是当前的运行状态
    public void onRecieve(String msg) { // 消息的接收处理
        LOGGER.info("【接收到新消息】{}", msg); // 日志输出消息内容
    }

    @Override
    public void start() {
        LOGGER.info("【通道连接】服务器消息通道建立成功。");
        this.running = true; // 当前已经启动了
    }

    @Override
    public void stop() {
        LOGGER.info("【通道关闭】断开服务器连接,关闭消息监听服务。");
        this.running = false; // 关闭处理
    }

    @Override
    public boolean isRunning() {
        // 判断当前的操作Bean是否已经启动,如果没有启动返回false,则触发start()执行
        // 如果当前的方法返回的是一个true,则表示已经启动了,那么不调用start()方法
        return this.running;
    }
}

SmartLifecycle 生命周期扩展

SmartLifecycle 扩展生命周期接口关联结构

  • Lifecycle 提供了最基础的生命周期管理方法,然而在实际的项目应用中,可能会有若干个不同的 Bean 都需要生命周期的扩展处理,在这样的情况下为了更好的编排多个 Bean 的生命周期方法的执行顺序,在 Spring 3.0 版本中开始提供有 SmartLifecycle 子接口,并提供了生命周期控制的新方法
1、
package com.yootk.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;


@Component
public class MessageListener implements SmartLifecycle { // 自定义的消息监听方法
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageListener.class);
    private boolean running = false; // 描述的是当前的运行状态
    public void onRecieve(String msg) { // 消息的接收处理
        LOGGER.info("【{1} 接收到新消息】{}", msg); // 日志输出消息内容
    }

    @Override
    public void start() {
        LOGGER.info("【{1} 通道连接】服务器消息通道建立成功。");
        this.running = true; // 当前已经启动了
    }

    @Override
    public void stop() {
        LOGGER.info("【{1} 通道关闭】断开服务器连接,关闭消息监听服务。");
        this.running = false; // 关闭处理
    }

    @Override
    public boolean isRunning() {
        // 判断当前的操作Bean是否已经启动,如果没有启动返回false,则触发start()执行
        // 如果当前的方法返回的是一个true,则表示已经启动了,那么不调用start()方法
        return this.running;
    }

    @Override
    public boolean isAutoStartup() { // 配置是否自动执行start()方法
        // 返回true:上下文对象调用refresh()方法的时候,会自动执行start()方法
        // 返回false:上下文对象必须显示通过start()方法启动时才会调用start()方法
        return true; // 不需要调用start()方法
    }

    @Override
    public int getPhase() {
        return 1;
    }
}


2、
package com.yootk.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;


@Component
public class NewsListener implements SmartLifecycle { // 自定义的消息监听方法
    private static final Logger LOGGER = LoggerFactory.getLogger(NewsListener.class);
    private boolean running = false; // 描述的是当前的运行状态
    public void onRecieve(String msg) { // 消息的接收处理
        LOGGER.info("【{2} 接收到新闻】{}", msg); // 日志输出消息内容
    }

    @Override
    public void start() {
        LOGGER.info("【{2} 新闻通道连接】服务器消息通道建立成功。");
        this.running = true; // 当前已经启动了
    }

    @Override
    public void stop() {
        LOGGER.info("【{2} 新闻通道关闭】断开服务器连接,关闭消息监听服务。");
        this.running = false; // 关闭处理
    }

    @Override
    public boolean isRunning() {
        // 判断当前的操作Bean是否已经启动,如果没有启动返回false,则触发start()执行
        // 如果当前的方法返回的是一个true,则表示已经启动了,那么不调用start()方法
        return this.running;
    }

    @Override
    public boolean isAutoStartup() { // 配置是否自动执行start()方法
        // 返回true:上下文对象调用refresh()方法的时候,会自动执行start()方法
        // 返回false:上下文对象必须显示通过start()方法启动时才会调用start()方法
        return true; // 不需要调用start()方法
    }

    @Override
    public int getPhase() {
        return Integer.MIN_VALUE;
    }
}


3、
package com.yootk.main;

import com.yootk.listener.MessageListener;
import com.yootk.listener.NewsListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.concurrent.TimeUnit;

public class StartYootkSpringApplication { // Spring容器的启动类
    private static final Logger LOGGER =
            LoggerFactory.getLogger(StartYootkSpringApplication.class);
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(); // 注解方式来启动Spring容器
        context.scan("com.yootk.listener"); // 定义扫描包
        context.refresh(); // 刷新操作
//        context.start(); // 已经配置了自动的启动方式,不再手工启动容器
        startMessageThread(context);
        startNewsThread(context);
        TimeUnit.SECONDS.sleep(3); // 延迟3秒结束
        context.close(); // 关闭容器
    }

    public static void startMessageThread(ApplicationContext context) {
        new Thread(() -> {
            MessageListener listener = context.getBean(MessageListener.class); // 获取Bean实例
            for (int x = 0; x < 3; x++) {   // 接收消息内容
                listener.onRecieve("沐言科技:www.yootk.com");
            }
        }, "MessageThread").start();
    }
    public static void startNewsThread(ApplicationContext context) {
        new Thread(() -> {
            NewsListener listener = context.getBean(NewsListener.class); // 获取Bean实例
            for (int x = 0; x < 3; x++) {   // 接收消息内容
                listener.onRecieve("沐言科技:www.yootk.com");
            }
        }, "NewsThread").start();
    }
}

SmartlnitializingSingleton 回调处理

SmartInitializingSingleton 接回调处理

  • 在 Bean 对象的实例化生命周期的管理过程之中,由于不同的 Bean 在实例化后可能会存在有各自特殊的配置处理操作,所以为了统一这一功能,在 Spring 中提供了 SmartlnitializingSingleton 接口,该接口主要应用于单例 Bean 的管理,开发者只需要在 afterSingletonsInstantiated0 方法中编写所需的回调操作即可自动执行。
1、
package com.yootk.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;


public class Emp implements SmartInitializingSingleton {
    private static final Logger LOGGER = LoggerFactory.getLogger(Emp.class);
    private String ename;
    private double salary;
    @Override
    public void afterSingletonsInstantiated() {
        LOGGER.info("【属性修改前】salary = {}", this.salary); // 属性修改前的信息
        if (this.salary < 0) {  // 配置的属性有错误
            this.salary = 800.0; // 纠正属性的错误项
        }
        LOGGER.info("【属性修改后】salary = {}", this.salary);
    }
    // 在Spring进行Bean配置的处理过程之中,可以通过settter方法进行属性内容的设置
    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
}


2、
package com.yootk.config;

import com.yootk.listener.Emp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class EmpConfig {
    @Bean
    public Emp emp() {
        Emp emp = new Emp();
        emp.setEname("李兴华");
        emp.setSalary(-33.33); // 工资不合理
        return emp;
    }
}


3、
package com.yootk.main;

import com.yootk.config.EmpConfig;
import com.yootk.listener.Emp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class StartYootkSpringApplication { // Spring容器的启动类
    private static final Logger LOGGER =
            LoggerFactory.getLogger(StartYootkSpringApplication.class);
    public static void main(String[] args) throws Exception {
        SmartInitializingSingleton s;
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(); // 注解方式来启动Spring容器
        context.register(EmpConfig.class); // Bean注册
        context.refresh(); // 刷新操作
        Emp emp = context.getBean(Emp.class); //  获取Bean实例
        LOGGER.info("【雇员信息】姓名:{}、工资:{}", emp.getEname(), emp.getSalary());
    }
}


4、
package com.yootk.config;

import com.yootk.listener.Emp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class EmpConfig {
    @Scope("prototype")
    @Bean
    public Emp emp() {
        Emp emp = new Emp();
        emp.setEname("李兴华");
        emp.setSalary(-33.33); // 工资不合理
        return emp;
    }
}

demo


上次编辑于: