Featured image of post 手动Demo一个Spring boot

手动Demo一个Spring boot

基于SpringMVC,理解Spring boot原理

Spring Boot在Sping MVC基础上,实现了"0"配置运行Spring项目,那他是如何实现的呢,原理其实很简单,在Tomcat、Servlet、Spring、SpringMVC发展过程中,都逐步实现了通过Java代码运行,而不需要任何配置,Spring Boot就是把整个过程通过代码实现了;自己也可以通过代码一步步实现,下面是官方介绍。

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration.

构建Tomcat容器

通过官方介绍,Spring Boot基于SpringMVC、Spring MVC则是在Servlet基础上,Servlet一般运行与Tomcat容器中;如果实现SpringBoot,就要用纯代码方式实现Tomcat容器,而不依赖外部。先要创建Java项目通过代码实现Tomcat容器,方法就是引入相关tomcat jar包,通过maven更简单:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina</artifactId>
            <version>9.0.54</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>9.0.52</version>
        </dependency>

接下来简单几句话实现Tomcat容器运行;这样完全可以在调试阶段替代掉IDE中的Tomcat插件或者tomcat容器了,命令行或者maven就可以运行了;so easy!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class APP {
    public static void main(String[] args) {
        System.out.println("begin custom boot!");

        /**
         * 启动tomcat
         */
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8081);
        tomcat.getHost().setAutoDeploy(false);
        tomcat.addWebapp("", "d:\\temp\\");
        //tomcat9之后要加
        tomcat.getConnector();
        try {
            tomcat.start();
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }
}

通过APP.main()方法运行项目,访问:http://localhost:8081/, 发现端口监听可以了,不过现在还没有Servlet,接下来实现一个简单Servlet:

实现Servlet

1
2
3
4
5
public class Hello extends javax.servlet.http.HttpServlet {
    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException {
        response.getWriter().print("Hello World");
    }
}

之前Servlet要想访问,都是web.xml文件中配置Servlet,Spring Boot既然不用xml文件,那怎么注册?在Servlet3.0之后,新增ServletContainerInitializer接口实现免web.xml配置:

  • ServletContainerInitializer接口的实现类通过java SPI声明自己是ServletContainerInitializer 的provider.
  • 容器启动阶段依据java spi获取到所有ServletContainerInitializer的实现类,然后执行其onStartup方法.
  • 另外在实现ServletContainerInitializer时还可以通过@HandlesTypes注解定义本实现类希望处理的类型,容器会将当前应用中所有这一类型(继承或者实现)的类放在ServletContainerInitializer接口的集合参数c中传递进来。如果不定义处理类型,或者应用中不存在相应的实现类,则集合参数c为空.
  • 这一类实现了 SCI 的接口,如果做为独立的包发布,在打包时,会在JAR 文件的 META-INF/services/javax.servlet.ServletContainerInitializer 文件中进行注册。 容器在启动时,就会扫描所有带有这些注册信息的类(@HandlesTypes(WebApplicationInitializer.class)这里就是加载WebApplicationInitializer.class类)进行解析,启动时会调用其 onStartup方法——也就是说servlet容器负责加载这些指定类, 而ServletContainerInitializer的实现者(例如Spring-web中的SpringServletContainerInitializer对接口ServletContainerInitializer的实现中,是可以直接获取到这些类的)

1、创建ServletContainerInitializer实现类,并注册Servlet,或者另外配置@HandlesTypes:

1
2
3
4
5
6
7
8
9
public class CustomInitializer implements ServletContainerInitializer {

//    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("CustomInitializer onStartup");
        //注册Servlet
        servletContext.addServlet("hello",new Hello()).addMapping("/hello");
    }
}

2、SPI声明,供tomcat库调用创建文件:

SPI

文件中写入类名全路径:

1
ml.yixiu.initializer.CustomInitializer

再次运行项目访问:http://localhost:8081/hello 成功访问到Servlet。

集成Spring MVC 实现 Spring Boot

最后集成Spring MVC就算初步成功了。实际上SpringMVC中就是通过该方式配置的,在Srping-web的jar包中可以找到该文件, 在理解了Java Servlet Web 代码构建后,下一步我们代码构建Spring MVC, 在spring的官方文档中,推荐的就是代码集成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

分析代码就能发现,其实做了两件事,注册配置类(AppConfig.class)、注册DispatcherServlet;这不就是Spring MVC在web.xml中做的事情吗,另外该类就是通过实现ServletContainerInitializer时Springweb源码中配置了@HandlesTypes,则,调用了该类: Sping源码

上面代码中的AppConfig就是Spring MVC的配置文件,配置ComponentScan,bean注册、以及其它配置:

1
2
3
4
5
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "ml.yixiu")
public class AppConfig implements WebMvcConfigurer {
}

到这我们就可以愉快的写Controller

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Controller("/app")
public class MyController {

    @RequestMapping("/index")
    @ResponseBody
    public String hello() {
        return "hello spring!";
    }

}

访问http://localhost:8081/app/index,就能访问controller。

最后整个过程没有用xml,实现从main方法启动,这不就是最基本的SpringBoot的启动原理吗,当然,SpringBoot最强大的地方在于他和各种框架的无xml集成,只要在有相关jar包引入,则自动配置,基本原理也很简单,就是用了SPI标准。

源码地址:git@github.com:shanyutou/custom-boot.git

Built with Hugo     主题 StackJimmy 设计