SpringMVC源码分析之HttpServletBean

 

此篇源码分析版本为 5.1.4

DispatcherServlet的继承体系

SpringMVC源码分析之HttpServletBean

HttpServletBean是一个class直接实现了HttpServlet,这个类主要负责配置文件(springmvc.xml)的属性操作


这个是HttpServletBean的init() 方法 ,当初始化springmvc时首先会进入这个方法

public final void init() throws ServletException {
    // 1. 操作配置文件里的属性 
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),this.requiredProperties);
 
    if (!pvs.isEmpty()) {
        try {
            // 2.获取目标对象的beanwrapper对象
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            // 空实现
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
            }catch (BeansException ex) {
                throw ex;
            }
           }
    // 空方法 让子类实现
    initServletBean();
}

主要做了2件事

1. 操作配置文件里的属性

2. beanwrapper的操作

 

1.1  SpringMVC的属性处理

public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)throws ServletException {

    Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?new HashSet<>(requiredProperties) : null);
    // 1. 获取web.xml里springmvc的属性
    Enumeration<String> paramNames = config.getInitParameterNames();
    while (paramNames.hasMoreElements()) {
        // 标签名称 web.xml 里的 <param-name>
        String property = paramNames.nextElement();
        // 标签值  web.xml 里的 <param-value>
        Object value = config.getInitParameter(property);
        // 2.将上面获取到的key-value封装进 perpertyValue 对象
        addPropertyValue(new PropertyValue(property, value));
        
        if (missingProps != null) {
            missingProps.remove(property);
        }
    }
    if (!CollectionUtils.isEmpty(missingProps)) {
        // 一些异常处理操作    
    }
}

此方法的两个参数:

ServletConfig就是servlet的配置对象,通过他可以获取到web.xml里的参数

requiredProperties 就是一些我们指定必须存在servlet参数名

PropertyValue 对象

此时 name = "contextConfigLocation"   value=" classpath:springmvc.xml"

public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {

    private final String name;
    private final Object value;
     ...
}

 然后再将propertyValue对象装进

private final List<PropertyValue> propertyValueList;

/**
 * pv  需要添加的数据
 */ 
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
    for (int i = 0; i < this.propertyValueList.size(); i++) {    
        PropertyValue currentPv = this.propertyValueList.get(i); 
        // 如果有name是一样的
        if (currentPv.getName().equals(pv.getName())) {          
            // 判断到底采用哪一个
            pv = mergeIfRequired(pv, currentPv);                 
            // 把值再设置上
            setPropertyValueAt(pv, i);                           
            return this;                                         
        }                                                        
    }                
    // 如果不存在name一样 就直接添加到集合中                                            
    this.propertyValueList.add(pv);                              
    return this;                                                 
} 

/**
 * 判断到底采用哪一个
 * newPv 新加入的,  currentPv本身存在的
 */ 
private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
    Object value = newPv.getValue();
    if (value instanceof Mergeable) {
        Mergeable mergeable = (Mergeable) value;
        if (mergeable.isMergeEnabled()) {
            Object merged = mergeable.merge(currentPv.getValue());
            return new PropertyValue(newPv.getName(), merged);
            }
        }
        //如果不是Mergeable的子类就直接使用新的值覆盖原来的
        return newPv;
   }
                                                               

总结 

以上就是获取到web.xml中springmvc中的初始化参数后设置到MutablePropertyValues中的 propertyValueList中,如果发生name重复就根据Mergeable 类再次判断采用哪一个值

 

2.1 创建BeanWrapper

 

什么是beanWrapper ?

 BeanWrapper 是一个用来操作JavaBean属性的类,使用它可以直接给一个JavaBean设置,获取 属性值

以下是简单的小例子,出自<看透springMvc源代码分析与实践

class User{
	private  String username;
	private  String password;
	/** setter-getter **/
}
public class BeanWrapperTest {
	public static void main(String[] args) {
		User user = new User();
		// 获取一个对象的beanWrapper对象
		BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user);
		//设置值
		bw.setPropertyValue("username", "516");
		bw.setPropertyValue("password", "sx516");
		System.out.println(user.getUsername()); // 输出 516
		System.out.println(user.getPassword());// 输出 sx516
	}
}

再来看看HttpServletBean的init方法里的2步骤

public final void init() throws ServletException {
    // 1. 操作配置文件里的属性 
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),this.requiredProperties);
 
    if (!pvs.isEmpty()) {
        try {
            // 2.获取目标对象的beanwrapper对象
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            // 空实现
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
            }catch (BeansException ex) {
                throw ex;
            }
           }
    // 空方法 让子类实现
    initServletBean();
}

获取servletContext的资源加载器 ,然后通过beanWrapper初始化编辑器

把PropertyValues装进当前对象

至于什么是编辑器,又为什么通过BeanWrapper来操作,为什么不直接弄一setter方法呢,我还搞不懂,留着以后发现。。。。

总结:

获取到HttpServletBean的beanWrapper对象后,会通过beanWrapper对象给当前属性一些赋值,并初始化一个默认编辑器