自定义简易版SpringBoot,实现SpringBoot MVC及内嵌Tomcat启动、DispatcherServlet注册和组件扫描功能
实现及思路
-
新建项目,引入依赖
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>8.5.32</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.8.RELEASE</version> </dependency>
-
创建SpringApplication类,定义run()方法,run()方法内创建WebApplicationContext容器及创建Tomcat对象并启动
public class SpringApplication { private Class<?> mainClass; public SpringApplication(Class<?> cls) { this.mainClass = cls; } public static void run(Class<?> cls, String... args) { new SpringApplication(cls).run(args); } private void run(String[] args) { // 创建web容器,并刷新 AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(mainClass); context.refresh(); /** * 完成 * tomcat的创建及启动 */ Tomcat tomcat = new Tomcat(); tomcat.setPort(8080); File tempDir = null; try { tempDir = createTempDir("tomcat"); tomcat.addWebapp("/", tempDir.getAbsolutePath()); tomcat.start(); tomcat.getServer().await(); } catch (LifecycleException e) { e.printStackTrace(); return; } catch (Exception e) { e.printStackTrace(); return; } } File createTempDir(String prefix) throws Exception { try { File tempDir = File.createTempFile(prefix + ".", "." + 8080); tempDir.delete(); tempDir.mkdir(); tempDir.deleteOnExit(); return tempDir; } catch (IOException ex) { throw new Exception( "Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"), ex); } } }
-
创建启动类MyRunBoot,main() 方法中调用SpringApplication的run()方法。
@ComponentScan public class MyRunBoot { public static void main(String[] args) { SpringApplication.run(MyRunBoot.class, args); } }
SpringApplication.run()方法接受两个参数,第一个是启动的主类,第二个是启动参数。
@ComponentScan注解会使WebApplicationContext在刷新时扫描当前类所在包及其子包。
-
创建DispatcherServlet对象,并将其存入ServletContext中。这里有两种实现方式:
- Spring官方文档中给出的实现WebApplicationInitializer接口;
- 实现ServletContainerInitializer接口,并在META-INF/services/javax.servlet.ServletContainerInitializer,在该文件中配置ServletContainerInitializer的实现类的全限定名。
4.1 Spring官方文档中给出的实现WebApplicationInitializer接口
public class MyWebApplicationInitializer implements WebApplicationInitializer { public void onStartup(ServletContext servletContext) throws ServletException { ApplicationContext context = ContextUtils.getContext(); DispatcherServlet dispatcherServlet = null; if (context instanceof WebApplicationContext) { dispatcherServlet = new DispatcherServlet((WebApplicationContext) context); } else { dispatcherServlet = new DispatcherServlet(); } ServletRegistration.Dynamic registration = servletContext.addServlet("app_1", dispatcherServlet); registration.setLoadOnStartup(1); registration.addMapping("/"); } }
这里向servletContext添加了名为app_1的DispatcherServlet.
4.2 实现ServletContainerInitializer接口
@HandlesTypes(value = {MyServletContainerInitializer.class}) public class MyServletContainerInitializer implements ServletContainerInitializer { public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException { ApplicationContext context = ContextUtils.getContext(); DispatcherServlet dispatcherServlet = null; if (context instanceof WebApplicationContext) { dispatcherServlet = new DispatcherServlet((WebApplicationContext) context); } else { dispatcherServlet = new DispatcherServlet(); } ServletRegistration.Dynamic registration = servletContext.addServlet("app", dispatcherServlet); registration.setLoadOnStartup(1); registration.addMapping("/"); } }
这里向servletContext添加了名为app的DispatcherServlet.
META-INF/services/javax.servlet.ServletContainerInitializer文件
com.szile.springboot.context.MyServletContainerInitializer
-
ContextUtils工具类
@Component public class ContextUtils implements ApplicationContextAware { public static ApplicationContext context; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } public static ApplicationContext getContext() { return context; } public static <T> T getBean(Class<T> beanType) { return context.getBean(beanType); } }
-
创建HelloController
@RestController("app") public class HelloController { @RequestMapping public String hello() { return "hello"; } }
验证
运行MyRunBoot的main()方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zSgk8jzS-1625584469394)(image-20210706223714380.png)]
控制台显示app和app_1应用都已经完成
浏览器中访问localhost:8080/app/,成功返回hello
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EIAwaJ42-1625584469397)(image-20210706223920068.png)]