我们在之前使用spring做项目时候都要使用一个web.xml文件这个文件是tomcat能够得到spring环境的入口,而在使用springboot时是没有这些配置文件的。
首先我们看一下在spring项目中web.xml都干了什么
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
1.通过listener加载了一个application.xml文件也就是常说的spring环境初始化
2.加入前端控制器
3.通过前端控制器加载一个mvc.xml配置文件并设置前端控制器的基本属性
4.application.xml中我们配置的信息通常是一些数据源之类的bean和包扫描或者是开启事务之类的东西,
mvc.xml中配置的基本是包扫描,视图解析,处理器映射器和适配器,开启注解等
我们现在的目的就是可以在spring项目中不通过配置文件就完成以上功能第一步就完成了
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
在spring最新的官方文档中我们可以看到这样一串代码,这串代码是我们在spring中不使用配置文件来把前端控制器和spring配置信息加入到tomcat中的方法
//1.创建前端控制器
DispatcherServlet dispatcherServlet = new DispatcherServlet();
//2.将前端控制器加入到tomcat中
ServletRegistration.Dynamic web = servletContext.addServlet(“web”, dispatcherServlet);
web.setLoadOnStartup(1);
web.addMapping("/");
这样前端控制器就写好了但是spring环境和springmvc的配置信息还没有加进去
创建配置类扫描最外层的包(springboot就是这么干的,直接扫描启动器类所在的包而启动器是放在最外层中的)
@Configuration
@ComponentScan(“com.caohao”)
public class applicationConfig {}
//加入spring环境的配置信息类
AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
annotationConfigWebApplicationContext.register(applicationConfig.class);
annotationConfigWebApplicationContext.refresh();
//1.创建前端控制器
DispatcherServlet dispatcherServlet = new DispatcherServlet(annotationConfigWebApplicationContext);
//2.将前端控制器加入到tomcat中
ServletRegistration.Dynamic web = servletContext.addServlet(“web”, dispatcherServlet);
web.setLoadOnStartup(1);
web.addMapping("/");
这样就可以直接启动tomcat而不使用web.xml文件了
但是springboot中tomcat是内嵌的
我们模拟实现一下首先我们要导入tomcat包以此来new出一个tomcat
public class springBootApplication {
static public void run(){
File file = new File("C:\\Users\\ASUS\\Desktop\\test");
if (!file.exists()){file.mkdirs();}
Tomcat tomcat = new Tomcat();
tomcat.setPort(8081);
tomcat.addContext("/",file.getAbsolutePath());
try {
tomcat.start();
//就像我们使用server socket一样要获取请求
//再让tomcat进入等待否则启动一下就关闭了
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}
但是这样的话我们的前端控制器就没办法放入tomcat中了,甚至是连一开始重写的onstart方法都没有被调用。那么tomcat是如何知道要调用这个方法的呢?
以前使用配置文件时我们的启动流程时先开启tomcat在通过tomcat加载web.xml之后就可以初始化spring了
而现在我们可以启动tomcat 也有方法加载spring配置环境(就是我们实现接口后重写的onstart方法)但是中间缺少一个办法将这两个步骤链接起来
在springweb的源码中可以看到这样一个配置文件里面存的是一个类的全类名
我们进入这个类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
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 {
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) {
initializer.onStartup(servletContext);
}
}
}
可以看到一个配置注解和这个类实现了一个servlet提供的接口并且重写了接口中的onstart方法
@HandlesTypes(WebApplicationInitializer.class)而这个注解中标记的接口类就是我们之前实现的那个类通过这个接口可以获得所有实现了这个接口的实现类,那么我们就可以调用所有实现类的方法,这样就可以调用我们之前重写的那个onstart方法了。
@HandlesTypes(WebApplicationInitializer.class)这个注解配合ServletContainerInitializer接口可以获取所有的注解中接口的实现类
第二点就是要想tomca执行这些方法一定要保证tomcat将你写的项目当作web项目才行而tomcat判断web项目的标准就是是否有webinf文件夹
可以在tomcat中使用tomcat.addWebapp();方法但是这样启动tomcat时就会自动调用jsp这是不行的springboot默认时没有jsp依赖的。
现在我们的目的就是避开这个地方代码如下将之前写在配置中的东西拿到这边改写就可以了
package com.caohao;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletRegistration;
import java.io.File;
public class caohaoqidongqi {
public static void run(){
File file = new File("C:\\Users\\ASUS\\Desktop\\test");
if (!file.exists()){file.mkdirs();}
Tomcat tomcat = new Tomcat();
tomcat.setPort(8081);
tomcat.addContext("/",file.getAbsolutePath());
//tomcat.addWebapp();
try {
//加入spring环境的配置信息类
AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
annotationConfigWebApplicationContext.register(applicationConfig.class);
annotationConfigWebApplicationContext.refresh();
//1.创建前端控制器
DispatcherServlet dispatcherServlet = new DispatcherServlet(annotationConfigWebApplicationContext);
//2.将前端控制器加入到tomcat中
Wrapper wrapper = tomcat.addServlet("/","mvc", dispatcherServlet);
wrapper.setLoadOnStartup(1);
wrapper.addMapping("/");
//ServletRegistration.Dynamic web = servletContext.addServlet("web", dispatcherServlet);
// web.setLoadOnStartup(1);
// web.addMapping("/");
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}