重构
【链接暂时未有】:代表以后需要配置链接,关联起来
从头开始重构,刚开始学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 】
- 项目目录
参考:https://blog.csdn.net/u012675150/article/details/79351990
ssm框架目录:https://blog.csdn.net/qq598535550/article/details/51703190
-
- Cache
- Config:配置【必须】
- Controller:控制层【必须】
- Controler负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面。【不需要处理逻辑】参考:https://blog.csdn.net/qq_22771739/article/details/82344336
- Dao【必须】
- 概念:是sun的一个标准j2ee设计模式的接口之一,负责持久层的操作
- DAO层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表的增删改查,也就是说某个DAO一定是和数据库的某一张表一一对应的,其中封装了增删改查基本操作,建议DAO只做原子操作,增删改查。【查找数据】
- 详细解释:https://www.runoob.com/note/27029 【具体可以查看链接里面的栗子,即可清楚明了的知道Dao】
- 组成
- DAO接口: 把对数据库的所有操作定义成抽象方法,可以提供多种实现。【对应链接中的petDao】
- DAO 实现类: 针对不同数据库给出DAO接口定义方法的具体实现。【对应链接中的petDaoImpl】
- 实体类:用于存放与传输对象数据。【对应链接中的Pet】
- 数据库连接和关闭工具类: 避免了数据库连接和关闭代码的重复使用,方便修改。【对应链接中的BaseDao】
- 个人理解:根据b)组成,可以得出Dao就是将数据库的链接与关闭封装成一个类BaseDao。然后创建这个数据库表(模型)的Pet类,然后定义这个PetDao的接口,接口中定义我们需要用到的方法,然后在实现类PetDaoImpl一一实现这些方法。要记住这个类要继承BaseDao,使我们可以调用连接关闭数据库的方法。完成逻辑操作。
- 组成
- Service:逻辑层【必须】
- Service层叫服务层,被称为服务,粗略的理解就是对一个或多个DAO进行的再次封装,封装成一个服务,所以这里也就不会是一个原子操作了,需要事物控制。【处理逻辑】
- Util:工具类
- VO:数据传输层【必须】
- Constant:常量接口类
- Interceptor:拦截器
- 拦截层
- entity【模型实体类】:
- 简介:对应数据库表的pojo
- Cache
-
-
- 介绍:https://blog.csdn.net/qijingwang/article/details/80353829,实际就是和数据库模型的表明表字段一一对应的一个类【并且只能对应数据库字段】
- 一、实体类的名字尽量和数据库的表的名字对应相同。
- 二、实体类应该实现java.io.Serializable接口。
- 三、实体类应该有个无参的构造方法。
- 四、实体类应该有个有参(所有的参数)的构造方法。
- 五、实体类有属性和方法,属性对应数据库中表的字段,主要有getter和setter方法。
- 六、实体类还应该有个属性serialVersionUID。例如:private static final long serialVersionUID = -6125297654796395674L;
- 序列化:https://blog.csdn.net/so_geili/article/details/78931742 , 详细梳理:https://blog.csdn.net/so_geili/article/details/99836043
- VO,PO,POJO: https://blog.csdn.net/jikefzz1095377498/article/details/79237618
- 介绍:https://blog.csdn.net/qijingwang/article/details/80353829,实际就是和数据库模型的表明表字段一一对应的一个类【并且只能对应数据库字段】
-
【仅供参考】
-
-
-
- VO: value object值对象 ,他可以和表对应,也可以不用,取决于业务的需要和复杂程度。通常用于业务层之间数据的传递,也是用于包含数据。view object表现层对象,主要对应页面显示的数据对象,用VO来封装页面需要展示的数据对象。
- 详细解释:
- 使用范围:活动范围要控制在service层、controller层、展现层(view)中
- 业务层之间数据的传递:就是说我们内部调用我们的方法时需要传递参数时,比较复杂的对象时,可以封装成一个类,然后这个类存储我们所需要传递的参数,这就是一个VO对象。或者:代表值对象的意思,通常用于业务层之间的数据传递,由new创建,由GC回收。
- view object表现层对象:就是说们当要给app端或者web端传递数据时,可以封装一个类,然后用来传递参数。这也是一个VO对象。或者:对于一个WEB页面将整个页面的属性封装成一个对象,然后用一个VO对象在控制层与视图层进行传输交换。
- 使用方法:https://blog.csdn.net/G0_hw/article/details/78326359
- PO:Persistence Object 持久化对象
- 详细解释
- 使用范围:dao层和数据库(数据库直接和PO交互),而不能够出现在service层、controller层、展现层(view)中
- 概念:在 O/R 映射的时候出现的概念,如果没有 O/R 映射,没有这个概念存在了。【原因暂时不明白】
- 代表持久层对象的意思,对应数据库中表的字段
- 详细1:通常对应数据模型 ( 数据库 ), 本身还有部分业务逻辑的处理。可以看成是与数据库中的表相映射的 Java 对象。最简单的 PO 就是对应数据库中某个表中的一条记录,多个记录可以用 PO 的集合。里面除了私有的成员变量之外,就只有其对应的set/get方法。
- VO和PO为什么分开使用
- VO: value object值对象 ,他可以和表对应,也可以不用,取决于业务的需要和复杂程度。通常用于业务层之间数据的传递,也是用于包含数据。view object表现层对象,主要对应页面显示的数据对象,用VO来封装页面需要展示的数据对象。
-
-
首先PO是持久化类,其属性的改变很有可能直接导致数据库中的数据变化,而不知道原因(为什么我的数据库中的数据变化了?)。引入了VO之后可以很好的解决类似的问题,甚至会很好的帮你解决页面(JSP,freemarker,asp,aspx)和控制层的直接便利的交互,而不用担心其各种属性的变化会不会导致数据库中数据的变化,这对于使用hibernate之后控制其操作数据时出现的持久化、瞬态、脱管都是有很大好处的。
-
-
-
- BO (business object) 业务对象
- Bo就是把业务逻辑封装为一个对象(注意是逻辑,业务逻辑),这个对象可以包括一个或多个其它的对象。通过调用Dao方法,结合Po或Vo进行业务操作。
- 形象描述为一个对象的形为和动作,当然也有涉及到基它对象的一些形为和动作。比如处理一个人的业务逻辑,该人会睡觉,吃饭,工作,上班等等行为,还有可能和别人发关系的行为,处理这样的业务逻辑时,我们就可以针对BO去处理。
- 再比如投保人是一个Po,被保险人是一个Po,险种信息也是一个Po等等,他们组合起来就是一张保单的Bo。
- Dto:
- 拓展名:Data Transfer Object 数据传输对象
- 概念:主要用于远程调用等需要大量传输对象的地方。
- 优点:提高数据传输的速度(减少了传输字段),二能隐藏后端表结构
- 详解:
- 比如我们一张表有 100 个字段,那么对应的 PO 就有 100 个属性。 但是我们界面上只要显示 10 个字段, 客户端用 WEB service 来获取数据,没有必要把整个 PO 对象传递到客户端, 这时我们就可以用只有这 10 个属性的 DTO 来传递结果到客户端,这样也不会暴露服务端表结构 . 到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为 VO
- 与Dao区别:
- DAO :数据访问对象 —— 同时还有 DAO 模式
- DTO :数据传输对象 —— 同时还有 DTO 模式
- POJO
- 拓展名:plain ordinary java object 简单无规则 java 对象
- 纯的传统意义的 java 对象。没有增加别的属性和方法只有属性字段及 setter 和 getter 方法
- BO (business object) 业务对象
-
-
- 正式开始
- 创建初始目录结构
- 如果是mongo数据库,基本只需要Config/Controller/
- Config
- dao
- Controller
- Service
- Util
- Constant
- Vo
- Entity
- Dto
- Interceptor【非必须】
- annotations【注解接口层】
- Impl【注解接口实现层】
- handler【消息统一处理层】
- group:【validated校验分组层】
- 如果是mongo数据库,基本只需要Config/Controller/
b) 设置注释格式
https://www.cnblogs.com/pcheng/p/10121683.html 记得在两种注释上加上@description, 【简介的意思】
c) gitlab上传
- gitlab中新建项目
- 进入首页:创建一个新项目
- Initialize repository with a README。勾选选项的意思是,可以初始化项目,不用我们创建项目之后自己再手动添加一个文件进行初始化了。建议勾选
- 克隆项目
- 将新建项目完成时页面上的http网址进行复制:例如【http://123.170.121.225:1234/root/project】这种格式的网址
- 访问我们部署gitlab服务器的网址:xx.xx.xxx.xxx: port在我们自己服务器的gitlab上注册账号
- 登陆项目的创建人的账户,然后选择我们需要克隆的项目进入,然后点击设置-->成员,将刚注册的成员账户加入到我们的项目,并选择到期日期
- 然后通过sourceTree软件的新建--》从URL克隆, 然后输入我们第一步记录下的网址【http://123.170.121.225:1234/root/project】,依次输入我们新注册的密码,最后点击克隆
- 将手头项目进度添加到gitlab中
- 点击此按钮,找到克隆在本地的源文件夹
- 在我们克隆的本地的源文件夹添加一个.gitignore忽略文件【记住这个文件一定要在最开始进行添加,添加完毕之后再将我们本地项目复制进去】
- 内容如下,如需忽略其他格式请自己手动继续添加
- 点击此按钮,找到克隆在本地的源文件夹
# Maven #
target/
# IDEA #
.idea/
*.iml
# Eclipse #
.settings/
.classpath
.project
-
-
- 然后将文件提交,并推送到远程分支
-
c) 将我们当前项目的文件夹下的所有文件原封不动的复制过来,然后正常上传即可
d) 建议新建一个自己的分支,不要直接推送到master生产分支上,或者如果项目比较大,开发人员较多,建议同时有一个生产分支,一个测试分支,和多个开发人员自己的分支
【参考资料:https://cloud.tencent.com/developer/article/1496155 】
d) 数据库
-
-
- 数据库建表
- 工具建表:
- mysql
- Window:PowerDesigner
- Mac:
- mysql
- 手动建表
- Mysql
- 通过代码动态建表
- 直接在navicat上创建
- Mongo
- 直接通过代码建表就好了,调用springboot自带的mongo。:https://spring.io/projects/spring-data-mongodb#learn lear->x.x.x CURRENT GA->后面两个选项
- 语句实例说明:https://www.cnblogs.com/xiaoqi/p/queries-in-spring-data-mongodb.html
https://blog.csdn.net/sinat_35821285/article/details/83511203
is
- Mysql
- 工具建表:
- 数据库建表
-
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eric"));
List<User> 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<User> users = mongoTemplate.find(query,User.class);
LT(小于)和GT(大于)
Query query = new Query();
query.addCriteria(Criteria.where("age").lt(50).gt(20));
List<User> users = mongoTemplate.find(query,User.class);
排序
Query query = new Query();
query.with(new Sort(Sort.Direction.ASC, "age"));
List<User> 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<User> userList = mongoTemplate.find(query, User.class);
- 数据库实践篇
- 建立一个测试流程,在mongo中添加一个测试数据。 参考资料:https://blog.csdn.net/qq_33619378/article/details/81544711#commentBox
- 创建过程中:需要引入的插件:
- 第一个插件可以通过在idea上创建的时候直接安装
- 第二个插件有时候会出现与idea与其不兼容,导致失效的问题。
- 解决办法:在本地的pom.xml中配置好文件并引入lombok插件后。然后通过此处https://jingyan.baidu.com/article/597a0643a51fac712b5243c7.html 安装好idea的lombok插件,重启后即可正常调用.
<dependency>
<!--springboot框架自带的mongo操作插件-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<!--这个是@Data注解,可以通过此注解标明在实体类上,使其不在需要添加setter/getter-->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
-
-
-
- 数据库表的名字与entity中实体类的名字是一样的,只是表明首字母小写
- 中途出现情况:
- post请求,时请求不通
- 错误信息: error"error":"Unsupported Media Type", "message":"Content type 'application/octet-stream' not supported"
- 解决:请求头部设置,Content-type:application/json
- 请求接口成功,但无法添加数据
- 错误信息: "error":"Internal Server Error", Command failed with error 13 (Unauthorized): 'command update requires authentication' on server 。。。。
- 解决:application.xxxx配置文件中,配置数据库信息不正确。一般是由于压根没有配置数据库信息,或者配置的数据库没有设置账户名和密码
- post请求,时请求不通
-
- 在db/mysql.sql中创建多张表的模型
- 在entity中创建实体类
-
e) 接口测试
- 使用软件eolinker
- 下载地址:https://www.eolinker.com/pc/
- 新建工作空间
- 正常使用即可,如需添加其他人员,再另行添加
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基本是前端工作
- 参考资料:https://www.jianshu.com/p/e88d3f8151db API资料:https://github.com/auth0/java-jwt
- 添加jwt插件
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
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
插件:
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.0</version>
</dependency>
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对象中的校验规则进行校验
通过第一种基本就可以实现大部分校验
- 通过springboot原生javax进行校验
- 校验实体类:在实体类的属性上【注意:光写了校验规则没用,需要配合注解@Validated使用】
配置校验规则注解栗子:
- 校验实体类:在实体类的属性上【注意:光写了校验规则没用,需要配合注解@Validated使用】
@NotEmpty(message = "号码不允许为空", groups = {AddGroup.class, UpdateGroup.class})
@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$", 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); }
-
- 自定义校验实体类规则:
分组与嵌套的规则与上面的一样
注解层文件夹【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<? extends Payload>[] 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<PhoneValidate, String> {
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;
-
- 校验传过来的不规则map对象
1. 可以直接通过if ()语句判断
- 校验传过来的不规则map对象
2. 定义不规则对象的实体类,用上面的规则进行校验
-
- 自定义校验工具类【自己暂时不太明白,暂时先会写就行】
异常捕获:
- 单一捕获一个路由的异常
@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);
}
- 统一捕获异常消息
- 参考资料:https://www.jianshu.com/p/3536243e9a5a
- 新建一个错误信息类
/**
* @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);
}
-
- 在handler文件夹中定义一个全局捕获错误的类【注意加上注解:@RestControllerAdvice】
- 通过注解@ExceptionHandler(错误类型.clsss)来捕获所属的同一种类型的错误,然后返回错误信息
- 在handler文件夹中定义一个全局捕获错误的类【注意加上注解:@RestControllerAdvice】
@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);
}
}
-
- 然后当我们抛出 诸如上面注解@ExceptionHandler里的异常类型时,此处就会自动调用与其对应的方法。然后给前端返回相应格式的错误
缓存处:
- 当用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<String, Object>来接收此请求对象,或者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. 反射
- Handler
- 集中捕获处理消息用的
java常见异常
- java.lang.IllegalAccessException
java常用方法
传入一个类,反射。获得所有字段
(适合复杂逻辑,如果只是修改几个特定字段, 无需如此)【参考资料:https://www.jianshu.com/p/9be58ee20dee】 https://blog.csdn.net/j_bean/article/details/79094783
-
- 自我理解:下面的方法就相当与将一个有类型的json对象,转换成一个无类型{ k1: v1, k2: v2... }的无类型json对象
public static <T> 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; }
- 自我理解:下面的方法就相当与将一个有类型的json对象,转换成一个无类型{ k1: v1, k2: v2... }的无类型json对象