zoukankan      html  css  js  c++  java
  • 20191103 《Spring5高级编程》笔记-第4章

    第4章 详述Spring配置和Spring Boot

    4.2 管理bean生命周期

    通常,有两个生命周期事件与bean特别相关:post-initializationpre-destruction

    一旦完成bean的所有属性值设置以及所配置的依赖项检查,就会触发post-initialization事件。在销毁bean实例之前,pre-destruction事件被触发。对于原型作用域的bean来说,不会触发pre-destruction事件。

    Spring为这两个事件提供了三种机制:

    1. 基于接口;InitializingBean/DisposableBean
    2. 基于方法;bean定义的init-method/destroy-method(或@BeaninitMethod/destroyMethod
    3. 基于注解(JSR-250 JavaBean生命周期);@PostConstruct/@PreDestroy

    Spring bean的生命周期:

    image

    4.3 挂钩到bean的创建

    4.3.1 在创建bean时执行方法

    <beans>中,添加属性 default-lazy-init="true" (对应@Lazy注解)来指示Spring仅在应用程序请求bean时才实例化配置文件中定义的bean。如果没有指定该属性,那么Spring将尝试在启动ApplicationContext的过程中初始化所有的bean。

    <beans>中,还可以指定默认的初始化方法和销毁方法,default-init-method="" default-destroy-method=""

    初始化方法的唯一限制是不能接受任何参数。

    4.3.3 使用JSR-250 @PostConstruct注解

    使用@PostConstruct 需要在配置文件中添加 <context:annotation-config/>

    三种方法,如果不考虑移植性,使用 InitializingBean 接口。

    4.4 使用@Bean声明一个初始化方法

    Spring首先调用@PostConstruct注解的方法(1. 基于注解),然后调用afterPropertiesSet()2. 基于接口),最后调用配置文件中指定的初始化方法(3. 基于方法)。

    4.7 了解解析的顺序

    使用关闭钩子

    在Spring中销毁回调函数的唯一缺点是它们不会自动触发;需要记住在应用程序关闭之前调用 AbstractApplicationContext.close()或使用AbstractApplicationContext.registerShutdownHook()。该方法自动指示Spring注册底层JVM运行时的关闭钩子。

    4.8 让Spring感知bean

    4.8.1 使用BeanNameAware接口

    想要获取自己名称地bean,可以实现 BeanNameAware 接口,它有一个方法 setBeanName(String)。在完成bean的配置之后且在调用任何生命周期回调(初始化回调或销毁回调)之前,Spring会调用setBeanName()方法。

    这里获取名称是获取bean的id,而不是name。

    4.8.2 使用ApplicationContextAware接口

    通过使用 ApplicationContextAware 接口,bean可以获得对配置它们的 ApplicationContext 实例的引用。创建此接口的主要原因,是为了允许bean在应用程序中访问Spring的ApplicationContext ,但是应该避免这种做法,并使用依赖注入为bean添加协作者。

    AbstractApplicationContext.close()AbstractApplicationContext.registerShutdownHook()区别:
    close()立即调用doClose()方法进行容器销毁工作;registerShutdownHook()将销毁工作加入虚拟机关闭钩子,随虚拟机关闭时销毁。

    如果在实现ApplicationContextAware接口的setApplicationContext方法中分别调用这两个方法,会有差别。
    Spring的refresh()方法的顺序是先调用
    finishBeanFactoryInitialization(beanFactory); -> postProcessBeforeInitialization -> ApplicationContextAware,后finishRefresh -> initLifecycleProcessor,也就是说,先调用ApplicationContextAware接口的setApplicationContext方法,后初始化生命周期相关的方法。
    这导致,如果在setApplicationContext方法中调用close方法,不会调用bean的销毁方法。而在setApplicationContext方法中调用registerShutdownHook方法,因为实在虚拟机关闭时才执行销毁方法,此时已经初始化好了生命周期相关的方法,所以会执行bean的销毁方法。

    4.9 使用FactoryBean

    当使用的类无法通过new操作符创建时,FactoryBean是完美的解决方案。如果使用通过工厂方法创建的对象,并且希望在Spring应用程序中使用这些类,那么可以创建FactoryBean以充当适配器,从而使类可以充分利用Spring的IOC功能。

    FactoryBean是一个bean,可以作为其他bean的工厂。FactoryBean像任何普通bean一样在ApplicationContext中配置,但是当Spring使用FactoryBean接口来满足依赖或查找请求时,它并不返回FactoryBean,而是调用FactoryBean.getObject()方法并返回调用的结果。

    4.10 直接访问FactoryBean

    访问FactoryBean很简单,在调用getBean()时用"&"符号作为bean名称的前缀即可。

    4.11 使用factory-beanfactory-method属性

    有时需要实例化由非Spring的第三方应用程序提供的JavaBean。此时,不知道如何实例化该类,只知道第三方应用程序提供了一个可用于获取Spring应用程序所需的JavaBean实例的类。这种情况下,可以使用<bean>中的factory-beanfactory-method属性。

    4.12 JavaBean PropertyEditor

    java.beans.PropertyEditor 是一个接口,将属性值从其本机类型表示形式转换为字符串。最初,该接口的设计目的是允许将属性值作为字符串值输入到编辑器中,并将它们转换为正确的类型。

    为了避免认为创建String类型的属性,Spring允许定义PropertyEditor 以实现基于字符串的属性值到正确的类型的转换。Spring对PropertyEditor 的支持在org.springframework.beans.propertyeditors 包下,以及org.springframework.core.io.ResourceEditor类。

    4.12.1 使用内置的PropertyEditor

    Spring在refresh()prepareBeanFactory(beanFactory);beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    这里添加了默认的PropertyEditorRegistrar,会注册一些,具体可看ResourceEditorRegistrar#registerCustomEditors

    自定义PropertyEditorRegistrar时,可以通过org.springframework.beans.factory.config.CustomEditorConfigurer,这是一个BeanFactoryPostProcessor,Spring在 invokeBeanFactoryPostProcessors(beanFactory);中会调用beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);PropertyEditorRegistrar加入容器,也就是AbstractBeanFactory#propertyEditorRegistrars中。

    示例:

    <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer"
          p:propertyEditorRegistrars-ref="propertyEditorRegistrarsList"/>
    
    <util:list id="propertyEditorRegistrarsList">
        <bean class="study.hwj.chapter04.propertyeditor.PropertyEditorBean$CustomPropertyEditorRegister"/>
    </util:list>
    

    image

    对映射文件中的String进行处理时,查找对应类型的PropertyEditor代码在TypeConverterDelegate#convertIfNecessary,调用阶段位于refresh()finishBeanFactoryInitialization(beanFactory);
    默认类型与PropertyEditor的映射关系可以查看 PropertyEditorRegistrySupport#createDefaultEditors

    4.12.2 创建自定义PropertyEditor

    通过继承 java.beans.PropertyEditorSupport 并实现 setAsText 方法,可以快速实现自定义 PropertyEditor

    通过CustomEditorConfigurer将自定义的PropertyEditor注册进Spring。

    <bean name="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="study.hwj.chapter04.propertyeditor.FullName"
                       value="study.hwj.chapter04.propertyeditor.NamePropertyEditor"/>
            </map>
        </property>
    </bean>
    

    原理
    CustomEditorConfigurer 是一个BeanFactoryPostProcessor,在postProcessBeanFactory方法中,调用了this.customEditors.forEach(beanFactory::registerCustomEditor);

    从版本3开始,Spring引入了类型转换API(Type Conversion API)和字段格式化SPI(Field Formatting Service Provider Interface),它们提供了一个更简单且结构良好的API来执行类型转换和字段格式化。这对于WEB应用程序开发来说尤其有用。

    4.13 更多的Spring ApplicationContext配置

    在Spring中,BeanFactory接口的各种实现负责bean实例化,为Spring管理的bean提供依赖注入和生命周期支持。作为BeanFactory接口的扩展,ApplicationContext的主要功能是提供一个更丰富的框架来构建应用程序。它允许以完全声明的方式配置和管理Spring以及Spring所管理的资源。这意味着Spring 尽可能提供支持类来自动将ApplicationContext加载到应用程序中,从而不需要编写任何代码来访问ApplicationContext。目前仅在构建Web应用程序时可用。

    4.13.1 使用MessageSource进行国际化

    Spring真正擅长的领域是支持国际化(i18n)。通过MessageSource接口,应用程序可以访问以各种语言存储的字符串资源(称为消息)。对于希望在应用程序中得到支持的每种语言,都会维护一个与其他语言中的消息相对应的消息列表。

    ApplicationContext接口扩展了MessageSource,并对加载信息以及特定环境下的可用性提供了特别的支持。虽然消息的自动加载在任何环境都可用,但是只有在某些Spring管理的场景中才提供自动访问。

    使用MessageSource进行国际化

    初了ApplicationContext,Spring还提供了三个MessageSource实现:

    • ResourceBundleMessageSource
    • ReloadableResourceBundleMessageSource
    • StaticMessageSource

    StaticMessageSource无法在外部配置,不应该在生产环境使用。ResourceBundleMessageSource通过使用Java ResourceBundle加载消息。ReloadableResourceBundleMessageSource基本上相同,只不过它支持对基础源文件进行预定的重新加载。这三个MessageSource都实现了另一个名为HierarchicalMessageSource的接口,它允许嵌套多个MessageSource实例。

    要想充分利用ApplicationContext对MessageSource的支持,必须定义一个名为messageSource的MessageSource类型的bean。

    在查找特定语言环境下的消息时,ResourceBundle会查找以基本名称和语言环境名称组合的文件。例如,如果基本名称是label,在简体中文(zh_CN)的语言环境中,那么ResourceBundle会查找label_zh_CN.properties的文件。

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
          p:basenames-ref="basenames"/>
    
    <util:list id="basenames">
        <value>label</value>
    </util:list>
    

    为什么使用ApplicationContext作为MessageSource

    为了Spring对Web应用程序的支持。Spring可以尽可能的将ApplicationContext作为MessageSource公开到视图层。这意味着当使用Spring的JSP标记库时,<spring:message>标记会自动从ApplicationContext读取信息,当使用JSTL时,<fmt:message>也会执行相同的操作。

    4.13.2 在独立的应用程序中使用MessageSource

    MessageSourceResolvable接口

    从MessageSource查找消息时,可以使用实现了MessageSourceResolvable的对象代替键和一组参数。该接口在Spring验证库中被广泛使用,用来将Error对象链接到对应的国际化错误消息。

    4.13.3 应用程序事件

    事件是派生自ApplicationEvent的类,而ApplicationEvent派生自java.util.EventObject。任何bean都可以通过实现ApplicationListener<T>接口来监听事件;当配置时,ApplicationContext会自动注册实现此接口的任何bean作为监听器。事件通过ApplicationEventPublisher.publishEvent()方法发布,而ApplicationContext扩展了ApplicationEventPublisher接口,所以可以让发布bean实现ApplicationContextAware,使其获得ApplicationContext后发布事件。

    不需要特殊配置就可以使用ApplicationContext注册ApplicationListener,它由Spring自动获取。

    发布事件时,最好发布ApplicationEvent,而不是其他类型。发布xxxApplicationEvent,实现了ApplicationListener<xxxApplicationEvent>Listener可以接收到消息;发布其他类型,内容会被封装为PayloadApplicationEvent,可以通过实现ApplicationListener<PayloadApplicationEvent>的Listener接收到消息。

    4.14 访问资源

    Spring以独立于协议的方式提供了访问资源的统一机制。

    Spring的资源支持的核心是org.springframework.core.io.Resource接口。

    Spring的ApplicationContext接口扩展了ResourcePatternResolverResourceLoader接口的子接口),用来访问文件(file:)、类路径(classpath:)或URL资源(http:)。

    classpath:协议是特定于Spring的,指示ResourceLoader应该在类路径中查找资源。

    一旦获得Resource实例,就可以自由的访问内容。当使用http:协议时,对getFile()的调用会导致FileNotFoundException异常。出于这个原因,建议使用getInputStream()来访问资源内容,因为它可能适用于所有可能的资源类型。

    4.15.1 Java中的ApplicationContext配置

    可以使用配置类(被@Configuration注解)来替代xml配置文件。@Configuration注解用来告知Spring这是一个基于Java的配置文件。该类将包含用@Bean注解的方法,它们表示bean声明。@Bean注解等同于<bean>标记,方法名称等同于<bean>标记内的id属性。

    image

    有时出于测试目的,可以将配置类声明为静态内部类。

    @ComponentScan告诉Spring应该扫描那些带有bean定义注解的包。它与XML配置中的<context:component-scan>标签相同。

    @Import注解通过导入配置类所定义的bean,可以从另一个配置类访问该bean。

    4.15.2 Spring混合配置

    在Java配置类中可以使用@ImportResource注解从XML文件导入bean声明。另外,也可以执行相反的操作:在Java配置类中定义的bean可以导入XML配置文件。必须声明配置类类型的bean,并且必须使用<context:annotation-config/>启用对注解方法的支持。这使得在类中声明的bean可以被配置为在XML文件中声明的bean的依赖项。

    @Configuration
    public class AppConfigSix {
    
        @Bean
        public MessageProvider provider() {
            return new ConfigurableMessageProvider("Love 6");
        }
    }
    
    <?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:p="http://www.springframework.org/schema/p"
           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/>
    
        <bean class="study.hwj.chapter04.javaconfig.AppConfigSix"/>
    
        <bean id="messageRenderer" class="study.hwj.chapter04.javaconfig.StandardOutMessageRenderer"
              p:messageProvider-ref="provider"/>
    </beans>
    

    4.15.3 Java或XML配置?

    每种方法都有各自的优缺点,但决定使用其中一种方法时,就应该坚持使用并保持配置样式一致,而不是一会用Java类,一会儿用XML文件。使用一种方法会让维护工作更容易。

    4.16 配置文件(Profile)

    配置文件(configuration profile)只是Spring仅配置在指定配置文件处于活动状态时定义的ApplicationContext实例。

    <beans>标签的profile属性指定配置文件的profile,只有当指定的profile处于active状态时,文件中的bean才会被实例化。

    通过传递JVM参数,-Dspring.profiles.active=xxx,来激活profile。调用ctx.getEnvironment().setActiveProfiles("kindergarten");可以以编程的方式在代码中激活profile。注意,需要在调用ctx.refresh()之前设置,否则无效。

    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
    ctx.getEnvironment().setActiveProfiles("kindergarten");
    ctx.load("classpath:chapter04/ac_configfile_*.xml");
    ctx.refresh();
    

    4.17 使用Java配置来配置Spring配置文件

    @Profile等价于<beans>profile属性。一般配合@Configuration注解使用。

    解析Profile在ctx.load("classpath:chapter04/ac_configfile_*.xml");这一步完成。

    4.18 EnviromentPropertySource抽象

    除配置文件外,Environment接口封装的其他关键信息都是属性。属性用来存储应用程序的底层环境配置,例如应用程序文件夹的文职、数据库连接信息等。

    Spring中的EnvironmentPropertySource抽象功能帮助开发人员访问来自运行平台的各种配置信息。在抽象环境中,所有系统属性、环境变量和应用程序属性都有Environment接口提供,Spring启动ApplicationContext时将填充该接口。

    对于PropertySource抽象,Spring按照以下默认顺序访问属性:

    1. 运行JVM的系统属性;
    2. 环境变量;
    3. 应用程序定义的属性;

    可以通过 propertySources.addxxx 调整访问顺序。

    现实中,很少需要直接与Environment接口进行交互,但会以 ${} 的形式使用要给属性占位符,并将解析后的值注入Spring bean中。

    使用<context:property-placeholder />标签将属性从属性文件中加载到Spring的Environment,该Environment被封装到ApplicationContext接口中。PropertySource会按照默认行为去解析,如果想要覆盖并使用自定义的属性值,使用local-override="true"

    4.19 使用JSR-330注解进行配置

    JSR-330提供Java的依赖注入支持,使用JSR-330可以帮助我们轻松从Spring迁移到JEE6容器或其他兼容的IOC容器。

    要支持JSR-330注解,需要将javax.inject3依赖项添加到项目中。

    JSR-330标准注解:

    • @Named:与Spring中的@Component注解相同
    • @Inject:用于自动注入,类似于@Autowired
    • @Singleton:标识单例bean,在JSR-330标准中,bean默认作用域是非单例,即Spring的prototype作用域

    JSR-330注解与Spring注解差异:

    • Spring的@Autowired可以指定required属性表明必须完成DI,JSR-330的@Inject没有等价属性。Spring还提供了@Qualifier注解精细控制自动装配
    • JSR-330仅支持单例和非单例作用域,Spring支持更多作用域。
    • Spring中,可以使用@Lazy实现懒加载,JSR-330没有等价注解

    相比于使用JSR-330,更推荐使用Spring注解,除非应用程序需要独立于IOC容器。

    4.20 使用Groovy进行配置

    可以通过Groovy语言来配置bean定义和ApplicationContext。

    4.21 Spring Boot

    Spring Boot项目旨在简化使用Spring构建应用程序的入门体验。

    最简单的Spring Boot配置:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
    

    如果@SpringBootApplication没有定义组件扫描属性,那么它将扫描其注解的类所在的包。

    Spring Boot为Web应用提供了起始依赖项spring-boot-starter-web

  • 相关阅读:
    1.2 JAVA的String类和StringBuffer类
    1.7 JAVA异常总结
    2.1 JQuery框架(封装JavaScript框架)
    1.6 JSON存储数据方式(JavaScript对象表示法)
    1.33 JavaScript之HTML的DOM(三)
    1.32 JavaScript的BOM(二)
    【转】SQL 生成连续字符
    木兰国产编程语言 Mulan--附带下载地址
    【python】两行代码实现近百年的正反日期查询--20200202
    Linux下扫描服务器IP地址是否冲突(arp-scan)
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/11789233.html
Copyright © 2011-2022 走看看