springmvc零配置
spring之前都是采用spring.xml,springmvc.xml,web.xml三个配置文件完成web项目配置,但是随着注解开发的应用升级,逐渐抛弃了这种繁重的xml风格开发,本文主要介绍mvc零配置原理以及spring boot的雏形
mvc零配置
官方给我们如下代码替换掉web.xml
public class LryWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig1.class);
// ac.refresh();//这行代码在我电脑上跑会出错,原因未知,另外我好奇的是注释掉这行代码spring的环境是怎么初始化完成的
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
AppConfig1
@Configuration
@ComponentScan("com.lry")
@EnableWebMvc
public class AppConfig1 implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/page/", ".html");
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new FastJsonHttpMessageConverter());
}
}
HelloController
@Controller
public class HelloController {
@GetMapping("hello")
@ResponseBody
public String hello(){
return "hello SpringMVC";
}
@GetMapping("user")
@ResponseBody
public User user(){
User user = new User("lry",20);
return user;
}
@GetMapping("index")
public String index(){
return "index";
}
}
pom依赖
<dependencies>
<!--spring -core-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.9.RELEASE</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>8.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
就上面三个类就完成了mvc的环境,一个xml都没有。
servlet3新特性
思考上面例子如下问题:LryWebApplicationInitializer 的onStartup是如何被调用到的
我们可以给onStartup方法打个断点,看看调用链,结果发现如下类
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
遍历webAppInitializerClasses反射调用每一个WebApplicationInitializer的onStartup方法
}
}
上述代码属于servlet3的新特性,只要我们在resources下建一个 META-INF.services文件夹,里面添加一个文件名字为javax.servlet.ServletContainerInitializer的文件,文件内容是实现ServletContainerInitializer接口的类的全路径。
例如spring-web的下图
这个文件的内容是org.springframework.web.SpringServletContainerInitializer
tomcat启动的时候会执行SpringServletContainerInitializer的onStartup方法,并且会把实现@HandlesTypes里面的value接口的所有类找到给onStartup的第一个参数webAppInitializerClasses,然后spring遍历webAppInitializerClasses回调onStartup方法就会执行到LryWebApplicationInitializer 。
我们可以自己写一个MySpringServletContainerInitializer 实现ServletContainerInitializer ,然后把MySpringServletContainerInitializer的全路径配置到那个文件中,tomcat启动也会帮我们执行
示例如下
@HandlesTypes({MyWebApplicationInitializer.class})
public class MySpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
Iterator<Class<?>> it = webAppInitializerClasses.iterator();
while (it.hasNext()) {
Class clazz = it.next();
try {
MyWebApplicationInitializer myWebApplicationInitializer = (MyWebApplicationInitializer) clazz.newInstance();
myWebApplicationInitializer.onStartup(servletContext);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
public class LiuWebApplicationInitializer implements MyWebApplicationInitializer{
@Override
public void onStartup(ServletContext servletContext) {
// AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
// ac.register(AppConfig1.class);
//// ac.refresh();
//
// DispatcherServlet servlet = new DispatcherServlet(ac);
// ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
// registration.setLoadOnStartup(1);
// registration.addMapping("/");
}
}
public interface MyWebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
内嵌tomcat
public class MySpringApplication {
public static void main(String[] args) throws ServletException {
MySpringApplication.run();
}
public static void run() throws ServletException {
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
String sourcePath = MySpringApplication.class.getResource("/").getPath();
//告訴tomcat你的源碼在哪裏
Context ctx = tomcat.addWebapp("/",new File("src/main/webapp").getAbsolutePath());
WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes",
sourcePath, "/"));
ctx.setResources(resources);
try {
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}
这段代码会去调用SpringServletContainerInitializer 的 onStartup方法,我们只需要把这段代码加上,上面代码都不用改,就可以代替外部tomcat执行