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="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);
        
    }
//...

}
//结束

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

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

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

上一篇:ASP.NET:/WebResource.axd


下一篇:第2阶段 web安全篇 2019网络安全训练营 第六阶段 漏洞利用 6-4漏洞利用-SSH Banner信息获取