1、搭建项目,请参考:idea搭建web maven项目
2、在web.xml中配置Spring以及SpringMVC等配置,如图:
<!-- Spring beans 配置文件所在目录 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- spring mvc 配置 -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 默认加载方式
默认加载必须规范:
* 文件命名:servlet-name-servlet.xml====springmvc-servlet.xml
* 路径规范:必须在WEB-INF目录下面
-->
可以修改配置文件名称,在.DispatcherServlet</servlet-class>后面,如:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/springmvc.xml</param-value>
</init-param>
这里解释一下 / 和 /*
/ 指的是匹配/login /hello 等没有后缀的 不会匹配.jsp等资源
/* 指的是匹配所有的资源 css、jsp、html等等
3、配置Encoding 编码,如图:
<!-- Encoding -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4、配置访问的默认路径,如图:
2022-03-31
start:
完整的web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring beans 配置文件所在目录 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- spring mvc 配置 -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Encoding -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- druid -->
<servlet>
<servlet-name>DruidStatServlet</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<param-name>loginUsername</param-name>
<param-value>druid</param-value>
</init-param>
<init-param>
<param-name>loginPassword</param-name>
<param-value>druid</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatServlet</servlet-name>
<url-pattern>/sys/druid/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>*.js,*.css,*.jpg,*.png,*.ico,*.gif,/sys/druid/*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DruidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- <login-config>-->
<!-- <!– Authorization setting for SSL –>-->
<!-- <auth-method>CLIENT-CERT</auth-method>-->
<!-- <realm-name>Client Cert Users-only Area</realm-name>-->
<!-- </login-config>-->
<!-- <security-constraint>-->
<!-- <!– Authorization setting for SSL –>-->
<!-- <web-resource-collection >-->
<!-- <web-resource-name >SSL</web-resource-name>-->
<!-- <url-pattern>/*</url-pattern>-->
<!-- </web-resource-collection>-->
<!-- <user-data-constraint>-->
<!-- <transport-guarantee>CONFIDENTIAL</transport-guarantee>-->
<!-- </user-data-constraint>-->
<!-- </security-constraint>-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
end
2022-04-01
start:
1、完整的项目截图
2、配置文件applicationContext.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.alibaba.com/schema/stat http://www.alibaba.com/schema/stat.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="xyz.haijin.shiro">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:settings.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driverClassName}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
<property name="initialSize" value="3" />
<property name="minIdle" value="3" />
<property name="maxActive" value="20" />
<property name="maxWait" value="60000" />
<property name="filters" value="stat,wall" />
</bean>
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="globalConfig" ref="globalConfig"/> <!-- 非必须 -->
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="xyz.haijin.wallpaperserver.entity" />
<property name="mapperLocations" value="classpath:mapper/*.xml" />
</bean>
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" ref="dbConfig"/> <!-- 非必须 -->
</bean>
<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
<property name="selectStrategy" value="NOT_EMPTY"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="xyz.haijin.wallpaperserver.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" />
</bean>
<!-- tx -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- <!– druid –>-->
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="3000" />
<property name="logSlowSql" value="true" />
<property name="mergeSql" value="true" />
</bean>
<bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">
<property name="dbType" value="mysql" />
</bean>
</beans>
3、配置文件logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- 打印mp sql语句 -->
<logger name="xyz.haijin.wallpaperserver.mapper" level="DEBUG" />
<!-- TRACE < DEBUG < INFO < WARN < ERROR -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4、配置文件mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="safeRowBoundsEnabled" value="true"/>
<setting name="cacheEnabled" value="false" />
<setting name="useGeneratedKeys" value="true" />
<!-- 打印查询语句 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING -->
<setting name="logImpl" value="SLF4J" />
</settings>
<plugins>
<!-- 3.4.0以下 -->
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
<property name="dialect" value="mysql" />
<property name="pageSqlId" value="ByPage" />
</plugin>
<!-- 3.4.0以上 -->
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
<property name="@page" value="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor"/>
<property name="page:dbType" value="mysql"/>
</plugin>
<plugin interceptor="xyz.haijin.wallpaperserver.interceptor.SqlStatementInterceptor">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
</configuration>
5、配置文件settings.properties:
db.driverClassName=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/wallpaper?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Hongkong&autoReconnect=true&failOverReadOnly=false
db.username=root
db.password=123456
6、配置文件springmvc.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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config />
<!-- 启动注解驱动的spring mvc 功能 -->
<mvc:annotation-driven/>
<!-- 校验配置-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<property name="validationMessageSource" ref="messageSource" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<!-- <value>classpath:messages</value>-->
<!-- <value>classpath:xyz/haijin/wallpaperserver/ValidationMessages</value>-->
</list>
</property>
<property name="useCodeAsDefaultMessage" value="false" />
<property name="defaultEncoding" value="UTF-8" />
<property name="cacheSeconds" value="60" />
</bean>
<mvc:resources mapping="/**" location="xyz/haijin/wallpaperserver" />
<!-- 启动包扫描功能 -->
<context:component-scan base-package="xyz.haijin.wallpaperserver" />
<!-- thymeleaf 模板解析器 -->
<bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/user/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML" />
<property name="cacheable" value="false" />
<property name="characterEncoding" value="UTF-8"/>
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<!-- 视图解析器-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="characterEncoding" value="UTF-8"/>
</bean>
<!--配置拦截器, 多个拦截器,顺序执行 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->
<!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller -->
<mvc:mapping path="/**" />
<bean class="xyz.haijin.wallpaperserver.interceptor.StartAndEndHandlerInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
7、打印接口前后执行的时间拦截器
StartAndEndHandlerInterceptor:
package xyz.haijin.wallpaperserver.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author liuhaijin
* @Date 2022/4/1
*/
@Slf4j
public class StartAndEndHandlerInterceptor extends HandlerInterceptorAdapter {
/**
* springMVC拦截器
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
log.info("start time is "+ sdf.format(new Date()) + " ; request url is " + request.getRequestURI() + " : " + request.getMethod());
return true;
}
/**
* This implementation is empty.
*/
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
log.info("end time is "+ sdf.format(new Date()) + " ; request url is " + request.getRequestURI() + " : " + request.getMethod());
}
}
8、打印sql的执行时间拦截器
SqlStatementInterceptor:
package xyz.haijin.wallpaperserver.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.util.CollectionUtils;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;
/**
* 数据库操作性能拦截器,记录耗时
* @Author liuhaijin
* @Date 2022/4/1
*/
@Intercepts(value = {
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class})})
@Slf4j
public class SqlStatementInterceptor implements Interceptor {
private Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object returnValue;
long start = System.currentTimeMillis();
returnValue = invocation.proceed();
long end = System.currentTimeMillis();
long time = end - start;
try {
final Object[] args = invocation.getArgs();
//获取原始的ms
MappedStatement ms = (MappedStatement) args[0];
Object parameter = null;
//获取参数,if语句成立,表示sql语句有参数,参数格式是map形式
if (invocation.getArgs().length > 1) {
parameter = invocation.getArgs()[1];
}
// 获取到节点的id,即sql语句的id
String sqlId = ms.getId();
// BoundSql就是封装myBatis最终产生的sql类
BoundSql boundSql = ms.getBoundSql(parameter);
// 获取节点的配置
Configuration configuration = ms.getConfiguration();
// 获取到最终的sql语句
String sql = getSql(configuration, boundSql, sqlId, time);
log.debug(sql);
} catch (Exception e) {
log.error("拦截sql处理出差" + e.getMessage());
e.printStackTrace();
}
return returnValue;
}
// 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句
public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId, long time) {
String sql = showSql(configuration, boundSql);
StringBuilder str = new StringBuilder(100);
str.append("\n\n============================begin================================\n\n");
str.append(sqlId);
str.append(":耗时【");
str.append(time);
str.append("】毫秒\n\n");
str.append(sql);
str.append("\n\n============================end================================\n\n");
return str.toString();
}
/*<br> *如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理<br> */
private static String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
Date date = (Date) obj;
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(date) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
// 进行?的替换
public static String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject(); // 获取参数
List<ParameterMapping> parameterMappings = boundSql
.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " "); // sql语句中多个空格都用一个空格代替
if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); // 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换<br> // 如果根据parameterObject.getClass()可以找到对应的类型,则替换
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);// MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName); // 该分支是动态sql
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else {
sql = sql.replaceFirst("\\?", "缺失");
}//打印出缺失,提醒该参数缺失并防止错位
}
}
}
return sql;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
end