java使用内嵌tomcat开发web项目
写在前面
这一篇博客,是在java篇-(java使用内嵌Tomcat开发javaWeb项目-初级篇)这篇博客之上进行扩展,整合spring mvc,让spring来管理路由关系
java篇-(java使用内嵌Tomcat开发javaWeb项目-初级篇)
这里删除初级篇文章里面的部分代码,只保留如下结构
EmbedTomcatApplication.java
package com.lhstack.embed;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URL;
import java.util.Objects;
/**
* @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);
URL resource = Thread.currentThread().getContextClassLoader().getResource("");
Context context = tomcat.addContext("/", Objects.nonNull(resource) ? resource.getPath() : null);
addCharacterFilter(context,"UTF-8");
//启动tomcat
tomcat.start();
}
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);
}
}
EmbedTomcatApplication.kt
package com.lhstack.embed
import org.apache.catalina.Context
import org.apache.catalina.startup.Tomcat
import org.apache.tomcat.util.descriptor.web.FilterDef
import org.apache.tomcat.util.descriptor.web.FilterMap
import java.util.*
import javax.servlet.Filter
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
fun main() {
val port = 9090
val tomcat = Tomcat()
tomcat.setPort(port)
tomcat.connector.port = port
val resource = Thread.currentThread().contextClassLoader.getResource("")
val context = tomcat.addContext("/", if(Objects.nonNull(resource)) resource!!.file else null )
addCharacterFilter(context,"UTF-8")
tomcat.start()
}
fun addCharacterFilter(context: Context?, encoding: String) {
val filterDef = FilterDef()
filterDef.filterName = "CharacterFilterEncoding"
filterDef.filter = Filter {
req, res, chain ->
if(req is HttpServletRequest){
req.characterEncoding = encoding
}
if(res is HttpServletResponse){
res.characterEncoding = encoding
}
chain.doFilter(req,res)
}
val filterMap = FilterMap()
filterMap.addURLPattern("/*")
filterMap.filterName = "CharacterFilterEncoding"
context?.addFilterDef(filterDef)
context?.addFilterMap(filterMap)
}
在pom.xml里面加入spring相关依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>embed-tomcat-example</artifactId>
<groupId>com.lhstack</groupId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>embed-tomcat-example</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
<tomcat.embed.version>9.0.31</tomcat.embed.version>
<thymeleaf.version>3.0.12.RELEASE</thymeleaf.version>
<spring.version>5.3.5</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>${thymeleaf.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>1.4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.embed.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.4.10</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<target>11</target>
<source>11</source>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>1.4.10</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>
编写spring相关配置及代码
java语言
添加spring的DispatcherServlet,并在main方法里面调用
private static void addDispatcherServlet(Context context) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setContextConfigLocation("classpath:spring-*.xml");
Wrapper servlet = Tomcat.addServlet(context, "DispatcherServlet", dispatcherServlet);
//跟着tomcat一起启动
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
添加spring-mvc.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:mvc="http://www.springframework.org/schema/mvc"
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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解驱动 -->
<mvc:annotation-driven />
<!-- 添加静态资源映射 -->
<mvc:resources mapping="/static/**" location="classpath:/static/" />
<context:component-scan base-package="com.lhstack.embed.controller" />
</beans>
根据配置文件,创建对应测试文件
1.在static目录下面创建index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
this is static directory html file
</body>
</html>
2.在controller包下面创建HelloController.java
package com.lhstack.embed.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lhstack
*/
@RequestMapping
@RestController
public class HelloController {
@GetMapping
public String hello(){
return "hello world";
}
}
启动项目,通过浏览器访问
访问controller接口
访问静态资源
使用kotlin语言
添加DispatcherServlet
启动项目,使用浏览器访问
访问controller接口
访问静态资源
添加json序列化支持
pom文件里面添加fastjson依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
在配置文件里面添加fastjson序列化支持
spring-mvc.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:mvc="http://www.springframework.org/schema/mvc"
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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="charset" value="UTF-8" />
<property name="dateFormat" value="yyyy-MM-dd HH:mm:ss" />
<property name="writeContentLength" value="true" />
</bean>
</property>
<property name="supportedMediaTypes">
<array>
<value>application/json</value>
<value>application/json;charset=utf-8</value>
</array>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 添加静态资源映射 -->
<mvc:resources mapping="/static/**" location="classpath:/static/" />
<context:component-scan base-package="com.lhstack.embed.controller" />
</beans>
在controller添加复杂对象输出
package com.lhstack.embed.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author lhstack
*/
@RequestMapping
@RestController
public class HelloController {
@GetMapping
public String hello(){
return "hello world";
}
@GetMapping("map")
public Map<String,Object> map(){
Map<String,Object> result = new HashMap<>();
result.put("currentTime",new Date());
result.put("msg","hello world");
result.put("number",1);
result.put("map",System.getenv());
return result;
}
}
启动项目,通过浏览器访问
访问controller接口
添加validation验证器
添加pom依赖
pom.xml,这里内容太多了,我只复制了使用的部分
...
...
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
<tomcat.embed.version>9.0.31</tomcat.embed.version>
<thymeleaf.version>3.0.12.RELEASE</thymeleaf.version>
<spring.version>5.3.5</spring.version>
<fastjson.version>1.2.73</fastjson.version>
<validation-api.version>2.0.1.Final</validation-api.version>
<hibernate-validator.version>6.2.0.Final</hibernate-validator.version>
<javax.el-api.version>3.0.0</javax.el-api.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.glassfish/javax.el -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>${javax.el-api.version}</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>${javax.el-api.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation-api.version}</version>
</dependency>
...
...
配置validator支持
在spring-mvc.xml里面配置validator支持
<?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:mvc="http://www.springframework.org/schema/mvc"
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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置validator -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
</bean>
<!-- 开启注解驱动,并使用validator -->
<mvc:annotation-driven validator="validator">
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="charset" value="UTF-8" />
<property name="dateFormat" value="yyyy-MM-dd HH:mm:ss" />
<property name="writeContentLength" value="true" />
</bean>
</property>
<property name="supportedMediaTypes">
<array>
<value>application/json</value>
<value>application/json;charset=utf-8</value>
</array>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 添加静态资源映射 -->
<mvc:resources mapping="/static/**" location="classpath:/static/" />
<context:component-scan base-package="com.lhstack.embed.controller" />
</beans>
创建entity,添加验证注解
Message.java
package com.lhstack.embed.entity;
import javax.validation.constraints.NotEmpty;
/**
* @author lhstack
*/
public class Message {
@NotEmpty(message = "msg不能为空")
private String msg;
public void setMsg(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
@Override
public String toString() {
return "Message{" +
"msg='" + msg + '\'' +
'}';
}
}
在controller添加对应接口
package com.lhstack.embed.controller;
import com.lhstack.embed.entity.Message;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author lhstack
*/
@RequestMapping
@RestController
public class HelloController {
@PostMapping("msg")
public Message message(@RequestBody @Validated Message message){
return message;
}
@GetMapping
public String hello(){
return "hello world";
}
@GetMapping("map")
public Map<String,Object> map(){
Map<String,Object> result = new HashMap<>();
result.put("currentTime",new Date());
result.put("msg","hello world");
result.put("number",1);
result.put("map",System.getenv());
return result;
}
}
启动项目,使用postman测试接口
只传空对象,然后报了400的错误,这里没法直接看到异常,后面加入异常拦截器之后,可以处理
传了msg字段
添加log4j2作为日志支持
添加pom依赖
这里同样也只复制部分
...
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
<tomcat.embed.version>9.0.31</tomcat.embed.version>
<thymeleaf.version>3.0.12.RELEASE</thymeleaf.version>
<spring.version>5.3.5</spring.version>
<fastjson.version>1.2.73</fastjson.version>
<validation-api.version>2.0.1.Final</validation-api.version>
<hibernate-validator.version>6.2.0.Final</hibernate-validator.version>
<javax.el-api.version>3.0.0</javax.el-api.version>
<slf4j.version>1.7.30</slf4j.version>
<log4j2.version>2.14.1</log4j2.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
...
添加log4j2.xml文件,配置日志
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<!-- 控制台输出debug级别以上的日志-->
<Console name="DebugConsole" target="SYSTEM_OUT">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- 控制台输出info级别以上的日志-->
<Console name="InfoConsole" target="SYSTEM_OUT">
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- info-->
<RollingRandomAccessFile name="InfoAppendLogger" fileName="logs/info/info.log"
filePattern="logs/info/%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<LevelMatchFilter level="INFO" />
<Policies>
<OnStartupTriggeringPolicy />
<!-- 以日志的pattern决定,如果后面是d,则interval是天,如果是h,则是小时,依次类推-->
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<!-- 当日志达到多大时,才进行切割-->
<SizeBasedTriggeringPolicy size="50 MB"/>
</Policies>
<DefaultRolloverStrategy>
<Delete basePath="logs/info/" maxDepth="1">
<IfFileName glob="*.log"/>
<IfLastModified age="7d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<Async name="AsyncInfoLoggerAppender">
<AppenderRef ref="InfoAppendLogger"/>
</Async>
<!-- error-->
<RollingRandomAccessFile name="ErrorAppendLogger" fileName="logs/error/error.log"
filePattern="logs/error/%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<OnStartupTriggeringPolicy />
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<SizeBasedTriggeringPolicy size="50 MB"/>
</Policies>
<DefaultRolloverStrategy>
<Delete basePath="logs/error/" maxDepth="2">
<IfFileName glob="*.log"/>
<IfLastModified age="7d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<Async name="AsyncErrorLoggerAppender">
<AppenderRef ref="ErrorAppendLogger"/>
</Async>
<!-- warn-->
<RollingRandomAccessFile name="WarnAppendLogger" fileName="logs/warn/warn.log"
filePattern="logs/warn/%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<LevelMatchFilter level="WARN"/>
<Policies>
<OnStartupTriggeringPolicy />
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<SizeBasedTriggeringPolicy size="50 MB"/>
</Policies>
<DefaultRolloverStrategy>
<Delete basePath="logs/warn/" maxDepth="2">
<IfFileName glob="*.log"/>
<IfLastModified age="7d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<Async name="AsyncWarnLoggerAppender">
<AppenderRef ref="WarnAppendLogger"/>
</Async>
<!-- debug-->
<RollingRandomAccessFile name="DebugAppendLogger" fileName="logs/debug/debug.log"
filePattern="logs/debug/%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<LevelMatchFilter level="DEBUG"/>
<Policies>
<OnStartupTriggeringPolicy />
<!-- 已日志的pattern决定,如果后面是d,则interval是天,如果是h,则是小时,依次类推-->
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<!-- 当日志达到多大时,才进行切割-->
<SizeBasedTriggeringPolicy size="50 MB"/>
</Policies>
<DefaultRolloverStrategy>
<Delete basePath="logs/debug/" maxDepth="2">
<IfFileName glob="*.log"/>
<IfLastModified age="7d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<Async name="AsyncDebugLoggerAppender">
<AppenderRef ref="DebugAppendLogger"/>
</Async>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="AsyncInfoLoggerAppender"/>
<AppenderRef ref="AsyncErrorLoggerAppender"/>
<AppenderRef ref="AsyncWarnLoggerAppender"/>
<!-- <AppenderRef ref="AsyncDebugLoggerAppender"/>-->
<AppenderRef ref="InfoConsole"/>
</Root>
</Loggers>
</Configuration>
启动项目,查看日志情况
启动之后,控制台情况
访问接口,并且输入验证失败的数据
输出了验证失败的日志信息
添加异常处理器
在controller包下面创建ExceptionHandlerController.java
ExceptionHandlerController.java
package com.lhstack.embed.controller;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author lhstack
* 异常处理器
*/
@RestControllerAdvice
public class ExceptionHandlerController {
@ExceptionHandler(value = {
Exception.class,RuntimeException.class})
public Map<String,Object> exceptionHandler(Exception e){
e.printStackTrace();
return Map.of("status",500,"success",false,"message",e.getMessage());
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Map<String,Object> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
String message = e.getBindingResult().getFieldErrors()
.stream().map(item -> item.getField() + "=" + item.getDefaultMessage())
.collect(Collectors.joining(","));
return Map.of("status",500,"success",false,"message",message);
}
}
在HelloController.java中添加异常接口
@GetMapping("e")
public void exception(){
throw new RuntimeException("this is exception throw");
}
启动项目,测试异常handler
测试空消息,测试验证失败
测试异常接口
转载:https://blog.csdn.net/qq_42413011/article/details/116309368
查看评论