SpringBoot实践项目

开发工具

插件:

Maven Helper
Free Mybatis Plugin

API工具

PostMan

数据库

mysql

可视化工具

Navicat

JDK

version 1.8

IDE

IDEA 2023.1

技术栈

SpringBoot 2.2.1.RELEASE
Mybatis 3.4.6
Maven 3.6.0
Log4j2 2.12.1

开发步骤

新建SpringBoot项目

直接使用IDEA 选择Spring Initializr 然后选择 Spring Web 就能生成最基本的 springBoot项目

生成逆向文件,这个主要是根据数据库自动生成pojo实体

步骤如下:

  1. 增加依赖 mybatis,mysql-connector (这个其实是数据库配置)
  2. 增加插件 mybatis-generator
  3. 引入配置文件 generatorConfig.xml

引入mybatis

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

将日志替换为log4j2

  1. 首先pom.xml中去除掉原本的log日志,同时引入log4j2依赖
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
  1. resource下新建log4j2.xml配置文件,此处注意配置的日志级别,容易出现不打印sql日志问题

  2. 使用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获取用户
    修改数据
该模块重难点,常见错误
  1. 重难点:统一响应对象(ApiRestResponse),登录状态保持(session),统一异常处理(ExceptionHandler)
  2. 常见错误:响应对象不规范,异常不统一处理

商品目录管理

这个模块主要引入了几个重要的技术点
使用@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());
    }
  1. 引入过滤器,统一拦截需要管理员权限的接口url,然后做鉴权处理

引入过滤器实现重点是声明一个 过滤器实现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);
    }
}

注意点是:依赖注入的时候需要关注,配置的拦截地址需要注意

  1. 引入swagger文档
    引入swagger主要有以下几个步骤:
    ①pom.xml依赖导入:
        <!-- 生成API文档-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </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

  1. 整合Redis自动缓存查询结果
    ① pom.xml增加redis缓存依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </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

⑤邮箱注册登录
⑥JWT使用
⑦excel导入:POI和EasyExcel
⑧图片操作:Thumbnailator

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
课程的实战源码是我在 GitHub 上开源项目 spring-boot-projects 中的其中一个项目代码,目前已有 2300 多个 star,项目截图如下: 由于项目比较受大家欢迎,因此心中就出现了将它做成教学视频的想法,也就是你现在了解的这个课程《SpringBoot入门及前后端分离项目开发》,本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 大部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 项目实践开发。Spring Boot 介绍、前后端分离、API 规范等内容旨在让读者更加熟悉 SpringBoot 及企业开发中需要注意的事项并具有使用 SpringBoot 技术进行基本功能开发的能力;这最后的项目实战为课程的主要部分,我会带着大家实际的开发一个前后端分离的 Spring Boot 实践项目,让大家实际操作并从无到有开发一个线上项目,并学习到一定的开发经验以及其中的开发技巧,旨在让读者具有将 Spring Boot 真正应用于项目开发的能力; 以下为实践项目的页面和功能展示,分别为:登录页面 列表页面(分页功能) 图片上传功能 富文本编辑器整合使用 实践项目的主要功能和页面就是这些,通过项目展示大家也能够感受到,在实际应用开发中的高频次功能都已经实现,稍加修改就可以运用到企业开发中,整个项目的开发模式为前后端分离的模式,即 Spring Boot 提供后端接口,前端页面通过 Ajax 异步调用接口的方式与后端服务器进行交互并将数据填充至页面中,这也是目前企业开发中比较重用的开发模式,希望大家能够了解并且能够实际的上手开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知虚

权当做笔记,打赏随您心意

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值