springboot 使用外置servlet 容器

一、嵌入是和外置容器优缺点对比

嵌入式Servlet容器:应用打成可执行的jar

​优点:简单、便携;

​缺点:默认不支持JSP、优化定制比较复杂;

外置的Servlet容器:外面安装Tomcat - - - 应用war包的方式打包;

二、使用外置的Servlet容器步骤

(1)、使用Spring初始化向导创建warSpringBoot项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)在Project Structure窗口创建目录结构
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(3) 在运行窗口配置外置Tomcat
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(4) 将嵌入式的Tomcat指定为provided(创建想的时候其实就是了)

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

(5) 必须编写一个SpringBootServletInitializer的子类,并调用configure方法

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        //传入SpringBoot应用的主程序
        return application.sources(SpringbootwarApplication.class);
    }

}

(6) 修改 application.properties
application.properties

spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp

(7) 现在就可以愉快的写代码测试了
在这里插入图片描述
index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index</title>
</head>
<body>
    <h1>Hello JSP</h1>
    <a href="abc">abc</a>
</body>
</html>

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>SUCCESS</h1>
    <h3>${msg}</h3>
</body>
</html>

HelloController

@Controller
public class HelloController {
    @GetMapping("/abc")
    public  String hello(Model model){
        model.addAttribute("msg","你好");
        return "success";
    }
}

启动项目测试:
在这里插入图片描述
在这里插入图片描述

三、外置Servlet容器启动原理

jar包启动步骤:
执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet容器;
war包启动步骤:
启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器;

规则

  • ​1.服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例;
  • 2.ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
  • 3.还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;

流程:启动Servlet容器,再启动SpringBoot应用

1、首先我们启动tomcat
2、应用启动ServletContainerInitializer

Spring的web模块里面有这个文件: javax.servlet.ServletContainerInitializer
在这里插入图片描述
内容为:

org.springframework.web.SpringServletContainerInitializer

3、为这些WebApplicationInitializer类型的类创建实例;

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	//SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)
	//标注的这个类型的所有类都传入到onStartup方法的Set<Class<?>>集合里边
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

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

		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 {
						//为这些WebApplicationInitializer类型的类创建实例;
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(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);
		for (WebApplicationInitializer initializer : initializers) {
			//调用每个实例自己的onStartup方法
			initializer.onStartup(servletContext);
		}
	}

}

4.每一个WebApplicationInitializer都调用自己的onStartup;
在这里插入图片描述
5.相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法

6.SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建容器

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		// Logger initialization is deferred in case an ordered
		// LogServletContextInitializer is being used
		this.logger = LogFactory.getLog(getClass());
		//createRootApplicationContext(servletContext) 这是创建容器的方法 
		WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
		if (rootAppContext != null) {
			servletContext.addListener(new ContextLoaderListener(rootAppContext) {
				@Override
				public void contextInitialized(ServletContextEvent event) {
					// no-op because the application context is already initialized
				}
			});
		}
		else {
			this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
					+ "return an application context");
		}
	}

}

7.Spring的应用就启动并且创建IOC容器

	protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
		//1、创建SpringApplicationBuilder
		SpringApplicationBuilder builder = createSpringApplicationBuilder();
		builder.main(getClass());
		ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
		if (parent != null) {
			this.logger.info("Root context already created (using as parent).");
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
			builder.initializers(new ParentContextApplicationContextInitializer(parent));
		}
		
		builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
		builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
		//调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
		builder = configure(builder);
		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
		//使用builder创建了一个spring应用
		SpringApplication application = builder.build();
		if (application.getAllSources().isEmpty()
				&& MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
			application.addPrimarySources(Collections.singleton(getClass()));
		}
		Assert.state(!application.getAllSources().isEmpty(),
				"No SpringApplication sources have been defined. Either override the "
						+ "configure method or add an @Configuration annotation");
		// Ensure error pages are registered
		if (this.registerErrorPageFilter) {
			application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
		}
		//启动spring应用
		return run(application);
	}

7.spring的应用启动并且创建ioc容器

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//刷新iooc容器
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值