Spring容器

内部机制

AbstractApplicationContext的refresh方法刻画了Spring容器启动后所执行的各项操作

//初始化BeanFactory
postProcessBeanFactory(beanFactory);
//调用工厂后处理器
//反射找到所有实现BeanFactoryPostProcessor接口的Bean,调用postProcessBeanFactory()方法
invokeBeanFactoryPostProcessors(beanFactory);
//注册Bean后处理器
//反射找到所有实现BeanPostProcessor接口的Bean,注册到容器Bean后处理器的注册表中
registerBeanPostProcessors(beanFactory);
//初始化消息源
//国际消息资源
initMessageSource();
//初始化应用上下文事件广播器
initApplicationEventMulticaster();
//初始化特殊Bean
onRefresh();
//注册事件监听
registerListeners();
//初始化所有单例的Bean
finishBeanFactoryInitialization(beanFactory);
//完成刷新并发布容器刷新事件
//上下文刷新事件
finishRefresh();

作业流程

  1. ResourceLoader加载Spring配置,使用Resource表示这个配置文件资源
  2. BeanDefinitionReader读取Resource所指向的配置文件资源,解析<bean> 成为BeanDefinition保存在BeanDefinitionRegistry中
  3. 容器扫描BeanDefinitionRegistry中的BeanDefinition,反射找到所有实现BeanFactoryPostProcessor接口的Bean,调用后处理对BeanDefinition进行加工处理,
    (1) 半成品BeanDefinition加工成成品的BeanDefinition
    (2) 反射找到实现了PropertyEditor的bean,注册到Spring容器属性编辑器中(PropertyEditorRegistry)

  4. 取出加工后的BeanDefinition,调用instantiationStrategy进行Bean实例化

  5. 使用BeanWrapper对Bean进行封装,它结合Bean 的BeanDefinition以及容器中属性编辑器,完成Bean 的属性注入
  6. 利用Bean后处理器(所有实现BeanPostProcessor接口的Bean)对以及完成属性设置工作的Bean进行后续加工

BeanDefinition

BeanDefinition与<bean> 一一对应,<bean>有class、scope、lazy-init,BeanDefinition有beanClass、scope、lazyInit。
Spring通过BeanDefinition将配置文件<bean>的信息转化为容器的内部表示,注册到BeanDefinitionRegistry中。
创建BeanDefinition步骤:

  1. 通过BeanDefinitionReader读取配置信息,然后xml解析成BeanDefinition(半成品,占位符没有被处理${}
  2. 利用BeanFactoryPostProcessor把半成品BeanDefinition变成成品的BeanDefinition(替换占位符为实际的值)

InstantiationStrategy

负责根据BeanDefinition进行Bean 的创建,SimplInstantiationStrategy是默认的实现类,通过构造函数、带参构造函数、工厂方法进行Bean实例化。

BeanWrapper

负责完成Bean属性填充的任务。一个BeanWrapperImpl实例内部封装了两类组件:待处理的Bean、设置Bean属性的属性编辑器。Spring从BeanDefinition中获取Bean 的propertyValue,通过属性编辑器对propertyValue进行转换得到Bean的属性。

属性编辑器

主要功能就是把外部的属性值转化为JVM内部对应的类型,就是一个类型转化器(String转int…)
BeanWrapperImpl扩展PropertyEditorRegisterSupprt,PropertyEditorRegisterSupprt默认有32个属性编辑器,用于处理一些常用的属性转换保存在defaultEditors的Map中,
用户可以自定义属性编辑器,会保存在customEditors的Map中。
默认的编辑器例子

defaultEditors.put(char.class,CharacterEditor(false));
defaultEditors.put(Character.class,CharacterEditor(true));
自定义属性编辑器

比如我们有一个User类,他需要一个Address类

public class User {
    private Address address;
    ......
}

普通方法会使用<bean>ref直接注入Address

 <bean id="address" class="com.sunjie.Address">
       <property name="province" ref="浙江"></property>
       <property name="city" ref="杭州"></property>
 </bean>
 <bean class="com.sunjie.User">
       <property name="address" ref="address"></property>
 </bean>

我们这里尝试使用属性编辑器来注入
继承PropertyEditorSupport重写setAsText方法,用户设置Address属性

public class CustomAddressEditor extends PropertyEditorSupport {

    public void setAsText(String text){
        if(text == null || text.indexOf(",") == -1){
            throw new IllegalArgumentException("设置的字符串格式不正确");
        }
        String[] infos = text.split(",");
        Address address = new Address();
        address.setProvince(infos[0]);
        address.setCity(infos[1]);
        setValue(address);
    }

    public String getAsText() {
        Object value = getValue();
        if(value == null){
           return "";
        }else{
           Address address = (Address)value;
           return address.getProvince()+","+address.getCity();
        }
    }
}

注册自定义编辑器


    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="com.sunjie.Address"
                       value="com.sunjie.CustomAddressEditor"/>
            </map>
        </property>
    </bean>

注入

  <bean id="user" class="com.sunjie.User">
        <property name="address" value="浙江,杭州"/>
    </bean>

使用外部属性文件

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
            p:fileEncoding="utf-8">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>

PropertyPlaceholderConfigurer属性

  • locations:如果一个外部配置文件使用 location,多个使用locations,类似于list
  • fileEncoding:属性文件的编码格式,默认使用操作系统默认编码方式
  • order:多个PropertyPlaceholderConfigurer架子啊顺序
  • placeholderPrefix:占位符前缀,默认${
  • placeholderSuffix:占位符后缀,默认}

如果外部属性文件直接定义password的明文,不安全。可以使用加密解密配置
继承PropertyPlaceholderConfigurer,里面有三个方法

  • converProperties 对所有属性进行转换
  • converProperty读取每个属性的时候,都会调用
  • converPropertyValue读取每个属性的时候,都会调用只是没有key

这里改写converProperty,用户对指定的几个Key进行处理,
外部配置文件使用密文,加载的时候判断key是否是password,如果是就进行解密得到明文。

public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {   

    //如果是password就进行解密
    @Override
    protected String convertProperty(String propertyName, String propertyValue) {       
        if(propertyName.equals("password")){
            String decryptValue = DESUtils.getDecryptString(propertyValue);
            return decryptValue;
        }else{
            return propertyValue;
        }
    }
}

加载配置文件就使用这个类去加载

<bean class="com.sunjie.EncryptPropertyPlaceholderConfigurer"
            p:fileEncoding="utf-8">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>

从配置文件中引用值使用${password}这种方式,从Bean中应用值使用#{user.password}
比如

public class SystemConfig {
    private String password;
    ......
}

则通过以下方式获取

#{systemConfig.password}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值