模拟sringboot启动

模拟sringboot启动

我们在使用spring和springboot之间还是有很多不同的,比如springboot内置tomcat..等容器,我们这里就简单通过模拟简单的springboot启动,来解释mvc

依赖

需要tomcat embed的jar包 ,我是使用的gradle

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile(project(":spring-context"))
    compile(project(":spring-webmvc"))
    // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.53'
}

简易版

先创建一个启动类

public class Main {
	public static void main(String[] args) {
		MySpringApplication.run();
	}
}

建立MySpringApplication

package debugdir.mvc.main;

import debugdir.mvc.AppConfig;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import java.io.File;

public class MySpringApplication {

	public static void run(){

		// 创建支持注解的web上下文
		AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        // 加载配置类
		ac.register(AppConfig.class);
		ac.refresh();
        // 创建tomcat
		Tomcat tomcat = new Tomcat();
		String webAppDirLocation = "src/main/";
		//Set Port #
		tomcat.setPort(8080);
		// 指定context目录
		File base = new File(MySpringApplication.class.getResource("/").getPath());
		//StandardContext ctx = (StandardContext) tomcat.addWebapp("/", new File(webAppDirLocation).getAbsolutePath());
		Context ctx = tomcat.addContext("/",base.getAbsolutePath());
		// Create and register the DispatcherServlet
		DispatcherServlet servlet = new DispatcherServlet(ac);
		// 设置映射,这里其实就是xml里的<servlet-mapping>
		Tomcat.addServlet(ctx,"testServlet",servlet);
		ctx.addServletMappingDecoded("/","testServlet");
		try {
			tomcat.start();
		} catch (LifecycleException e) {
			e.printStackTrace();
		}
        // tomcat 等待请求
		tomcat.getServer().await();

	}
}

AppConfig

package debugdir.mvc;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
// 扫描包
@ComponentScan("debugdir.mvc")
public class AppConfig {

}

spring实现思路

ServletContainerInitializer

曾经开发是离不开XML配置的,现在servlet 3.0支持通过实现ServletContainerInitializer方式代替XML.但是需要配置项目META-INF/services/javax.servlet.ServletContainerInitializer文件

spring是如何支持的?

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
     public void onStartup(Set<Class<?>> c, ServletContext ctx) 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);
		}
     }
}
  1. 首先在spring-webmvc项目中,META-INF/services/javax.servlet.ServletContainerInitializer中添加org.springframework.web.SpringServletContainerInitializer,这样web容器启动后就会加载SpringServletContainerInitializer
  2. @HandlesTypes(WebApplicationInitializer.class)扫描所有接口WebApplicationInitializer的实现类传入参数Set<Class<?>> c中,在onStartup()中循环调用initializer.onStartup(servletContext);
  3. 方法直接传入ServletContext,因此可以直接注册一些servlet,这样DispatcherServlet可以在此处注册.

模拟sringboot启动
https://www.blaaair.com/archives/mo-ni-sringboot-qi-dong
作者
Glo6f
发布于
2024年08月01日
许可协议