企业中,需要知道以下几点:

港控/mmm° 2023-10-04 23:23 26阅读 0赞

项目搭建之初就会有的几个东西

1.拦截器:

(项目搭建之初的配置,不需要会写,能看懂,主要只会改里面的业务逻辑)

在没有使用权限框架时,就会使用原生的springmvc拦截器做拦截:

1.1配置自定义拦截器,实现HandlerInterceptor接口:

  1. /**--------------------------------------------------------
  2. 登录检查拦截器
  3. --------------------------------------------------------**/
  4. @Component
  5. public class LoginCheckInterceptor implements HandlerInterceptor{
  6. /**--------------------------------------------------------
  7. 方法说明:方法执行前做登录检查
  8. handler:就是当前要请求的方法
  9. 步骤说明:
  10. 1.从Session中获取Employee
  11. 2.如果为空,没登录,跳转登录页面
  12. 3.如果不为空,放行
  13. --------------------------------------------------------**/
  14. @Override
  15. public boolean preHandle(HttpServletRequest request,
  16. HttpServletResponse response, Object handler) throws Exception {

1.2配置springmvc: 拦截器注入:

  1. /**--------------------------------------------------------
  2. 针对web的配置
  3. @Configuration:Spring的配置标签
  4. --------------------------------------------------------**/
  5. @Configuration
  6. public class WebConfig implements WebMvcConfigurer{
  7. @Autowired
  8. private LoginCheckInterceptor loginCheckInterceptor;
  9. /**--------------------------------------------------------
  10. 注册拦截器
  11. --------------------------------------------------------**/
  12. @Override
  13. public void addInterceptors(InterceptorRegistry registry) {
  14. registry.addInterceptor(loginCheckInterceptor)
  15. //拦截所有资源
  16. .addPathPatterns("/**")
  17. //放行资源
  18. .excludePathPatterns(
  19. "/login",
  20. "/login.html",
  21. "/assets/**"
  22. );

1.3以上是因为项目中没有使用权限框架做拦截,真实项目中会直接使用权限框架

外面市面上主流的 springboot框架使用权限框架有两种:apache shiro,Spring Security.

外面市面上主流的springcloud框架使用权限框架只有一种:OAuth2+JWT, oauth2其实也是对spring Security做的二次封装.

那么上面三种框架 apache shiro, Spring Security,OAuth2+JWT其实都是原生的框架,主要是中小企业为了快速开发而使用的.

真正的中大型企业,或者自主研发的项目 (ps:自主研发的项目就是该项目只属于公司,而不是小老板在外面接活帮别人做,或者外包,一般自主研发公司不分大小,都是从小做起来的,这个看老板对产品的定义) 都是自己封装的权限框架(手写拦截器Interceptor,或者过滤器Firelt),手写,因为每个机构对应的权限力度都不一样,目前市面上的框架拿去做权限,都不太好用,或者说很难用(难上手)

2.全局异常捕获器:

(项目搭建之初的配置,不需要会写,能看懂,主要只会改里面的业务逻辑)

2.1其实就是通过aop实现的,回忆一下aop的 几个方法,其中有一个@After throwing注解,就是用来捕获异常的,

全局异常捕获器使用,就是避免了在写代码时平凡的去try catch,

2.2使用方式:定义一个class类,打上注解 @ControllerAdvice

再自定义一个方法,在方法上打上注解@ExceptionHandler(XXXException.class)

@ExceptionHandler 该注解中的参数就是你们抛出的异常类,例如:Exception.class

  • package com.app;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.http.HttpServletResponse;

    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.multipart.MaxUploadSizeExceededException;

    /**

    • ContorllerAdvice 最常见的使用场景是全局异常处理
    • 一般搭配 @ExceptionHandler @ModelAttribute 以及 @InitBinder 使用
    • 如下是, 当单个文件超出最大size时 对应的自定义处理方法
    • all Controllers.
    • spring:
      servlet:
      multipart:
      max-file-size: 50KB
      */
      @ControllerAdvice
      public class CustomExceptionHandler {
  1. @ExceptionHandler(MaxUploadSizeExceededException.class)
  2. public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
  3. resp.setContentType("text/html;charset=utf-8");
  4. PrintWriter out = resp.getWriter();
  5. out.write("文件大小超出限制!");
  6. out.flush();
  7. out.close();
  8. }
  9. }
  10. 具体理解可百度:https://www.cnblogs.com/luffystory/p/12010494.html
  11. 根据该案例可详细理解作用.
3.全局参数校验:

3.1全局参数校验就是指我不在需要判断从前端传入的参数到Controller层时,还需要手动判断,该参数或者该对象是否等于null,

或者根据业务判断是否是邮箱 还是手机号,还是说长度超长,直接通过注解判断

以前我们需要这样判断前端传入的参数:
在这里插入图片描述

3.2使用方式:

3.2.1先在pom.xml项目中引入 依赖:
在这里插入图片描述

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-validation</artifactId>
  4. </dependency>

3.2.2然后开始在实体类对象上打上对应注解:

在这里插入图片描述

3.2.3有且不仅有以下对参数做校验的注解: 以下参数都是作用在实体类也就是你们写的domain对象上

在这里插入图片描述

3.2.4 domain对象中使用该注解后,还需要在Controller层的参数入口打上注解@valida注解,

下面的例子使用的是@validated 注解,该注解还可以使用分组,在这不做过多说明,详情可

百度:该链接会比较简单直观,适合新手上手

在这里插入图片描述

4.全局事务处理器:

4.1全局事务管理使用:@Transactional

一般该注解会作用于service层,那么作用service层原因在与,一般业务都会在这一层,所以容易报错,或者抛出异常,

出现异常就会出现事务问题:

  1. import java.util.List;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Service;
  4. import cn.qlq.bean.user.User;import cn.qlq.mapper.user.UserMapper;
  5. import cn.qlq.service.user.UserService;
  6. @Service
  7. public class UserServiceImpl implements UserService {
  8. @Autowired
  9. private UserMapper userMapper;
  10. @Override
  11. public void addUser(User user) {
  12. userMapper.insert(user);
  13. int i = 1 / 0;
  14. }
  15. }

以上代码做新增操作时,明显就报错了,那么该条sql就不应该执行,所以需要加上@Transactional 注解,来保证数据库不会因为逻辑问题导致存入脏数据,

上面例子不够直观,打个比方,做员工修改 userMapper.update(user);此时我在员工修改之前,改变了他的密码,但是由于我们代码操作失误导致抛出异常,该修改就应该回滚(rollback)而不应该提交(commit)

5:自定义日志:

5.1这个地方有的同学会费解,为什么需要自定义日志,明明控制台就可以打印日志,配合lombok使用 log.info()就能很好的打印日志,

那么为何需要自定义日志?

这里需要注意,今后真实开发,都是直接将项目打包到服务器,也就是linux操作系统的,你不可能去服务器上在去查看控制台日志,当然也可以将日志打印成一个xx.log的文件,但是这样查看也很麻烦,

真实企业会手动封装 ,通过aop 或者过滤器来做日志打印,将需要的信息,存入mysql数据库,方便后续线上出现问题排查

5.2如果使用aop,可以先自定义注解,或者看情况直接将切面定义层所有Controller,这个需要根据业务需要,在此我先演示自定义注解玩法,

帮助回顾自定义注解作用:

  1. /**
  2. * 自定义注解LogAop,正常情况下注解没有任何实际含义,一般都会配合Filter 或者aop使用
  3. */
  4. @Documented
  5. //@Target({ElementType.TYPE}) 标识该注解作用在哪个范围,type表示在类,或者接口
  6. @Target({
  7. ElementType.TYPE})
  8. public @interface LogAop {
  9. }

mysql中创建一张log表:

  1. /*
  2. Navicat Premium Data Transfer
  3. Source Server : localhost
  4. Source Server Type : MySQL
  5. Source Server Version : 50525
  6. Source Host : localhost:3306
  7. Source Schema : rbac
  8. Target Server Type : MySQL
  9. Target Server Version : 50525
  10. File Encoding : 65001
  11. Date: 08/01/2022 19:58:21
  12. */
  13. SET NAMES utf8mb4;
  14. SET FOREIGN_KEY_CHECKS = 0;
  15. -- ----------------------------
  16. -- Table structure for log
  17. -- ----------------------------
  18. DROP TABLE IF EXISTS `log`;
  19. CREATE TABLE `log` (
  20. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  21. `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前访问的url',
  22. `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前用户ip',
  23. `aclass` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前调用的类',
  24. `method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前调用的方法',
  25. `args` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '当前传入的参数',
  26. `user_id` bigint(20) NULL DEFAULT NULL COMMENT '当前访问的用户id',
  27. PRIMARY KEY (`id`) USING BTREE
  28. ) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
  29. SET FOREIGN_KEY_CHECKS = 1;

简单版aop:该aop在获取参数时只能拿到对象的地址值:

  1. //@Aspect 开启切面
  2. //@Component 交给spring容器管理
  3. @Aspect
  4. @Component
  5. @Slf4j
  6. public class MyLogAop {
  7. @Autowired
  8. LogMapper logMapper;
  9. @Pointcut("@annotation(myLog)")
  10. public void cut(MyLog myLog){
  11. }
  12. // 在方法之前执行
  13. @Before("cut(Object)")
  14. public void before(JoinPoint joinPoint){
  15. //RequestContextHolder 从tomcat里面拿到 ServletRequest 拿 HttpServletRequest
  16. ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  17. HttpServletRequest request = requestAttributes.getRequest();
  18. //拿到对应的url
  19. String url = request.getRequestURI();
  20. //拿到对应的ip
  21. String ip = request.getRemoteHost();
  22. String remoteAddr = request.getRemoteAddr();
  23. //拿到所有参数的名称
  24. // Enumeration<String> attributeNames = request.getAttributeNames();
  25. // //获取所有的参数
  26. // StringBuilder stringBuilder = new StringBuilder();
  27. //
  28. // while (attributeNames.hasMoreElements()){
  29. // stringBuilder.append("参数名:")
  30. // .append(attributeNames.nextElement())
  31. // .append("参数值:")
  32. // .append(request.getAttribute(attributeNames.nextElement()));
  33. // }
  34. // String s = stringBuilder.toString();
  35. Object[] argss = joinPoint.getArgs();
  36. String args = argss.toString();
  37. //获取对应的类以及方法
  38. Object target = joinPoint.getTarget();
  39. Class<?> aClass = target.getClass();
  40. //获取当前类
  41. String typeName = aClass.getTypeName();
  42. //获取当前方法
  43. String method = joinPoint.getSignature().getName();
  44. log.info("获取到当前的ip:{},获取到当前的url:{}",ip,url);
  45. //获取session
  46. HttpSession session = request.getSession();
  47. Employee employee = (Employee) session.getAttribute(Constant.USER_SESSION);
  48. Long id = 0L;
  49. if (Objects.nonNull(employee)){
  50. id = employee.getId();
  51. }
  52. logMapper.insert(new Log(url,ip,typeName,method,args,id));
  53. }
  54. }

下面是通过aop 去完成 ,该aop可以将所有参数以及返回值全部打印出来

  1. @Aspect
  2. @Component
  3. @Log4j
  4. @Order(0)
  5. public class LogAop {
  6. @Autowired
  7. LogEntityMapper logEntityMapper;
  8. private static String[] types = {
  9. "java.lang.Integer", "java.lang.Double",
  10. "java.lang.Float", "java.lang.Long", "java.lang.Short",
  11. "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
  12. "java.lang.String", "int", "double", "long", "short", "byte",
  13. "boolean", "char", "float" };
  14. @AfterReturning(value = "@annotation(around)")
  15. public void log(JoinPoint joinPoint,LogAnnotation around)throws Throwable{
  16. //获取request
  17. RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
  18. ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
  19. HttpServletRequest request = servletRequestAttributes.getRequest();
  20. //获取当前类
  21. UserInfo user = TokenKit.getUser (request);
  22. String classType = joinPoint.getTarget().getClass().getName();
  23. Class<?> clazz = Class.forName(classType);
  24. String clazzName = clazz.getName();
  25. String methodName = joinPoint.getSignature().getName();
  26. String[] paramNames = getFieldsName(this.getClass(), clazzName, methodName);
  27. LogAnnotation.Logtype actionvalue = around.actionvalue ();
  28. String actiondese = around.actiondese ();
  29. String returnValue = getReturnValue(paramNames, joinPoint);
  30. String ipAddress = ZhaolaobaoIPUtil.getIPAddress (request);
  31. String requestURI = request.getRequestURI ();
  32. //最后返回目标方法
  33. LogEntity logEntity = new LogEntity (user.getId (), actiondese,returnValue, ipAddress, requestURI);
  34. logEntityMapper.insertSelective (logEntity);
  35. log.info("logaop切面执行结束");
  36. }
  37. private static String getReturnValue(String[] paramNames, JoinPoint joinPoint){
  38. String typeName = null;
  39. Object[] args = joinPoint.getArgs();
  40. StringBuilder sb = new StringBuilder();
  41. boolean clazzFlag = true;
  42. for(int k=0; k<args.length; k++){
  43. Object arg = args[k];
  44. sb.append(paramNames[k]+" ");
  45. // 获取对象类型
  46. if (arg==null){
  47. arg="null";
  48. }else{
  49. typeName= arg.getClass().getTypeName();}
  50. for (String t : types) {
  51. if (t.equals(typeName)) {
  52. sb.append("=" + arg+"; ");
  53. }
  54. }
  55. if (clazzFlag) {
  56. sb.append(getFieldsValue(arg));
  57. }
  58. }
  59. return sb.toString();
  60. }
  61. /**
  62. * 得到参数的值
  63. * @param obj
  64. */
  65. public static String getFieldsValue(Object obj) {
  66. Field[] fields = obj.getClass().getDeclaredFields();
  67. String typeName = obj.getClass().getTypeName();
  68. for (String t : types) {
  69. if(t.equals(typeName)) {
  70. return "";
  71. }
  72. }
  73. StringBuilder sb = new StringBuilder();
  74. sb.append("【");
  75. for (Field f : fields) {
  76. f.setAccessible(true);
  77. try {
  78. for (String str : types) {
  79. if (f.getType().getName().equals(str)){
  80. sb.append(f.getName() + " = " + f.get(obj)+"; ");
  81. }
  82. }
  83. } catch (IllegalArgumentException e) {
  84. e.printStackTrace();
  85. } catch (IllegalAccessException e) {
  86. e.printStackTrace();
  87. }
  88. }
  89. sb.append("】");
  90. return sb.toString();
  91. }
  92. /**
  93. * 得到方法参数的名称
  94. * @param cls
  95. * @param clazzName
  96. * @param methodName
  97. * @return
  98. * @throws
  99. */
  100. private static String[] getFieldsName(Class cls, String clazzName, String methodName) throws NotFoundException {
  101. ClassPool pool = ClassPool.getDefault();
  102. //ClassClassPath classPath = new ClassClassPath(this.getClass());
  103. ClassClassPath classPath = new ClassClassPath(cls);
  104. pool.insertClassPath(classPath);
  105. CtClass cc = pool.get(clazzName);
  106. CtMethod cm = cc.getDeclaredMethod(methodName);
  107. MethodInfo methodInfo = cm.getMethodInfo();
  108. CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
  109. LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
  110. if (attr == null) {
  111. // exception
  112. }
  113. String[] paramNames = new String[cm.getParameterTypes().length];
  114. int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
  115. for (int i = 0; i < paramNames.length; i++){
  116. paramNames[i] = attr.variableName(i + pos); //paramNames即参数名
  117. }
  118. return paramNames;
  119. }
  120. }
6.自定义过滤器

通过注解@WebFilter(“/*”) 定义一个类成为一个Filter,并且需要去实现filter的方法,

实现过滤器的逻辑,其实本质就是拦截器intercept,都是用来做一些权限校验以及静态资源放行的

  1. /**
  2. * 登录过滤器
  3. * @version 1.0
  4. * 文件名称:LoginFilter.java
  5. * 类说明:过滤不符合要求的请求,使请求跳转到登录页面
  6. */
  7. @Configuration
  8. @WebFilter("/*")
  9. @Slf4j
  10. public class LoginFilter implements Filter {
  11. // 白名单 url 集合,符合条件的请求不需要登录也可以访问目标资源
  12. private List<String> whiteUrlList = new ArrayList<String>();
  13. /**
  14. * 初始化方法
  15. * @方法名: init
  16. * @方法说明: 项目启动时会执行该方法,初始化处理写在这个方法中
  17. * @参数 @param filterConfig
  18. * @参数 @throws ServletException
  19. * @创建时间: 2018年6月8日
  20. */
  21. @Override
  22. public void init(FilterConfig filterConfig) throws ServletException {
  23. log.info("登陆过滤器初始化");
  24. // 添加白名单
  25. whiteUrlList.add("/resource/");
  26. whiteUrlList.add("/login");
  27. whiteUrlList.add("/index");
  28. whiteUrlList.add("/register");
  29. //支付 回调以及 后期需要添加 支付成功 或者失败页面
  30. whiteUrlList.add("/wxpay");
  31. whiteUrlList.add("/wxpaynotify");
  32. whiteUrlList.add("/alipayNotify");
  33. whiteUrlList.add("/order/my/wxpaysuccess");
  34. whiteUrlList.add("/order/my/wxpayfail");
  35. whiteUrlList.add("/order/my/alipaysuccess");
  36. whiteUrlList.add("/alirefund");
  37. whiteUrlList.add("/wxrefund");
  38. }
  39. /**
  40. * filter处理方法
  41. * @方法名: doFilter
  42. * @方法说明: 每次请求都会执行,在方法中判断用户是否可用访问目标资源
  43. * @参数 @param request 请求
  44. * @参数 @param response 响应
  45. * @参数 @param chain
  46. * @参数 @throws IOException
  47. * @参数 @throws ServletException
  48. * @创建时间: 2018年6月8日
  49. */
  50. @Override
  51. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  52. HttpServletRequest httpRequest = (HttpServletRequest) request;
  53. HttpServletResponse httpResponse = (HttpServletResponse) response;
  54. // 判断是否在白名单列表中,如果在就直接放行
  55. String requestURI = httpRequest.getRequestURI();
  56. log.info("验证 requestURI = " + requestURI);
  57. for (String whiteUrl : whiteUrlList) {
  58. if (requestURI.indexOf(whiteUrl) >= 0) {
  59. log.info("属于白名单请求 " + whiteUrl + " ,验证通过");
  60. chain.doFilter(request, response);
  61. return;
  62. }
  63. }
  64. // 不在白名单列表中的请求需要验证,判断是否登陆,如果已经登陆直接放行
  65. HttpSession session = httpRequest.getSession();
  66. Object user = session.getAttribute(GlobalConstants.SessionKey.CURRENT_LOGIN_USER);
  67. log.info("验证 session 中的用户 = " + user);
  68. if (user != null) {
  69. chain.doFilter(request, response);
  70. return;
  71. }
  72. // 登录验证不通过,跳转到登录页面
  73. log.info("未通过登录验证,跳转到登录页面");
  74. httpResponse.sendRedirect("/login");
  75. }
  76. /**
  77. * 销毁方法
  78. * @方法名: destroy
  79. * @方法说明:销毁时执行
  80. * @参数
  81. */
  82. @Override
  83. public void destroy() {
  84. log.info("登陆过滤器被销毁");
  85. }
  86. }
7:关于第三方的一些插件;

7.1lombok

7.2pageHpler

7.3hutool工具类

8.关于db(数据库层面)

8.1:表设计三范式

8.2表设计字段命名规范

8.3索引:聚簇索引与非聚簇索引

8.4表中固定几个字段:

id ———索引

create_time(gmt_create)——创建时间

update_time(gmt_update)——更新时间

status(state)———状态

del——————-逻辑删除字段

8.5逻辑删除&物理删除

物理删除:直接使用 delete from table 删除表中的某一行 或者多行数据

逻辑删除:在删除时只会做update操作, 整体应该是 update table set del = 0 ,将del逻辑删除字段从1改为0

注意:数据库中,0代表false ,1代表true

真实企业开发,很少使用 物理删除,就是直接使用 delete sql去删

发表评论

表情:
评论列表 (有 0 条评论,26人围观)

还没有评论,来说两句吧...

相关阅读

    相关 项目导入eclipse需要注意的

    1、导入外部项目时,导入路径千万不能有中文(即:文件夹名称中不可以有中文),一旦有中文,可能会导不进去,即使导进去了,配置jdk版本,或者配置项目中用到的PHP配置文件都会出错