美文网首页
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