对于SpringBoot的总结
其实对于SpringBoot来说,它做的事情主要有几个点
- 整合了SpringMVC
- 内置了Tomcat
- 去除了XML这类型的配置文件
- web.xml
- application.xml
- springmvc.xml
- ContextLoaderListener (即web.xml中配置的listener,目的是为了借助xml中的一些配置信息进行spring容器的初始化)
- DispatcherServlet (即向Spring容器中注册了一个Servlet,目的是为了完成Controller扫描配置视图解析器)
点击查看官网资料
这里主要查看的是如果不是用web.xml而是使用代码的方式的话,要如何实现DispatcherServlet的配置。
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/*");
}
}
结合SpringMVC简单实现SpringBoot
1. 修改pom,加入 spring-context 和 spring-mvc 的依赖
- spring-context 和 spring-mvc
org.springframework
spring-context
5.1.3.RELEASE
org.springframework
spring-webmvc
5.1.3.RELEASE
2. 新建MyWebApplicationInitalizer(查看上方 DispatcherServlet 的代码)即可
package cn.lazyfennec.springboot;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
/**
* @Author: Neco
* @Description: Copy from https://docs.spring.io/spring-framework/docs/5.2.7.RELEASE/spring-framework-reference/web.html#mvc-servlet
* @Date: create in 2022/7/25 12:45
*/
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("/");
}
}
3. 修改pom,添加 tomcat 的依赖
- embed-tomcat
org.apache.tomcat.embed
tomcat-embed-core
8.5.30
- 这里也顺带加入一下jasper的依赖,其实不加入也不会有什么大的问题,就是等下启动的时候会提示缺少Jasper类
org.apache.tomcat
tomcat-jasper
8.5.30
4. 添加 NecoApplication类
package cn.lazyfennec.springboot;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import javax.servlet.ServletException;
/**
* @Author: Neco
* @Description:
* @Date: create in 2022/7/25 11:47
*/
public class NecoApplication {
public static void run() {
// 这就是为什么SpringBoot可以直接运行jar包无需丢到tomcat的原因,因为它内置了tomcat
try {
Tomcat tomcat = new Tomcat();
tomcat.addWebapp("/boot", "G:\\workspaces\\neco-springboot");// 这里替换成你们的当前项目的路径,加上这个程序才会认为这个是一个web项目
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
}
5. 添加 AppConfig配置类
package cn.lazyfennec.springboot;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @Author: Neco
* @Description:
* @Date: create in 2022/7/25 11:52
*/
@Configuration
@ComponentScan("cn.lazyfennec") // 去除了一部分xml的定义
public class AppConfig {
}
6. 创建一个启动类
/**
* @Author: Neco
* @Description: 启动类
* @Date: create in 2022/7/25 12:52
*/
public class App {
public static void main(String[] args) {
NecoApplication.run();
}
}
7. 创建controller
package cn.lazyfennec.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: Neco
* @Description:
* @Date: create in 2022/7/25 12:59
*/
@Controller
public class NecoController {
@RequestMapping("/index1")
@ResponseBody
public String test() {
System.out.println("=========我接收到请求了=============");
return "Hello Neco";
}
@RequestMapping("/index2")
@ResponseBody
public Map test2() {
System.out.println("==========这里返回的应该是Map的数据===============");
HashMap map = new HashMap<>();
map.put("neco", "27");
return map;
}
}
8. 测试
测试发现,访问http://localhost8080/boot/index1 访问正常
访问index2,报500错误
这里的问题是,需要对Map数据进行转换处理,这里我们需要引入fast-json
9. 处理返回Map类型时,500错误的问题
- 引入 fast-json
com.alibaba
fastjson
1.2.72
- 修改AppConfig配置类
- 使类实现WebMvcConfigurer接口,并Override configureMessageConverters 方法
@Override public void configureMessageConverters(List
> converters) { FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); converters.add(fastJsonHttpMessageConverter); }
- 添加@EnableWebMvc 注解
最后的修改结果如下
package cn.lazyfennec.springboot;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @Author: Neco
* @Description:
* @Date: create in 2022/7/25 11:52
*/
@Configuration
@ComponentScan("cn.lazyfennec") // 去除了一部分xml的定义
@EnableWebMvc //
public class AppConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List> converters) {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
converters.add(fastJsonHttpMessageConverter);
}
}
- 然后尝试启动,发现报错,无法正常启动
严重: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[/boot]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:941)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:872)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1421)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1411)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[/boot]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
... 7 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
at cn.lazyfennec.springboot.MyWebApplicationInitializer.onStartup(MyWebApplicationInitializer.java:24)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:171)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5229)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 7 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622)
... 23 more
Caused by: java.lang.IllegalStateException: No ServletContext set
at org.springframework.util.Assert.state(Assert.java:73)
at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:486)
at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$afba84d0.CGLIB$resourceHandlerMapping$35()
at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$afba84d0$$FastClassBySpringCGLIB$$c325d314.invoke()
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$afba84d0.resourceHandlerMapping()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 24 more
其中重点在于 No ServletContext set
10. 解决无法启动的问题
这里可以进行查看,发现AnnotationConfigWebApplicationContext 其实是DispatcherServlet的父容器,但是在执行ac.refresh的时候,其实是会initServlet的,但是这个时候,子容器其实还没有初始化完成,所以就报了No ServletContext set
(更具体的内容建议查看源代码),这里要解决也很简单,设置一下相对应的ServletContext即可
即在MyWebApplicationInitializer中的合适位置(执行refresh之前)添加ac.setServletContext(servletCxt);
即可。
最后完整的代码如下:
package cn.lazyfennec.springboot;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
/**
* @Author: Neco
* @Description: Copy from https://docs.spring.io/spring-framework/docs/5.2.7.RELEASE/spring-framework-reference/web.html#mvc-servlet
* @Date: create in 2022/7/25 12:45
*/
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.setServletContext(servletCxt); // 当启用了@EnableWebMvc 的时候,需要设置这个,否则会报错 "No ServletContext set" 无法正常执行
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("/");
}
}
如果觉得有收获就点个赞吧,更多知识,请点击关注查看我的主页信息哦~