您的位置:首页 > 运维架构 > Tomcat

Spring学习(一)tomcat加载web.xml、以及项目集成Spring支持

2014-07-29 20:44 441 查看
tomcat容器加载web.xml

一、

1 、启动一个 WEB 项目的时候, WEB 容器会去读取它的配置文件 web.xml ,读取 <listener> 和 <context-param> 两个结点。

2 、紧急着,容创建一个 ServletContext ( servlet 上下文),这个 web 项目的所有部分都将共享这个上下文。

3 、容器将 <context-param> 转换为键值对,并交给 servletContext 。

4 、容器创建 <listener> 中的类实例,创建监听器。

二、

load-on-startup 元素在 web 应用启动的时候指定了 servlet 被加载的顺序,它的值必须是一个整数。如果它的值是一个负整数或是这个元素不存在,那么容器会在该 servlet 被调用的时候,加载这个 servlet 。如果值是正整数或零,容器在配置的时候就加载并初始化这个 servlet ,容器必须保证值小的先被加载。如果值相等,容器可以自动选择先加载谁。 在 servlet 的配置当中, <load-on-startup>5</load-on-startup> 的含义是: 标记容器是否在启动的时候就加载这个 servlet 。 当值为 0 或者大于 0 时,表示容器在应用启动时就加载这个 servlet ; 当是一个负数时或者没有指定时,则指示容器在该 servlet 被选择时才加载。 正数的值越小,启动该 servlet 的优先级越高。

三、

在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰。

首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter 。最终得出的结论是: listener -> filter -> servlet

同时还存在着这样一种配置节: context-param ,它用于向 ServletContext 提供键值对,即应用程序上下文信息。我们的 listener, filter 等在初始化时会用到这些上下文中的信息,那么 context-param 配置节是不是应该写在 listener 配置节前呢?实际上 context-param 配置节可写在任意位置,因此真正的加载顺序为: context-param -> listener -> filter -> servlet

对于某类配置节而言,与它们出现的顺序是有关的。以 filter 为例, web.xml 中当然可以定义多个 filter ,与 filter 相关的一个配置节是 filter-mapping ,这里一定要注意,对于拥有相同 filter-name 的 filter 和 filter-mapping 配置节而言, filter-mapping 必须出现在 filter 之后,否则当解析到 filter-mapping 时,它所对应的 filter-name 还未定义。 web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时, filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法的。

servlet 同 filter 类似 ,此处不再赘述。

由此,可以看出, web.xml 的加载顺序是: context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。

以上内容摘自网络。

项目集成Spring支持

1、java项目或者javaweb项目集成Spring支持,首先需要在项目中引用Spring相关jar文件以及Spring需要的第三方依赖库。

2、web.xml里面加入:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  或者

   <servlet><!--此种配置Spring3.0后不在支持-->
    <servlet-name>context</servlet-name>
    <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>-->

3、bean.xml中配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 将PersonService类部署成Spring容器中的Bean -->
<bean id="personService" class="com.ank.test.PersonService">
<property name="name" value="ankai"/>
</bean>
</beans>

4、java代码中获取Spring容器

  ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

  得到Spring容器后,通过容器ctx实例的getBean(beanid)得到对应bean配置文件相应ID的bean实例。

  这就是Spring的依赖注入或者控制反转,依赖注入(Ioc)和控制反转(DI)其实就是一个事情,在此谨列出自己的理解,如下:

    在通常的代码中,当一个调用者需要调用一个被调用者的实例时,需要调用者实例化被调用者,但是在依赖注入模式下,创建被调用者的工作

    不再由调用者来实现,这就构成了控制反转。创建被调用者的工作由Spring容器来完成,然后注入到调用者,这就构成了依赖注入。

5、Spring是如何加载bean的,这里引用http://hzieept.iteye.com/blog/748283博客的分享,主要是通过代码来分析Spring加载一个bean的过程:

观看规则
接下 表示下一层代码。
接上 表示最近上面要调用的代码的详细部分。

public class XmlBeanFactory extends DefaultListableBeanFactory {

//新建一个bean分析器,把this注册到里面是因为,在分析器解析好一个bean时,可以立即用这个this里的注册方法去保存bean,往下看就明白。任何bean到最后都是保存在XmlBeanFactory里的(其实是DefaultListableBeanFactory)。

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {

super(parentBeanFactory);
//载入xml文件
this.reader.loadBeanDefinitions(resource); //往下->
}
}

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
//接上
public int loadBeanDefinitions(Resource resource) throws BeansException {

InputStream is = null;

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

factory.setValidating(this.validating);

DocumentBuilder docBuilder = factory.newDocumentBuilder();

docBuilder.setErrorHandler(this.errorHandler);

if (this.entityResolver != null) {
docBuilder.setEntityResolver(this.entityResolver);
}
is = resource.getInputStream();
//用Xerces解析xml,生成dom
Document doc = docBuilder.parse(is);
//registerBeanDefinitions分析dom
return registerBeanDefinitions(doc, resource); //往下

}

//接上
public int registerBeanDefinitions(Document doc, Resource resource) throws BeansException {

XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
//这个parserClass 是  DefaultXmlBeanDefinitionParser.class
return parser.registerBeanDefinitions(this, doc, resource); //往下->

}

}

public class DefaultXmlBeanDefinitionParser implements XmlBeanDefinitionParser {
//明显就是bean.xml里面出现的很熟悉的标签,说明已经快到底层类了

public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";
public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
public static final String NAME_ATTRIBUTE = "name";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String BEAN_ELEMENT = "bean";
public static final String ID_ATTRIBUTE = "id";
public static final String PARENT_ATTRIBUTE = "parent";
public static final String CLASS_ATTRIBUTE = "class";
public static final String SINGLETON_ATTRIBUTE = "singleton";
public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
public static final String AUTOWIRE_ATTRIBUTE = "autowire";
//...
//接上
public int registerBeanDefinitions(BeanDefinitionReader reader, Document doc, Resource resource) throws BeanDefinitionStoreException {

this.beanDefinitionReader = reader;
this.resource = resource;;
Element root = doc.getDocumentElement();
//...

//这里准备开始正式解析bean
int beanDefinitionCount = parseBeanDefinitions(root);//往下->
//这个beanDefinitionCount 就是解析出了多少个<bean></bean>

//...
return beanDefinitionCount;
}

protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException {
//Xerces开始循环找<bean>标签
NodeList nl = root.getChildNodes();
int beanDefinitionCounter = 0;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if // ...
//..
else if (BEAN_ELEMENT.equals(node.getNodeName())) {//这里是重点,开始解析bean
beanDefinitionCounter++;
//分两步走,看下面详解。1.先把bean放到BeanDefinitionHolder
BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele);//往下 1.->
//2.然后XmlBeanFactory去注册
BeanDefinitionReaderUtils.registerBeanDefinition(
bdHolder, this.beanDefinitionReader.getBeanFactory()); //往下 2. ->
}
}
}
return beanDefinitionCounter;
}

//接上1. 哈哈,下面是第一步,是正常解析bean,在同一个类中
protected BeanDefinitionHolder parseBeanDefinitionElement(Element ele) throws BeanDefinitionStoreException {
//...
//下面可以看到其实最底层的解析bean在同一个类的parseBeanDefinitionElement方法里。因为spring把bean封装成BeanDefinition 再把BeanDefinition 封装成BeanDefinitionHolder

BeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName);//往下

//...
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

//接上 , 这个方法很长,毕竟<bean>里attribute很多。
protected BeanDefinition parseBeanDefinitionElement(Element ele, String beanName) throws BeanDefinitionStoreException {
try {
//下面解析<bean>里的<property>,这个我不分析了。
MutablePropertyValues pvs = parsePropertyElements(ele, beanName);
//将BeanDefinition封装成AbstractBeanDefinition
AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(
className, parent, cargs, pvs, this.beanDefinitionReader.getBeanClassLoader());
//...
return bd;
}
catch (/*...*/)
//...

}
}

}
//bean解析部分到此结束。。。。

//接上2. 这里是第二部,注册部分,回到上面注释里的分两部走这里。
public class BeanDefinitionReaderUtils {
public static void registerBeanDefinition(
BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeansException {

//beanFactory就是XmlBeanFactory,其实是它的父类 DefaultListableBeanFactory在执行registerBeanDefinition

beanFactory.registerBeanDefinition(bdHolder.getBeanName(), bdHolder.getBeanDefinition()); //往下
//...
}

}

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {

/** Whether to allow re-registration of a different definition with the same name */
private boolean allowBeanDefinitionOverriding = true;

/** Map of bean definition objects, keyed by bean name */
//下面是真正藏bean的地方,其实是个Map,跟我预想的一样。
private final Map beanDefinitionMap = new HashMap();
//下面List可能是给bean的名字做个索引,这是我的初步猜想。
/** List of bean definition names, in registration order */
private final List beanDefinitionNames = new ArrayList();
//接上
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
//...
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
//根据allowBeanDefinitionOverriding这个变量来决定在bean.xml里的bean万一有同名的情况下否覆盖,因为allowBeanDefinitionOverriding默认是true,所以覆盖。
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(...);
}
else {
//...只用注释提醒相同bean将要被覆盖了
}
}
else {
//索引List里加上这个bean名字
this.beanDefinitionNames.add(beanName);
}
//将bean藏在map里。用名字来索引。
this.beanDefinitionMap.put(beanName, beanDefinition);

}
//...

}
//结束


本人刚刚开始写博客,希望各位大神轻喷,有什么不对的地方希望和大家一起探讨学习,共同进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐