springboot框架重构oa项目

重构

【链接暂时未有】:代表以后需要配置链接,关联起来

从头开始重构,刚开始学java,有过nodejs工作经验,开发环境Mac

JDK1.8: CHM文档:链接: https://pan.baidu.com/s/1dK1ZEk2aAvmAypE8ymidVQ   密码:30hh【阅读chm:下载chm reader。乱码问题:https://blog.csdn.net/qq_37193694/article/details/79352748 】

  1. 项目目录

参考:https://blog.csdn.net/u012675150/article/details/79351990 

ssm框架目录:https://blog.csdn.net/qq598535550/article/details/51703190 

    1. Cache
      1.  
    2. Config:配置【必须】
      1.  
    3. Controller:控制层【必须】
      1. Controler负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面。【不需要处理逻辑】参考:https://blog.csdn.net/qq_22771739/article/details/82344336 
      2.  
    4. Dao【必须】
      1. 概念:是sun的一个标准j2ee设计模式的接口之一,负责持久层的操作
      2. DAO层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表的增删改查,也就是说某个DAO一定是和数据库的某一张表一一对应的,其中封装了增删改查基本操作,建议DAO只做原子操作,增删改查。【查找数据】
      3. 详细解释:https://www.runoob.com/note/27029 【具体可以查看链接里面的栗子,即可清楚明了的知道Dao】
        1. 组成
          1. DAO接口: 把对数据库的所有操作定义成抽象方法,可以提供多种实现。【对应链接中的petDao】
          2. DAO 实现类: 针对不同数据库给出DAO接口定义方法的具体实现。【对应链接中的petDaoImpl】
          3. 实体类:用于存放与传输对象数据。【对应链接中的Pet】
          4. 数据库连接和关闭工具类: 避免了数据库连接和关闭代码的重复使用,方便修改。【对应链接中的BaseDao】
        2. 个人理解:根据b)组成,可以得出Dao就是将数据库的链接与关闭封装成一个类BaseDao。然后创建这个数据库表(模型)的Pet类,然后定义这个PetDao的接口,接口中定义我们需要用到的方法,然后在实现类PetDaoImpl一一实现这些方法。要记住这个类要继承BaseDao,使我们可以调用连接关闭数据库的方法。完成逻辑操作。
    5. Service:逻辑层【必须】
      1. Service层叫服务层,被称为服务,粗略的理解就是对一个或多个DAO进行的再次封装,封装成一个服务,所以这里也就不会是一个原子操作了,需要事物控制。【处理逻辑】
      2.  
    6. Util:工具类
      1.  
    7. VO:数据传输层【必须】
    8. Constant:常量接口类
      1. 参考资料:https://www.v2ex.com/t/502085 
    9. Interceptor:拦截器
      1. 拦截层
    10. entity【模型实体类】:
      1. 简介:对应数据库表的pojo
      1.  介绍:https://blog.csdn.net/qijingwang/article/details/80353829,实际就是和数据库模型的表明表字段一一对应的一个类【并且只能对应数据库字段】
        1. 一、实体类的名字尽量和数据库的表的名字对应相同。
        2. 二、实体类应该实现java.io.Serializable接口。
        3. 三、实体类应该有个无参的构造方法。
        4. 四、实体类应该有个有参(所有的参数)的构造方法。
        5. 五、实体类有属性和方法,属性对应数据库中表的字段,主要有getter和setter方法。
        6. 六、实体类还应该有个属性serialVersionUID。例如:private static final long serialVersionUID = -6125297654796395674L; 
      2. 序列化:https://blog.csdn.net/so_geili/article/details/78931742 , 详细梳理:https://blog.csdn.net/so_geili/article/details/99836043 
      3. VO,PO,POJO: https://blog.csdn.net/jikefzz1095377498/article/details/79237618 
        springboot框架重构oa项目_第1张图片

仅供参考

        1. VO: value object值对象 ,他可以和表对应,也可以不用,取决于业务的需要和复杂程度。通常用于业务层之间数据的传递,也是用于包含数据。view object表现层对象,主要对应页面显示的数据对象,用VO来封装页面需要展示的数据对象。
          1. 详细解释:
          2. 使用范围:活动范围要控制在service层、controller层、展现层(view)中
          3. 业务层之间数据的传递:就是说我们内部调用我们的方法时需要传递参数时,比较复杂的对象时,可以封装成一个类,然后这个类存储我们所需要传递的参数,这就是一个VO对象。或者:代表值对象的意思,通常用于业务层之间的数据传递,由new创建,由GC回收。
          4. view object表现层对象:就是说们当要给app端或者web端传递数据时,可以封装一个类,然后用来传递参数。这也是一个VO对象。或者:对于一个WEB页面将整个页面的属性封装成一个对象,然后用一个VO对象在控制层与视图层进行传输交换。
          5. 使用方法:https://blog.csdn.net/G0_hw/article/details/78326359 
        2. PO:Persistence Object 持久化对象
          1. 详细解释
          2. 使用范围:dao层和数据库(数据库直接和PO交互),而不能够出现在service层、controller层、展现层(view)中
          3. 概念:在 O/R 映射的时候出现的概念,如果没有 O/R 映射,没有这个概念存在了。【原因暂时不明白】
          4. 代表持久层对象的意思,对应数据库中表的字段
          5. 详细1:通常对应数据模型 ( 数据库 ), 本身还有部分业务逻辑的处理。可以看成是与数据库中的表相映射的 Java 对象。最简单的 PO 就是对应数据库中某个表中的一条记录,多个记录可以用 PO 的集合。里面除了私有的成员变量之外,就只有其对应的set/get方法。
          6. VO和PO为什么分开使用

首先PO是持久化类,其属性的改变很有可能直接导致数据库中的数据变化,而不知道原因(为什么我的数据库中的数据变化了?)。引入了VO之后可以很好的解决类似的问题,甚至会很好的帮你解决页面(JSP,freemarker,asp,aspx)和控制层的直接便利的交互,而不用担心其各种属性的变化会不会导致数据库中数据的变化,这对于使用hibernate之后控制其操作数据时出现的持久化、瞬态、脱管都是有很大好处的。

        1. BO (business object) 业务对象
          1. Bo就是把业务逻辑封装为一个对象(注意是逻辑,业务逻辑),这个对象可以包括一个或多个其它的对象。通过调用Dao方法,结合Po或Vo进行业务操作。
          2. 形象描述为一个对象的形为和动作,当然也有涉及到基它对象的一些形为和动作。比如处理一个人的业务逻辑,该人会睡觉,吃饭,工作,上班等等行为,还有可能和别人发关系的行为,处理这样的业务逻辑时,我们就可以针对BO去处理。
          3. 再比如投保人是一个Po,被保险人是一个Po,险种信息也是一个Po等等,他们组合起来就是一张保单的Bo。
        2. Dto:
          1. 拓展名:Data Transfer Object 数据传输对象 
          2. 概念:主要用于远程调用等需要大量传输对象的地方。
          3. 优点:提高数据传输的速度(减少了传输字段),二能隐藏后端表结构 
          4. 详解:
            1. 比如我们一张表有 100 个字段,那么对应的 PO 就有 100 个属性。 但是我们界面上只要显示 10 个字段, 客户端用 WEB service 来获取数据,没有必要把整个 PO 对象传递到客户端, 这时我们就可以用只有这 10 个属性的 DTO 来传递结果到客户端,这样也不会暴露服务端表结构 . 到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为 VO
          5. 与Dao区别:
            1. DAO :数据访问对象 —— 同时还有 DAO 模式
            2. DTO :数据传输对象 —— 同时还有 DTO 模式
        3. POJO
          1. 拓展名:plain ordinary java object 简单无规则 java 对象
          2. 纯的传统意义的 java 对象。没有增加别的属性和方法只有属性字段及 setter 和 getter 方法

 

  1. 正式开始
  1. 创建初始目录结构
    1. 如果是mongo数据库,基本只需要Config/Controller/
      1. Config
      2. dao
      3. Controller
      4. Service
      5. Util
      6. Constant
      7. Vo
      8. Entity
      9. Dto
      10. Interceptor【非必须】
      11. annotations【注解接口层】
        1. Impl【注解接口实现层】
      12. handler【消息统一处理层】
      13. group:【validated校验分组层】
      14.  

b) 设置注释格式

https://www.cnblogs.com/pcheng/p/10121683.html 记得在两种注释上加上@description, 【简介的意思】

c) gitlab上传

  1. gitlab中新建项目
    1. 进入首页:创建一个新项目
    2. Initialize repository with a README。勾选选项的意思是,可以初始化项目,不用我们创建项目之后自己再手动添加一个文件进行初始化了。建议勾选
  1. 克隆项目
    1. 将新建项目完成时页面上的http网址进行复制:例如【http://123.170.121.225:1234/root/project】这种格式的网址
    2. 访问我们部署gitlab服务器的网址:xx.xx.xxx.xxx: port在我们自己服务器的gitlab上注册账号
    3. 登陆项目的创建人的账户,然后选择我们需要克隆的项目进入,然后点击设置-->成员,将刚注册的成员账户加入到我们的项目,并选择到期日期
      springboot框架重构oa项目_第2张图片springboot框架重构oa项目_第3张图片
    4. 然后通过sourceTree软件的新建--》从URL克隆, 然后输入我们第一步记录下的网址【http://123.170.121.225:1234/root/project】,依次输入我们新注册的密码,最后点击克隆

springboot框架重构oa项目_第4张图片

  1. 将手头项目进度添加到gitlab中
    1. 点击此按钮,找到克隆在本地的源文件夹
       
    2. 在我们克隆的本地的源文件夹添加一个.gitignore忽略文件【记住这个文件一定要在最开始进行添加,添加完毕之后再将我们本地项目复制进去】
      1. 内容如下,如需忽略其他格式请自己手动继续添加
# Maven #
target/

# IDEA #
.idea/
*.iml

# Eclipse #
.settings/
.classpath
.project
      1. 然后将文件提交,并推送到远程分支

c) 将我们当前项目的文件夹下的所有文件原封不动的复制过来,然后正常上传即可

d) 建议新建一个自己的分支,不要直接推送到master生产分支上,或者如果项目比较大,开发人员较多,建议同时有一个生产分支,一个测试分支,和多个开发人员自己的分支

【参考资料:https://cloud.tencent.com/developer/article/1496155 】

d) 数据库

      1. 数据库建表
        1. 工具建表:
          1. mysql
            1. Window:PowerDesigner
            2. Mac:
        2. 手动建表
          1. Mysql
            1. 通过代码动态建表
            2. 直接在navicat上创建
          2. Mongo
            1. 直接通过代码建表就好了,调用springboot自带的mongo。:https://spring.io/projects/spring-data-mongodb#learn  lear->x.x.x CURRENT GA->后面两个选项
            2. 语句实例说明:https://www.cnblogs.com/xiaoqi/p/queries-in-spring-data-mongodb.html 
              https://blog.csdn.net/sinat_35821285/article/details/83511203 
              is

Query query = new Query();

query.addCriteria(Criteria.where("name").is("Eric"));

List users = mongoTemplate.find(query, User.class);

Regex

Query query = new Query();

// 以A开头

query.addCriteria(Criteria.where("name").regex("^A"));

// 以c结尾

query.addCriteria(Criteria.where("name").regex("c$"));

List users = mongoTemplate.find(query,User.class);

LT(小于)和GT(大于)

Query query = new Query();

query.addCriteria(Criteria.where("age").lt(50).gt(20));

List users = mongoTemplate.find(query,User.class);

  排序

Query query = new Query();

query.with(new Sort(Sort.Direction.ASC, "age"));

List users = mongoTemplate.find(query,User.class);

分页

int page = (int)map.get("page");
int pageNumber = (int)map.get("pageNumber");
final Pageable pageableRequest = PageRequest.of(page - 1, pageNumber);
Query query = new Query();
query.with(pageableRequest);
List userList = mongoTemplate.find(query, User.class);

 

 

 

 

  1. 数据库实践篇
    1. 建立一个测试流程,在mongo中添加一个测试数据。 参考资料:https://blog.csdn.net/qq_33619378/article/details/81544711#commentBox 
    2. 创建过程中:需要引入的插件:
      1. 第一个插件可以通过在idea上创建的时候直接安装
      2. 第二个插件有时候会出现与idea与其不兼容,导致失效的问题。
        1. 解决办法:在本地的pom.xml中配置好文件并引入lombok插件后。然后通过此处https://jingyan.baidu.com/article/597a0643a51fac712b5243c7.html 安装好idea的lombok插件,重启后即可正常调用.

 


    
    org.springframework.boot
    spring-boot-starter-data-mongodb


    
    org.projectlombok
    lombok
    1.16.18
        1. 数据库表的名字与entity中实体类的名字是一样的,只是表明首字母小写
        2. 中途出现情况:
          1. post请求,时请求不通
            1. 错误信息: error"error":"Unsupported Media Type",    "message":"Content type 'application/octet-stream' not supported"
            2. 解决:请求头部设置,Content-type:application/json
          2. 请求接口成功,但无法添加数据
            1. 错误信息: "error":"Internal Server Error",  Command failed with error 13 (Unauthorized): 'command update requires authentication' on server 。。。。
            2. 解决:application.xxxx配置文件中,配置数据库信息不正确。一般是由于压根没有配置数据库信息,或者配置的数据库没有设置账户名和密码
    1. 在db/mysql.sql中创建多张表的模型
    2. 在entity中创建实体类

e) 接口测试

  1. 使用软件eolinker
  2. 下载地址:https://www.eolinker.com/pc/ 
  3. 新建工作空间
    springboot框架重构oa项目_第5张图片
  4. 正常使用即可,如需添加其他人员,再另行添加

f) 三种环境的配置

参考资料:https://blog.csdn.net/qq_35139965/article/details/82255479 

强烈建议使用资料中的第三种方式

mvn的配置方法详细见 Intellij idea 与 Maven【链接暂时未有】

搭配mvn,生成可通过java -jar 包名 --spring.profiles.active=prod这种方式决定环境的选择【但是记住每次实际有了修改,需要重新打包然后选择环境重启】

代码技术

jwt权限校验

参考资料:https://blog.csdn.net/ljk126wy/article/details/82751787 

关于token刷新问题:https://www.cnblogs.com/minirice/p/9232355.html 一般后端永久就可以

https://www.jianshu.com/p/58f05bf13b7d 但是如果后端定义token有过期时间,前端通过后端返回的401错误码(后端自己定义的token过期返回的错误码)来决定刷新token,所以刷新token基本是前端工作

  1. 参考资料:https://www.jianshu.com/p/e88d3f8151db  API资料:https://github.com/auth0/java-jwt 
  2. 添加jwt插件

    com.auth0
    java-jwt
    3.4.0

ncy>

3. 资料中的@passToken相当于跳过校验注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

4. @UserLoginToken相当于需要进行用户token校验的注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
    boolean required() default true;
}

5. 将配置添加到容器进行管理

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 如果有此注解进行拦截【是根据judgeToken里preHandle方法中写的三个if判断来进行选择拦截的】
    }
    @Bean
    public JudgeToken authenticationInterceptor() {
        return new JudgeToken();
    }
}

6. 在拦截器中写校验逻辑

public class JudgeToken implements HandlerInterceptor {

    @Autowired
    UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String phone;
                try {
                    phone = JWT.decode(token).getAudience().get(0);
                    System.out.println(phone);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                User user = userService.findUserByPhone(phone);
                if (user == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
    }
}

7. 创建生成token的方法

@Service("TokenService")
public class TokenService {


    public String getToken(User user) {
        String token = "";
        token = JWT.create().withAudience(user.getPhone())
                .sign(Algorithm.HMAC256(user.getPassword()));
        return token;
    }
}

8. 需要配合写出User表,以及service层方法,才可进行校验

汉字转拼音

参考:https://blog.csdn.net/sky_limitless/article/details/79443540 

插件:


    com.belerweb
    pinyin4j
    2.5.0

cy>

// 将汉字转换为全拼  
    public static String getPingYin(String src) {  
  
        char[] t1 = null;  
        t1 = src.toCharArray();  
        String[] t2 = new String[t1.length];  
        HanyuPinyinOutputFormat t3 = new HanyuPinyinOutputFormat();  
  
        t3.setCaseType(HanyuPinyinCaseType.LOWERCASE);  
        t3.setToneType(HanyuPinyinToneType.WITHOUT_TONE);  
        t3.setVCharType(HanyuPinyinVCharType.WITH_V);  
        String t4 = "";  
        int t0 = t1.length;  
        try {  
            for (int i = 0; i < t0; i++) {  
                // 判断是否为汉字字符  
                if (java.lang.Character.toString(t1[i]).matches(  
                        "[\\u4E00-\\u9FA5]+")) {  
                    t2 = PinyinHelper.toHanyuPinyinStringArray(t1[i], t3);  
                    t4 += t2[0];  
                } else  
                    t4 += java.lang.Character.toString(t1[i]);  
            }  
            // System.out.println(t4);  
            return t4;  
        } catch (BadHanyuPinyinOutputFormatCombination e1) {  
            e1.printStackTrace();  
        }  
        return t4;  
    }  
  
    // 返回中文的首字母  
    public static String getPinYinHeadChar(String str) {  
  
        String convert = "";  
        for (int j = 0; j < str.length(); j++) {  
            char word = str.charAt(j);  
            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);  
            if (pinyinArray != null) {  
                convert += pinyinArray[0].charAt(0);  
            } else {  
                convert += word;  
            }  
        }  
        return convert;  
    }  
  
    // 返回第一个中文首字母大写  
    public static String getFirstPinYinHeadChar(String str) {  
  
        String convert = "";  
        char word = str.charAt(0);  
        String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);  
        if (pinyinArray != null) {  
            convert += pinyinArray[0].charAt(0);  
        } else {  
            convert += word;  
        }  
        return convert.toUpperCase();  
    }  
  
    // 将字符串转移为ASCII码  
    public static String getCnASCII(String cnStr) {  
        StringBuffer strBuf = new StringBuffer();  
        byte[] bGBK = cnStr.getBytes();  
        for (int i = 0; i < bGBK.length; i++) {  
            strBuf.append(Integer.toHexString(bGBK[i] & 0xff));  
        }  
        return strBuf.toString();  
    }

参数校验

参考:https://www.jianshu.com/p/3536243e9a5a https://www.cnblogs.com/jpfss/p/10937031.html 

多种校验方法:https://www.cnblogs.com/cjsblog/p/8946768.html 

参考:https://blog.csdn.net/wangjiangongchn/article/details/86477386 【分组校验/嵌套校验】

分组校验简单说明:分组校验就是在你路由上标明所属检验的分组,然后如果此分组存在于实体类上某属性groups所包含的校验分组中,那么就会对其进行校验,反之则不会

嵌套校验简单说明:传入a对象中,包含了另外一个b对象,如果在a对象的实体类中没有对b属性标注@Valid注解,将不会对b对象中的校验规则进行校验

 

通过第一种基本就可以实现大部分校验

 

  1. 通过springboot原生javax进行校验
    1. 校验实体类:在实体类的属性上【注意:光写了校验规则没用,需要配合注解@Validated使用】
      配置校验规则注解栗子:

@NotEmpty(message = "号码不允许为空", groups = {AddGroup.class, UpdateGroup.class})
@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",springboot框架重构oa项目_第6张图片 message = "号码格式不正确")

 

配置开启校验注解栗子:
 

@RestController
@Validated		----》 代表所有路由都需要通过校验[一般不会直接加在这上面]
public class UserController {
@PostMapping("/organziation/user/save")
							!————————————》代表只有此路由开启校验功能【上面的@Validated开启时,无需再次配置此处】
							|—————————————》此处括号中可以添加接口的.class,来进行分组校验
public String saveObj(@RequestBody @Validated(AddGroup.class) User user) { return userService.createUser(user); }
    1. 自定义校验实体类规则:

分组与嵌套的规则与上面的一样

注解层文件夹【annotations】

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
//用于校验手机号的逻辑类
@Constraint(validatedBy = PhoneValidator.class)
public @interface PhoneValidate {
    //手机号的校验格式
    String regexp() default "^1(3|4|5|7|8)\\d{9}$";

    //出现错误返回的信息
    String message() default "手机号格式错误";

    Class[] groups() default { };

    Class[] payload() default { };

    //@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE })
    //@Retention(RetentionPolicy.RUNTIME)
    //@Documented
    //public @interface List {
    //    PhoneValidate[] value();
    //}
}

注解层文件夹中的实现【annotations/impl】

public class PhoneValidator implements ConstraintValidator {

    private String regexp;

    //初始化方法
    @Override
    public void initialize(PhoneValidate constraintAnnotation) {
        //获取校验的手机号的格式
        this.regexp = constraintAnnotation.regexp();
    }

    //value是@Phone注解所注解的字段值
    //校验,返回true则通过校验,返回false则校验失败,错误信息为注解中的message
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(value == null){
            return true;
        }

        return value.matches(regexp);
    }
}

调用:通过在实体类中的某个属性上配置此注解即可 @PhoneValidate.  栗子如下:

@PhoneValidate
private String phone;
    1. 校验传过来的不规则map对象
      1. 可以直接通过if ()语句判断

2. 定义不规则对象的实体类,用上面的规则进行校验

    1. 自定义校验工具类【自己暂时不太明白,暂时先会写就行】

异常捕获:

springboot框架重构oa项目_第7张图片

  1. 单一捕获一个路由的异常
@PostMapping("/organziation/user/save")
public String saveObj(@Validated(AddGroup.class) @RequestBody User user, BindingResult bindingResult) {
    if(bindingResult.hasErrors()){
        throw new RuntimeException(bindingResult.getFieldError().getDefaultMessage());
    }
    return userService.createUser(user);
}
  1. 统一捕获异常消息
    1. 参考资料:https://www.jianshu.com/p/3536243e9a5a 
    2. 新建一个错误信息类


/**
 * @author wangrui
 * @description:
 * @date 2019/12/27 上午11:52
 */
@Data
public class ErrorInfo implements Serializable {
    private static final long serialVersionUID = -1946193220290386110L;

    public static final boolean SUCCESS = true;
    public static final boolean FAIL = false;

    private boolean status;
    private int code;
    private String msg;
    private Object data;

    /**
     *
     * @param status 状态
     * @param code 状态码
     * @param msg 消息
     * @param data 返回数据
     */
    public ErrorInfo(boolean status, int code, String msg, Object data) {
        this.status = status;
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static ErrorInfo success(Object data) {
        return new ErrorInfo(SUCCESS, 200, "ok", data);
    }

    public static ErrorInfo success(String msg) {
        return new ErrorInfo(SUCCESS, 200, msg, null);
    }
    // 直接返回400
    public static ErrorInfo fail(String msg) {
        return fail(400, msg);
    }
    // 返回带状态码的消息
    public static ErrorInfo fail(int code, String msg) {
        return new ErrorInfo(FAIL, code, msg, null);
    }
    1. 在handler文件夹中定义一个全局捕获错误的类【注意加上注解:@RestControllerAdvice】
      1. 通过注解@ExceptionHandler(错误类型.clsss)来捕获所属的同一种类型的错误,然后返回错误信息
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * post请求参数校验抛出的异常
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ErrorInfo methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
        //获取异常中随机一个异常信息
        String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage();
        return ErrorInfo.fail(defaultMessage);
    }

    /**
     * get请求参数校验抛出的异常
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    public ErrorInfo bindExceptionHandler(BindException e){
        //获取异常中随机一个异常信息
        String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage();
        System.out.println(defaultMessage);
        return ErrorInfo.fail(defaultMessage);
    }

    /**
     * 请求方法中校验抛出的异常
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ErrorInfo constraintViolationExceptionHandler(ConstraintViolationException e){
        //获取异常中第一个错误信息
        String message = e.getConstraintViolations().iterator().next().getMessage();
        return ErrorInfo.fail(message);
    }
    @ExceptionHandler(RuntimeException.class)
    public ErrorInfo runtimeExceptionHandler(RuntimeException e) {
        String message = e.getLocalizedMessage();
        System.out.println(e);
        System.out.println(message);
        return ErrorInfo.fail(504, message);
    }
}
    1. 然后当我们抛出 诸如上面注解@ExceptionHandler里的异常类型时,此处就会自动调用与其对应的方法。然后给前端返回相应格式的错误

缓存处:

  1. 当用lombok的@Date注解时,失效问题解决方法

在本地的pom.xml中配置好文件并引入lombok插件后。然后通过此处https://jingyan.baidu.com/article/597a0643a51fac712b5243c7.html 安装好idea的lombok插件,即可正常调用.

 

注解学习

参考小资料:https://www.cnblogs.com/ranandrun/p/annotation.html 

@Autowired注解、@Resource注解和@Service注解

http://www.imooc.com/article/263102

自我总结:

Autowired是在Service层对象中调用创建其他类对象private xxClass xx; 时通过此注解自动引入。如果是接口注入,并且有多个实现类,就通过Autowired与Qualifier(“具体实现类类名”)完成指定。

Resource就和Autowired相似它是注入类中的一个属性Resource(name=”xx”)

@requestparam和@requestbody

参考资料:https://www.cnblogs.com/lxh520/p/8760664.html 

https://blog.csdn.net/WGH100817/article/details/101720537 

requestParam在get请求与post请求中都可以使用,相当于get请求时ip后面加的数据,

注意:@RequestParam注解的时候,路由中定义的每个参数名,必须与url中传递的参数名一致

 

requestBody只有在post请求时可以使用,相当于 { x: 1, x: 2 },一般用 map来接收此请求对象,或者jsonObject:https://blog.csdn.net/u012448904/article/details/84292821 

@Target

@Target:注解的作用目标

 

@Target(ElementType.TYPE)——接口、类、枚举、注解

@Target(ElementType.FIELD)——字段、枚举的常量

@Target(ElementType.METHOD)——方法

@Target(ElementType.PARAMETER)——方法参数

@Target(ElementType.CONSTRUCTOR) ——构造函数

@Target(ElementType.LOCAL_VARIABLE)——局部变量

@Target(ElementType.ANNOTATION_TYPE)——注解

@Target(ElementType.PACKAGE)——包

栗子:

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)

@Retention:注解的保留位置

 

RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。

RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。

RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。

@Document:说明该注解将被包含在javadoc中

@Inherited:说明子类可以继承父类中的该注解

@Validated与@Valid区别

https://blog.csdn.net/wangjiangongchn/article/details/86477386 

总结:

@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Service

https://zhidao.baidu.com/question/511501876.html 

自我总结,student是person的实现类,teacher也是person的实现类,当你用注解@Autoawired实例化person接口的时候,因为有多个实现类,需要通过@Qualiflier注解指定要实例化的类。比如我们在student上配置的@Service(“student”),在teacher上配置的@Service(“teacher”)那么当我们想实例化Person对应student的时候,就需要两个注解@Autowired与@Qualifier(“student”)

实际操作学习

1. 获得配置文件中的配置值

参考:https://blog.csdn.net/dkbnull/article/details/81953190 

自我总结:使用链接中的第二个就ok

2.

java方法/类学习

1. 拦截器

应用场景:

1、日志记录,可以记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等等。
2、权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);

4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。

5、OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。

  本质也是AOP(面向切面编程),也就是说符合横切关注点的所有功能都可以放入拦截器实现。

2. isAnnotationPresent()方法

参考资料: https://blog.csdn.net/qq_41084324/article/details/83787052 

自我总结:指定类型的注释是否存在于此元素上,以此来进行各种各样的逻辑

3. Caleander时间操作

参考资料:https://www.cnblogs.com/ben-future/p/10872634.html 

4. 反射

    1. 参考资料:https://www.jianshu.com/p/9be58ee20dee 
  1. Handler
    1. 集中捕获处理消息用的




java常见异常

  1. java.lang.IllegalAccessException
    1. 参考:https://blog.csdn.net/broccoli2/article/details/78280052 

java常用方法

传入一个类,反射。获得所有字段

(适合复杂逻辑,如果只是修改几个特定字段, 无需如此)【参考资料:https://www.jianshu.com/p/9be58ee20deehttps://blog.csdn.net/j_bean/article/details/79094783 

    1. 自我理解:下面的方法就相当与将一个有类型的json对象,转换成一个无类型{ k1: v1, k2: v2... }的无类型json对象
      public static  DBObject beanToDBObject(T bean)
              throws IllegalArgumentException, IllegalAccessException {
          if (bean == null)
              return null;
          DBObject dbObject = new BasicDBObject();
          // 获取对象类的属性域
          Field[] fields = bean.getClass().getDeclaredFields();
          for (Field field : fields) {
              // 获取变量的属性名
              String varName = field.getName();
              // 修改访问控制权限
              boolean accessFlag = field.isAccessible();
              if (!accessFlag) {
                  field.setAccessible(true);
              }
              Object param = field.get(bean);
              if (param == null) {
                  continue;
              } else if (param instanceof Integer) {
                  // 判断变量的类型
                  int value = ((Integer) param).intValue();
                  dbObject.put(varName, value);
              } else if (param instanceof String) {
                  String value = (String) param;
                  dbObject.put(varName, value);
              } else if (param instanceof Double) {
                  double value = ((Double) param).doubleValue();
                  dbObject.put(varName, value);
              } else if (param instanceof Float) {
                  float value = ((Float) param).floatValue();
                  dbObject.put(varName, value);
              } else if (param instanceof Long) {
                  long value = ((Long) param).longValue();
                  dbObject.put(varName, value);
              } else if (param instanceof Boolean) {
                  boolean value = ((Boolean) param).booleanValue();
                  dbObject.put(varName, value);
              } else if (param instanceof Date) {
                  Date value = (Date) param;
                  dbObject.put(varName, value);
              }
              // 恢复访问控制权限
              field.setAccessible(accessFlag);
          }
          return dbObject;
      }
      // 用来将DBObject里的值修改到我们实际要传入mongodb的修改对象update中
      public static Update fromDBObjectExcludeNullFields(DBObject object) {
          Update update = new Update();
          for (String key : object.keySet()) {
              Object value = object.get(key);
              if(value!=null){
                  update.set(key, value);
              }
          }
          return update;
      }

       

你可能感兴趣的:(项目)