SpringMVC源码分析之HttpServletBean
此篇源码分析版本为 5.1.4
DispatcherServlet的继承体系
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.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对象给当前属性一些赋值,并初始化一个默认编辑器