首页 > 代码库 > Spring学习(一)tomcat加载web.xml、以及项目集成Spring支持

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

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="http://www.mamicode.com/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 = "http://www.mamicode.com/byName";    public static final String AUTOWIRE_BY_TYPE_VALUE = "http://www.mamicode.com/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);            }//...}//结束

 本人刚刚开始写博客,希望各位大神轻喷,有什么不对的地方希望和大家一起探讨学习,共同进步。