Maven Helper
Free Mybatis Plugin
PostMan
mysql
Navicat
version 1.8
IDEA 2023.1
SpringBoot 2.2.1.RELEASE
Mybatis 3.4.6
Maven 3.6.0
Log4j2 2.12.1
直接使用IDEA 选择Spring Initializr 然后选择 Spring Web 就能生成最基本的 springBoot项目
步骤如下:
application.properties配置文件增加mybatis配置
spring.datasource.name=imooc_mall_datasource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/imooc_mall? serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
# 配置mapper文件映射
mybatis.mapper-locations=classpath:mappers/*.xml
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
dependency>
resource下新建log4j2.xml配置文件,此处注意配置的日志级别,容易出现不打印sql日志问题
使用AOP打印请求URL及请求参数
这里有几个重要点值得注意
统一接口返回{
a. 成功无数据返回
b. 成功有数据返回
c. 处理失败返回 便于管理,定义失败枚举类
d. 异常处理返回 统一返回,使用异常处理器获取异常后处理为统一返回码(这一块重点关注,使用了SpringBoot注解) => 抛出的异常直接转换为ApiRestResponse的json串
}
备注:使用统一接口返回类时,返回对象使用报错:No converter found for return value of type: class com.imooc.mall.common.ApiRestResponse" ,分析后时缺少部分字段的get/set方法
这里给出异常处理的代码,有两个重要的注解
@RestControllerAdvice 表明全局拦截异常
@ExceptionHandler表明处理的异常类型
@RestControllerAdvice
public class GlobalExceptionHandle {
private final Logger log = LoggerFactory.getLogger(GlobalExceptionHandle.class);
@ExceptionHandler(Exception.class)
public Object handleException(Exception ex){
log.error("Default Exception: ", ex);
return ApiRestResponse.error(BusinessExceptionEnum.SYSTEM_ERROR);
}
@ExceptionHandler(BusinessException.class)
public ApiRestResponse handleBusinessException(BusinessException ex){
log.error("Business Exception: ", ex);
return ApiRestResponse.error(ex.getCode(),ex.getMessage());
}
}
此处使用MD5加密密码,引入异常处理器处理异常为统一返回
登录状态保存
利用session
服务端把用户信息保存到session
登录状态删除
利用session
服务端把用户信息从session删除
利用session获取用户
修改数据
这个模块主要引入了几个重要的技术点
使用@Valid增加参数校验,然后在异常统一处理中针对校验异常做处理[ ex.getBindingResult() 获取异常绑定信息 ]
@Valid注解使用已经挺熟悉了,就不加赘述,此处主要说明一下,参数校验不通过之后的异常处理怎么弄
从异常中获取办绑定的返回结果,然后取出message作为返回信息,利用统一接口返回做处理
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiRestResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
// 把异常处理为对外暴露的提示
log.error("MethodArgumentNotValid Exception: ", ex);
return handleBindingResult(ex.getBindingResult());
}
/**
* 将异常转换为标准接口返回
*
* @param bindingResult
* @return
*/
private ApiRestResponse handleBindingResult(BindingResult bindingResult) {
List<String> list = new ArrayList<>();
if (bindingResult.hasErrors()) {
List<ObjectError> allErrors = bindingResult.getAllErrors();
for (ObjectError error : allErrors) {
String message = error.getDefaultMessage();
list.add(message);
}
}
if (list == null) {
return ApiRestResponse.error(BusinessExceptionEnum.PARAM_ERROR);
}
return ApiRestResponse.error(BusinessExceptionEnum.PARAM_ERROR.getCode(), list.toString());
}
引入过滤器实现重点是声明一个 过滤器实现Filter 接口,然后重写其中的处理方法
然后配置过滤器的配置类
需要注意的点是:过滤后的返回使用out回写,另外就是过滤器的配置类
另外就是 拦截器与过滤器的区别与联系
还有注意点就是过滤器的注册会在springContext容器初始化前,所以如果配置过滤器时可能会导致依赖注入失败问题,需要在配置文件中使用bean注入。
package com.imooc.mall.config;
import com.imooc.mall.filter.AdminFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 描述: Admin过滤器的配置
*/
@Configuration
public class AdminFilterConfig {
@Bean
public AdminFilter adminFilter() {
return new AdminFilter();
}
@Bean(name = "adminFilterConf")
public FilterRegistrationBean adminFilterConfig() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(adminFilter());
filterRegistrationBean.addUrlPatterns("/admin/category/*");
filterRegistrationBean.addUrlPatterns("/admin/product/*");
filterRegistrationBean.addUrlPatterns("/admin/order/*");
filterRegistrationBean.setName("adminFilterConf");
return filterRegistrationBean;
}
}
package com.imooc.mall.filter;
import com.imooc.mall.common.ApiRestResponse;
import com.imooc.mall.common.Constant;
import com.imooc.mall.exception.BusinessExceptionEnum;
import com.imooc.mall.model.pojo.User;
import com.imooc.mall.service.UserService;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* todo
*
* @author wangwei
* @version 1.0.0
* @since 2023-04-03
*/
public class AdminFilter implements Filter {
@Autowired
UserService userService;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
filterChain.doFilter(servletRequest, servletResponse);
HttpSession session = request.getSession();
User currentUser = (User) session.getAttribute(Constant.MAIL_USER);
if (currentUser == null) {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) servletResponse).getWriter();
out.write("{\n"
+ " \"status\": 10007,\n"
+ " \"msg\": \"NEED_LOGIN\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
return;
}
// 校验是否是管理员
if (!userService.checkAdminRole(currentUser)) {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) servletResponse).getWriter();
out.write("{\n"
+ " \"status\": 10009,\n"
+ " \"msg\": \"NEED_ADMIN\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
上述是过滤器的实现,接下来也分析一下拦截器的实现
主要有以下步骤
① 实现拦截器继
package com.imooc.mall.aop;
import com.imooc.mall.common.Constant;
import com.imooc.mall.model.pojo.User;
import com.imooc.mall.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;
/**
* todo
*
* @author wangwei
* @version 1.0.0
* @since 2023-04-04
*/
public class AdminInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
User user = (User) session.getAttribute(Constant.MAIL_USER);
if (user == null) {
System.out.println("这里拦截器拦截住需要用户校验的请求");
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) response).getWriter();
out.write("{\n"
+ " \"status\": 10007,\n"
+ " \"msg\": \"NEED_LOGIN\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
return false;
}
// 校验是否是管理员
if (!userService.checkAdminRole(user)) {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) response).getWriter();
out.write("{\n"
+ " \"status\": 10009,\n"
+ " \"msg\": \"NEED_ADMIN\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
② 拦截器配置文件
package com.imooc.mall.config;
import com.imooc.mall.aop.AdminInterceptor;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 静态资源映射
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/META-INF/resources/") // 映射swagger2
.addResourceLocations("file:/workspaces/images/"); // 映射本地静态资源
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
/**
* IOC容器注入拦截器
* @return
*/
@Bean
public AdminInterceptor adminInterceptor() {
return new AdminInterceptor();
}
/**
* 注册拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminInterceptor())
.addPathPatterns("/admin/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
注意点是:依赖注入的时候需要关注,配置的拦截地址需要注意
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.9.2version>
dependency>
②引入配置文件
WebMallMvcConfig
package com.imooc.mall.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 配置地址映射
*
* @author wangwei
* @version 1.0.0
* @since 2023-04-03
*/
@Configuration
public class WebMallMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations(
"classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
}
SpringFoxConfig
package com.imooc.mall.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SpringFoxConfig {
//访问http://localhost:8083/swagger-ui.html可以看到API文档
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("生鲜商城")
.description("")
.termsOfServiceUrl("")
.build();
}
}
③ 最后在需要描述的API上增加注解 @ApiOperation(“后台添加目录”) 启动类增加注解 @EnableSwagger2
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
② properties配置redis配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
③ 增加缓存配置
package com.imooc.mall.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import java.time.Duration;
/**
* 描述: 缓存的配置类
*/
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter
.lockingRedisCacheWriter(connectionFactory);
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
cacheConfiguration = cacheConfiguration.entryTtl(Duration.ofSeconds(1110));
RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter,
cacheConfiguration);
return redisCacheManager;
}
}
④ 在需要缓存的实现类上加上注解 @Cacheable(value = “listForCustomer”) 启动类增加注解 @Cacheable