對于SpringBoot的總結(jié)
其實對于SpringBoot來說腊敲,它做的事情主要有幾個點
- 整合了SpringMVC
- 內(nèi)置了Tomcat
- 去除了XML這類型的配置文件
- web.xml
- application.xml
- springmvc.xml
- ContextLoaderListener (即web.xml中配置的listener娘汞,目的是為了借助xml中的一些配置信息進行spring容器的初始化)
- DispatcherServlet (即向Spring容器中注冊了一個Servlet棘利,目的是為了完成Controller掃描配置視圖解析器)
點擊查看官網(wǎng)資料
這里主要查看的是如果不是用web.xml而是使用代碼的方式的話梯澜,要如何實現(xiàn)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/*");
}
}
結(jié)合SpringMVC簡單實現(xiàn)SpringBoot
1. 修改pom纫版,加入 spring-context 和 spring-mvc 的依賴
- spring-context 和 spring-mvc
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- spring mvc 相關(guān) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
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
<!-- 內(nèi)嵌tomcat -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.30</version>
</dependency>
- 這里也順帶加入一下jasper的依賴饼疙,其實不加入也不會有什么大的問題溺森,就是等下啟動的時候會提示缺少Jasper類
<!-- 解決Jasper的問題 -->
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.30</version>
</dependency>
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的原因,因為它內(nèi)置了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. 創(chuàng)建一個啟動類
/**
* @Author: Neco
* @Description: 啟動類
* @Date: create in 2022/7/25 12:52
*/
public class App {
public static void main(String[] args) {
NecoApplication.run();
}
}
7. 創(chuàng)建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<String, Object> test2() {
System.out.println("==========這里返回的應該是Map的數(shù)據(jù)===============");
HashMap<String, Object> map = new HashMap<>();
map.put("neco", "27");
return map;
}
}
8. 測試
測試發(fā)現(xiàn)屏积,訪問http://localhost8080/boot/index1 訪問正常
index1
訪問index2,報500錯誤
index2 500錯誤
這里的問題是磅甩,需要對Map數(shù)據(jù)進行轉(zhuǎn)換處理炊林,這里我們需要引入fast-json
9. 處理返回Map類型時,500錯誤的問題
- 引入 fast-json
<!-- 用于轉(zhuǎn)換成json -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
- 修改AppConfig配置類
- 使類實現(xiàn)WebMvcConfigurer接口卷要,并Override configureMessageConverters 方法
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); converters.add(fastJsonHttpMessageConverter); }
- 添加@EnableWebMvc 注解
最后的修改結(jié)果如下
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<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
converters.add(fastJsonHttpMessageConverter);
}
}
- 然后嘗試啟動铛铁,發(fā)現(xiàn)報錯,無法正常啟動
嚴重: 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(<generated>)
at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$afba84d0$$FastClassBySpringCGLIB$$c325d314.invoke(<generated>)
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(<generated>)
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. 解決無法啟動的問題
這里可以進行查看却妨,發(fā)現(xiàn)AnnotationConfigWebApplicationContext 其實是DispatcherServlet的父容器,但是在執(zhí)行ac.refresh的時候括眠,其實是會initServlet的彪标,但是這個時候,子容器其實還沒有初始化完成掷豺,所以就報了No ServletContext set
(更具體的內(nèi)容建議查看源代碼)捞烟,這里要解決也很簡單,設(shè)置一下相對應的ServletContext即可
即在MyWebApplicationInitializer中的合適位置(執(zhí)行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 的時候题画,需要設(shè)置這個,否則會報錯 "No ServletContext set" 無法正常執(zhí)行
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("/");
}
}
如果覺得有收獲就點個贊吧德频,更多知識苍息,請點擊關(guān)注查看我的主頁信息哦~