springboot项目基本功能实现

概述

使用的是springboot2.x版本。
记录springboot项目中常见的配置和功能实现。

Mybatisplus

官网地址:https://baomidou.com/
使用mybatisplus

导入依赖

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.2</version>
		</dependency>

xml路径配置

mybatisplus默认的路径是:classpath*:/mapper/**/*.xml。
这个路径表示在classpath下寻找mapper目录及其所有子目录下的所有xml文件
根据自己的xml位置去选择是否配置,否则不用配置,使用默认路径即可。

classpath包括两个路径,第一个是src/main/resources,第二个是src/main/java。

classpath*会递归查询类路径的子目录,classpath则不会。
** 表示递归地匹配任意多个子目录。
所以mybatisplus默认会递归查询类路径下的mapper包中所有子目录的xml文件。

需要注意的是,一般情况下我们会把mapper包放在另一个文件夹中,这个时候默认的路径就会找不到xml,需要将路径改成
在这里插入图片描述

xml文件模板

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.StudentMapper">
</mapper>

mapper标签的namespace用来给xml指定关联的mapper接口

Invalid bound statement (not found)的坑

很多人都碰过这个问题。
第一种:打的文件包taget里没有xml
在这里插入图片描述
在pom文件的build标签下指定打包的资源

		<resources>
			<resource>
				<directory>src/main/java</directory>
				<includes>
					<include>**/*.xml</include>
				</includes>
				<filtering>true</filtering>
			</resource>
			<resource>
				<directory>src/main/resources</directory>
				<includes>
					<include>**/*.*</include>
				</includes>
			</resource>
		</resources>

然后用maven的clean把原来的target包删掉,再用下面的install重新打包
在这里插入图片描述
在这里插入图片描述
在新的target中看到了xml
第二种:配置的xml路径有问题。

配置全局字段

@Configuration
public class MybatisPlusConfig implements MetaObjectHandler {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }

    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createdAt", new Timestamp(System.currentTimeMillis()), metaObject);
        System.out.println(metaObject);
        this.setFieldValByName("updateAt", new Timestamp(System.currentTimeMillis()), metaObject);
        System.out.println(metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateAt", new Timestamp(System.currentTimeMillis()), metaObject);
        System.out.println(metaObject);
    }

}

主键生成策略

mybatisplus的主键生成方式有以下几种:
1 主键自增,但数据库里面也要设置成自增
2 …
可查看这篇blog:小书MybatisPlus第6篇-主键生成策略精讲
具体看下面源码

@Getter
public enum IdType {
    /**
     * 数据库ID自增
     * <p>该类型请确保数据库设置了 ID自增 否则无效</p>
     */
    AUTO(0),
    /**
     * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
     */
    NONE(1),
    /**
     * 用户输入ID
     * <p>该类型可以通过自己注册自动填充插件进行填充</p>
     */
    INPUT(2),

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 分配ID (主键类型为number或string),
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
     *
     * @since 3.3.0
     */
    ASSIGN_ID(3),
    /**
     * 分配UUID (主键类型为 string)
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
     */
    ASSIGN_UUID(4);

    private final int key;

    IdType(int key) {
        this.key = key;
    }
}

字段的全局配置

mybatis-plus:
  global-config:
    db-config:
      # 主键自增
      id-type: auto
      # 逻辑删除使用的字段
      logic-delete-field: deleted
      # 没删用0表示
      logic-not-delete-value: 0
      # 删了用1表示
      logic-delete-value: 1

逻辑删除

局部:
使用Mybatisplus的BaseMapper的deleteById
全局:

mybatis-plus:
  global-config:
    db-config:
      # 逻辑删除使用的字段
      logic-delete-field: deleted
      # 没删用0表示
      logic-not-delete-value: 0
      # 删了用1表示
      logic-delete-value: 1

再在相应的字段使用@TableLogic

统一数据返回体

@Data
@NoArgsConstructor
public class ResponseResult<T> {
    private Integer code;
    private String message;
    private T data;

    private static <T> ResponseResult<T> buildResponse(Integer code, String message, T data){
        ResponseResult<T> response = new ResponseResult<>();
        response.setCode(code);
        response.setMessage(message);
        response.setData(data);
        return response;
    }

    public static <T> ResponseResult<T> success(T data){
        return buildResponse(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getMsg(), data);
    }

    public static <T> ResponseResult<T> success(Integer code, String message, T data){
        return buildResponse(code, message, data);
    }

    public static <T> ResponseResult<T> error(){
        return buildResponse(ResponseEnum.SERVICE_ERROR.getCode(), ResponseEnum.SERVICE_ERROR.getMsg(), null);
    }

    public static <T> ResponseResult<T> error(Integer code, String message, T data){
        return buildResponse(code, message, data);
    }


}
@Getter
@AllArgsConstructor
public enum ResponseEnum {
    /**
     * 响应的枚举类
     */
    SUCCESS(200, "响应成功"),
    SERVICE_ERROR(500, "服务端响应异常");

    private final Integer code;

    private final String msg;
}

统一异常处理

在这里可以通过@ExceptionHandler处理指定的异常
例子1:

@Slf4j
@RestControllerAdvice(basePackages = "com.zou.metabox")
public class ExceptionControllerAdvice {

    /**
     * 处理验证异常的方法
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseResult<Map<String,String>> handlerValidExecption(MethodArgumentNotValidException e){
        Map<String,String> map = new HashMap<>();
        e.getFieldErrors().forEach(fieldError-> map.put(fieldError.getField(),fieldError.getDefaultMessage()));
        return ResponseResult.error(BusinessCodeEnum.VALID_EXCEPTION.getCode(), BusinessCodeEnum.VALID_EXCEPTION.getMsg(), map);
    }

    /**
     * 系统其他的异常处理
     */
    @ExceptionHandler(Throwable.class)
    public ResponseResult<String> handlerExecption(Throwable throwable){
        log.error("错误信息:",throwable);
        return ResponseResult.error(BusinessCodeEnum.UNKNOW_EXCEPTION.getCode(), BusinessCodeEnum.UNKNOW_EXCEPTION.getMsg(), throwable.getMessage());
    }

	 /**
     * 指定异常去处理,这里处理了自己定义的异常
     */
    @ExceptionHandler(value = MyException.class)
    public ResponseResult<Map<String, String>> handlerMyException(MyException e){
        return ResponseResult.error(BusinessCodeEnum.MYEXCEPTION.getCode(), BusinessCodeEnum.MYEXCEPTION.getMsg(), null);
    }
}

在这里插入图片描述
例子2:

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.ArrayList;
import java.util.List;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ValidationErrorResponse handleValidationException(MethodArgumentNotValidException ex) {
        List<String> details = new ArrayList<>();
        for (FieldError error : ex.getBindingResult().getFieldErrors()) {
            details.add(error.getField() + ": " + error.getDefaultMessage());
        }
        return new ValidationErrorResponse("Validation failed", details);
    }
}

public class ValidationErrorResponse {

    private String message;
    private List<String> details;

    // constructors, getters, and setters
}

自定义异常

参数校验

在Spring Boot中,可以使用JSR-303标准的注解来进行接口参数校验。以下是一些常用的注解:
@NotNull:用于验证参数不能为null。
@NotEmpty:用于验证字符串参数不能为空。
@NotBlank:用于验证字符串参数不能为空且长度必须大于0。
@Min:用于验证数字参数的最小值。
@Max:用于验证数字参数的最大值。
@Size:用于验证字符串、集合或数组参数的长度或大小。
@Pattern:用于验证字符串参数是否匹配指定的正则表达式。
要在接口方法中使用参数校验,可以在方法参数上添加相应的注解。

日志

设置日志级别

在application.yml中设置

logging:
  level:
    root: INFO
    com.zou.metabox.mapper: DEBUG

AOP日志

使用aop做日志记录,记录输入的参数名及参数值,并且记录接口响应结果。

package com.zou.metabox.common.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;

/**
 * @author BIGSHU0923
 * @description com.zou.metabox 中Controller层的的日志切面
 * @since 7/30/2023  5:32 PM
 */
@Aspect
@Component
@Slf4j
public class LoggingAspect {
    /**
     * com.zou.metabox.controller 包中公共方法的切入点
     */
    @Pointcut("execution(public * com.zou.metabox.controller.*.*(..))")
    public void loggingPointcut(){
        // 暂不用处理
    }
	
	/**
	 * 此方法用于在日志中记录请求和返回信息。
	 *
	 * @param pjp ProceedingJoinPoint对象,用于执行目标方法
	 * @return 目标方法的返回结果
	 * @throws Throwable 抛出异常时,将它传递给调用方
	 */
	@Around("loggingPointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 获取类名
        String className = pjp.getTarget().getClass().getTypeName();
        // 获取方法名
        String methodName = pjp.getSignature().getName();
        // 获取参数名
        String[] parameterNames = ((MethodSignature) pjp.getSignature()).getParameterNames();

        Object result = null;
        // 获取参数值
        Object[] args = pjp.getArgs();

        // 获取请求
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 获取请求的url
        String url = request.getRequestURL().toString();

        // 请求参数,以参数名和值为键值对
        Map<String, Object> paramMap = new HashMap<>();
        IntStream.range(0, parameterNames.length).forEach(i -> paramMap.put(parameterNames[i], args[i]));

        // header参数
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, Object> headerMap = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headerMap.put(headerName, headerValue);
        }

        // 打印请求参数,记录起始时间
        long start = System.currentTimeMillis();
//        log.info("请求| 请求接口:{} | 类名:{} | 方法:{} | header参数:{} | 参数:{} | 请求时间:{}", url, className, methodName, headerMap, paramMap, LocalDateTime.now());
        log.info("请求| 请求接口:{} | 类名:{} | 方法:{} | 参数:{} | 请求时间:{}", url, className, methodName, paramMap, LocalDateTime.now());
        try {
            result = pjp.proceed();
        } catch (Exception e) {
            log.error("返回| 处理时间:{} 毫秒 | 返回结果 :{}", (System.currentTimeMillis() - start), "failed");
            throw e;
        }

        // 获取执行完的时间 打印返回报文
        log.info("返回| 处理时间:{} 毫秒 | 返回结果 :{}", (System.currentTimeMillis() - start), "success");
        return result;
    }
}

接口请求的结果
在这里插入图片描述

Springboot事务

单数据源

配置单数据源

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/xinyan
    port: 3306
    username: root
    password: root

使用事务

代码:
下面这代码会更新,但是传入的id>5时,会抛出个异常,如果没有加事务,则不会回滚。

    public void testRollback(Long id) {
        MyRecord myRecord = new MyRecord();
        myRecord.setId(id);
        myRecord.setRecord("66666");
        myRecordMapper.updateRecord(myRecord);
        if(id > 5){
            int a = 10/0;
        }
    }

使用事务前,使用事务后。

    @Transactional(rollbackFor = Exception.class)
    public void testRollback(Long id) {
        MyRecord myRecord = new MyRecord();
        myRecord.setId(id);
        myRecord.setRecord("66666");
        myRecordMapper.updateRecord(myRecord);
        if(id > 5){
            int a = 10/0;
        }
    }

如果抛异常后,会回滚更改。

多数据源

引入依赖

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
			<version>3.4.1</version>
		</dependency>

配置多数据源

spring:
  datasource:
    dynamic:
      primary: master
      strict: true
      lazy: true
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/xinyan
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql://localhost:3306/metabox
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver

使用事务

    @DSTransactional
//    @Transactional(rollbackFor = Exception.class)
    @DS("slave")
    public void testRollback2(Integer id){
        Student student = new Student();
        student.setId(id);
        student.setAge(100);
        studentMapper.updateStudent(student);
        if(id == 3){
            int a = 10/0;
        }
    }

多数据源,所以使用@DSTransactional才有用,使用@Transactional(rollbackFor = Exception.class)没用

Springboot拦截器

概念

拦截器是基于Java反射机制实现的一种AOP思想,它可以在请求到达Controller层之前和之后对请求进行拦截。Spring Boot中使用HandlerInterceptor接口定义拦截器,在实现类中重写预定义好的方法来实现自己的业务逻辑。
拦截器的使用步骤如下:

1 创建一个实现了 HandlerInterceptor 接口的类,并实现该接口所需要的三个方法:preHandle(), postHandle() 和 afterCompletion()。
2 配置拦截器。在 Web MVC 配置文件中配置拦截器的 Bean。
3 定义拦截规则和拦截顺序。

可以通过以下示例来了解如何编写和配置拦截器:

public class MyInterceptor implements HandlerInterceptor {
    // 在请求处理之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor: preHandle method is running");
        return true;
    }

    // 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor: postHandle method is running");
    }

    // 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行。主要用于进行资源清理工作
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("Interceptor: afterCompletion method is running");
    }
}

然后在配置类中添加 InterceptorRegistration 来注册拦截器,并指定其需要拦截的路径。addPathPatterns() 方法用于设置拦截的路径,而 excludePathPatterns() 方法用于设置不拦截的路径。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private MyInterceptor interceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration registration = registry.addInterceptor(interceptor);
        registration.addPathPatterns("/**"); //需要拦截的路径
        registration.excludePathPatterns("/login"); //不需要拦截的路径
    }
}

Springboot监听器

概念

过滤器是Servlet中的组件,它可以对HTTP请求进行过滤处理。在 Spring Boot 中使用Filter接口来实现过滤器,在实现类中重写 doFilter() 方法以实现自己的业务逻辑。

过滤器的使用步骤如下:

创建一个实现了 Filter 接口的类,并实现 doFilter() 方法。
在 Configuration 类中注册该 Filter。
下面是一个简单的过滤器示例:

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter: init method is running ");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter: doFilter method is running");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("Filter: destroy method is running");
    }
}

然后在配置类中添加 FilterRegistrationBean 来注册过滤器。

@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean myFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new MyFilter());
        registration.addUrlPatterns("/*");
        registration.setName("MyFilter");
        registration.setOrder(1);
        return registration;
    }
}

Springboot控制器

控制器是Spring MVC中的组件,它可以接收用户的HTTP请求,并将请求交给相应的Service进行业务处理,最终返回客户端所需的结果。在 Spring Boot 中,我们通常使用 @Controller 或 @RestController 注解来标注我们的控制器类。

下面是一个简单的控制器示例:

@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.getUsers();
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }

    @PutMapping("/users/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }

    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

在上述示例中,我们定义了一个 UserController 类,并使用 @RestController 注解标注它。我们使用 @RequestMapping 注解来指定该控制器的路由前缀,进而可以通过 /api/users、/api/users/{id} 等 URL 访问到对应的控制器方法。

Springboot swagger

导入依赖

		<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>

创建配置类

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.zou.metabox.controller"))
                .paths(PathSelectors.any())
                .build();
    }


    /**
     * 构建api文档的详细信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                // 设置页面标题
                .title("Spring Boot集成Swagger2接口总览")
                // 设置接口描述
                .description("metabox")
                // 设置联系方式
                .contact("123456")
                // 设置版本
                .version("1.0")
                // 构建
                .build();
    }
}

启动服务,然后输入http://localhost:8001/metaBox/swagger-ui.html
在这里插入图片描述

后面的/swagger-ui.html是固定的,前面的http://localhost:8001/metaBox则根据自己项目配置的端口号和上下文路径去决定。我的项目配置的端口号是8001,上下文路径是/metaBox

cors跨域配置

@Configuration
public class CorsWebFilterConfig {
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedOrigin("http://localhost:8080");
        corsConfiguration.addAllowedOrigin("http://localhost:8082");
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); // 4
        return new CorsFilter(source);
    }

    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
                argumentResolvers.add(new UserIdArgumentResolver());
            }
        };
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Spring Boot 项目实现登录功能,你可以按照以下步骤进行操作: 1. 创建用户实体类:创建一个表示用户的实体类,包含用户名和密码等必要属性。 2. 创建用户存储库:创建一个用于操作用户数据的存储库接口,可以使用 Spring Data JPA 或者其他持久化框架。 3. 创建登录表单:创建一个登录表单,包含用户名和密码的输入字段。 4. 创建登录控制器:创建一个用于处理登录请求的控制器,可以使用 `@Controller` 或 `@RestController` 注解。 5. 实现用户认证:使用 Spring Security 或者其他认证框架来实现用户的认证和授权功能。你可以使用基于表单的认证方式,将用户输入的用户名和密码与存储库中的用户数据进行比对。 6. 处理登录请求:在登录控制器中处理用户提交的登录请求,验证用户输入的用户名和密码是否正确。如果验证通过,可以生成一个认证令牌并将其返回给客户端。 7. 验证登录状态:在需要验证登录状态的接口或页面中,使用拦截器、过滤器或注解来验证用户的登录状态。如果用户未登录,则需要进行相应的处理,例如跳转到登录页面或返回错误信息。 8. 实现注销功能(可选):如果需要实现注销功能,可以创建一个注销控制器或接口,用于处理用户的注销请求。 以上是一个基本的登录功能的实现步骤,具体的实现方式和代码结构可能会因项目的需求和架构而有所不同。你可以参考 Spring Boot 官方文档和相关的教程来详细了解和实践这些步骤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值