美文网首页
2021-05-18_JavaEE容器SPI查找机制学习笔记3

2021-05-18_JavaEE容器SPI查找机制学习笔记3

作者: kikop | 来源:发表于2021-05-26 12:22 被阅读0次

    20210518_J2EE容器SPI查找机制学习笔记3

    1概述

    SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。

    Java提供的一套用来被第三方实现或者扩展的API(JDK ServiceLoader),它可以用来启用框架扩展和替换组件。

    Java SPI 实际上是 “基于接口的编程+策略模式+配置文件” 组合实现的动态加载机制。

    本文主要基于Tomcat容器结合代码进行ServletContainerInitializer(简称SCI)机制的分析。

    1.1原理分析

    tomcat容器在启动时,会去扫描当前classpath类路径下的所有jar包。META-INF/services下的文件,文件特定接口名称为javax.servlet.ServletContainerInitializer,文件内容为该接口的实现类。

    该实现类作用:

    1. 定义了注解中自己感兴趣的HandlesTypes(接口XXX)。
    2. tomcat启动时,调用了ServiceLoader对象中的api方法(基于Jdk 默认spi机制),并且内置自动执行SCI接口默认onStartup方法,找到所有实现该接口XXX的所有实现类,并反射创建对象,缓存到Set集合中。
    3. 执行用户后续onStartup操作。

    1.2web.xml配置回顾

    tomcat下的conf中也有一个web.xml文件,没错的,所有的JavaWeb项目中web.xml都继承自服务器下的web.xml。

    1.1.1配置schema

    <?xml version="1.0" encoding="UTF-8"?>
    
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
    

    1.1.2传统servlet配置

    创建ContextLoaderListener对象,扫描service、dao,并根据配置初始化。

    <!--5.begin_传统servlet配置-->
    <servlet>
        <servlet-name>myindexServletName</servlet-name>
        <servlet-class>com.kikop.mytraditionspringmvc.myservlert.IndexServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>myindexServletName</servlet-name>
        <url-pattern>/index.do</url-pattern>
    </servlet-mapping>
    <!--end_传统servlet配置-->
    

    2代码实战

    2.1启动原生tomcat

    2.1.1maven配置

    <!--1.引入内嵌 tomcat-->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>9.0.17</version>
    </dependency>
    
    <!--2.tomcat-embed-jasper-->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <version>9.0.17</version>
    </dependency>
    

    2.1.2测试

    package com.kikop.mytraditionspringmvc.Test;
    
    import com.kikop.mytraditionspringmvc.myservlert.IndexServlet;
    import org.apache.catalina.Context;
    import org.apache.catalina.LifecycleException;
    import org.apache.catalina.connector.Connector;
    import org.apache.catalina.startup.Tomcat;
    
    /**
     * @author kikop
     * @version 1.0
     * @project Name: mytomcatspidemo
     * @file Name: MyNativeTomcatTest
     * @desc MyNativeServletTomcatTest
     * @date 2021/5/19
     * @time 10:50
     * @by IDE: IntelliJ IDEA
     */
    public class MyNativeServletTomcatTest {
    
        // 1.JDK:ServiceLoader
        // 2.SpringMvc
        // 默认处理 WebApplicationInitializer 类型
        // 扩展点实现:SpringServletContainerInitializer impl -->扩展点:javax.servlet.ServletContainerInitializer
    
        /**
         * 启动原生tomcat
         * http://localhost:8088/tomcatspi/index.do
         */
        private static void startNativeTomcat() throws LifecycleException {
    
    
            // 创建内嵌tomcat
            Tomcat tomcat = new Tomcat();
    
            // 1.配置connector
            Connector connector = new Connector();
            connector.setPort(8088);
            connector.setURIEncoding("UTF-8");
            tomcat.getService().addConnector(connector);
    
    
            // 2.关联Servlet
            String contextPath = "/tomcatspi";
            String myindexServletName = "myindexServletName";
    
    
            // 2.1.普通 servlet注册tomcat(3种方式)
            // 1.web.xml
            // 2.注解 @WebServlet,加在类:IndexServlet上
            // 3.api
            IndexServlet indexServlet = new IndexServlet();
    
    
            // 2.2.配置上下文
            Context context = tomcat.addContext(contextPath, null);
            tomcat.addServlet(context, myindexServletName, indexServlet);
    
            // 2.3.配置servletMapping
            context.addServletMappingDecoded("/index.do", myindexServletName);
    
            // 3.启动
            tomcat.start();
    
            // 4.并阻塞
            tomcat.getServer().await();
    
        }
    
    
        public static void main(String[] args) throws LifecycleException {
            startNativeTomcat();
        }
    }
    
    

    2.2启动web tomcat spi

    2.2.1定义感兴趣的接口MyWebApplicationInitializer

    package com.kikop.mytraditionspringmvc.mycontainer;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    public interface MyWebApplicationInitializer {
        void onStartup(ServletContext servletContext) throws ServletException;
    }
    

    2.2.2定义实现类

    package com.kikop.mytraditionspringmvc.mycontainer.impl;
    
    
    import com.kikop.mytraditionspringmvc.mycontainer.MyWebApplicationInitializer;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    /**
     * @author kikop
     * @version 1.0
     * @project Name: mytomcatspidemo
     * @file Name: MyCustomAppInitializer
     * @desc MyCustomAppInitializer
     * @date 2021/5/19
     * @time 10:50
     * @by IDE: IntelliJ IDEA
     */
    public class MyCustomAppInitializer implements MyWebApplicationInitializer {
    
        public void onStartup(ServletContext servletContext) throws ServletException {
            System.out.println("【MyCustomAppInitializer】自定义扩展服务");
    
        }
    }
    

    2.2.3定义SPI接口实现类

    package com.kikop.mytraditionspringmvc.mechanism;
    
    
    import com.kikop.mytraditionspringmvc.mycontainer.MyWebApplicationInitializer;
    
    import javax.servlet.ServletContainerInitializer;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.HandlesTypes;
    import java.lang.reflect.Modifier;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Set;
    
    /**
     * @author kikop
     * @version 1.0
     * @project Name: mytomcatspidemo
     * @file Name: MyCustomAppInitializer
     * @desc WebApplicationInitializer类采集入口
     * 测试时,需将 META-INF.notservices--> META-INF.services
     * @date 2021/5/19
     * @time 10:50
     * @by IDE: IntelliJ IDEA
     */
    @HandlesTypes(MyWebApplicationInitializer.class)
    public class MyTotalWebInitializer implements ServletContainerInitializer {
    
        public void onStartup(Set<Class<?>> myWebAppInitializerClasses, ServletContext servletContext) throws ServletException {
            System.out.println("【MyTotalWebInitializer】开始执行 tocmat spi查找机制");
    
            // 1.收集感兴趣的类
            for (Class<?> gatherClazz : myWebAppInitializerClasses) {
                System.out.println(gatherClazz.toString());
            }
            // 2.创建对象列表XXX
            // 3.执行对象列表XXXonStartup
    
        }
    }
    

    2.2.4测试

    package com.kikop.mytraditionspringmvc.Test;
    
    import org.apache.catalina.LifecycleException;
    import org.apache.catalina.connector.Connector;
    import org.apache.catalina.startup.Tomcat;
    
    /**
     * @author kikop
     * @version 1.0
     * @project Name: mytomcatspidemo
     * @file Name: MyNativeTomcatTest
     * @desc MyWebAppTomcatTest
     * @date 2021/5/19
     * @time 10:50
     * @by IDE: IntelliJ IDEA
     */
    public class MyWebAppTomcatTest {
    
    
        /**
         * 启动原生tomcat
         * 需声明项目为 webapp项目,否则不会执行SPI查找机制
         * http://localhost:8088/tomcatspi/index.do
         */
        private static void startTomcatWebSupport() throws LifecycleException {
            Tomcat tomcat = new Tomcat();
    
            // 1.配置connector
            Connector connector = new Connector();
            connector.setPort(8088);
            connector.setURIEncoding("UTF-8");
            tomcat.getService().addConnector(connector);
    
            String contextPath = "/mytomcatspi";
    
    
            // 2.注意点:
            // 必须需声明项目为 webapp项目,否则tomcat.start不会执行SPI查找机制
            // 等同于配置了web.xml
            tomcat.addWebapp(contextPath,
                    "D:\\workdirectory\\mapexperiment\\mytomcatspi\\");
    
            // 3.启动
            tomcat.start();
    
            // 4.并阻塞
            tomcat.getServer().await();
    
        }
    
    
        public static void main(String[] args) throws LifecycleException {
            startTomcatWebSupport();
        }
    }
    

    参考

    1java SPI机制

    https://blog.csdn.net/gaohaicheng123/article/details/105824988

    2java SPI 机制 在Tomcat,spring-mvc启动及servlet3.0中的应用

    https://blog.csdn.net/gaohaicheng123/article/details/105827295

    3Features:Spring MVC and Spring WebFlux web frameworks

    https://spring.io/projects/spring-framework

    https://docs.spring.io/spring-framework/docs/current/reference/html/web.html

    相关文章

      网友评论

          本文标题:2021-05-18_JavaEE容器SPI查找机制学习笔记3

          本文链接:https://www.haomeiwen.com/subject/qwiujltx.html