java使用内嵌Tomcat开发javaWeb项目
写在前面
这一篇博客,主要讲解项目开发中,接口文档编写,日志记录,项目部署,
java使用内嵌Tomcat开发javaWeb项目-高级篇2这篇博客,主要讲解在开发中,如何集成其他功能,博客地址:
java篇-(java使用内嵌Tomcat开发javaWeb项目-高级篇2 ,java使用内嵌Tomcat开发javaWeb项目源代码下载地址:
embed-tomcat-example.zip
集成Swagger,提供优雅api文档
在pom.xml添加swagger依赖
...
<springfox.swagger.version>3.0.0</springfox.swagger.version>
<swagger.version>1.6.2</swagger.version>
<jackson.version>2.11.4</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.swagger/swagger-annotations -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-oas</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
...
创建swagger配置类
package com.lhstack.embed.config;
import io.swagger.annotations.Api;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ui.Model;
import org.springframework.web.servlet.ModelAndView;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author lhstack
*/
@Configuration
@EnableOpenApi
public class SwaggerConfiguration {
@Bean
public Docket docket() {
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.ignoredParameterTypes(HttpServletRequest.class,
HttpServletResponse.class,
HttpSession.class,
ServletContext.class,
Model.class,
ModelAndView.class)
.select()
.apis(RequestHandlerSelectors.basePackage("com.lhstack.embed.controller")
.and(RequestHandlerSelectors.withClassAnnotation(Api.class)))
.paths(PathSelectors.any())
.build();
}
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.contact(new Contact("lhstack", "https://www.lhstack.com", "lhstack@foxmail.com"))
.description("embed tomcat")
.title("embed tomcat")
.version("0.0.1").build();
}
}
在spring-config.xml中配置扫描
<context:component-scan base-package="com.lhstack.embed.config" />
在controller下面创建ApiController,用于测试接口描述
package com.lhstack.embed.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lhstack
*/
@RestController
@RequestMapping("api")
@Api(tags = "api接口")
public class ApiController {
@GetMapping("hello")
@ApiOperation("打印hello")
public String hello(){
return "hello";
}
@GetMapping("hello/{param}")
@ApiOperation("带参数")
public String hello(@ApiParam(value = "打印的值") @PathVariable("param") String hello){
return hello;
}
}
在spring-mvc.xml和spring-shiro.xml中开启swagger资源映射和关闭权限拦截
<mvc:resources mapping="/swagger-ui/**" location="classpath:/META-INF/resources/webjars/springfox-swagger-ui/" />
<entry key="/swagger-ui/**" value="anon"/>
<entry key="/swagger-resources/**" value="anon" />
<entry key="/v3/**" value="anon" />
<entry key="/api/**" value="anon" />
启动项目
通过浏览器输入http://localhost:8080/swagger-ui/index.html
,访问swagger稳定,进行接口调试
使用HandlerInterceptor记录访问controller的日志
编写LoggingHandlerInterceptor
package com.lhstack.embed.handler.log;
import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** `
* @author lhstack
*/
public class LoggingHandlerInterceptor implements HandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingHandlerInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
LOGGER.info("\n" +
"--------------\n" +
"Method: {}\n" +
"Url: {}\n" +
"Param: {}\n" +
"Handler: {}\n" +
"Principal: {}\n" +
"--------------",
request.getMethod(),
request.getRequestURI(),
JSONObject.toJSONString(request.getParameterMap()),
handlerMethod.getMethod(),
SecurityUtils.getSubject().getPrincipal()
);
}
return true;
}
}
在spring-mvc.xml中配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.lhstack.embed.handler.log.LoggingHandlerInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
启动项目
访问swagger接口,并请求接口
项目部署环境修改
j将配置和静态资源移动到外部目录,这是方便项目的配置修改,静态页面的修改,如数据库配置,或者页面替换等等,提高项目的灵活性,同时也减少了jar包的大小,实现配置,页面,与后台完全解耦
修改启动类
修改context path,设置路径为jar包所在同级目录下webapp
修改spring配置文件加载路径,修改为加载contextPath目录下的WEB-INFO/spring目录里面的配置文件
package com.lhstack.embed;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.websocket.server.WsContextListener;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author lhstack
*/
public class EmbedTomcatApplication {
public static void main(String[] args) throws Exception {
int port = 8080;
Tomcat tomcat = new Tomcat();
tomcat.setPort(port);
tomcat.getConnector().setPort(port);
String webapp = System.getProperty("user.dir") + "/webapp";
Context context = tomcat.addContext("/", webapp);
addCharacterFilter(context, "UTF-8");
addWsContextListener(context);
addShiroFilter(context);
addDispatcherServlet(context);
//启动tomcat
tomcat.start();
}
private static void addWsContextListener(Context context) {
context.addApplicationListener(WsContextListener.class.getName());
}
private static void addShiroFilter(Context context) {
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
filterProxy.setTargetFilterLifecycle(true);
FilterDef filterDef = new FilterDef();
//这里与shiroFilterFactoryBean的id必须一致
filterDef.setFilterName("shiroFilterFactoryBean");
filterDef.setFilter(filterProxy);
//定义filter映射
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("shiroFilterFactoryBean");
filterMap.addURLPattern("/*");
//添加filter
context.addFilterDef(filterDef);
context.addFilterMap(filterMap);
}
private static void addDispatcherServlet(Context context) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setContextConfigLocation("WEB-INF/spring/spring-*.xml");
Wrapper servlet = Tomcat.addServlet(context, "DispatcherServlet", dispatcherServlet);
//跟着tomcat一起启动
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
private static void addCharacterFilter(Context context, String encoding) {
//定义filter
FilterDef filterDef = new FilterDef();
filterDef.setFilterName("CharacterEncodingFilter");
filterDef.setFilter((req, res, chain) -> {
if (req instanceof HttpServletRequest) {
req.setCharacterEncoding(encoding);
}
if (res instanceof HttpServletResponse) {
res.setCharacterEncoding(encoding);
}
chain.doFilter(req, res);
});
//定义filter映射
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("CharacterEncodingFilter");
filterMap.addURLPattern("/*");
//添加filter
context.addFilterDef(filterDef);
context.addFilterMap(filterMap);
}
}
修改配置文件加载路径
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="WEB-INF/config/db.properties,WEB-INF/config/redis.properties" />
<context:component-scan base-package="com.lhstack.embed.config" />
</beans>
修改静态资源映射路径
<mvc:resources mapping="/static/**" location="/static/"/>
创建与项目同级webapp目录,迁移文件到webapp目录
启动项目
在pom.xml添加打包插件,将项目打包成可运行的jar包
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<!-- 启动类 -->
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.lhstack.embed.EmbedTomcatApplication</mainClass>
</transformer>
<!-- 合并spring jar包下面的META-INF/spring.handlers文件里面的内容 -->
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<!-- 合并spring jar包下面的META-INF/spring.schemas文件里面的内容 -->
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
打包项目
在target目录下面得到可运行jar包
部署项目
将embed-tomcat-example-0.0.1.jar和webapp目录放在同一个目录下面,如下
jar包里面也没有静态文件和配置文件
编写启动脚本,并放在与jar包同级目录
双击启动
浏览器访问
修改静态资源目录里面的内容
第一次访问可能有缓存,多试几次,或者ctrl+f5强制刷新
这样就实现了使用内嵌tomcat开发项目到部署的过程,同时实现页面和配置与项目的解耦,保证项目可以随意移植到不同的系统上面运行