实战:自定义简易版SpringBoot

实战:自定义简易版SpringBoot


一、功能要求

自定义简易版SpringBoot,实现SpringBoot MVC及内嵌Tomcat启动、DispatcherServlet注册和组件扫描功能

程序通过main方法启动,可以自动启动tomcat服务器

可以自动创建和加载DispatcherServlet组件到ServletContext中

可以自动通过@ComponentScan扫描Controller等组件

Controller组件可以处理浏览器请求,返回响应结果

二、实现思路分析

传统SpringMVC框架web.xml的配置内容:

<web-app>
   <!-- 初始化Spring上下文 -->
   <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
   <!-- 指定Spring的配置文件 -->
   <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>/WEB-INF/app-context.xml</param-value>
   </context-param>
   <!-- 初始化DispatcherServlet -->
   <servlet>
       <servlet-name>app</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value></param-value>
       </init-param>
       <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
       <servlet-name>app</servlet-name>
       <url-pattern>/app/*</url-pattern>
   </servlet-mapping>
</web-app>

Spring官方文档中给出了基于Servlet3.0规范如何使用Java代码实现web.xml配置的example

public class MyWebApplicationInitializer implements WebApplicationInitializer {

   @Override
   public void onStartup(ServletContext servletCxt) {

       // Load Spring web application configuration
       //通过注解的方式初始化Spring的上下文
       AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
       //注册spring的配置类(替代传统项目中xml的configuration)
       ac.register(AppConfig.class);
       ac.refresh();

       // Create and register the DispatcherServlet
       //基于java代码的方式初始化DispatcherServlet
       DispatcherServlet servlet = new DispatcherServlet(ac);
       ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
       registration.setLoadOnStartup(1);
       registration.addMapping("/app/*");
   }
}
  1. 当实现了Servlet3.0规范的容器(比如Tomcat7及以上版本)启动时
    • 会通过SPI扩展机制自动扫描所有jar包里META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的全路径类(该类需实现ServletContainerInitializer接口)

    • Servlet3.0+容器启动时将自动扫描类路径,以查找Spring的WebApplicationInitializer接口的所有实现类

    • 将实现类放置到一个set集合中,提供给ServletContainerInitializer中onStartup的第一个参数使用

  2. 实例化ServletContainerInitializer实现类
  3. 回调类中的onStartup方法
  4. 在onStartup方法中(形参webAppInitializerClasses 已经封装了WebApplicationInitializer接口的所有实现类),可以回调WebApplicaitonInitializer的onStartup方法
  5. 在WebApplicaitonInitializer的onStartup方法中 进行传统SpringMVC框架web.xml的 内容配置

在这里插入图片描述

三、编码(实现方式一:使用框架的SpringServletContainerInitializer)

(1)创建maven工程,导入以下依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>8.5.32</version>
        </dependency>
        <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-context</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
    </dependencies>

(2)创建SpringApplication类,编写run方法(方法中要求完成tomcat的创建及启动)

public class SpringApplication {

    public static void run(){
        //创建tomcat实例
        Tomcat tomcat = new Tomcat();
        //设置tomcat端口
        tomcat.setPort(8888);
        try {
            //设置项目文件路径(完成一些自动配置,否则SpringServletContainerInitializer的onStartup方法可能不执行)
            tomcat.addWebapp("/", "G:\\");
            //启动tomcat
            tomcat.start();
            // 监听关闭端口,阻塞式。没有这一句,方法执行完会直接结束
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }
}

(3)创建spring的配置类 AppConfig该类上要通过@ComponentScan来进行包扫描

@Configuration
@ComponentScan("com.dabing")
public class AppConfig {
    {
        System.out.println("ComponentScan...........");
    }
}

(4)创建MyWebApplicationInitializer实现WebApplicationInitializer接口,重写onstartup方法(WebApplicationInitializer实现web.xml的配置)

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext servletContext) throws ServletException {
        System.out.println("初始化 MyWebApplicationInitializer");
        //通过注解的方式初始化Spring的上下文
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        //注册spring的配置类(替代传统项目中xml的configuration)
        ac.register(AppConfig.class);

        // Create and register the DispatcherServlet
        //基于java代码的方式初始化DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletContext.addServlet("/", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/*");
    }
}

(5)编写一个Controller测试类及目标方法,响应输出“hello”即可

@RestController
public class TestController {

    @RequestMapping("/test/hello")
    public String test(){
        System.out.println("--- hello ---");
        return "hello";
    }

}

(6)编写一个启动类Main,通过执行main方法启动服务

public class Main {

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

(7)通过浏览器对目标方法进行方法
在这里插入图片描述

四、扩展:验证当实现了Servlet3.0规范的容器启动时 会通过SPI扩展机制 自动扫描 所有jar包里 META-INF/services/javax.servlet.ServletContainerInitializer 文件中指定的全路径类

自定义MySpringServletContainerInitializer 进行验证

(1)创建maven工程,导入以下依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>8.5.32</version>
        </dependency>
        <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-context</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
    </dependencies>

(2)创建SpringApplication 类,编写run方法(方法中要求完成tomcat的创建及启动)

public class SpringApplication {

    public static void run(){
        //创建tomcat实例
        Tomcat tomcat = new Tomcat();
        //设置tomcat端口
        tomcat.setPort(8888);
        try {
            //设置项目文件路径
            tomcat.addWebapp("/", "G:\\");
            //启动tomcat
            tomcat.start();
            // 监听关闭端口,阻塞式。没有这一句,方法执行完会直接结束
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }
}

(3)创建spring的配置类 AppConfig该类上要通过@ComponentScan来进行包扫描

@Configuration
@ComponentScan("com.dabing")
public class AppConfig {
    {
        System.out.println("ComponentScan...........");
    }
}

(4)创建MySpringServletContainerInitializer,实现ServletContainerInitializer接口,重写onstartup方法,方法中调用第4步中MyWebApplicationInitializer的onstartup方法

@HandlesTypes(WebApplicationInitializer.class)
public class MySpringServletContainerInitializer implements ServletContainerInitializer {
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {

        System.out.println("初始化 MySpringServletContainerInitializer");
        List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                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) {
            System.out.println("for--------------");

            initializer.onStartup(servletContext);
        }
    }
}

(5)创建文件:META-INF/services/javax.servlet.ServletContainerInitializer,在该文件中配置ServletContainerInitializer的实现类MySpringServletContainerInitializer
在这里插入图片描述

com.dabing.web.MySpringServletContainerInitializer

(6)创建MyWebApplicationInitializer实现WebApplicationInitializer接口,重写onstartup方法(WebApplicationInitializer实现web.xml的配置)

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext servletContext) throws ServletException {
        System.out.println("初始化 MyWebApplicationInitializer");
        //通过注解的方式初始化Spring的上下文
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        //注册spring的配置类(替代传统项目中xml的configuration)
        ac.register(AppConfig.class);

        // Create and register the DispatcherServlet
        //基于java代码的方式初始化DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletContext.addServlet("/", servlet);
        if(registration==null) return;
        registration.setLoadOnStartup(1);
        registration.addMapping("/*");
    }
}

(7)编写一个Controller测试类及目标方法,响应输出“hello”即可

@RestController
public class TestController {

    @RequestMapping("/test/hello")
    public String test(){
        System.out.println("--- hello ---");
        return "hello";
    }

}

(8)编写一个启动类Main,通过执行main方法启动服务

public class Main {

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

(9)通过浏览器对目标方法进行方法
在这里插入图片描述

项目地址

测试demo

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穿城大饼

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值