实现简单SpringBoot框架的初体验

依赖环境

<properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
    	<!-- tomcat依赖 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>8.5.32</version>
        </dependency>
        <!-- spring 环境 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
        <!-- springMVC依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
    </dependencies>

Servlet3.0规范

实现了Servlet3.0规范的容器在启动时,会通过SPI扩展机制自动扫描所有jar包中包含META-INF/services/javax.servlet.ServletContainerInitializer的文件(该文件指定了ServletContainerInitializer接口的实现),实例化该类,并回调类中的onStartup方法。

实现过程

1.创建AppConfig

@Configuration
@ComponentScan(basePackages = {"com.liang.boot"})
public class AppConfig {
}

这个类有两个注解。
@Configuation:告诉Spring是一个配置类,需要加载到IOC容器
@ComponentScan:配置包扫描路径,spring会扫描其传入的包及其子包的class文件

2.创建MyWebApplicationServletInitializer

public class MyWebApplicationInitializer implements WebApplicationInitializer {


    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
    		// 初始化Sring 应用上下文环境
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        // 配置Appconfig,也就是设置包扫描路径,用于ApplicationContext的初始化
        ac.register(AppConfig.class);
        // spring整合tomcat和springmvc
        ac.refresh();

        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic app = servletContext.addServlet("app", servlet);
        // loadOnStartup > 0:tomcat容器启动时就加载 DispatcherServlet
        app.setLoadOnStartup(1);
        // 设置访问路径前缀
        app.addMapping("/app/*");
    }
}

3.创建MySpringServletContainerInitializer

public class MySpringServletContainerInitializer implements ServletContainerInitializer {


    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList<>();

        if (set != null) {
            for (Class<?> waiClass : set) {
                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()) {
            return;
        }

        AnnotationAwareOrderComparator.sort(initializers);
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }
}

MySpringServletContainerInitializer 实现了ServletContainerInitializer接口。实现了ServletContainerInitializer接口意味着在tomcat启动时,MySpringServletContainerInitializer会被ServletContext扫描到并调用他的onStartup()方法。从来执行MyWebApplicationInitializer中的onstartUp()方法,完成ApplicationContext的创建以及DispatcherServlet的创建。

4.创建javax.servlet.ServletContainerInitializer文件

这就我们在Servlet3.0规范提到的tomcat的SPI机制
在这里插入图片描述该文件需指定在resources/META-INF/services下。千万不要写错!!!
完整的路径:resources/META-INF/services/javax.servlet.ServletContainerInitializer

5.创建SpringApplication类,编写run方法。

/**
* <h1>SpringApplication就只运行tomcat容器</h1>
*/
public class SpringApplication {

    //tomcat端口号为:8080
    private int port = 8080;

    private Class<?> primary;

    private String[] args;

    public SpringApplication(Class<?> primary, String... args) {
        this.primary = primary;
        this.args = args;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public static void run(Class<?> primary, String... args) {
        new SpringApplication(primary,args).run();
    }


    public void run() {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(port);
        try {
            File docBase = createTempDir("tomcat");
            //自动配置
            tomcat.addWebapp("/", docBase.getAbsolutePath());
            tomcat.start();
            tomcat.getServer().await();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
		
		// 创建临时目录
    protected final File createTempDir(String prefix) {
        try {
            File tempDir = File.createTempFile(prefix + ".", "." + getPort());
            tempDir.delete();
            tempDir.mkdir();
            tempDir.deleteOnExit();
            return tempDir;
        }
        catch (IOException ex) {
            throw new RuntimeException(
                    "Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"), ex);
        }
    }
}

SpringApplication就只有一个职责,创建tomcat并完成启动。

6.创建主启动类MyRunBoot

public class MyRunBoot {
    public static void main(String[] args) {
        SpringApplication.run(MyRunBoot.class, args);
    }
}

主启动类调用SpringApplication的run方法,从而完成tomcat的启动。

7.编写测试Controller

@RestController
public class HelloController {

    @RequestMapping("/test")
    public String test() {
        return "hello";
    }
}

8.测试

页面输入 http://localhost:8080/app/test
在这里插入图片描述
返回hello,测试完成。

小结

  1. 参考SpringBoot项目,SpringApplication的实例化放到SpringApplication类里面。
  2. tomcat还需要配置端口以及在项目路径类创建tomcat的工作目录,以及映射的自动配置工作。

总结

一、SpringServletContainerInitializer

在Servlet容器启动的时候,会自动探测WebApplicationInitializer的实现类,并把他交给SpringServletContainerInitializer的onStartup()方法的第一个参数,从而执行ServletContainerInitializer的onStartup()方法完成Spring的流程。

二、思考

为什么有了WebApplicationServletContext,springboot在注册DispatcherServlet的时候使用的是ServletContextInitializer?

1.1 WebApplicationInitializer是spring提供的API,它的生命周期受第三方Servlet容器控制。(在Servlet容器启动时回调)。

1.2 和WebApplicationInitializer不同的地方是:这些ServletContextInitializer实例不会被SpringServletContainerInitializer检测,因此不会随着Servlet容器启动,而是在ServletContextInitializer#onStartup接口调用的地方执行初始化

ServletContextInitializer是springboot基于嵌入式容器,提供的API,其生命周期是springboot自身控制的。

上面这个问题参考了下面这个博主的文章。

ServletContextInitializer和WebApplicationInitializer的区别

如有错误,请各位大佬指正。 谢谢!!!!!!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值