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); } //... } //结束
本人刚刚开始写博客,希望各位大神轻喷,有什么不对的地方希望和大家一起探讨学习,共同进步。