spring3.x第五章 Spring容器高级主题

5.1 Spring容器技术内幕

  Spring容器像一台构造精妙的机器,我们通过配置文件向机器传达控制信息,机器就能够按照设定的模式进行工作。如果我们将Spring容器比喻为一辆汽车,可以将BeanFactory看成汽车的发动机,而ApplicationContext则是整辆汽车。

5.1.1 内部工作机制

  Spring组件按其所承担的角色可以划分为两类:
  1)物料组件:Resource、BeanDefinition、PropertyEditor以及最终的Bean等。它们是加工流程中被加工、被消费的组件,就像流水线上被加工的物料;
  2)加工设备组件:ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy以及BeanWrap等组件。

5.1.2 BeanDefinition

  RootBeanDefinition是最常见的实现类。将的配置信息,如class,scope等注册到BeanDefinitionResistry(就像Spring配置信息的内存数据库),后续操作直接从BeanDefinitionRegistry中读取配置信息。
  利用BeanDefinitionReader对配置信息Resource进行读取,通过XML解析器解析配置信息的DOM对象。简单的为每个生成BeanDefinition对象。
  利用容器中注册的BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,形成最终的BeanDefinition。

5.1.3 InstantiationStrategy

  负责根据BeanDefinition对象创建一个Bean实例。其中的实现类:CglibSubclassingInstantiationStrategy为Bean动态生成子类。属性填充留待BeanWrapper来完成。

5.1.4 BeanWrapper

  BeanWrapper相当于一个代理,Spring通过BeanWrapper完成Bean属性的填充工作。其中的BeanWrapperImpl对BeanDefinition属性编辑。

5.2 属性编辑器

  在Spring配置文件里,不管是double类型还是int类型,在配置文件中都对应字符串类型的字面值,这个转换器就是属性编辑器。
  PropertyEditor就是属性编辑器,是JavaBean规范定义的接口。

5.2.1 JavaBean的编辑器
  BeanInfo

  描述了JavaBean哪些属性可以编辑以及对应的属性编辑器。

5.2.2 Spring默认属性编辑器
5.3 使用外部属性文件

  Spring提供了一个PropertyPlaceholderConfigurer,它能够使Bean在配置时引用外部属性文件。这个类实现了BeanFactoryPostprocessorBean接口,因此也是衣蛾Bean工厂后处理器。

5.3.1 使用外部属性文件
  使用属性文件
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close"
    p:driverClassName="com.mysql.jdbc.Driver"
    p:url="jdbc:mysql//localhost:3306/sampledb"
    p:userName="root"
    p:password="123456" />

  抽取到一个配置文件中:jdbc.properties:

driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql//localhost:3306/sampledb
userName = root
password = 123456

  引入jdbc.properties属性文件

<!-- 引入jdbc.properties -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
    p:location="classpath:com/baobaotao/jdbc.properties"
    p:fileEncoding="utf-8" />
<!-- 通过属性名引用属性值 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
    p:driverClassName="${driverClassName}"
    p:url="${url}"
    p:userName="${userName}"
    p:password="${password}" />
  PropertyPlaceholderConfigurer其他属性

  locations:像set一样配置多个属性文件。
  order对多个属性文件设置优先顺序。
  placeholderPrefix前缀设置(默认为${)
  placeholderSuffix后缀设置(默认为})

  使用

  配置utf-8需要额外声明字符串Bean

    <context:property-placeholder
        location="classpath:com/baobaotao/jdbc.properties"
        file-encoding="utf8" />
    <bean id="utf8" class="java.lang.String">
        <constructor-arg value="utf-8"></constructor-arg>
    </bean>
  在基于注解及基于Java类配置中引用属性
@Component
public class MyDataSource{
    @Value("${driverClassName}")
    private String driverClassName;
}
5.3.2 使用加密的属性文件

  PropertyPlaceholderConfigurer继承于PropertyResourceConfigurer类,用于对属性文件中的属性进行转换处理。
  DES加密解密工具类
  信息的解密分为对称和非对称。前者表示加密后的信息可以解密成原值,而后者则不能根据加密后的信息还原成原值。MD5属于非对称加密,而DES属于对称加密。
  DES加密的工具类:

package com.baobaotao.app;

import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class DESUtils {
    //指定DES加密解密所用的密钥
    private static Key key;
    private static java.lang.String KEY_STR = "myKey";
    static{
        try {
            KeyGenerator generator = KeyGenerator.getInstance("DES");
            generator.init(new SecureRandom(KEY_STR.getBytes()));
            key = generator.generateKey();
            generator = null;
        } catch (Exception e) {
            // TODO: handle exception
            throw new RuntimeException(e);
        }
    }

    //对字符串进行DES加密,返回BASE64编码的加密字符串
    public static String getEncryptString(String str){
        BASE64Encoder base64en = new BASE64Encoder();
        try {
            byte[] strBytes = str.getBytes("UTF-8");
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptStrBytes = cipher.doFinal(strBytes);
            return base64en.encode(encryptStrBytes);
        } catch (Exception e) {
            // TODO: handle exception
            throw new RuntimeException();
        }
    }

    //对BASE64编码编码的加密字符串进行解密,返回解密后的字符串
    public static String getDecryptString(String str){
        BASE64Decoder base64De = new BASE64Decoder();
        try {
            byte[] strBytes = base64De.decodeBuffer(str);
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decryptStrBytes = cipher.doFinal(strBytes);
            return new String(decryptStrBytes, "UTF-8");
        } catch (Exception e) {
            // TODO: handle exception
            throw new RuntimeException(e);
        }
    }

    /*public static void main(String[] args){
        String[] a = {"root", "123456"};
        for (String string : a) {
            System.out.println(getEncryptString(string)+",");
        }
    }*/
}

  对属性进行解密的覆写:

package com.baobaotao.app;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

public class EncryptPropertyPlaceholderConfigurer extends
        PropertyPlaceholderConfigurer {

    private String[] encryptPropNames={"username","password"};
    //对特定的属性的属性值进行转换
    @Override
    protected String convertProperty(String propertyName, String propertyValue) {
        // TODO Auto-generated method stub
        if (isEncryptProp(propertyName)) {
            String decryptValue = DESUtils.getDecryptString(propertyValue);
            System.out.println(decryptValue);
            return decryptValue;
        }else{
            return propertyValue;
        }
    }

    //判断是否是需要进行解密的属性
    private boolean isEncryptProp(String propertyName){
        for (String encryPropName : encryptPropNames) {
            if (encryPropName.equals(propertyName)) {
                return true;
            }
        }
        return false;
    }
}
......
<bean class="com.baobaotao.app.EncryptPropertyPlaceholderConfigurer"
        p:location="classpath:com/baobaotao/app/jdbc.properties"
        p:fileEncoding="utf-8" />
......
5.3.3 属性文件自身的引用
dbName =sampledb
url =jdbc:mysql://localhost:3306/${dbName}
5.4 引用Bean的属性值
public class SysConfig{
    private int sessionTimeout;
    private int maxTabPageNum;
    private DataSource dataSource;
    //模拟从数据库中获取配置值
    public void initFromDB(){
        this.sessionTimeout=30;
        this.maxTabOPageNum=10;
    }
    //set\get方法
}
<bean id="sysConfig" class="com.baobaotao.beanprop.SysConfig"
    init-method="inifFromDB"
    p:dataSource-ref="dataSource" />
//引用Bean的属性值
<bean class="com.baobaotao.beanprop.AppManager"
    p:maxTabPageNum="#{sysConfig.maxTabPageNum}"
    p:sessionTimeout="#{sysConfig.sessionTimeout}" />

  在类中使用即@Value(“#{maxTabPageNum}”)

5.5 国际化信息
5.6 容器事件

  Spring的ApplicationContext能后发布事件并且允许注册相应的事件监听器,因此有一套完善的事件发布和监听机制。
  事件源
  事件监听器注册表:组件或框架的事件监听器保存在事件监听器注册表中
  事件广播器:负责把事件通知给事件监听器。
  事件体系体系是观察者模式的一种具体实现方式。

5.6.1 Spring事件类结构

  事件类
ApplicationContextEvent:容器事件,拥有4个子类分别表示容器启动、刷新、停止及关闭的事件。
  RequestHandleEvent:当一个HTTP请求被处理后,产生该事件。只有在web.xnl中定义了DispatcherServlet时才会产生该事件。有两个子类,分别代表Servlet及Portlet的请求事件。
  事件监听器接口
  事件广播器

5.6.2 解构Spring事件体系的具体实现
5.6.3 一个实例

  事件源与这个事件。即事件源:

package com.baobaotao.event;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class MailSender implements ApplicationContextAware{

    private ApplicationContext ctx;
    //ApplicationContextAware的接口方法,以便容器启动时注入容器实例
    @Override
    public void setApplicationContext(ApplicationContext ctx)
            throws BeansException {
        // TODO Auto-generated method stub
        this.ctx = ctx;
    }
    public void sendMail(String to){
        System.out.println("MailSender:模拟发送邮件...");
        MailSendEvent mse = new MailSendEvent(this.ctx, to);
        ctx.publishEvent(mse);
    }
}
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;
    }
}

  事件监听器:

package com.baobaotao.event;

import org.springframework.context.ApplicationListener;

public class MailSendListener implements ApplicationListener<MailSendEvent>{

    @Override
    public void onApplicationEvent(MailSendEvent event) {
        // TODO Auto-generated method stub
        MailSendEvent msEvent = event;
        System.out.println("MailSendListener:向" + msEvent.getTo() + "发送完一封邮件");
    }
}

  事件转化为一个Bean

<bean class="com.baobaotao.event.MailSendListener" />
<bean id="mailSender" class="com.baobaotao.event.MailSender" />

  启动这个容器以及开启这个事件。

public class FactoryTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ClassPathXmlApplicationContext c = new ClassPathXmlApplicationContext("classpath:/com/baobaotao/event/beans.xml");
        System.out.println("init BeanFactory.");
        //事件源,即初始化了这个Bean
        MailSender mailSender = c.getBean("mailSender", MailSender.class);
        mailSender.sendMail("1254755805@qq.com");
        //output
        //init BeanFactory.
        //MailSender:模拟发送邮件...
        //MailSendListener:向1254755805@qq.com发送完一封邮件
    }
}
5.7 小结

  主要学习了容器事件体系的观察者模式。属性编辑器、外部属性的加密处理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值