造轮子之整合嵌入式 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
完结~~~