欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

IDEA搭建純注解版本SpringMVC的web開發(fā)環(huán)境全過程并分析啟動原理

 更新時間:2024年11月14日 10:31:17   作者:莫失莫忘hh  
本文詳細(xì)介紹了如何使用注解開發(fā)搭建Spring Web環(huán)境,包括創(chuàng)建Maven工程、配置web環(huán)境、設(shè)置pom.xml、創(chuàng)建配置類和控制器等步驟,同時,文章還探討了注解開發(fā)中如何創(chuàng)建IOC容器和添加DispatcherServlet組件,并通過Servlet 3.0規(guī)范

現(xiàn)在spring開發(fā)的項目,越來越多的用到注解開發(fā)了,所以這里就記錄一下,存注解開發(fā)搭建sping的web開發(fā)。

創(chuàng)建一個maven工程

直接點next,這里不添加原型插件,(到創(chuàng)建后面在添加web環(huán)境)

設(shè)置groupId 和artifactid 信息。

然后next.

點擊finish創(chuàng)建項目工程

然后進(jìn)入這個頁面,配置文件結(jié)構(gòu)的屬性,點擊加號。來添加web環(huán)境

點擊web,添加web環(huán)境

選中這個之后出現(xiàn)這個頁面(設(shè)置web資源的目錄,也就是我們屬性的webapp所在的位置,它默認(rèn)的位置并不正確,所有我們要修改一下)

修改為 src\main\webapp

下面這個是設(shè)置web.xml的因為我們現(xiàn)在搭建的注解版,所以就不需要,(點擊-號把默認(rèn)的刪除)

然后點擊ok.

這里就有了一個web環(huán)境的標(biāo)識了,說明可以了

然后查看文件目錄結(jié)構(gòu):

到這里項目的結(jié)構(gòu)就搭建好了

配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!--注意默認(rèn)情況下 創(chuàng)建出的maven 項目并沒有設(shè)置打包的類型,這里設(shè)置成war包-->
	<packaging>war</packaging>
    <groupId>com.kuake</groupId>
    <artifactId>springmvc-web</artifactId>
    <version>1.0-SNAPSHOT</version>
	   <!--配置依賴  -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.23.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!--導(dǎo)入jackson包,使用@responseBody與@requestBody所需要-->
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                	<!--設(shè)置忽略沒有web.xml文件-->
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <!--這里就不使用外部的tomcat,使用maven的tomcat插件-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>8080</port>
                    <path>/</path>
                    <uriEncoding>UTF-8</uriEncoding>
                    <server>tomcat7</server>
                </configuration>
            </plugin>
                        <!--如果不指定,maven指定maven編譯的jdk版本,如果不指定,maven3默認(rèn)用jdk 1.5 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

(1)創(chuàng)建一個RootConfig根容器

package com.kuake.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

/**
 * @author hao
 * @create 2019-06-14 ${TIM}
 */

/**
 * 這個是跟容器,相當(dāng)于ApplicationContxt.xml,包掃描的時候要排除@Controller注解,避免重復(fù)掃描
 */
@ComponentScan(value ={"com.kuake"},
        excludeFilters={@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class})})
@Configuration//聲明這是一個配置類
public class RootConfig {
}

(2)創(chuàng)建一個AppConfig

package com.kuake.config;

/**
 * @author hao
 * @create 2019-06-14 ${TIM}
 */

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * Springweb應(yīng)用的配置 相當(dāng)于配置文件SpringMVC.xml
 *
 */
@Configuration
@EnableWebMvc//開啟全面接管spingmvc
//配置只會掃描Controller注解
@ComponentScan(value = {"com.kuake"},includeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Controller.class})},useDefaultFilters = false)
public class AppConfig extends WebMvcConfigurerAdapter {

    //配置視圖解析器
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        //默認(rèn)前綴 /WEB-INF/   后綴.jsp
        registry.jsp();
    }

    //可以自定義添加各種組件 通過重寫方法
}

(3)創(chuàng)建一個容器的初始化器MyWebAppInitializer

package com.kuake.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * @author hao
 * @create 2019-06-14 ${TIM}
 */
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//加載容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }
//加載webapp容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { AppConfig.class };
    }
//設(shè)置DispatcherServlet的攔截規(guī)則  / 代表攔截所有但是不包括jsp
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

(4)創(chuàng)建一個helloController

package com.kuake.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @author hao
 * @create 2019-06-14 ${TIM}
 */
@Controller
public class UserController {
    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }
}

在WEB-INF下創(chuàng)建一個hello.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2019/6/14
  Time: 18:48
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
hello world!
</body>
</html>

(5)啟動web應(yīng)用,使用maven的tomcat插件

控制臺打印日志:

[INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ springmvc --- [INFO] Running war on http://localhost:8080/ [INFO] Using existing Tomcat server configuration at F:\ideaworkplace\spring-test\springmvc\target\tomcat [INFO] create webapp with contextPath: 六月 14, 2019 9:09:40 下午 org.apache.coyote.AbstractProtocol init 信息: Initializing ProtocolHandler ["http-bio-8080"] 六月 14, 2019 9:09:40 下午 org.apache.catalina.core.StandardService startInternal 信息: Starting service Tomcat 六月 14, 2019 9:09:40 下午 org.apache.catalina.core.StandardEngine startInternal 信息: Starting Servlet Engine: Apache Tomcat/7.0.47 六月 14, 2019 9:09:44 下午 org.apache.catalina.core.ApplicationContext log 信息: 1 Spring WebApplicationInitializers detected on classpath 六月 14, 2019 9:09:44 下午 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring root WebApplicationContext 六月 14, 2019 9:09:44 下午 org.springframework.web.context.ContextLoader initWebApplicationContext 信息: Root WebApplicationContext: initialization started 六月 14, 2019 9:09:44 下午 org.springframework.web.context.support.AnnotationConfigWebApplicationContext prepareRefresh 信息: Refreshing Root WebApplicationContext: startup date [Fri Jun 14 21:09:44 CST 2019]; root of context hierarchy 六月 14, 2019 9:09:44 下午 org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions 信息: Registering annotated classes: [class com.kuake.config.RootConfig] 六月 14, 2019 9:09:45 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init> 信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring 六月 14, 2019 9:09:45 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping register 信息: Mapped "{[/hello],methods=[GET]}" onto public java.lang.String com.kuake.controller.UserController.hello() 六月 14, 2019 9:09:49 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache 信息: Looking for @ControllerAdvice: Root WebApplicationContext: startup date [Fri Jun 14 21:09:44 CST 2019]; root of context hierarchy 六月 14, 2019 9:09:49 下午 org.springframework.web.context.ContextLoader initWebApplicationContext 信息: Root WebApplicationContext: initialization completed in 5098 ms 六月 14, 2019 9:09:49 下午 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring FrameworkServlet 'dispatcher' 六月 14, 2019 9:09:49 下午 org.springframework.web.servlet.DispatcherServlet initServletBean 信息: FrameworkServlet 'dispatcher': initialization started 六月 14, 2019 9:09:49 下午 org.springframework.web.context.support.AnnotationConfigWebApplicationContext prepareRefresh 信息: Refreshing WebApplicationContext for namespace 'dispatcher-servlet': startup date [Fri Jun 14 21:09:49 CST 2019]; parent: Root WebApplicationContext 六月 14, 2019 9:09:49 下午 org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions 信息: Registering annotated classes: [class com.kuake.config.AppConfig] 六月 14, 2019 9:09:49 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init> 信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring 六月 14, 2019 9:09:49 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping register 信息: Mapped "{[/hello],methods=[GET]}" onto public java.lang.String com.kuake.controller.UserController.hello() 六月 14, 2019 9:09:49 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache 信息: Looking for @ControllerAdvice: WebApplicationContext for namespace 'dispatcher-servlet': startup date [Fri Jun 14 21:09:49 CST 2019]; parent: Root WebApplicationContext 六月 14, 2019 9:09:49 下午 org.springframework.web.servlet.DispatcherServlet initServletBean 信息: FrameworkServlet 'dispatcher': initialization completed in 359 ms 六月 14, 2019 9:09:49 下午 org.apache.coyote.AbstractProtocol start 信息: Starting ProtocolHandler ["http-bio-8080"]

進(jìn)入瀏覽器:

到這里說明我們的環(huán)境搭建成功

相比傳統(tǒng)搭建傳統(tǒng)的web環(huán)境,要在web.xml當(dāng)中配置前端控制器組件DispatcherServlet,需要在springmvc.xml當(dāng)中配置視圖解析器,…等等,那么在這種純注解的情況下,他是如何創(chuàng)建出ioc容器的呢,如何添加DispatcherServlet這個組件的呢?,F(xiàn)在來一探究竟。

我們MyWebAppInitializer這個類繼承了一個AbstractAnnotationConfigDispatcherServletInitializer就從這個類入手。

1、AbstractAnnotationConfigDispatcherServletInitializer

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.support;

import org.springframework.util.ObjectUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;



public abstract class AbstractAnnotationConfigDispatcherServletInitializer
		extends AbstractDispatcherServletInitializer {

	@Override
	protected WebApplicationContext createRootApplicationContext() {
		//獲得rootConfig.class
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			//創(chuàng)建一個跟容器
			AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
			rootAppContext.register(configClasses);
			return rootAppContext;
		}
		else {
			return null;
		}
	}
	
	@Override
	protected WebApplicationContext createServletApplicationContext() {
		//創(chuàng)建一個web的ioc容器
		AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			servletAppContext.register(configClasses);
		}
		return servletAppContext;
	}

	
	protected abstract Class<?>[] getRootConfigClasses();

	protected abstract Class<?>[] getServletConfigClasses();

}

這個類 有兩個主要的方法createRootApplicationContext()和createServletApplicationContext(),分別用來創(chuàng)建根容器子容器。

因為定義了兩個抽象方法getRootConfigClasses()和getServletConfigClasses(),在創(chuàng)建的過程中也都調(diào)用了這倆個方法,如果我們的子類重寫了這兩個抽象方法,那么父類在創(chuàng)建的時候,就會回調(diào)子類的方法 (這其實符合一個設(shè)計模式,模板方法)

(2)接著看他的父類:AbstractDispatcherServletInitializer

/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.web.servlet.support;

import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.FilterRegistration.Dynamic;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.core.Conventions;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.AbstractContextLoaderInitializer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FrameworkServlet;

/**
* Base class for {@link org.springframework.web.WebApplicationInitializer}
* implementations that register a {@link DispatcherServlet} in the servlet context.
*
* <p>Concrete implementations are required to implement
* {@link #createServletApplicationContext()}, as well as {@link #getServletMappings()},
* both of which get invoked from {@link #registerDispatcherServlet(ServletContext)}.
* Further customization can be achieved by overriding
* {@link #customizeRegistration(ServletRegistration.Dynamic)}.
*
* <p>Because this class extends from {@link AbstractContextLoaderInitializer}, concrete
* implementations are also required to implement {@link #createRootApplicationContext()}
* to set up a parent "<strong>root</strong>" application context. If a root context is
* not desired, implementations can simply return {@code null} in the
* {@code createRootApplicationContext()} implementation.
*
* @author Arjen Poutsma
* @author Chris Beams
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 3.2
*/
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {

   /**
    * The default servlet name. Can be customized by overriding {@link #getServletName}.
    */
   public static final String DEFAULT_SERVLET_NAME = "dispatcher";


   @Override
   public void onStartup(ServletContext servletContext) throws ServletException {
   	super.onStartup(servletContext);
   	registerDispatcherServlet(servletContext);
   }

   /**
    * Register a {@link DispatcherServlet} against the given servlet context.
    * <p>This method will create a {@code DispatcherServlet} with the name returned by
    * {@link #getServletName()}, initializing it with the application context returned
    * from {@link #createServletApplicationContext()}, and mapping it to the patterns
    * returned from {@link #getServletMappings()}.
    * <p>Further customization can be achieved by overriding {@link
    * #customizeRegistration(ServletRegistration.Dynamic)} or
    * {@link #createDispatcherServlet(WebApplicationContext)}.
    * @param servletContext the context to register the servlet against
    */
   protected void registerDispatcherServlet(ServletContext servletContext) {
   	String servletName = getServletName();
   	Assert.hasLength(servletName, "getServletName() must not return empty or null");

   	WebApplicationContext servletAppContext = createServletApplicationContext();
   	Assert.notNull(servletAppContext,
   			"createServletApplicationContext() did not return an application " +
   			"context for servlet [" + servletName + "]");

   	FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
   	dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

   	ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
   	Assert.notNull(registration,
   			"Failed to register servlet with name '" + servletName + "'." +
   			"Check if there is another servlet registered under the same name.");

   	registration.setLoadOnStartup(1);
   	registration.addMapping(getServletMappings());
   	registration.setAsyncSupported(isAsyncSupported());

   	Filter[] filters = getServletFilters();
   	if (!ObjectUtils.isEmpty(filters)) {
   		for (Filter filter : filters) {
   			registerServletFilter(servletContext, filter);
   		}
   	}

   	customizeRegistration(registration);
   }

   /**
    * Return the name under which the {@link DispatcherServlet} will be registered.
    * Defaults to {@link #DEFAULT_SERVLET_NAME}.
    * @see #registerDispatcherServlet(ServletContext)
    */
   protected String getServletName() {
   	return DEFAULT_SERVLET_NAME;
   }

   /**
    * Create a servlet application context to be provided to the {@code DispatcherServlet}.
    * <p>The returned context is delegated to Spring's
    * {@link DispatcherServlet#DispatcherServlet(WebApplicationContext)}. As such,
    * it typically contains controllers, view resolvers, locale resolvers, and other
    * web-related beans.
    * @see #registerDispatcherServlet(ServletContext)
    */
   protected abstract WebApplicationContext createServletApplicationContext();

   /**
    * Create a {@link DispatcherServlet} (or other kind of {@link FrameworkServlet}-derived
    * dispatcher) with the specified {@link WebApplicationContext}.
    * <p>Note: This allows for any {@link FrameworkServlet} subclass as of 4.2.3.
    * Previously, it insisted on returning a {@link DispatcherServlet} or subclass thereof.
    */
   protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
   	return new DispatcherServlet(servletAppContext);
   }

   /**
    * Specify application context initializers to be applied to the servlet-specific
    * application context that the {@code DispatcherServlet} is being created with.
    * @since 4.2
    * @see #createServletApplicationContext()
    * @see DispatcherServlet#setContextInitializers
    * @see #getRootApplicationContextInitializers()
    */
   protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() {
   	return null;
   }

   /**
    * Specify the servlet mapping(s) for the {@code DispatcherServlet} &mdash;
    * for example {@code "/"}, {@code "/app"}, etc.
    * @see #registerDispatcherServlet(ServletContext)
    */
   protected abstract String[] getServletMappings();

   /**
    * Specify filters to add and map to the {@code DispatcherServlet}.
    * @return an array of filters or {@code null}
    * @see #registerServletFilter(ServletContext, Filter)
    */
   protected Filter[] getServletFilters() {
   	return null;
   }

   /**
    * Add the given filter to the ServletContext and map it to the
    * {@code DispatcherServlet} as follows:
    * <ul>
    * <li>a default filter name is chosen based on its concrete type
    * <li>the {@code asyncSupported} flag is set depending on the
    * return value of {@link #isAsyncSupported() asyncSupported}
    * <li>a filter mapping is created with dispatcher types {@code REQUEST},
    * {@code FORWARD}, {@code INCLUDE}, and conditionally {@code ASYNC} depending
    * on the return value of {@link #isAsyncSupported() asyncSupported}
    * </ul>
    * <p>If the above defaults are not suitable or insufficient, override this
    * method and register filters directly with the {@code ServletContext}.
    * @param servletContext the servlet context to register filters with
    * @param filter the filter to be registered
    * @return the filter registration
    */
   protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) {
   	String filterName = Conventions.getVariableName(filter);
   	Dynamic registration = servletContext.addFilter(filterName, filter);
   	if (registration == null) {
   		int counter = -1;
   		while (counter == -1 || registration == null) {
   			counter++;
   			registration = servletContext.addFilter(filterName + "#" + counter, filter);
   			Assert.isTrue(counter < 100,
   					"Failed to register filter '" + filter + "'." +
   					"Could the same Filter instance have been registered already?");
   		}
   	}
   	registration.setAsyncSupported(isAsyncSupported());
   	registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName());
   	return registration;
   }

   private EnumSet<DispatcherType> getDispatcherTypes() {
   	return (isAsyncSupported() ?
   			EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) :
   			EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
   }

   /**
    * A single place to control the {@code asyncSupported} flag for the
    * {@code DispatcherServlet} and all filters added via {@link #getServletFilters()}.
    * <p>The default value is "true".
    */
   protected boolean isAsyncSupported() {
   	return true;
   }

   /**
    * Optionally perform further registration customization once
    * {@link #registerDispatcherServlet(ServletContext)} has completed.
    * @param registration the {@code DispatcherServlet} registration to be customized
    * @see #registerDispatcherServlet(ServletContext)
    */
   protected void customizeRegistration(ServletRegistration.Dynamic registration) {
   }

}

主要方法是registerDispatcherServlet(ServletContext servletContext)他的作用是:

  • 1、創(chuàng)建一個web的ioc容器【createServletApplicationContext()】
  • 2、創(chuàng)建了DispatcherServlet【createDispatcherServlet()】
  • 3、將創(chuàng)建的DispatcherServlet添加到ServletContext中; 

(3)接著再上一個父類:AbstractContextLoaderInitializer

/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.context;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.web.WebApplicationInitializer;

/**
 * Convenient base class for {@link WebApplicationInitializer} implementations
 * that register a {@link ContextLoaderListener} in the servlet context.
 *
 * <p>The only method required to be implemented by subclasses is
 * {@link #createRootApplicationContext()}, which gets invoked from
 * {@link #registerContextLoaderListener(ServletContext)}.
 *
 * @author Arjen Poutsma
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.2
 */
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {

	/** Logger available to subclasses */
	protected final Log logger = LogFactory.getLog(getClass());


	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}

	/**
	 * Register a {@link ContextLoaderListener} against the given servlet context. The
	 * {@code ContextLoaderListener} is initialized with the application context returned
	 * from the {@link #createRootApplicationContext()} template method.
	 * @param servletContext the servlet context to register the listener against
	 */
	protected void registerContextLoaderListener(ServletContext servletContext) {
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());
			servletContext.addListener(listener);
		}
		else {
			logger.debug("No ContextLoaderListener registered, as " +
					"createRootApplicationContext() did not return an application context");
		}
	}

	/**
	 * Create the "<strong>root</strong>" application context to be provided to the
	 * {@code ContextLoaderListener}.
	 * <p>The returned context is delegated to
	 * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will
	 * be established as the parent context for any {@code DispatcherServlet} application
	 * contexts. As such, it typically contains middle-tier services, data sources, etc.
	 * @return the root application context, or {@code null} if a root context is not
	 * desired
	 * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
	 */
	protected abstract WebApplicationContext createRootApplicationContext();

	/**
	 * Specify application context initializers to be applied to the root application
	 * context that the {@code ContextLoaderListener} is being created with.
	 * @since 4.2
	 * @see #createRootApplicationContext()
	 * @see ContextLoaderListener#setContextInitializers
	 */
	protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
		return null;
	}

}

主要的方法createRootApplicationContext()創(chuàng)建一個根容器。 

(4)最頂層接口WebApplicationInitializer

public interface WebApplicationInitializer {

	/**
	 * Configure the given {@link ServletContext} with any servlets, filters, listeners
	 * context-params and attributes necessary for initializing this web application. See
	 * examples {@linkplain WebApplicationInitializer above}.
	 * @param servletContext the {@code ServletContext} to initialize
	 * @throws ServletException if any call against the given {@code ServletContext}
	 * throws a {@code ServletException}
	 */
	void onStartup(ServletContext servletContext) throws ServletException;

}

那么容器啟動時候,為什么這么MyWebAppInitializer這個類會被加載呢,進(jìn)而創(chuàng)建根容器,創(chuàng)建web的ioc容器呢 

來看這個類的介紹:

  • WebApplicationInitializer是Spring MVC提供的一個接口,它確保檢測到您的實現(xiàn)并自動用于初始化Servlet 3容器。
  • WebApplicationInitializer的抽象基類實現(xiàn)AbstractDispatcherServletInitializer通過重寫方法來指定servlet映射和DispatcherServlet配置的位置,使得注冊DispatcherServlet更加容易。

這里提到了初始化servlet3.0容器,那就有必要了解一下sevrvlet3.0的一個初始化規(guī)范,根據(jù)官方文檔的解釋,用自己的話總結(jié)一下有關(guān)的重要幾點,大概意思就是:

  • web容器在啟動的時候,會掃描每個jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
  • 加載這個文件制定的類,并且可以通過@HandlesTypes注解,
  • 把感興趣的類的信息,注入到onStartup(Set<Class<?>> var1, ServletContext var2)var1當(dāng)中

經(jīng)過查看源碼,以及debug調(diào)試發(fā)現(xiàn),這個ServletContainerInitializer其實就是SpringServletContainerInitializer,來看SpringServletContainerInitializer所在在包下的目錄結(jié)構(gòu)。

根據(jù)上面介紹的servlet3.0規(guī)范,當(dāng)servlet容器啟動的時候,就會加載javax.servlet.ServletContainerInitializer文件中指定的類

其內(nèi)容就是org.springframework.web.SpringServletContainerInitializer,所有也就會在容器啟動的時候,加載SpringServletContainerInitializer 

SpringServletContainerInitializer代碼如下:

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web;

import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer) waiClass.newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		//遍歷容器中所有的WebApplicationInitializer#onStartup方法
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

這段代碼的簡單的介紹一下:因為這個類上標(biāo)注了@HandlesTypes(WebApplicationInitializer.class),所以會加載所有WebApplicationInitializer信息都會被注入到onStartup()方法的形參webAppInitializerClasses上,然后遍歷,判斷如果不是接口【!waiClass.isInterface()】,不是抽象類【!Modifier.isAbstract(waiClass.getModifiers()】,那么就實例化,并且添加在集合當(dāng)中。最后遍歷集合initializers,代用每一個對象的#onStartup(servletContext)方法。在這個遍歷上,打上一個斷點,看一下initializers中有哪些對象。

		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}

查看結(jié)果如下:(加載的感興趣的類信息一共有4個,因為其他三個都是抽象類,不符合實例化的條件,所有集合中也就一個類,就是我們定義的MyWebAppInitializer

經(jīng)過上面的分析,可以大概小結(jié)一下: 

因為servlet3.0容器加載規(guī)范,會加載特定位置的文件中指定的類,在這里也就是SpringServletContainerInitializer,然后加載@HandleType注解標(biāo)注的感興趣的類,然后根據(jù)條件實例化這些類,添加到集合中,遍歷集合然后調(diào)用他們的onStartup方法

有了這些基礎(chǔ),那么就可以來看一下,他的執(zhí)行調(diào)用過程,來看一下容器如何被創(chuàng)建的,核心控制器DispatcherServlet是如何被添加到容器中…

執(zhí)行initializer.onStartup(servletContext),所以來到MyWebAppInitializer#onStartup的方法,因為他本身沒有重寫這個方法,所以往上找他的父類AbstractAnnotationConfigDispatcherServletInitializer,但是這里也

沒有那么就接著再找父類AbstractDispatcherServletInitializer,在這個類中onStartup方法代碼如下:

@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);
		registerDispatcherServlet(servletContext);
	}

他分為兩步,先執(zhí)行super.onStartup(servletContext),所以接著來到他的父類AbstractContextLoaderInitializer中這個方法的實現(xiàn),

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}
	
		protected void registerContextLoaderListener(ServletContext servletContext) {
		//調(diào)用#createRootApplicationContext()方法,因為自己沒有實現(xiàn),調(diào)用子類的方法
		//執(zhí)行AbstractAnnotationConfigDispatcherServletInitializer這個類的createRootApplicationContext()
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
		//添加容器監(jiān)聽事件
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());
			servletContext.addListener(listener);
		}
		else {
			logger.debug("No ContextLoaderListener registered, as " +
					"createRootApplicationContext() did not return an application context");
		}
	}

進(jìn)入AbstractAnnotationConfigDispatcherServletInitializer

	@Override
	protected WebApplicationContext createRootApplicationContext() {
	//getRootConfigClasses()方法,這個類自己沒有實現(xiàn),調(diào)用的其實是MyWebAppInitializer#getRootConfigClasses方法
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			//創(chuàng)建一個根容器
			AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
			//把我們的配置類注冊進(jìn)去
			rootAppContext.register(configClasses);
			return rootAppContext;
		}
		else {
			return null;
		}
	}

執(zhí)行到這里,super.onStartup(servletContext),方法執(zhí)行完,根容器已經(jīng)被創(chuàng)建出來了,接著調(diào)用registerDispatcherServlet(servletContext)這個方法,方法如下:

protected void registerDispatcherServlet(ServletContext servletContext) {
		//獲得名字 默認(rèn)是dispatcher
		String servletName = getServletName();
		Assert.hasLength(servletName, "getServletName() must not return empty or null");
		// 因為本類沒有實現(xiàn),調(diào)動子類AbstractAnnotationConfigDispatcherServletInitializer的
		//createServletApplicationContext方法,創(chuàng)建出web的ioc容器  這個方法的代碼與createRootApplicationContext()的執(zhí)行過程類似
		/*調(diào)用子類MyWebAppInitializer 的getServletConfigClasses方法,加載AppConfig.class 然后創(chuàng)建出一個容器。
		*/
		WebApplicationContext servletAppContext = createServletApplicationContext();
		Assert.notNull(servletAppContext,
				"createServletApplicationContext() did not return an application " +
				"context for servlet [" + servletName + "]");
		//其實就是new DispatcherServlet(servletAppContext),創(chuàng)建出一個DispatcherServlet
		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
		// 將創(chuàng)建出來的DispatcherServlet添加到容器中
		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
		Assert.notNull(registration,
				"Failed to register servlet with name '" + servletName + "'." +
				"Check if there is another servlet registered under the same name.");
		//容器加載,就創(chuàng)建
		registration.setLoadOnStartup(1);
		//設(shè)置攔截的mapping  回調(diào)子類的getServletMappings()方法 也就是MyWebAppInitializer#getServletMappings方法
		registration.addMapping(getServletMappings());
		//設(shè)置異步支持 默認(rèn)是true
		registration.setAsyncSupported(isAsyncSupported());
		//添加過濾器,可以通過重寫ServletFilters方法 ,來添加過濾器
		Filter[] filters = get	ServletFilters();
		if (!ObjectUtils.isEmpty(filters)) {
			for (Filter filter : filters) {
				registerServletFilter(servletContext, filter);
			}
		}

		customizeRegistration(registration);
	}

執(zhí)行到這里,作用是創(chuàng)建出web的ioc容器,并且創(chuàng)建出DispatcherServlet,設(shè)置了他的啟動機(jī)制,設(shè)置了ServletMapping,所有就不需要的web.xml配置,就能成功啟動容器。

(其實根容器和web容器其實是一個父子關(guān)系),每一個容器,裝載一些特定的組件。

DispatcherServlet需要一個WebApplicationContext(一個普通ApplicationContext的擴(kuò)展)來進(jìn)行自己的配置。

WebApplicationContext有一個指向它關(guān)聯(lián)的ServletContext和Servlet的鏈接。

它還綁定到ServletContext,以便應(yīng)用程序可以在requestcontext tutils上使用靜態(tài)方法來查找需要訪問的WebApplicationContext。

對于許多只有一個WebApplicationContext的應(yīng)用程序來說,這是簡單而充分的。

還可以有一個上下文層次結(jié)構(gòu),其中一個根WebApplicationContext在多個DispatcherServlet(或其他Servlet)實例之間共享,每個實例都有自己的子WebApplicationContext配置。

有關(guān)上下文層次結(jié)構(gòu)特性的更多信息,根WebApplicationContext通常包含基礎(chǔ)設(shè)施bean,比如需要跨多個Servlet實例共享的數(shù)據(jù)存儲庫和業(yè)務(wù)服務(wù)。

這些bean是有效繼承的,可以在特定于Servlet的子WebApplicationContext中重寫(即重新聲明),該上下文通常包含給定Servlet的本地

總結(jié)一下

畫了一個uml時序圖:

  • web容器在啟動的時候,會掃描每個jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
  • spring的應(yīng)用一啟動會加載感興趣的WebApplicationInitializer接口的下的所有組件;
  • 并且為WebApplicationInitializer組件創(chuàng)建對象(組件不是接口,不是抽象類)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 一文帶你探究Spring中Bean的線程安全性問題

    一文帶你探究Spring中Bean的線程安全性問題

    很多人都想spring中的bean是線程安全的嗎?本文將帶你探究Spring中Bean的線程安全性問題,感興趣的同學(xué)可以參考閱讀下
    2023-05-05
  • Java中同步與并發(fā)用法分析

    Java中同步與并發(fā)用法分析

    這篇文章主要介紹了Java中同步與并發(fā)用法,較為詳細(xì)的分析了java同步與并發(fā)所涉及的相關(guān)類與使用技巧,需要的朋友可以參考下
    2015-06-06
  • SpringBoot 如何從配置文件讀取值到對象中

    SpringBoot 如何從配置文件讀取值到對象中

    這篇文章主要介紹了SpringBoot 如何從配置文件讀取值到對象中,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 詳解Mybatis多參數(shù)傳遞入?yún)⑺姆N處理方式

    詳解Mybatis多參數(shù)傳遞入?yún)⑺姆N處理方式

    這篇文章主要介紹了詳解Mybatis多參數(shù)傳遞入?yún)⑺姆N處理方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • java計算兩個日期中間的時間

    java計算兩個日期中間的時間

    這篇文章主要介紹了java計算兩個日期中間的時間的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • Java學(xué)習(xí)筆記:基本輸入、輸出數(shù)據(jù)操作實例分析

    Java學(xué)習(xí)筆記:基本輸入、輸出數(shù)據(jù)操作實例分析

    這篇文章主要介紹了Java學(xué)習(xí)筆記:基本輸入、輸出數(shù)據(jù)操作,結(jié)合實例形式分析了Java輸入、輸出數(shù)據(jù)相關(guān)函數(shù)使用技巧與操作注意事項,需要的朋友可以參考下
    2020-04-04
  • java利用java.net.URLConnection發(fā)送HTTP請求的方法詳解

    java利用java.net.URLConnection發(fā)送HTTP請求的方法詳解

    如何通過Java(模擬瀏覽器)發(fā)送HTTP請求是我們在日常經(jīng)常會遇到的問題,下面這篇文章主要給大家介紹了關(guān)于java利用java.net.URLConnection發(fā)送HTTP請求的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-05-05
  • springboot異步@Async的使用及失效場景介紹

    springboot異步@Async的使用及失效場景介紹

    本文主要介紹了springboot異步@Async的使用及失效場景介紹,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Java編程實現(xiàn)時間和時間戳相互轉(zhuǎn)換實例

    Java編程實現(xiàn)時間和時間戳相互轉(zhuǎn)換實例

    這篇文章主要介紹了什么是時間戳,以及Java編程實現(xiàn)時間和時間戳相互轉(zhuǎn)換實例,具有一定的參考價值,需要的朋友可以了解下。
    2017-09-09
  • Java 8 Stream流強大的原理

    Java 8 Stream流強大的原理

    這篇文章主要介紹了Java 8 Stream流強大的原理,Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數(shù)據(jù)。本文會對Stream的實現(xiàn)原理進(jìn)行剖析,具有一定的參考價值,需要的朋友可以參考一下
    2021-12-12

最新評論