Spirng前置处理器

SpringIOC容器它会以某种方式,加载配置文件中的 信息,将其解析为一个个的BeanDefinition.之后将BeanDefinition注册到容器之中。Spring IOC容器在实现的时候经过的过程可以使用如下图片表示:

分为两个主要部分:
其一 :容器启动阶段
其二:bean实例化阶段。


在这里插入图片描述

在容器的启动阶段主要做的是一些信息收集的过程(主要依赖于工具类BeanDefinitionReader),将收集的信息组成BeanDefinition.将BeanDefinition注册到相应的BeaneanRegistry.
Bean实例化的过程在请求方通过容器的getBean方法明确请求某个对象时候触发/隐式依赖关系调用时候也会触发该动作。此阶段做的操作主要是判断当前的请求对象是否已经被实例化过了。根据情况进行注入。当该对象实现某些回调接口。也会根据回调函数接口装配它

容器的前置处理 BeanFactoryPostProcess:

此后内容主要是对于容器实现第一阶段进行处理。Spring提供了容器扩展机制BeanFactoryPostProcess。这个机制允许我们在实例化相应对象之前对注册到容器中的BeanDefinition的存储信息进行修改。可以根据这个机制对Bean增加其它信息。修改Bean定义的某些属性值。
想自定义前置处理器需要实现BeanFactoryPostProcess接口。当一个容器存在多种前置处理的时候,可以让前置处理器的实现类同时继承Ordered接口。
Spring容器提供了数种现成的前置处理器,常见的如:

  • PropertyPlaceholderConfigurer:允许在xml文件中使用占位符。将占位符代表的资源单独配置到简单的Properties文件中加载
  • PropertyOverrideConfigurer:不同于PropertyPlaceholderConfigurer的是,该类用于处理容器中的默认值覆为新值的场景
  • CustomEditorConfigurer:此前的两个前置处理器处理的均是BeanDefinition.通过把BeanDefinition的数据修改达到目的。CustomEditorConfigurer没有对BeanDefinition做任何变动。负责的是将后期会用到的信息注册到容器之中。例如将类型转换器注册到BeanDefinition中。供BeanDefinition将获取到的String类型参数转换为需要的类型。

装配前置处理器

装配前置处理器分为两种情况。一种是普通的BeanFactory.一种是ApplicationContext.对于ApplicationContext可以自动识别并装配前置处理器。
ApplicationContext的装配可以直接用xml文件实现:
配置文件:

   
            
                
                    propertyHolder.properties
                
            
        

代码:

ApplicationContext factory=new ClassPathXmlApplicationContext("simple.xml");

启动的时候自动装载前置增强bean。无须额外操作。

BeanFactory装配方式:

        ConfigurableListableBeanFactory factory=new XmlBeanFactory(new ClassPathResource("simple.xml"));
        PropertyPlaceholderConfigurer configurer=new PropertyPlaceholderConfigurer();
        configurer.setLocation(new ClassPathResource("propertyHolder.properties"));
        configurer.postProcessBeanFactory(factory);

三种前置处理器

1.PropertyPlaceholderConfigurer前置处理器

应用场景:
不想将系统管理相关的业务配置信息混杂到XML文件中的时候,可以将文件配置到properties.如此可以在当系统参数发生变幻时候,只需修改properties配置文件。强化可读性的同时,也降低了维护难度。
代码:
配置文件

  
            
                ${java.version}
            
            
        

bean包:

package com.example.demo.postprocess;

public class MyPropertyHolderBean {
    private String username;
    private String password;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void say() {
        System.out.println("username:"+username+";password:"+password);
    }
}

装配测试:

        ConfigurableListableBeanFactory factory=new XmlBeanFactory(new ClassPathResource("simple.xml"));
        PropertyPlaceholderConfigurer configurer=new PropertyPlaceholderConfigurer();
        configurer.setLocation(new ClassPathResource("propertyHolder.properties"));
        configurer.postProcessBeanFactory(factory);
        
        MyPropertyHolderBean bean=(MyPropertyHolderBean) factory.getBean("propertyHolder");
        bean.say();

分析:
对于PropertyPlaceholderConfigurer前置处理器的处理分步:

  • 创建xml文件装配需要使用前置增强装配的bean,使用占位符,占用value值
  • 创建properties文件,键值与xml中占位符名称一致
  • 通过beanFactory装载BeanDefinition
  • 创建PropertyPlaceholderConfigurer对象并把beanFactory作为入参,为beanFactory增加前置增强操作,替换BeanDefinition中的展位符

PropertyPlaceholderConfigurer前置增强不止会扫描装载的properties文件还会扫描System类下的属性。
PropertyPlaceholderConfigurer提供了三种默认的装载规则:

  • SYSTEM_PROPERTIES_MODE_FALLBACK :默认的装载规则。当properties中存在的时候使用properties中的值,不存在的时候则选择System中的值
  • SYSTEM_PROPERTIES_MODE_NEVER:从不扫描System中的值
  • SYSTEM_PROPERTIES_MODE_OVERRIDE:扫描系统值,当与properties中的值冲突时候,采用System中的值

当不设置的时候默认采用的是SYSTEM_PROPERTIES_MODE_FALLBACK。可以通过PropertyPlaceholderConfigurer对象的
setSystemPropertiesMode()方法或者setSystemPropertiesModeName() 方法,修改其扫描注入的规则。

本例子采用的BeanFactory方式进行测试,Application容器方式的方式装配起来更为简单。对于装配模式的设定可以在其bean定义的XML文件中使用注入的方式处理。

举例如:

        
            
                
                    propertyHolder.properties
                
            
            
        

其余的操作同正常的使用Application一样。十分简便。

2.PropertyOverrideConfigurer前置处理器

应用场景:
可以通过PropertyOverrideConfigurer对容器中任何想处理的bean定义中的信息进行覆盖替换。如此当容器创建bean的时候其bean的属性值就不同于xml文件的值了。
代码:
配置文件:

        
            
                工具人
            
            
                简单的密码
            
        

properties文件内容

overrideBean.username=\u6700\u65B0\u7684\u5DE5\u5177\u4EBA
overrideBean.password=\u6700\u65B0\u7684\u5BC6\u7801

测试用小Demo:

        ConfigurableListableBeanFactory factory=new XmlBeanFactory(new ClassPathResource("simple.xml"));
        PropertyOverrideConfigurer configurer=new PropertyOverrideConfigurer();
        configurer.setLocation(new ClassPathResource("propertyOverride.properties"));
        configurer.postProcessBeanFactory(factory);
        
        MyPropertyOverrideBean bean=(MyPropertyOverrideBean)factory.getBean("overrideBean");
        bean.say();

分析:
PropertyOverrideConfigurer的用法与PropertyPlaceholderConfigurer的使用方式类似。不同之处在于PropertyOverrideConfigurer的properties文件中的键值是bean名加其内部属性。而PropertyPlaceholderConfigurer的键值是其占位符的名。其次两者值注入的方式一种是替换占位符,一种是覆盖旧有的值。同时PropertyOverrideConfigurer不存在扫描System的情况。

Spring还可以支持对其加密的properties进行处理。PropertyOverrideConfigurerPropertyPlaceholderConfigurer均继承了PropertyResourceConfigurer,PropertyResourceConfigurer中提供了一个方法convertPropertyValue()方法,可以通过覆盖该方法对相应的配置项内容进行转换。

例子:

    
            
                
                    propertyOverride.properties
                
            
            
        

这里使用了方法替换的方式实现处理,使用继承的方式也可以,自行创建一个后置处理类,继承系统的后置处理类,之后重写其convertPropertValue()即可。在此不给具体代码了。
方法替换类:

public class MyReplacePostProcess implements MethodReplacer{

    @Override
    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        
        return "对不起,"+obj.getClass().getSimpleName()+"的"+method.getName()+"被替换掉了";
    }

}

其余的操作与之前例子一致,获得的结果是进行转换处理后的值。对于其Application的操作与PropertyPlaceholderConfigurer的类似在此也不加解释了。

3.CustomEditorConfigurer前置处理器

场景:
CustomEditorConfigurer与其余的两个前置处理器有所不同,它负责辅助性将后期会用到的信息注入到容器之中,本身对于BeanDefinition没有做任何变更
在使用配置文件配置参数的过程中会出现一种情况。配置文件中记载的数据都是String类型的,但是程序中的对象的类型却是多种多样的。想要完成这种转化,需要转化规则相关信息。CustomEditorConfigurer前置处理器负责的就是处理传递类型转换信息消息的一种前置处理器。

对于Spring内部使用PropertyEditor帮助我们把String转化为其它类型。每个对象类型对应一个的PropertyEditor.在做类型转换的过程中,采用默认JavaBean框架的PropertyEditor搜寻逻辑,继承对原生类型及Color,Font等类型的转化。
Sping还提供了部分的PropertyEditor:

  • StringArrayPropertyEditor. :此类型会将符合CSV格式的字符串转化为String[] 数组的形式,默认为以“,”分割的字符串。可以选择字符串的分割方式。ByteArrayPropertyEditor,CharArrayPropertyEditor均属于功能的PropertyEditor

  • ClassEditor:.根据String类型的class名称,将其转换为对应的class对象,效果与Class.forName(String)类似,入参可以是一个String[]数组,以数组形式传入的场合其作用与ClassArrayPropertyEditor一致

  • FileEditor :Spring提供的 对应 File文件类型的PropertyEditor。同属于对资源进行定位类型的还包括InputStreamEditor,URLEditor

  • LocaleEditor :针对Locale类型的PropertyEditor

  • PatternEditor:针对Pattern的PropertyEditor

以上的这些PropertyEditor容器默认会加载使用,即使不告诉容器如何对这些类型进行转换,容器依旧可以正确的进行转换工作。当我们需要转换的类型在以上PropertyEditor之外的场合。需要我们给出针对这种情况的自定义PropertyEditor,并且通过CustomEditorConfigurer告知容器,以让其可以实现正确的转化。

例子:
下面给出一个针对日期转换的PropertyEditor例子。对于Spring而言提供了个简便的实现自定义PropertyEditor的方法,通过实现PropertyEditor接口太复杂的情况下,可以通过继承PropertyEditorSupport类重写setAsText(String)来实现自定义。
PropertyEditor代码:

public class DatePropertyEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        // TODO Auto-generated method stub
        SimpleDateFormat format=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        try {
            Date dateValue=format.parse(text);
            setValue(dateValue);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
}

xml配置文件:

  
            
                2019/1/1 11:11:11
            
        

测试用代码:

ConfigurableListableBeanFactory factory=new XmlBeanFactory(new ClassPathResource("simple.xml"));
        CustomEditorConfigurer configurer=new CustomEditorConfigurer();
        Map, Class> map=new HashMap();
        DatePropertyEditor editor= new DatePropertyEditor();
        map.put(java.util.Date.class,editor.getClass());
        configurer.setCustomEditors(map);
        configurer.postProcessBeanFactory(factory);
        
        DateEditorBean bean=(DateEditorBean)factory.getBean("dateEditor");
        System.out.println(bean.getDate());

分析:
本例子使用继承PropertyEditorSuppot自定义创建了一个字符串日期转换器。供给bean装配注入的时候使用。
具体操作方式分如下:

  • 继承PropertyEditorSupport,重写其setAsText()方法。将处理后的值通过setValue()的方式注入
  • 定义bean,同时使用setter注入方式注入值
  • 创建容器+前置处理器CustomEditorConfigurer对象,
  • 创建Map对象,将需要处理的类型的class与PropertyEditor对象的class对应上
  • 使用CustomEditorConfigurer对象的setCustomEditors()将PropertyEditor注册
  • 调用CustomEditorConfigurer对象的postProcessBeanFactory()告知容器前置处理的变更情况

至此容器前置增强内容结束,附上git代码地址:
github下载地址

你可能感兴趣的:(Spirng前置处理器)