天天看点

造轮子之整合嵌入式 tomcat9 以及 websocket造轮子之整合嵌入式 tomcat 以及 websocket

造轮子之整合嵌入式 tomcat 以及 websocket

去年周末的时候没事干,就造轮子玩,搞了一个简易版的 spring mvc。

今年周末的时候没事干,就造轮子玩,搞了一个简易版的 springboot。

造轮子的过程中,就属整合嵌入式 tomcat9 有点麻烦,特此记录一下。

talk is cleanup, show youer me code !

嵌入式 tomcat9 所需依赖:

<properties>
        	<tomcat.version>9.0.35</tomcat.version>
    	</properties>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-el</artifactId>
            <version>${tomcat.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>${tomcat.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
           

先搞个接口,万一以后心血来潮想整合 jetty 呢?

package com.kfyty.mvc;

/**
 * 描述: web 服务器
 *
 * @author kfyty725
 * @date 2021/5/28 14:49
 * @email [email protected]
 */
public interface WebServer {

    void start();

    void stop();

    boolean isStart();

    int getPort();
}
           

嵌入式 tomcat9 完整代码:

package com.kfyty.mvc.tomcat;

import com.kfyty.mvc.WebServer;
import com.kfyty.mvc.servlet.DispatcherServlet;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.Context;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.EmptyResourceSet;
import org.apache.catalina.webresources.JarResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.jasper.servlet.JasperInitializer;
import org.apache.tomcat.websocket.server.WsContextListener;

import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.EventListener;

/**
 * 描述: 嵌入式 tomcat
 *
 * @author kfyty725
 * @date 2021/5/28 14:51
 * @email [email protected]
 */
@Slf4j
@RequiredArgsConstructor
public class TomcatWebServer implements WebServer {
    private final Tomcat tomcat;

    private boolean started;

	/**
	 * servlet 上下文,后序导出 websocket 端点时需要
	 */
    @Getter
    private ServletContext servletContext;

	/**
	 * tomcat 的配置类
	 */
    @Setter
    private TomcatConfig config;

	/**
	 * 造轮子之 DispatcherServlet
	 */
    @Setter
    private DispatcherServlet dispatcherServlet;

    public TomcatWebServer() {
        this(new TomcatConfig());
    }

    public TomcatWebServer(TomcatConfig config) {
        this(config, null);
    }

    public TomcatWebServer(TomcatConfig config, DispatcherServlet dispatcherServlet) {
        this.tomcat = new Tomcat();
        this.config = config;
        this.dispatcherServlet = dispatcherServlet;
        this.configTomcat();
    }

    @Override
    public void start() {
        this.prepareConnector();
        this.started = true;
        log.info("tomcat started on port({})", getPort());
    }

    @Override
    public void stop() {
        try {
            this.started = false;
            this.stopTomcat();
            this.tomcat.destroy();
        } catch (Throwable throwable) {
            log.error("destroy tomcat error !");
            throw new RuntimeException(throwable);
        }
    }

    @Override
    public boolean isStart() {
        return this.started;
    }

    @Override
    public int getPort() {
        return this.config.getPort();
    }

	/**
	 * 配置 tomcat
	 * 注意:这里需要提前启动 tomcat,让一些监听器生效,比如初始化 websocket 的监听器
	 * 因此这里暂时不要配置 Connector,因为此时 ioc 容器还没有启动完成,还无法提供服务
	 * 配置 Connector 放在 start 方法中
	 */
    private void configTomcat() {
        try {
            tomcat.setPort(getPort());
            tomcat.setBaseDir(createTempDir("tomcat").getAbsolutePath());
            tomcat.getHost().setAutoDeploy(false);
            this.prepareContext();
            this.tomcat.start();                // 提前启动 tomcat 使监听器生效
        } catch (Throwable e) {
            throw new RuntimeException("config tomcat failed !", e);
        }
    }

	/**
	 * 配置 Connector
	 * 注意:要设置 throwOnFailure 为 true
	 * 否则 tomcat 启动过程中的异常不会向上抛出,而是直接打印一下就完了
	 */
    private void prepareConnector() {
        Connector connector = new Connector(this.config.getProtocol());
        connector.setPort(getPort());
        connector.setURIEncoding("UTF-8");
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        tomcat.setConnector(connector);
    }

	/**
	 * 配置 tomcat 上下文,并初始化 ServletContext
	 * 添加 websocket 初始化监听器(WsContextListener)
	 * 添加 FixContextListener,以支持无 web.xml
	 * 添加 WebResource,让 tomcat 知晓从哪里获取所需的资源,如:jsp
	 * 配置默认的 servlet 处理静态资源
	 * 配置默认的 servlet 处理 jsp 资源
	 * 配置监听器、拦截器
	 */
    private void prepareContext() throws Exception {
        StandardContext context = new StandardContext();
        context.setPath("");
        context.setDocBase(createTempDir("tomcat-docbase").getAbsolutePath());
        context.setCreateUploadTargets(true);
        context.setFailCtxIfServletStartFails(true);
        context.addApplicationListener(WsContextListener.class.getName());
        context.addLifecycleListener(new Tomcat.FixContextListener());
        this.prepareResources(context);
        this.prepareDefaultServlet(context);
        this.prepareJspServlet(context);
        this.prepareDispatcherServlet(context);
        this.prepareWebFilter(context);
        this.prepareWebListener(context);
        this.tomcat.getHost().addChild(context);
        this.servletContext = context.getServletContext();
    }

	/**
	 * 通过获取启动类的 URL,判断当前是通过 IDE 启动还是通过 jar 包启动
	 * 如果是 IDE 启动则设置 DirResourceSet
	 * 如果是通过 jar 启动则设置 JarResourceSet
	 * 否则设置一个 EmptyResourceSet
	 */
    private void prepareResources(Context context) throws URISyntaxException {
        WebResourceRoot resources = new StandardRoot(context);
        URL pathURL = config.getPrimarySource().getProtectionDomain().getCodeSource().getLocation();
        if(Files.isDirectory(Paths.get(pathURL.toURI()))) {
            resources.addPreResources(new DirResourceSet(resources, "/", pathURL.getPath(), "/"));
        } else if(pathURL.getPath().endsWith(".jar")) {
            resources.addJarResources(new JarResourceSet(resources, "/", pathURL.getPath(), "/"));
        } else {
            resources.addPreResources(new EmptyResourceSet(resources));
            log.warn("add empty source set !");
        }
        context.setResources(resources);
    }

	/**
	 * 添加默认的 servlet,用以处理静态资源
	 * 其映射路径由 tomcat 配置类给出
	 */
    private void prepareDefaultServlet(Context context) {
        Wrapper defaultServlet = context.createWrapper();
        defaultServlet.setName("default");
        defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
        defaultServlet.addInitParameter("debug", "0");
        defaultServlet.addInitParameter("listings", "false");
        defaultServlet.setLoadOnStartup(1);
        defaultServlet.setOverridable(true);
        context.addChild(defaultServlet);
        for (String pattern : this.config.getStaticPattern()) {
            context.addServletMappingDecoded(pattern, "default");
        }
    }

	/**
	 * 添加处理 jsp 的 servlet
	 */
    private void prepareJspServlet(Context context) {
        Wrapper jspServlet = context.createWrapper();
        jspServlet.setName("jsp");
        jspServlet.setServletClass("org.apache.jasper.servlet.JspServlet");
        jspServlet.addInitParameter("fork", "false");
        jspServlet.setLoadOnStartup(3);
        context.addChild(jspServlet);
        context.addServletMappingDecoded("*.jsp", "jsp");
        context.addServletMappingDecoded("*.jspx", "jsp");
        context.addServletContainerInitializer(new JasperInitializer(), null);
    }

	/**
	 * 添加自己的轮子,当然这个是可选的
	 */
    private void prepareDispatcherServlet(Context context) {
        if(this.dispatcherServlet != null) {
            Tomcat.addServlet(context, "dispatcherServlet", this.dispatcherServlet);
            context.addServletMappingDecoded(config.getDispatcherMapping(), "dispatcherServlet");
        }
    }

	/**
	 * 配置过滤器及其映射路径
	 */
    private void prepareWebFilter(Context context) {
        for (Filter webFilter : this.config.getWebFilters()) {
            FilterDef filterDef = new FilterDef();
            WebFilter annotation = webFilter.getClass().getAnnotation(WebFilter.class);
            filterDef.setFilter(webFilter);
            filterDef.setFilterClass(webFilter.getClass().getName());
            filterDef.setFilterName(webFilter.getClass().getSimpleName());
            filterDef.setAsyncSupported(Boolean.toString(annotation.asyncSupported()));
            filterDef.setDisplayName(annotation.displayName());
            filterDef.setDescription(annotation.description());
            filterDef.setSmallIcon(annotation.smallIcon());
            filterDef.setLargeIcon(annotation.largeIcon());
            if(CommonUtil.notEmpty(annotation.filterName())) {
                filterDef.setFilterName(annotation.filterName());
            }
            for (WebInitParam webInitParam : annotation.initParams()) {
                filterDef.addInitParameter(webInitParam.name(), webInitParam.value());
            }
            context.addFilterDef(filterDef);
            this.prepareWebFilterMapping(context, filterDef, annotation);
        }
    }

	/**
	 * 配置过滤器映射,如果没有配置,则默认拦截全部
	 */
    private void prepareWebFilterMapping(Context context, FilterDef filterDef, WebFilter annotation) {
        String[] patterns = CommonUtil.notEmpty(annotation.value()) ? annotation.value() : annotation.urlPatterns();
        patterns = CommonUtil.empty(patterns) ? new String[] {"/*"} : patterns;
        for (String pattern : patterns) {
            FilterMap filterMap = new FilterMap();
            filterMap.setCharset(StandardCharsets.UTF_8);
            filterMap.setFilterName(filterDef.getFilterName());
            filterMap.addURLPattern(pattern);
            context.addFilterMap(filterMap);
        }
    }

	/**
	 * 配置监听器
	 */
    private void prepareWebListener(Context context) {
        for (EventListener webListener : this.config.getWebListeners()) {
            context.addApplicationListener(webListener.getClass().getName());
        }
    }

    private void stopTomcat() {
        try {
            this.tomcat.stop();
        } catch (Throwable e) {
            log.error("stop tomcat error: {}", e.getMessage());
        }
    }
    
	/**
	 * 创建临时目录
	 */
    private File createTempDir(String prefix) throws IOException {
        File tempDir = File.createTempFile(prefix + ".", "." + getPort());
        tempDir.delete();
        tempDir.mkdir();
        tempDir.deleteOnExit();
        return tempDir;
    }
}
           

下面给出 tomcat 的配置类:

package com.kfyty.mvc.tomcat;

import lombok.Data;

import javax.servlet.Filter;
import java.util.EventListener;
import java.util.LinkedList;
import java.util.List;

/**
 * 描述: tomcat 配置类
 * 因为是写着玩,就没那么多配置,只有几项必须的配置
 *
 * @author kfyty725
 * @date 2021/5/28 14:52
 * @email [email protected]
 */
@Data
public class TomcatConfig {
    public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
    public static final String DEFAULT_DISPATCHER_MAPPING = "/";

    private int port;
    private String protocol;
    private List<String> staticPattern;
    private String dispatcherMapping;
    private Class<?> primarySource;
    private List<Filter> webFilters;
    private List<EventListener> webListeners;

    public TomcatConfig() {
        this(TomcatConfig.class);
    }

    public TomcatConfig(Class<?> primarySource) {
        this.port = 8080;
        this.protocol = DEFAULT_PROTOCOL;
        this.dispatcherMapping = DEFAULT_DISPATCHER_MAPPING;
        this.staticPattern = new LinkedList<>();
        this.primarySource = primarySource;
        this.webFilters = new LinkedList<>();
        this.webListeners = new LinkedList<>();
        this.addDefaultStaticPattern();
    }

    public void addStaticPattern(String pattern) {
        this.staticPattern.add(pattern);
    }
    
    public void addWebFilter(Filter filter) {
        this.webFilters.add(filter);
    }
    
    public void addWebListener(EventListener listener) {
        this.webListeners.add(listener);
    }

    private void addDefaultStaticPattern() {
        this.addStaticPattern("/static/*");
        this.addStaticPattern("*.js");
        this.addStaticPattern("*.css");
        this.addStaticPattern("*.html");
        this.addStaticPattern("*.png");
        this.addStaticPattern("*.jpg");
        this.addStaticPattern("*.jpeg");
        this.addStaticPattern("*.ico");
    }
}
           

接着给出导出 websocket 端点的工具类(看着是不是很熟悉?没错,就是从 springboot 中的精简过来的~~~):

package com.kfyty.mvc.tomcat;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletContext;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerContainer;
import java.util.HashSet;
import java.util.Set;

import static org.apache.tomcat.websocket.server.Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE;

/**
 * 描述: 导出 websocket 服务端点
 *
 * @author kfyty725
 * @date 2021/6/4 17:30
 * @email [email protected]
 */
@Slf4j
public class ServerEndpointExporter {
    private final ServletContext servletContext;
    private final Set<Class<?>> endpointClasses;

    public ServerEndpointExporter(ServletContext servletContext) {
        this(servletContext, new HashSet<>(2));
    }

    public ServerEndpointExporter(ServletContext servletContext, Set<Class<?>> endpointClasses) {
        this.servletContext = servletContext;
        this.endpointClasses = endpointClasses;
    }

    public void addEndpointClass(Class<?> endpointClass) {
        this.endpointClasses.add(endpointClass);
    }

	/**
	 * 从 ServletContext 的属性中获取 ServerContainer
	 * 而这个属性是通过 WsContextListener 这个监听器设置进去的
	 * 这也是为什么要提前启动 tomcat 但延迟设置 Connector 的原因之一
	 */
    public ServerContainer getServerContainer() {
        ServerContainer serverContainer = (ServerContainer) servletContext.getAttribute(SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
        if(serverContainer == null) {
            throw new IllegalStateException("javax.websocket.server.ServerContainer not available !");
        }
        return serverContainer;
    }

    public void registerEndpoints() {
        ServerContainer serverContainer = this.getServerContainer();
        for (Class<?> endpointClass : this.endpointClasses) {
            try {
                if (log.isDebugEnabled()) {
                    log.debug("registering @ServerEndpoint class: {}", endpointClass);
                }
                serverContainer.addEndpoint(endpointClass);
            }
            catch (DeploymentException ex) {
                throw new IllegalStateException("failed to register @ServerEndpoint class: " + endpointClass, ex);
            }
        }
    }
}
           

OK,基本工作做完了,最后再写一个自动配置类进行加载就好了:

package com.kfyty.mvc.autoconfig;

import com.kfyty.mvc.WebServer;
import com.kfyty.mvc.request.resolver.HandlerMethodArgumentResolver;
import com.kfyty.mvc.request.resolver.HandlerMethodReturnValueProcessor;
import com.kfyty.mvc.servlet.DispatcherServlet;
import com.kfyty.mvc.servlet.HandlerInterceptor;
import com.kfyty.mvc.tomcat.TomcatConfig;
import com.kfyty.mvc.tomcat.TomcatWebServer;
import com.kfyty.support.autoconfig.BeanRefreshComplete;
import com.kfyty.support.autoconfig.ConfigurableContext;
import com.kfyty.support.autoconfig.DestroyBean;
import com.kfyty.support.autoconfig.annotation.Autowired;
import com.kfyty.support.autoconfig.annotation.Bean;
import com.kfyty.support.autoconfig.annotation.Configuration;
import com.kfyty.support.autoconfig.annotation.Import;

import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebListener;
import java.util.EventListener;
import java.util.List;

/**
 * 描述:
 *
 * @author kfyty725
 * @date 2021/5/28 14:53
 * @email [email protected]
 */
@Configuration
@Import(config = WebSocketAutoConfig.class)
public class TomcatAutoConfig implements BeanRefreshComplete, DestroyBean {
    @Autowired
    private ConfigurableContext configurableContext;

	/**
	 * 处理器拦截器
	 */
    @Autowired(required = false)
    private List<HandlerInterceptor> interceptorChain;

	/**
	 * 处理器方法参数解析器
	 */
    @Autowired(required = false)
    private List<HandlerMethodArgumentResolver> argumentResolvers;

	/**
	 * 处理器方法返回值处理器
	 */
    @Autowired(required = false)
    private List<HandlerMethodReturnValueProcessor> returnValueProcessors;

    @Bean
    public TomcatConfig tomcatConfig() {
        TomcatConfig config = new TomcatConfig(configurableContext.getPrimarySource());
        configurableContext.getBeanWithAnnotation(WebFilter.class).values().forEach(e -> config.addWebFilter((Filter) e));
        configurableContext.getBeanWithAnnotation(WebListener.class).values().forEach(e -> config.addWebListener((EventListener) e));
        return config;
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        if(this.interceptorChain != null) {
            dispatcherServlet.getInterceptorChains().addAll(this.interceptorChain);
        }
        if(this.argumentResolvers != null) {
            dispatcherServlet.getArgumentResolvers().addAll(this.argumentResolvers);
        }
        if(this.returnValueProcessors != null) {
            dispatcherServlet.getReturnValueProcessors().addAll(this.returnValueProcessors);
        }
        return dispatcherServlet;
    }

    @Bean
    public TomcatWebServer tomcatWebServer(TomcatConfig config, DispatcherServlet dispatcherServlet) {
        return new TomcatWebServer(config, dispatcherServlet);
    }

	/**
	 * 将 ServletContext 暴露为 bean,供 websocket 自动配置使用
	 */
    @Bean
    public ServletContext servletContext(TomcatWebServer webServer) {
        return webServer.getServletContext();
    }

    @Override
    public void onComplete(Class<?> primarySource, String ... args) {
        WebServer server = configurableContext.getBean(WebServer.class);
        if(server != null) {
            server.start();
        }
    }

    @Override
    public void onDestroy() {
        WebServer server = configurableContext.getBean(WebServer.class);
        if(server != null) {
            server.stop();
        }
    }
}
           

接下来是 websocket 的自动导出配置类:

package com.kfyty.mvc.autoconfig;

import com.kfyty.mvc.tomcat.ServerEndpointExporter;
import com.kfyty.support.autoconfig.ConfigurableContext;
import com.kfyty.support.autoconfig.InitializingBean;
import com.kfyty.support.autoconfig.annotation.Autowired;
import com.kfyty.support.autoconfig.annotation.Bean;
import com.kfyty.support.autoconfig.annotation.Configuration;

import javax.servlet.ServletContext;
import javax.websocket.server.ServerEndpoint;

/**
 * 描述: websocket 自动配置
 *
 * @author kfyty725
 * @date 2021/6/4 18:29
 * @email [email protected]
 */
@Configuration
public class WebSocketAutoConfig implements InitializingBean {
    @Autowired
    private ConfigurableContext configurableContext;

    @Autowired
    private ServerEndpointExporter serverEndpointExporter;

    @Bean
    public ServerEndpointExporter serverEndpointExporter(ServletContext servletContext) {
        return new ServerEndpointExporter(servletContext);
    }

    @Override
    public void afterPropertiesSet() {
        for (Object value : configurableContext.getBeanWithAnnotation(ServerEndpoint.class).values()) {
            this.serverEndpointExporter.addEndpointClass(value.getClass());
        }
        this.serverEndpointExporter.registerEndpoints();
    }
}
           

最后将 tomcat 自动配置添加到 EnableWebMvc 注解中:

package com.kfyty.mvc.annotation;

import com.kfyty.mvc.autoconfig.MvcAutoConfig;
import com.kfyty.mvc.autoconfig.TomcatAutoConfig;
import com.kfyty.support.autoconfig.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 描述: 启用 kfyty-mvc 的自动配置
 *
 * @author fyty
 * @date 2021/6/5 20:32
 * @email [email protected]
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(config = {MvcAutoConfig.class, TomcatAutoConfig.class})
public @interface EnableWebMvc {
}
           

测试一波儿(我这里没有配置数据源的 Bean,所以要排除 MapperAutoConfig,如果配置了数据源的话就不用排除了):

package com.kfyty.test;

import com.kfyty.boot.K;
import com.kfyty.database.jdbc.autoconfig.MapperAutoConfig;
import com.kfyty.mvc.annotation.EnableWebMvc;
import com.kfyty.support.autoconfig.annotation.BootApplication;

/**
 * 描述:
 *
 * @author kfyty725
 * @date 2021/5/29 13:04
 * @email [email protected]
 */
@EnableWebMvc
@BootApplication(exclude = MapperAutoConfig.class)
public class WebApplication {

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

启动后控制台输出 debug 日志如下:

[2021-06-14 00:07:37 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:37 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:37 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.boot.processor.ConfigurationBeanProcessor.doEnhancerBean(ConfigurationBeanProcessor.java:47)enhanced configuration bean: [email protected] -> com.kfyt[email protected]6d7b4f4c
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.boot.processor.ConfigurationBeanProcessor.doEnhancerBean(ConfigurationBeanProcessor.java:47)enhanced configuration bean: [email protected] -> com.kfyty.b[email protected]13a5fe33
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.boot.processor.ConfigurationBeanProcessor.doEnhancerBean(ConfigurationBeanProcessor.java:47)enhanced configuration bean: [email protected] -> [email protected]753b6d
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.boot.processor.ConfigurationBeanProcessor.doEnhancerBean(ConfigurationBeanProcessor.java:47)enhanced configuration bean: [email protected] -> co[email protected]6f1fba17
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.AutowiredProcessor.doAutowired(AutowiredProcessor.java:47)autowired bean: [[email protected]] -> [co[email protected]6f1fba17] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.MethodBeanDefinition.createInstance(MethodBeanDefinition.java:77)instantiate bean from bean method: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.boot.processor.ConfigurationBeanProcessor.doEnhancerBean(ConfigurationBeanProcessor.java:47)enhanced configuration bean: [email protected] -> com.k[email protected]2758fe70
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.AutowiredProcessor.doAutowired(AutowiredProcessor.java:47)autowired bean: [[]] -> [com.k[email protected]2758fe70] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.AutowiredProcessor.doAutowired(AutowiredProcessor.java:47)autowired bean: [[]] -> [com.k[email protected]2758fe70] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.AutowiredProcessor.doAutowired(AutowiredProcessor.java:47)autowired bean: [[[email protected]]] -> [com.k[email protected]2758fe70] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.AutowiredProcessor.doAutowired(AutowiredProcessor.java:47)autowired bean: [[email protected]] -> [com.k[email protected]2758fe70] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.MethodBeanDefinition.createInstance(MethodBeanDefinition.java:77)instantiate bean from bean method: [[email protected]] !
[2021-06-14 00:07:38 上午]:DEBUG com.kfyty.support.autoconfig.beans.MethodBeanDefinition.createInstance(MethodBeanDefinition.java:77)instantiate bean from bean method: [TomcatConfig(port=8080, protocol=org.apache.coyote.http11.Http11NioProtocol, staticPattern=[/static/*, *.js, *.css, *.html, *.png, *.jpg, *.jpeg, *.ico], dispatcherMapping=/, primarySource=class com.kfyty.test.WebApplication, webFilters=[[email protected]], webListeners=[[email protected]])] !
六月 14, 2021 12:07:39 上午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Tomcat]
六月 14, 2021 12:07:39 上午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet engine: [Apache Tomcat/9.0.35]
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.support.autoconfig.beans.MethodBeanDefinition.createInstance(MethodBeanDefinition.java:77)instantiate bean from bean method: [[email protected]] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.support.autoconfig.beans.MethodBeanDefinition.createInstance(MethodBeanDefinition.java:77)instantiate bean from bean method: [[email protected]] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.boot.processor.ConfigurationBeanProcessor.doEnhancerBean(ConfigurationBeanProcessor.java:47)enhanced configuration bean: [email protected] -> [email protected]3
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.support.autoconfig.beans.GenericBeanDefinition.createInstance(GenericBeanDefinition.java:79)instantiate bean: [[email protected]] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.boot.processor.ConfigurationBeanProcessor.doEnhancerBean(ConfigurationBeanProcessor.java:47)enhanced configuration bean: [email protected] -> com.kfyt[email protected]8dbdac1
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.support.autoconfig.beans.MethodBeanDefinition.createInstance(MethodBeanDefinition.java:77)instantiate bean from bean method: [[email protected]] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.support.autoconfig.beans.AutowiredProcessor.doAutowired(AutowiredProcessor.java:47)autowired bean: [[email protected]] -> [com.kfyt[email protected]8dbdac1] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.support.autoconfig.beans.AutowiredProcessor.doAutowired(AutowiredProcessor.java:47)autowired bean: [[email protected]] -> [[email protected]753b6d] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.tomcat.ServerEndpointExporter.registerEndpoints(ServerEndpointExporter.java:51)registering @ServerEndpoint class: class com.kfyty.test.config.WebSocketConfig
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/home, RequestMethod:GET, MappingMethod:public java.lang.String com.kfyty.test.controller.TestController.index()] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/array, RequestMethod:POST, MappingMethod:public java.lang.String[] com.kfyty.test.controller.TestController.array(java.lang.String[])] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/list, RequestMethod:POST, MappingMethod:public java.util.List com.kfyty.test.controller.TestController.list(java.util.List)] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/map, RequestMethod:GET, MappingMethod:public java.util.Map com.kfyty.test.controller.TestController.map(java.util.Map)] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/rest/{name}, RequestMethod:GET, MappingMethod:public java.lang.String com.kfyty.test.controller.TestController.rest(java.lang.String)] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/user, RequestMethod:PUT, MappingMethod:public com.kfyty.test.dto.UserDto com.kfyty.test.controller.TestController.userDto(com.kfyty.test.dto.UserDto)] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/user-dept, RequestMethod:POST, MappingMethod:public com.kfyty.test.dto.UserDto com.kfyty.test.controller.TestController.userDto(com.kfyty.test.dto.UserDto,com.kfyty.test.dto.DeptDto)] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/hello, RequestMethod:GET, MappingMethod:public java.lang.String com.kfyty.test.controller.TestController.hello(java.lang.String)] !
[2021-06-14 00:07:39 上午]:DEBUG com.kfyty.mvc.handler.MvcAnnotationHandler.parseRequestMappingAnnotation(MvcAnnotationHandler.java:136)discovery request mapping: [URL:/test/upload, RequestMethod:POST, MappingMethod:public void com.kfyty.test.controller.TestController.upload(com.kfyty.mvc.multipart.MultipartFile) throws java.lang.Exception] !
六月 14, 2021 12:07:40 上午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-8080"]
六月 14, 2021 12:07:40 上午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]
[2021-06-14 00:07:40 上午]:INFO com.kfyty.mvc.tomcat.TomcatWebServer.start(TomcatWebServer.java:81)tomcat started on port(8080)
[2021-06-14 00:07:40 上午]:INFO com.kfyty.boot.K.run(K.java:55)Started WebApplication in 2.816 seconds
           

最后附上代码地址~

kfyty-mvc:https://github.com/kfyty/kfyty-utils/tree/master/kfyty-mvc

kfyty-mvc-test:https://github.com/kfyty/test/tree/embedded

完结~~~