5.1 Spring 容器技术内幕

5.1.1 内部工作机制

Spring 的 AbstractApplicationContext 是 ApplicationContext 抽象实现类,该抽象类的 refresh() 方法定义了 Spring 容器在加载配置文件后的各项处理过程

  • 1.初始化 BeanFactory:根据配置文件实例化 BeanFactory,getBeanFactory() 方法由具体子类实现。在这一步里,Spring 将配置文件的信息装入到容器的 Bean 定义注册表(BeanDefinitionRegistry)中,但此时 Bean 还未初始化;
  • 2.调用工厂后处理器:根据反射机制从 BeanDefinitionRegistry 中找出所有 BeanFactoryPostProcessor 类型的 Bean,并调用其 postProcessBeanFactory() 接口方法;
  • 3.注册 Bean 后处理器:根据反射机制从 BeanDefinitionRegistry 中找出所有 BeanPostProcessor 类型的 Bean,并将他们注册到容器 Bean 后处理器的注册表中;
  • 4.初始化消息源:初始化容器的国际化信息资源;
  • 5.初始化应用上下文事件广播器;
  • 6.初始化其他特殊的 Bean:这是一个钩子方法,子类可以借助这个方法执行一些特殊的操作:如 AbstractRefreshableWebApplicationContext 就使用该方法执行初始化 ThemeSource 的操作;
  • 7.注册事件监听器;
  • 8.初始化所有单实例的 Bean,使用懒初始化模式的 Bean 除外:初始化 Bean 后,将它们放入 Spring 容器的缓存中;
  • 9.发布上下文刷新事件:创建上下文刷新事件,事件广播器负责将这些事件广播到每个注册的事件监听器中。

Bean 从创建到销毁的生命历程,Spring 协调多个组件共同完成这个复杂的工作流程

  • 1.ResourceLoader 从存储介质中加载 Spring 配置信息,并使用 Resource 表示这个配置文件夹的资源;
  • 2.BeanDefinitionReader 读取 Resource 所指向的配置文件资源,然后解析配置文件。配置文件中每一个< bean >解析成一个 BeanDefinition 对象,并保存到 BeanDefinitionRegistry中;
  • 3.容器扫描 BeanDefinitionRegistry 中的BeanDefinittion,使用 Java的反射机制自动识别出 Bean 工厂后处理器(实现 BeanFactoryPostProcessor 接口)的 Bean,然后调用这些 Bean 工厂后处理器对 BeanDefinitionRegistry 中的 BeanDefinition 进行加工处理。主要完成两项工作:
    • 对使用到的占位符的< bean > 元素标签进行解析,得到最终的配置值,这意味对一些半成品式的 BeanDefinition 对象进行加工处理并得到的成品的 BeanDefinittion 对象;
    • 对 BeanDefinitionRegistry 中的 BeanDefinittion 进行扫描,通过 Java反射机制找出所有属性编辑器的 Bean (实现java.beans.PropertyEditor 接口的 Bean),并自动将他们注解到 Spring容器的属性编辑器注册表中(PropertyEditorRegistry)
  • 4.Spring 容器从 BeanDefinitionRegistry 中取出加工后的 BeanDefinittion, 并调用 InstantiationStrategy 着手进行 Bean 实例化的工作;
  • 5.在实例化 Bean 时,Spring 容器使用 BeanWrapper 对 Bean 进行封装,BeanWrapper提供了很多以 Java 反射机制操作的 Bean 方法,他将结合 Bean 的 BeanDefinittion 以及容器中属性编辑器,完成 Bean 属性的设置工作;
  • 6.利用容器中注册的 Bean 后处理器(实现 BeanPostProcessor 接口的 Bean)对已经完成属性设置工作的 Bean 进行后续加工,直接装配出一个准备就绪的 Bean。

Spring 组件按其所承担的角色可以划分为两类

  • 物料组件:Resource、BeanDefinition、PropertyEditor 以及最终的 Bean 等,他们是加工流程中被加工、被消费的组件;
  • 加工设备组件:ReshourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy 以及 BeanWrapper 等组件像是流水线上不同环节的加工设备,对物料组件进行加工处理。
5.1.2 BeanDefinition

org.springframework.beans.factory.config.BeanDefinition 是配置文件< bean >元素标签在容器中的内部表示。< bean >元素标签拥有 class、scope、lazy-init 等配置属性,BeanDefinition则提供了相应的 BeanClass、scope、lazyInit 属性,BeanDefinition 就像是 < bean >的镜中人,两者是一一对应的

RootBeanDefinition 是最常用的实现类,他对应一般性的< bean >元素标签,我们知道在配置文件中可以定义父< bean >和子< bean >,父< bean >用RootBeanDefinition 表示,而子 < bean >用 ChildBeanDefinition 表示,而没有父< bean >的< bean >就使用RootBeanDefinition表示。

Spring 通过 BeanDefinition 将配置文件中的< bean >配置信息转化为容器的内部表示,并将这些 BeanDefinition 注册到 BeanDefinitionRegistry 中。Spring 容器的BeanDefinitionRegistry 就像是 Spring 配置信息的内存数据库,后续操作直接从 BeanDefinitionRegistry 中读取配置信息。一般情况下,BeanDefinition 只是在容器启动时加载并解析,除非容器刷新或重启,这些信息不会发生变化,当然如果用户由特殊的需求,也可以通过编程的方式在运行期间调整 BeanDefinition 的定义。

创建最终的 BeanDefinition 主要包含两个步骤:

  • 利用BeandefinitionReader 对配置信息 Resource 进行读取,通过XML解析器解析配置信息的 DOM 对象,简单地为每个< bean >生成对应的BeanDefinition对象。但是这里生成的BeanDefinition可能时半成品,因为在配置文件中,我们可能通过占位符变量引用外部属性文件的属性,这些占位符变量在这一步还没有被解析出来。
  • 利用容器中注册的 BeanFactoryPostProcessor 对半成品的 BeanDefinition 进行加工处理,将以占位符表示的配置解析为最终的实际值,这样半成品的 BeanDefinition 就成品的 BeanDefinition。
5.1.3 InstantiationStrategy

org.springframework.beans.factory.support.InstantiationStrategy 负责根据 BeanDefinition对象创建一个 Bean 实例。Spring之所以将实例化Bean 的工作通过一个策略接口进行描述,是为了方便采用不同的实例化策略,以便满足不同的应用需求:如通过CGLib类库为Bean动态创建子类再进行实例化。

SimpleInstantiationStrategy 策略是最常用的实现类,该策略利用 Bean 实现类的默认构造函数、带参构造函数或工厂方法创建Bean的实例。

CglibSubclassingInstantiationStrategy 扩展了 SimpleInstantiationStrategy,为需要进行方法注入的Bean提供了支持。它利用CGLib类库为 Bean 动态生成子类,在子类中生成方法注入的逻辑,然后使用这个动态生成的子类创建Bean的实例。

InstantiationStrategy 仅负责实例化 Bean的操作,相当于执行Java语言中new的功能,它并不会参与Bean属性的设置工作。所以由InstantiationStrategy返回的Bean实例实际上是一个半成品的Bean实例,属性填充的工作留待BeanWrapper来完成。

5.1.4 BeanWrapper

org.springframework.beans.BeanWrapper 是Spring框架中重要的组件类。BeanWrapper相当于一个代理器,Spring 通过 BeanWrapper 完成 Bean属性的填充工作。在 Bean实例被InstantiationStrategy创建出来之后,容器主控制程序将 Bean实例通过BeanWrapper 包装起来,这是通过调用BeanWrapper#setWrappedInstance(Object obj) 方法完成的。

我们可以看出BeanWrapper还有两个顶级类接口,分别是 PropertyAccessor 和 PropertyEditorRegistry。PropertyAccessor接口定义了各种访问Bean属性的方法,如setPropertyValue(String, Object),setPropertyValue(Property Value)等,而PropertyEditorRegistry是属性编辑器的注册表。所以BeanWrapper实现类BeanWrapperImpl具有三重身份:

  • Bean包裹器;
  • 属性访问器;
  • 属性编辑器注册表。

一个BeanWrapperImpl实例内部封装了两类组件:被封装的目标Bean以及一套用于设置Bean属性的属性编辑器。

要顺利的填充Bean属性,除了目标Bean实例和属性编辑器以外,还需要获取Bean对应的BeanDefinition,它从Spring容器的BeanDefinitionRegistry中直接获取。Spring控程序从BeanDefinition获取Bean属性的配置信息PropertyValue,并使用属性编辑器对配置形式的PropertyValue 进行转换以得到Bean属性的值。对Bean其他属性重复这样的步骤,就可以完成Bean所有属性的注入工作。BeanWrapper在内部是使用到Spring的BeanUtils工具类对Bean进行反射操作,设置属性。

5.2 属性编辑器

  • 属性编辑器的主要功能就是将外部的设置值转换为JVM内部的对应类型,所以属性编辑器其实就是一个类型转换器
5.2.1 JavaBean 的编辑器
  • JavaBean 规范通过 java.beans.PropertyEditor 定义了设置 JavaBean属性的方法,通过BeanInfo描述了JavaBean哪些属性是可定制的,此外还描述了可定制属性的PropertyEditor的对应关系。
  • BeanInfo与JavaBean之间的对应关系,通过两者之间规范的命名确立:对应JavaBean的BeanInfo采用如下命名规范:< Bean >BeanInfo。如ChartBean对应BeanInfo为ChartBeanBeanInfo;
  • JavaBean规范提供了一个管理默认属性编辑器的管理器:PropertyEditorManager,该管理器内存保存着一些常见类型的属性编辑器,如果某个JavaBean的常见类型属性没有通过BeanInfo显示指定属性编辑器,IDE将自动使用PropertyEditorManager中注册的对应默认属性编辑器。

PropertyEditor

  • PropertyEditor是属性编辑器的接口,它规定了将外部设置值转化为内部JavaBean属性值的转换接口方法。主要方法说明:
    • Object getValue():返回属性的当前值,基本类型被封装成对应的封装类实例;
    • void setValue(Object new Value):设置属性的值,基本类型以装类传入;
    • String getAsText():将属性对象用一个字符串表示,以便外部属性编辑器能以可视化的方式显示。缺省返回null,表示该属性不能以字符串表示;
    • void setAsText(String text):用以字符串去更新属性的内部值,这个字符串一般从外部属性编辑器传入;
    • String[] getTags():返回表示有效属性值的字符串数组(如Boolean属性对应的有效Tag为true和false),以便属性编辑器能以下拉框的方式显示出来。缺省返回null,表示属性没有匹配的字符值有限集合;
    • String getJavaInitializationString():为属性提供一个表示初始值的字符串,属性编辑器以此值作为属性的默认值;

BeanInfo

  • BeanInfo主要描述了JavaBean哪些属性可以编辑以及对应的属性编辑器,每一个属性对应一个属性描述器PropertyDescriptor。PropertyDescriptor 的构造函数有两个入参:PropertyDescriptor(String propertyName, Class beanClass),其中propertyName为属性名;而beanClass为JavaBean对应的Class。此外PropertyDescriptor还有一个BeanInfo接口最重要的方法就是:PropertyDescriptor[] getPropertyDescriptors(),该方法返回JavaBean的属性描述器数组。
5.2.2 Spring 默认属性编辑器
  • BeanWrapperImpl类扩展了PropertyEditorRegistrySupport类,Spring在PropertyEditorRegistrySupport中为常见属性类型提供了默认的属性编辑器:可分为3大类:
类 别 说 明
基础数据类型 分为几个小类:1) 基本数据类型,如:boolean、byte、short、int等; 2) 基本数据类型封装类,如:Long、Character、Integer等; 3) 两个基本数据类型的数组,char[]和byte[]; 4) 大数类,BigDecimal 和 BigInteger
集合类 为5种类型的集合类 Collection、Set、SortedSet、List 和 SortedMap 提供了编辑器
资源类 用于访问外部资源的8个常见类 Class、Class[]、File、InputStream、Locale、Properties、Resource[] 和 Url
  • PropertyEditorRegistrySupport 中有两个用于保存属性编辑器的Map类型变量:
    • defaultEditors:用于保存默认属性类型的编辑器,元素的键为属性类型,值为对应的属性编辑器实例;
    • customEditors:用于保存用户自定义的属性编辑器,元素的键值和defaultEditors相同。
  • PropertyEditorRegistrySupport 通过类似以下的代码定义默认属性编辑器:
    • this.defaultEditors.pur(char.class, new CharacterEditor(false));
    • this.defaultEditors.pur(Character.class, new CharacterEditor(false));
    • this.defaultEditors.pur(Locale.class, new LocaleEditor(false));
    • this.defaultEditors.pur(Properties.class, newPropertiesEditor(false));
5.2.3 自定义属性编辑器
package com.baobaotao.ditype;import lombok.Getter;
import lombok.Setter;@Getter
@Setter
public class Car {private int maxSpeed;private String brand;private double price;}
package com.baobaotao.ditype;import lombok.Getter;
import lombok.Setter;@Setter
@Getter
public class Boss {private String name;private Car car = new Car();@Overridepublic String toString() {return "Boss [name=" + name + ", car=" + car + "]";}
}
  • 为Car类型提供一个自定义的属性编辑器,这样,我们就通过字面值为Boss 的car属性提供配置值。
package com.baobaotao.ditype;import java.beans.PropertyEditorSupport;public class CustomCarEditor extends PropertyEditorSupport {public void setAsText(String text) {if(text == null || text.indexOf(",") == -1) {throw new IllegalArgumentException("设置的字符串格式不正确");}String[] infos = text.split(",");Car car = new Car();car.setBrand(infos[0]);car.setMaxSpeed(Integer.parseInt(infos[1]));car.setPrice(Double.parseDouble(infos[2]));setValue(car);}
}
<!-- @1 配置自动注册属性编辑器的 CustomEditorConfigurer -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"><property name="customEditors"><map><!-- @2-1 属性编辑器对应的属性类型 --><entry key="com.baobaotao.ditype.Car"><!-- @2-2 对应的属性编辑器Bean --><bean class="com.baobaotao.ditype.CustomCarEditor" /></entry></map></property>
</bean><bean id="car" class="com.baobaotao.ditype.Car"/>
<!-- @3 改属性将使用@2 出的属性编辑器完成属性填充操作 -->
<bean id="boss" class="com.baobaotao.ditype.Boss"><property name="name" value="John" /><property name="car" value="红旗CA72,200,20000.00" />
</bean>
  • 在@1处,我们定义了用于注册自定义属性编辑器的 CustomEditorConfigurer ,Spring 容器将通过反射机制自动调用这个 Bean。CustomEditorConfigurer 通过一个Map属性定义需要自动注册的自定义属性编辑器。在@2处,我们为Car类型指定了一对应属性编辑器的类名。
  • @3处的配置,我们原来通过一个< bean >元素标签配置好car Bean,然后再boss的< bean > 中通过ref引用carBean,但是限制我们之间通过value为car属性提供配置。BeanWrapper 再设置boss的car属性时,他就检索自定义属性编辑器的注册表,当发现Car属性类型拥有对应的属性编辑器CustomEditorConfigurer 时,它就会利用 CustomEditorConfigurer 将"红旗CA72,200,20000.00"转换为Car对象。

5.3 使用外部属性文件

  • 在进行数据源或邮件服务器等资源的配置时,用户可以直接在Spring配置文件中配置用户名/密码、链接地址等信息。但最好将这些配置信息独立到一个外部属性文件中,并在Spring配置文件中通过形如:user{user}、{password}等占位符引用属性文件中的属性项:好处:

    • 减少维护的工作量:在多个应用使用同一资源的情况下,资源的配置信息可以被多个应用共享;
    • 使部署更简单:Spring配置文件主要描述应用工程中的Bean,这些配置信息在开发完成后就基本固定下来。但数据源、邮件服务器等资源配置信息却需要在部署时根据需要设定。
  • Spring 为我们提供了一个PropertyPlaceholderConfigurer,它能够使Bean在配置时引用外部属性文件。

5.3.1 使用外部属性文件

使用属性文件

  • 原始配置数据源方式
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"destroy-method="close"p:driverClassName="com.mysql.jdbc.Driver"p:url="jdbc:mysql://localhost:3306/hm_dream?characterEncoding=utf8"p:username="root"p:password="hm123"/>
  • 推荐使用数据源配置方式
  • jdbc.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/hm_dream?characterEncoding=utf8
username=root
password=hm123
<?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:util="http://www.springframework.org/schema/util"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-3.0.xsd"><!-- @1引入jdbc.properties属性文件 --><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"p:location="classpath:jdbc.properties"p:fileEncoding="utf-8"/><!-- @2通过属性名引用属性值 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"p:driverClassName="${driverClassName}"p:url="${url}"p:username="${username}"p:password="${password}"/>
</beans>

PropertyPlaceholderConfigurer

  • 除location属性外,PropertyPlaceholderConfigurer还有一些常用的属性,在一些高级应用中,可能会使用到:
    • 如果只有一个属性文件,直接使用Location属性指定就可以了,如果是多个属性文件,则可以通过locations属性进行设置,可以像配置List一样配置locations属性;
    • fileEncoding:指定属性文件的编码格式;
    • order:如果有过个PropertyPlaceholderConfigurer,通过该属性指定优先顺序;
    • placeholderPrefix:通过"{属性名}引用属性,"{"则默认为占位符的前缀,改属性可以根据需求改为其他的前缀符
    • palaceholderSuffix:占位符后最,默认为"}"。

使用< context:property-placeholder >引用属性文件

<?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:util="http://www.springframework.org/schema/util"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-3.0.xsd"><!-- @1引入jdbc.properties属性文件 --><context:property-placeholder location="classpath:jdbc.properties" file-encoding="utf8"/><bean id="utf8" class="java.lang.String"><constructor-arg value="utf-8"></constructor-arg></bean><!-- @2通过属性名引用属性值 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"p:driverClassName="${driverClassName}"p:url="${url}"p:username="${username}"p:password="${password}"/></beans>
  • 可以使用context 命名空间定义属性文件,优雅的方式

在基于注解及基于Java类配置中引用属性

<context:component-scan base-package="com.baobaotao.domain"/>
package com.baobaotao.domain;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import lombok.Getter;
import lombok.Setter;@Component
@Setter
@Getter
public class MyDataSource {@Value("${driverClassName}")private String driverClassName;@Value("${url}")private String url;@Value("${username}")private String username;@Value("${password}")private String password;@Overridepublic String toString() {return "MyDataSource [driverClassName=" + driverClassName + ", url=" + url + ", username=" + username+ ", password=" + password + "]";}
}
  • @Value 注解可以为Bean注入一个字面值,也可以通过@Value("${propName}")的形式根据属性名注入属性值。由于标注@Configuration的类本身相当于标注了@Component,所以在标注@Configuration 类中引用属性的方式和基于注解配置的引用方式是完全一样的。
5.3.2 使用加密的属性文件
  • 对于敏感信息需要加密处理,赋值时则将解密后的信息赋值于属性;加密分为对称加密于非对称加密(区别在于是否可还原)
  • PropertyPlaceholderConfigurer继承于PropertyResourceConfigurer类,后者有几个protected 方法,用于属性文件中的属性进行转换处理
    • void convertProperties(Properties propos):属性文件中的所有属性值都封装在props中,覆盖此方法,可以对所有的属性值进行转换处理。
    • String convertProperty(String propertyName, Stirng propertyValue):在加载属性文件并读取文件中的每个属性时,都会调用此方法进行转换处理。
    • String convertPropertyValue(String originalValue):和上一个方法类似;

DES 加密解密工具类

  • DESUtils.java DES加密工具类
package com.baobaotao.placeholder.utils;import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;public class DESUtils {//@1对字符串进行DES加密,返回BASE64编码的加密字符串public static String getEncryptString(String str) {Encoder base64en = Base64.getEncoder();try {byte[] strBytes = str.getBytes("UTF8");String result = base64en.encodeToString(strBytes);return result;} catch (Exception e) {throw new RuntimeException(e);}}//@2 对BASE64编码的加密字符串进行解密,返回解密后的字符串public static String getDecryptString(String str) {Decoder base64en = Base64.getDecoder();try {byte[] strBytes = base64en.decode(str);return new String(strBytes,"UTF8");} catch (Exception e) {throw new RuntimeException(e);}}public static void main(String[] args) {System.err.println(getEncryptString("root"));System.err.println(getEncryptString("hm123"));System.err.println("root  :" + getDecryptString("cm9vdA=="));System.err.println("hm123  :" + getDecryptString("aG0xMjM="));}
}
  • jdbc.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/hm_dream?characterEncoding=utf8
username=cm9vdA==
password=aG0xMjM=
package com.baobaotao.placeholder.utils;import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;//@1 继承PropertyPlaceholderConfigurer定义支持密文版属性的属性配置器
public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{private String[] encryptPropNames = {"username","password"};//@2 对特定属性的属性值进行转换protected String convertProperty(String propertyName, String propertyValue) {if(isEncryptProp(propertyName)) {String decryptValue = DESUtils.getDecryptString(propertyValue);System.out.println(decryptValue);return decryptValue;}else {return propertyValue;}}//@3 判断是否需要进行解密的属性private boolean isEncryptProp(String propertyName) {for (String encryptPropName : encryptPropNames) {if(encryptPropName.equals(propertyName)) {return true;}}return false;}
}
  • xml配置
<bean class="com.baobaotao.placeholder.utils.EncryptPropertyPlaceholderConfigurer"p:location="jdbc.properties"p:fileEncoding="utf-8"/><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"p:driverClassName="${driverClassName}"p:url="${url}"p:username="${username}"p:password="${password}" />
5.3.3 属性文件自身的引用
  • Spring 即允许在Bean定义中通过propName使{propName}引用属性值,还允许在属性文件中使用{propName},实现属性之间的引用:
dbName=hm_dream?characterEncoding=utf8
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/${dbName}
username=cm9vdA==
password=aG0xMjM=
  • url属性通过 ${dbName} 引用另一个属性的值。

5.4 引用 Bean 的属性值

  • 在@1 处的方法中,可以更改代码,从数据库中获取相应的属性信息。(这里直接赋值)
package com.baobaotao.placeholder.beanprop;import javax.sql.DataSource;public class SysConfig {private int sessionTimeout;private int maxTabPageNum;private DataSource dataSource;//@1 模拟从数据库中获取配置并设置相应的属性public void initFromDB() {this.sessionTimeout = 30;this.maxTabPageNum = 10;}public int getSessionTimeout() {return sessionTimeout;}public int getMaxTabPageNum() {return maxTabPageNum;}public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}	
}
  • 访问数据库获取配置属性的前提时链接到数据库,故,我们还得使用外部属性文件配置数据数据库的链接信息。然后,通过sysConfig的initFromDB()方法访问数据库,获取应用系统的配置信息,并将其保存在sysConfig的属性中。
  • 其他需要访问应用系统配置信息的Bean即可通过#{beanName.propName}的表达式引用sysConfig Bean 的属性了,如@2所示
  • 在基于注解基于Java类配置的Bean中,可以通过@Value("#{beanName.propName}")的注解形式引用Bean的属性值:
package com.baobaotao.placeholder.beanprop;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class ApplicationManager {@Value("#{sysConfig.sessionTimeout}")private int sessionTimeout;@Value("#{sysConfig.maxTabPageNum}")private int maxTabPageNum;public int getSessionTimeout() {return sessionTimeout;}public void setSessionTimeout(int sessionTimeout) {this.sessionTimeout = sessionTimeout;}public int getMaxTabPageNum() {return maxTabPageNum;}public void setMaxTabPageNum(int maxTabPageNum) {this.maxTabPageNum = maxTabPageNum;}}
  • 在XML配置文件,我们先将SysConfig定义为一个Bean,定义数据源时即可通过#{beanName.propName}的方式引用Bean的属性值了
<?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:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"><context:component-scan base-package="com.baobaotao" /><!-- <context:property-placeholderlocation="jdbc.properties"/> --><bean class="com.baobaotao.placeholder.utils.EncryptPropertyPlaceholderConfigurer"p:location="jdbc.properties"p:fileEncoding="utf-8"/><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"p:driverClassName="${driverClassName}"p:url="${url}"p:username="${username}"p:password="${password}" /><!-- @1 通过initFromDB 方法从数据源中获取配置属性值 --><bean id="sysConfig" class="com.baobaotao.placeholder.beanprop.SysConfig"init-method="initFromDB"p:dataSource-ref="dataSource"/><!-- @2 引用Bean的属性值 -->	<bean class="com.baobaotao.placeholder.beanprop.ApplicationManager"p:maxTabPageNum="#{sysConfig.maxTabPageNum}"p:sessionTimeout="#{sysConfig.sessionTimeout}" /></beans>

5.5 国际化信息

5.5.1 基础知识
  • 常见一些语言和国家/地区的标准代码
语言 代码 国家/地区代码 代号
中文 zh 中国大陆 CN
英文 en 中国台湾 TW
法语 fr 中国香港 HK
德语 de 英国 EN
日语 ja 美国 US
韩语 ko 加拿大 CA

Locale

  • java.util.Locale 是表示语言和国家/地区信息的本地化类,它是创建国际化应用的基础。
  • 创建本地化对象的示例:
//@1 带有语言和国家/地区信息的本地化对象
Locale locale1 = new Locale("zh", "CN");
//@2 只有语言信息的本地化对象
Locale locale2 = new Locale("zh");
//@3 等同于Locale("zh","CN")
Locale locale3 = Locale.CHINA;
//@4 等同于Locale("zh")
Locale locale4 = Locale.CHINESE;
//@5 获取本地系统默认的本地化对象
Locale locale5 = Locale.getDefault();
public static void main(String[] args) {Locale locale = new Locale("zh", "CN");NumberFormat currFmt = NumberFormat.getCurrencyInstance(locale);double amt = 123456.78;System.out.println(currFmt.format(amt));Locale locale6 = new Locale("en", "US");Date date = new Date();DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM,locale6);System.out.println(df.format(date));
}
  • “¥123,456.78”,“Dec 25, 2019” 为输出结果,NumberFormat按本地化的方式对货币金额进行格式化操作;

ResourceBoundle

  • 国际化资源文件的命名的规范规定资源名称采用以下的方式进行命名

    • < 资源名 >< 语言代码 >< 国家/地区代码 >.properties
  • 其中,语言代码和国家/地区代码都是可选的。< 资源名 >.properties命名的国际资源文件是默认的资源文件,即某个本地化类型在系统中找不到对应的资源文件,就采用这个默认的资源文件。< 资源名 >_< 语言代码 >.properties命名的国际化资源文件是某一语言默认的资源文件,即某个本地化类型在系统中找不到精确匹配的资源文件,将采用相应语言默认的资源文件。

  • Java为我们提供了用于加载本地资源文件的方便类java.util.ResourceBoundel。ResourceBoundel为加载及访问资源文件提供便捷的操作,如:

  • resource_en_US.properties

greeting.common=How are you!
greeting.morning=Good morning!
greeting.afternoon=Good Afternoon!
  • resource_zh_CN.properties
greeting.common=\u60A8\u597D\uFF01
greeting.morning=\u65E9\u4E0A\u597D\uFF01
greeting.afternoon=\u4E0B\u5348\u597D\uFF01
  • 测试代码
ResourceBundle rb1 = ResourceBundle.getBundle("resource", Locale.US);
ResourceBundle rb2 = ResourceBundle.getBundle("resource", Locale.CHINA);
System.out.println("us:" + rb1.getString("greeting.common"));
System.out.println("cn:" + rb2.getString("greeting.common"));
* us:How are you!
* cn:您好!
  • ResourceBundle 在加载资源时,如果指定的本地化资源文件不存在,它按以下顺序尝试加载其他的资源:本地系统默认本地化对象对应的资源 -> 默认的资源。

在资源文件中使用格式化串

  • resource_en_US.properties
greeting.common=How are you!{0}, today is {1}
greeting.morning=Good morning!{0}, now is {1 time shot}
greeting.afternoon=Good Afternoon!{0} new is {1 date long}
  • resource_zh_CN.properties
greeting.common=\u60A8\u597D\uFF01{0}, \u73B0\u5728\u662F {1}
greeting.morning=\u65E9\u4E0A\u597D\uFF01
greeting.afternoon=\u4E0B\u5348\u597D\uFF01
  • 测试代码
ResourceBundle rb1 = ResourceBundle.getBundle("resource", Locale.US);
ResourceBundle rb2 = ResourceBundle.getBundle("resource", Locale.CHINA);Object[] params = {"John", new GregorianCalendar().getTime()};String str1 = new MessageFormat(rb1.getString("greeting.common"), Locale.US).format(params);
String str2 = new MessageFormat(rb2.getString("greeting.common"), Locale.CHINA).format(params);System.out.println("us:" + str1);
System.out.println("cn:" + str2);
* us:How are you!John, today is 12/27/19 3:31 PM
* cn:您好!John, 现在是 19-12-27 下午3:31
5.5.2 MessageSource
  • Spring 定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。接口方法:
    • String getMessage(String code, Object[] args, String defaultMessage, Locale locale) cod 表示国际化资源中的属性名,args用于传递格式化子字符串占位符所用的运行期参数;当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;locale表示本地化对象;
    • String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException 于上面的方法类似,只不过在找不到的资源中对应的属性名时,直接抛出NoSuchMessageException 异常
    • String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageExceptioin MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一接口方法相同。

MessageSource 的类结构

  • MessageSource 分别被 HierarchicalMessageSource 和 ApplicationContext接口扩展。
    • HierarchicalMessageSource 接口添加了两个方法,建立父子层级的MessageSource 结构,类似于我们前面所介绍的 HierarchicalBeanFactory。该接口的 setParentMessageSource(MessageSource parent)方法用于设置父MessageSource,而 getParentMessageSource() 方法用于返回父 MessageSource。
    • HierarchicalMessageSource 接口最重要的两个实现类是 ResourceBundleMessageSource和ReloadResourceBundeleMessageSource。他们基于Java的ResourceBundle 基础类实现,允许仅通过资源名加载国际化资源。ReloadableResourceBundelMessageSource提供了定时刷新功能,允许在不重启系统的情况下,更新资源信息。StaticMessageSource 主要用于程序测试,它允许通过编程的方式提供国际化信息。而DelegationMessage Source 是为方便操作父MessageSource 而提供的代理类。

ResourceBundleMessageSource

  • bean.xml配置
<bean id="myResource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames"><list><value>resource</value></list></property>
</bean>
  • 测试
String[] configs = {"beans.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);MessageSource ms = (MessageSource)ctx.getBean("myResource");
Object[] params = {"John", new GregorianCalendar().getTime()};
String str1 = ms.getMessage("greeting.common", params, Locale.US);
String str2 = ms.getMessage("greeting.common", params, Locale.CHINA);System.out.println("us:" + str1);
System.out.println("cn:" + str2);
  • 控制台打印:us:How are you!John, today is 12/27/19 4:33 PM - cn:您好!John, 现在是 19-12-27 下午4:33

ReloadableResourceBundleMessageSource

<bean id="myResource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="basenames"><list><value>resource</value></list></property><property name="cacheSeconds" value="5"></property></bean>
String[] configs = {"beans.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);MessageSource ms = (MessageSource)ctx.getBean("myResource");
Object[] params = {"John", new GregorianCalendar().getTime()};for (int i = 0; i < 2; i++) {String str1 = ms.getMessage("greeting.common", params, Locale.US);System.out.println(str1);Thread.currentThread().sleep(20000);
}
5.5.3 容器级国际化信息资源
<bean id="myResource" class="org.springframework.context.support.ResourceBundleMessageSource"><property name="basenames"><list><value>resource</value></list></property>
</bean>
String[] configs = {"beans.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);Object[] params = {"John", new GregorianCalendar().getTime()};String str1 = ctx.getMessage("greeting.common", params, Locale.US);
String str2 = ctx.getMessage("greeting.common", params, Locale.CHINA);
System.out.println(str1);
System.out.println(str2);

5.6 国际化信息

  • Spring 的 ApplicationContext能够发布事件并且允许注册相应的事件监听器,因此它拥有一套完美的事件发布和监听机制。
    • 事件源:事件的生产者,任何一个EventObject都必须拥有一个事件源;
    • 事件监听器注册表:组件或框架的事件监听器不可能飘在水里悬在空中,而必须由所依存。也就是说组件或框架必须提供一个地方保存事件监听器,这便是事件监听器注册表。一个事件监听器注册到组件或框架中,其实就是保存在监听器注册表里,当组件和框架中的事件源产生事件是就会将事件通知这些位于注册表中的监听器;
    • 事件广播器: 它是事件和事件监听器沟通桥梁,负责把事件通知给事件监听器
5.6.1 Spring 事件类结构

事件类

  • ApplicationEvent 的唯一构造函数是 ApplicationEvent(Object source),通过source指定事件源,它的两个子类分别是:
    • ApplicationContextEvent:容器事件,拥有4个子类分别表示容器启动、刷新、停止及关闭的事件;
    • RequestHandlerEvent:这是一个与Web应用相关的事件,当一个HTTP请求被处理后,产生该事件。只有在web.xml中定义了DispatcherServlet时才会产生该事件。拥有两个子类,分别代表Servlet及Portlet的请求事件。

事件监听器接口

  • ApplicationListener 接口只定义了一个方法:onApplicationEvent(E event),该方法接受ApplicationEvent事件对象,该方法中编写事件的响应处理逻辑。而SmartApplicationListener 它定义了两个方法:
    • boolean supportsEventType(Class< ? extends ApplicationEvent > eventType): 指定监听器支持那种类型的容器事件,即它只会对该类型的事件做出响应;
    • boolean supportsSourceType(Class< ? > sourceType):该方法指定监听器仅对何种事件源对象做出响应

事件广播器

  • 当发生容器事件时,容器主控程序将调用事件广播器将事件通知给注册表中的事件监听器,监听器分别对事件进行响应。Spring为事件广播器定义了接口。
5.6.2 解构 Spring 事件体系的具体实现
  • Spring 在 ApplicationContext 接口的抽象实现类 AbstractApplicationContext 中完成了事件体系的搭建。AbstractApplicationContext 拥有一个ApplicationEventMulticaster 成员变量,applicationEventMulticaster 提供了容器监听器的注册表。AbstractApplicationContext在refresh()这个容器启动方法种通过以下三个步骤搭建了事件的基础设施。
//@5 初始化应用上下文事件广播器
initApplicationEventMulticaster();//@7 注册事件监听器
registerListeners();//@9 完成刷新并发布容器刷新事件
finishRefresh();
  • 首先,@5 处,Spring初始化事件的广播器。用户可以在配置文件中为容器定义一个自定义的广播器,只要实现ApplicationEventMulticaster就可以了,Spring会通过反射的机制将器注册成容器的事件广播器,如果没有找到配置的外部事件广播器,Spring自动使用SimpleApplicationEventMulticater作为事件广播器。
  • 在@7处,Spring将根据反射机制,从BeanDefinitionRegistry中找出所有实现org.springframework.context.ApplicationListener的Bean,将他们注册为容器的事件监听器,实际的操作就是将其添加到实际广播器所提供的接听器注册表中
  • 在@9处,容器启动完成,调用实际发布接口向容器中所有的监听器发布事件,在publishEvent()内部,我们可以道道Spring委托ApplicationEventMulticaster将事件通知给监听器。
5.6.3 一个实例
package com.baobaotao.event;import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;public class MailSendEvent extends ApplicationContextEvent{private String to;public MailSendEvent(ApplicationContext source, String to) {super(source);this.to = to;}public String getTo() {return this.to;}
}
  • 它直接扩展ApplicationContextEvent,事件对象除source外,还具有一个代表发送目的地to属性。
package com.baobaotao.event;import org.springframework.context.ApplicationListener;//事件监听器 MailSendListener 负责监听MailSendEvent事件
public class MailSendListener implements ApplicationListener<MailSendEvent>{//@1 对MailSendEvent事件进行处理@Overridepublic void onApplicationEvent(MailSendEvent event) {MailSendEvent mse = (MailSendEvent)event;System.out.println("MailSendEvent:向" + mse.getTo() + "发送完一封邮件");}
}
  • MailSenderListener 直接实现ApplicationListener 接口,在接口方法通过instanceof操作符判断事件的类型,仅对MailSendEvent类型的事件进行处理
package com.baobaotao.event;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;//MailSender 要有有发布事件的能力,必须事件ApplicationContextAware接口
public class MailSender implements ApplicationContextAware{private ApplicationContext ctx;//@1 ApplicationContextAware 的接口方法,以便容器启动时注入示例@Overridepublic void setApplicationContext(ApplicationContext ctx) throws BeansException {this.ctx = ctx;}public void sendMail(String to) {System.out.println("MailSender:模拟发送邮件");MailSendEvent mse = new MailSendEvent(this.ctx, to);//@2 向容器中的所有事件监听器发送事件ctx.publishEvent(mse);}
}
  • spring的配置文件中,仅需要如下配置:
<bean class="com.baobaotao.event.MailSendListener"/>
<bean id="mailSender" class="com.baobaotao.event.MailSender"/>
package com.baobaotao.anno;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.baobaotao.event.MailSender;public class Test {public static void main(String[] args) throws InterruptedException {String[] configs = {"beans.xml"};ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);MailSender mailSender = (MailSender)ctx.getBean("mailSender");mailSender.sendMail("aaa@bbb.com");}
}
  • MailSender:模拟发送邮件 & MailSendEvent:向aaa@bbb.com发送完一封邮件
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 各种加密算法内部原理实现

    众所周知数据对于一个公司来说,非常重要,一旦数据泄露,公司将面临非常大的威胁,因此对数据加密非常有必要,保证数据的安全性。1 加密算法的基本原理加密算法目前主流分为对称加密,不对称加密,散列加密。1.1 对称加密对称加密是指加密和解密使用相同密钥的加密算法,常见…...

    2024/4/17 15:16:35
  2. BitCherry的矫正与突围——技术型公链的迷失和商业型公链的崛起

    十多年后,中本聪所提出“一种点对点的电子现金系统”仍被视为经典的技术案例,而其背后中本聪关于“以技术为核心”的理念也正影响着更多区块链项目的设计者。BTC,EOS,ETH……无论你是否承认我们已大步迈进数字经济的时代,你总能说出这几个熟悉的的名字。也正是在这样的时代…...

    2024/4/16 14:21:06
  3. 第四章 在 IoC 容器中装配 Bean

    4.1 Spring 配置概述 4.1.1 Spring 容器高层视图Spring容器成功启动的必备条件Spring 框架的类包都已经放到应用程序的类路径下 应用程序为 Spring 提供完备的 Bean 配置信息 Bean 的类都已经放到应用程序的类路径下Bean 在Spring中加载的过程1、Bean 配置信息定义了 Bean 的实…...

    2024/4/20 8:35:13
  4. 23种设计模式汇总

    设计模式 23种设计模式,一个没少。比较常用的:单例、工厂、适配器、装饰者、代理、观察者这几个模式。其他的做了解。 创建型模式 单例模式 作用:确保一个类只有一个实例,只对外提供一个访问该实例的全局访问点。 特征: 1、无法通过new得到实例,构造器是被private修饰的 …...

    2024/4/16 14:21:06
  5. LeetCode # 452 投飞镖刺破气球

    在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。一支弓箭可以沿着x轴从不同点完全垂直地…...

    2024/4/24 8:41:47
  6. JS 中提高代码质量/速度小技巧

    JS 中提高代码质量/速度小技巧 1. 类型强制转换 1.1 string强制转换为数字 可以用*1来转化为数字(实际上是调用.valueOf方法) 然后使用Number.isNaN来判断是否为NaN,或者使用 a !== a 来判断是否为NaN,因为 NaN !== NaN 32 * 1 // 32 ds * 1 // NaN nu…...

    2024/4/24 8:41:48
  7. 03-GitLab简单使用

    GitLab简单使用 1 登录系统 系统安装完成后,在浏览器中输入: http://yourip:8081 进行登录说明:开发私服一般使用ip地址 端口号默认为80,但管理员可能修改了端口号,比如此处使用的是8081 管理员使用root身份登录,初次登录需要修改密码 没有账号可以点击register进行注册普…...

    2024/4/24 8:41:45
  8. 程序设计实习MOOC 指针练习:指向指针的指针

    描述 程序填空使得输出指定结果 #include <iostream> using namespace std; int main() {int x,y,z;x = 10;y = 20;z = 30;int * a[3] = { &x, &y,&z};for( // 在此处补充你的代码 p < a + 3; ++p) cout<< * (*p) << endl;return 0;}输入 无…...

    2024/4/24 8:41:52
  9. 将跳转的页面写在iframe中,执行完成后跳出

    现状:a页面的js会调用 window.location.href = url 来跳转到 b 页面,然后 b执行完后会跳转到另一个页面。 需求:b页面的执行情况隐藏。 方法:使用iframe,调用b的url放到iframe中,然后b页面写一个跳出iframe的方法。<iframe id="diagnosisIframe" name="…...

    2024/4/24 8:41:47
  10. 美团研发岗笔试真题练习(编程题4)——最大矩形

    题目描述 给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 输入描述: 输入一个仅包含’0’和’1’的二维字符数组。 第一行输入左大括号{,最后一行输入右大扩号}。 中间每行输入只包含’0’和’1’的字符数组*(字符数组的长度不超过20),…...

    2024/4/24 8:41:46
  11. 数据库系统概念笔记——第10章 存储和文件结构

    第10章 存储和文件结构 物理存储介质概述 大多数计算机系统中存在多种数据存储类型。可以根据访问数据的速度,购买介质时每单位数据的成本,以及介质的可靠性对这些存储介质进行分类。以下是几种有代表性的介质:高速缓冲存储器(cache)。高速缓冲存储器是最快最昂贵的存储介…...

    2024/4/24 8:41:42
  12. 按钮点击事件

    代码:按钮findViewBuId<Buttonandroid:id="@+id/mButton4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="跳转"app:layout_constraintLeft_toLeftOf="parent"app:layout_cons…...

    2024/4/24 8:41:44
  13. 20120-5-26

    jquery 的划入划出效果 $(".col-md-4").hover(function(){$(this).show(); }, function() {$(this).hide(); });百度富文本编辑器.工具栏浮动高度,编辑器高度,工具栏是否浮动 UE.getEditor(container, {topOffset : 60, initialFrameHeight : 600, autoFloatEnabled:…...

    2024/4/24 8:41:42
  14. Java集合类之Collection接口,集合的“爸爸”接口了解一下?

    目录什么是集合?集合和数组有什么区别?Collection接口如何遍历集合中的元素 Hello!没技术的大灰狼又来了,今天和小伙伴分享一个Java项目开发中比较常用的存储数据的方法----集合。 想到集合,可能很多小伙伴都会想到数组,那么集合和数组又有什么样的区别呢?且听大灰狼一一…...

    2024/4/24 8:41:38
  15. iOS实战开发之获取当前设备的网络类型,2G/3G/4G/WIFI

    在开发过程中,或多或少的考虑到用户的网络环境来加载不同的资源,提供用户体验,也是一种提升用户体验的途径。但是,苹果官方的开发SDK中并没有明确的类或者是接口来帮助我们简易的获取这个信息,目前针对苹果官方提供的Reachability类进行扩展实现此功能。希望对于有需要的同…...

    2024/4/25 20:21:13
  16. LeetCode # 435 不重叠的区间个数

    给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。注意:可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。示例 1:输入: [ [1,2], [2,3], [3,4], [1,3] ]输出: 1解释: 移除 [1,3] 后,剩下的区间没有重叠。…...

    2024/4/24 8:41:39
  17. 品优购电商系统开发第 7 章 六

    2.8 保存数据2.8.1 后端代码修改 pinyougou-sellergoods-interface 的 GoodsService.javapublic void update(Goods goods);修改 pinyougou-sellergoods-service 的 GoodsServiceImpl ,将 SKU 列表插入的代码提取出来,封装到私有方法中/** * 插入 SKU 列表数据 * @param goods…...

    2024/4/26 12:16:21
  18. 利用python爬取所有公司办公地址,并在百度地图个人开发平台标注。

    一、利用python爬取公司办公地址 IDLE编辑器,python3.8版本。 import requests from bs4 import BeautifulSoup import re import xlwt def getHTMLText(url, code=“utf-8”): kv={‘user-agent’:‘Mozilla/5.0’} try: r = requests.get(url,headers=kv) r.raise_for_statu…...

    2024/4/16 14:21:01
  19. 华为 USG6000防火墙配置镜像模式双机热备

    网络拓扑要求:企业前期是一台防火墙,为了提高网络可靠性,并且在不影响原先防火墙配置情况下,新增一台防火墙做双机热备。两台FW的业务接口都工作在三层,下行为三层核心交换机。上行为二层交换机连接运营商的接入点,运营商为企业分配的IP地址为100.1.1.1-100.1.1.6配置思路…...

    2024/4/15 6:37:41
  20. 利用Qt制作连续性图片

    有关于如何利用Qt使图片的连续性切换来达到动图的效果,下面给出事例:首先先看一看效果图:从上面可以看到–这是个连续性动图,是由一张张静态图片连续切换产生的。 要学会如何添加资源图片一直继续就行,然后先添加前缀再添加文件先在头文件定义函数部分然后在cpp文件里面完…...

    2024/4/16 14:22:03

最新文章

  1. 2024最新智慧医疗智慧医院大数据展示,医院数据采集概况、医院指标分析、医院就诊趋势分析等。源代码免费下载。

    系列文章目录 【复制就能用1】2分钟玩转轮播图,unslider的详细用法 【复制就能用2】css实现转动的大风车&#xff0c;效果很不错。 【复制就能用3】2分钟自己写小游戏&#xff1a;剪刀石头布小游戏、扫雷游戏、五子棋小游戏 【复制就能用4】2024最新智慧医疗智慧医院大数据…...

    2024/4/28 1:47:15
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 大话设计模式之简单工厂模式

    简单工厂模式&#xff08;Simple Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;属于工厂模式的一种。在简单工厂模式中&#xff0c;有一个工厂类负责根据输入参数的不同来创建不同类的实例。 简单工厂模式包含以下几个要素&#xff1a; 1. **工厂类&#xff0…...

    2024/4/25 12:18:15
  4. Jmeter 聚合报告之 90% Line 正确理解

    今天看了些关于Jmeter 聚合报告之 90% Line 的一些博客 关于90% Line 的算法各有各自的见解 。 90%Line可以用公式计算&#xff1a; 100/总个数每一个所占的百分比&#xff0c;90%/每一个所占的百分比90%Line的序号&#xff08;从小到大排&#xff09; 例如&#xff1a;1.2.3.…...

    2024/4/21 20:56:13
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/26 20:12:18
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/27 4:00:35
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/27 9:01:45
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/28 1:22:35
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57