0%

ssm-practice

Springboot练习

通过atguigu的今日头条项目进行项目驱动式学习
前端已准备

后端开发

要求:后端使用springboot整合mybatis和springmvc来进行简单的增删改查

  1. 导入依赖:
    • springboot启动包,springboot-web项目启动包,mybatis插件,数据库配置启动器springboot-starter-jdbc,druid启动器,mysql驱动类,lombok,aop,test,打包插件
  2. 编写配置类:
    mybatis的配置类可以使用yaml格式或者是properties格式的文件,推荐使用yaml格式的文件,有分层的效果
    [[Tools#yaml|查看Tools中的yaml]]
    菜鸟教程
    具体配置
    # server配置
    server:
      port: 8080
      servlet:
        context-path: / #默认的根路径
    
    # 连接池配置
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          url: jdbc:mysql:///sm_db1
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
    
    # mybatis-plus的配置
    mybatis-plus:
      type-aliases-package: com.atguigu.pojo
      global-config:
        db-config:
          logic-delete-field: isDeleted  #全局逻辑删除
          id-type: auto #直接使用springboot来进行配置,就不需要再加上这个注解了
          table-prefix: news_ # 设置表的前缀
  3. druid兼容文件
    文件名:
    org.springframework.boot.autoconfigure.AutoConfiguration.imports
    内容:
    com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure
  4. 编写启动类main
    疑问: 什么是乐观锁和悲观锁
    配置使用的插件 [[Java#^f13de1]]
    教程
    @SpringBootApplication
    @MapperScan("com.atguigu.mapper")
    public class Main {
    
        public static void main(String[] args) {
            SpringApplication.run(Main.class,args);
        }
    
        //配置mybatis-plus插件
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //分页
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());  //乐观锁
            interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());  //防全局修改和删除
            return interceptor;
        }
    
    }
    
  5. 工具类封装:主要是统一返回结果的类
    结果封装类
/**
 * 全局统一返回结果类
 */
public class Result<T> { //T是要使用的泛型,要在这里声明
    // 返回码
    private Integer code;
    // 返回消息
    private String message;
    // 返回数据
    private T data;
    public Result(){}
    // 返回数据
    //泛型方法,要将 要使用的泛型在返回类型之前进行声明
    protected static <T> Result<T> build(T data) {
        Result<T> result = new Result<T>();
        if (data != null)
            result.setData(data);
        return result;
    }
    public static <T> Result<T> build(T body, Integer code, String message) {
        Result<T> result = build(body);
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
    public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
        Result<T> result = build(body);
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        return result;
    }
    /**
     * 操作成功
     * @param data  baseCategory1List
     * @param <T>
     * @return
     */
    public static<T> Result<T> ok(T data){
        Result<T> result = build(data);
        return build(data, ResultCodeEnum.SUCCESS);
    }
    public Result<T> message(String msg){
        this.setMessage(msg);
        return this;
    }
    public Result<T> code(Integer code){
        this.setCode(code);
        return this;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

解决枚举类
枚举类可以使用 常量(具体的常量内容来进行枚举)

/**
 * 统一返回结果状态信息类
 *
 */
public enum ResultCodeEnum {

    SUCCESS(200,"success"),
    USERNAME_ERROR(501,"usernameError"),
    PASSWORD_ERROR(503,"passwordError"),
    NOTLOGIN(504,"notLogin"),
    USERNAME_USED(505,"userNameUsed");

    private Integer code;
    private String message;
    private ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
    public Integer getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
}

MD5加密工具类

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

@Component
public final class MD5Util {
    public static String encrypt(String strSrc) {
        try {
            char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
                    '9', 'a', 'b', 'c', 'd', 'e', 'f' };
            byte[] bytes = strSrc.getBytes();
//获得m5的实例
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(bytes);
            bytes = md.digest();
            int j = bytes.length;
            char[] chars = new char[j * 2];
            int k = 0;
            for (int i = 0; i < bytes.length; i++) {
                byte b = bytes[i];
                chars[k++] = hexChars[b >>> 4 & 0xf];
                chars[k++] = hexChars[b & 0xf];
            }
            return new String(chars);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException("MD5加密出错!!+" + e);
        }
    }
}
  1. 使用mybatisX插件,选中表之后逆向工程生成实体类和接口(注意自己补充和删减一些注释)
    @Data
    public class User implements Serializable {
        
        @TableId //主键
        private Integer uid;
    
        private String username;
    
        private String userPwd;
    
        private String nickName;
    
        @Version //版本
        private Integer version;
    
        @TableLogic //逻辑删除
        private Integer isDeleted;
    
        private static final long serialVersionUID = 1L;
    }
    补充:
    [[Java#^86b436||逻辑删除]]
  2. 使用jwt来生成[[Web学习#Token是一种令牌,用来识别访问人员的|Token]]
  3. JSON Web Token JWT由三部分组成: header(头部).payload(载荷).signature(签名)
    1. 导入依赖
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
  1. 编写配置

    application.yaml

    #jwt配置
    jwt:
      token:
        tokenExpiration: 120 #有效时间,单位分钟
        tokenSignKey: headline123456  #当前程序签名秘钥 自定义
  2. 导入工具类

    封装jwt技术工具类

    package com.atguigu.utils;
    
    import com.alibaba.druid.util.StringUtils;
    import io.jsonwebtoken.*;
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Data
    @Component
    @ConfigurationProperties(prefix = "jwt.token") //使用这个就可以省略前缀,如果后面的变量名和配置中相同的话就可以自动装配而不用手动装配了
    public class JwtHelper {
    
        private  long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒
        private  String tokenSignKey;  //当前程序签名秘钥
    
        //生成token字符串
        public  String createToken(Long userId) {
            System.out.println("tokenExpiration = " + tokenExpiration);
            System.out.println("tokenSignKey = " + tokenSignKey);
            String token = Jwts.builder()
    
                    .setSubject("YYGH-USER")
                    .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟
                    .claim("userId", userId)
                    .signWith(SignatureAlgorithm.HS512, tokenSignKey)
                    .compressWith(CompressionCodecs.GZIP)
                    .compact();
            return token;
        }
    
        //从token字符串获取userid
        public  Long getUserId(String token) {
            if(StringUtils.isEmpty(token)) return null;
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
            Claims claims = claimsJws.getBody();
            Integer userId = (Integer)claims.get("userId");
            return userId.longValue();
        }
    
    
    
        //判断token是否有效
        public  boolean isExpiration(String token){
            try {
                boolean isExpire = Jwts.parser()
                        .setSigningKey(tokenSignKey)
                        .parseClaimsJws(token)
                        .getBody()
                        .getExpiration().before(new Date());
                //没有过期,有效,返回false
                return isExpire;
            }catch(Exception e) {
                //过期出现异常,返回true
                return true;
            }
        }
    }
    
  3. 编写controller
    知识点:跨域: 例如从不同的服务器或域名获取信息。

时间格式解决

timeformat