【链接暂时未有】:代表以后需要配置链接,关联起来
从头开始重构,刚开始学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
【仅供参考】
首先PO是持久化类,其属性的改变很有可能直接导致数据库中的数据变化,而不知道原因(为什么我的数据库中的数据变化了?)。引入了VO之后可以很好的解决类似的问题,甚至会很好的帮你解决页面(JSP,freemarker,asp,aspx)和控制层的直接便利的交互,而不用担心其各种属性的变化会不会导致数据库中数据的变化,这对于使用hibernate之后控制其操作数据时出现的持久化、瞬态、脱管都是有很大好处的。
https://www.cnblogs.com/pcheng/p/10121683.html 记得在两种注释上加上@description, 【简介的意思】
# Maven #
target/
# IDEA #
.idea/
*.iml
# Eclipse #
.settings/
.classpath
.project
c) 将我们当前项目的文件夹下的所有文件原封不动的复制过来,然后正常上传即可
d) 建议新建一个自己的分支,不要直接推送到master生产分支上,或者如果项目比较大,开发人员较多,建议同时有一个生产分支,一个测试分支,和多个开发人员自己的分支
【参考资料:https://cloud.tencent.com/developer/article/1496155 】
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eric"));
List
Regex
Query query = new Query();
// 以A开头
query.addCriteria(Criteria.where("name").regex("^A"));
// 以c结尾
query.addCriteria(Criteria.where("name").regex("c$"));
List
LT(小于)和GT(大于)
Query query = new Query();
query.addCriteria(Criteria.where("age").lt(50).gt(20));
List
排序
Query query = new Query();
query.with(new Sort(Sort.Direction.ASC, "age"));
List
分页
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
org.springframework.boot
spring-boot-starter-data-mongodb
org.projectlombok
lombok
1.16.18
参考资料: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基本是前端工作
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对象中的校验规则进行校验
通过第一种基本就可以实现大部分校验
@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 {
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;
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);
}
/**
* @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);
}
@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);
}
}
在本地的pom.xml中配置好文件并引入lombok插件后。然后通过此处https://jingyan.baidu.com/article/597a0643a51fac712b5243c7.html 安装好idea的lombok插件,即可正常调用.
参考小资料:https://www.cnblogs.com/ranandrun/p/annotation.html
http://www.imooc.com/article/263102
自我总结:
Autowired是在Service层对象中调用创建其他类对象private xxClass xx; 时通过此注解自动引入。如果是接口注入,并且有多个实现类,就通过Autowired与Qualifier(“具体实现类类名”)完成指定。
Resource就和Autowired相似它是注入类中的一个属性Resource(name=”xx”)
参考资料: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
@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:说明子类可以继承父类中的该注解
https://blog.csdn.net/wangjiangongchn/article/details/86477386
总结:
@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。
@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。
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”)
参考:https://blog.csdn.net/dkbnull/article/details/81953190
自我总结:使用链接中的第二个就ok
应用场景:
1、日志记录,可以记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等等。
2、权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
5、OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。
本质也是AOP(面向切面编程),也就是说符合横切关注点的所有功能都可以放入拦截器实现。
参考资料: https://blog.csdn.net/qq_41084324/article/details/83787052
自我总结:指定类型的注释是否存在于此元素上,以此来进行各种各样的逻辑
参考资料:https://www.cnblogs.com/ben-future/p/10872634.html
(适合复杂逻辑,如果只是修改几个特定字段, 无需如此)【参考资料:https://www.jianshu.com/p/9be58ee20dee】 https://blog.csdn.net/j_bean/article/details/79094783
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;
}