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

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

1.拦截器:

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

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

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

/**--------------------------------------------------------
登录检查拦截器
 --------------------------------------------------------**/
@Component
public class LoginCheckInterceptor implements HandlerInterceptor{


    /**--------------------------------------------------------
     方法说明:方法执行前做登录检查
     handler:就是当前要请求的方法
     步骤说明:
     1.从Session中获取Employee
     2.如果为空,没登录,跳转登录页面
     3.如果不为空,放行
     --------------------------------------------------------**/
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {

1.2配置springmvc: 拦截器注入:

/**--------------------------------------------------------
针对web的配置
 @Configuration:Spring的配置标签
 --------------------------------------------------------**/
@Configuration
public class WebConfig implements WebMvcConfigurer{

    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
        /**--------------------------------------------------------
    注册拦截器
     --------------------------------------------------------**/
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor)
                //拦截所有资源
                .addPathPatterns("/**")
                //放行资源
                .excludePathPatterns(
                        "/login",
                        "/login.html",
                        "/assets/**"
                );

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 {
    
       @ExceptionHandler(MaxUploadSizeExceededException.class)
       public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
       resp.setContentType("text/html;charset=utf-8");
       PrintWriter out = resp.getWriter();
       out.write("文件大小超出限制!");
       out.flush();
       out.close();
       }
       }
    

    具体理解可百度:https://www.cnblogs.com/luffystory/p/12010494.html

    根据该案例可详细理解作用.

3.全局参数校验:

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

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

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

​ 3.2使用方式:

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

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

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

在这里插入图片描述

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

在这里插入图片描述

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

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

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

在这里插入图片描述

4.全局事务处理器:

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

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

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

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.qlq.bean.user.User;import cn.qlq.mapper.user.UserMapper;
import cn.qlq.service.user.UserService;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    
    @Override
    public void addUser(User user) {
        userMapper.insert(user);
        int i = 1 / 0;
    }

}

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

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

5:自定义日志:

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

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

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

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

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

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

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

mysql中创建一张log表:

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50525
 Source Host           : localhost:3306
 Source Schema         : rbac

 Target Server Type    : MySQL
 Target Server Version : 50525
 File Encoding         : 65001

 Date: 08/01/2022 19:58:21
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------

-- Table structure for log

-- ----------------------------

DROP TABLE IF EXISTS `log`;
CREATE TABLE `log`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前访问的url',
  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前用户ip',
  `aclass` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前调用的类',
  `method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前调用的方法',
  `args` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '当前传入的参数',
  `user_id` bigint(20) NULL DEFAULT NULL COMMENT '当前访问的用户id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

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

//@Aspect 开启切面
//@Component 交给spring容器管理
@Aspect
@Component
@Slf4j
public class MyLogAop {

    @Autowired
    LogMapper logMapper;


    @Pointcut("@annotation(myLog)")
    public void cut(MyLog myLog){

    }

//    在方法之前执行
    @Before("cut(Object)")
    public void before(JoinPoint joinPoint){
        //RequestContextHolder  从tomcat里面拿到 ServletRequest 拿 HttpServletRequest
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //拿到对应的url
        String url = request.getRequestURI();
        //拿到对应的ip
        String ip = request.getRemoteHost();
        String remoteAddr = request.getRemoteAddr();
        //拿到所有参数的名称
//        Enumeration<String> attributeNames = request.getAttributeNames();
//        //获取所有的参数
//        StringBuilder stringBuilder = new StringBuilder();
//
//        while (attributeNames.hasMoreElements()){
//            stringBuilder.append("参数名:")
//                    .append(attributeNames.nextElement())
//                    .append("参数值:")
//                    .append(request.getAttribute(attributeNames.nextElement()));
//        }
//        String s = stringBuilder.toString();
        Object[] argss = joinPoint.getArgs();
        String args = argss.toString();
        //获取对应的类以及方法
        Object target = joinPoint.getTarget();
        Class<?> aClass = target.getClass();
        //获取当前类
        String typeName = aClass.getTypeName();
        //获取当前方法
        String method = joinPoint.getSignature().getName();


        log.info("获取到当前的ip:{},获取到当前的url:{}",ip,url);
        //获取session
        HttpSession session = request.getSession();
        Employee employee = (Employee) session.getAttribute(Constant.USER_SESSION);
        Long id = 0L;
        if (Objects.nonNull(employee)){
            id = employee.getId();
        }
        logMapper.insert(new Log(url,ip,typeName,method,args,id));

    }
}

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

@Aspect
@Component
@Log4j
@Order(0)
public class LogAop {

    @Autowired
    LogEntityMapper logEntityMapper;

    private static String[] types = { "java.lang.Integer", "java.lang.Double",
            "java.lang.Float", "java.lang.Long", "java.lang.Short",
            "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
            "java.lang.String", "int", "double", "long", "short", "byte",
            "boolean", "char", "float" };
    @AfterReturning(value = "@annotation(around)")
    public void log(JoinPoint joinPoint,LogAnnotation around)throws Throwable{
        //获取request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        //获取当前类
        UserInfo user = TokenKit.getUser (request);
        String classType = joinPoint.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        String methodName = joinPoint.getSignature().getName();
        String[] paramNames = getFieldsName(this.getClass(), clazzName, methodName);
        LogAnnotation.Logtype actionvalue = around.actionvalue ();
        String actiondese = around.actiondese ();
        String returnValue = getReturnValue(paramNames, joinPoint);
        String ipAddress = ZhaolaobaoIPUtil.getIPAddress (request);
        String requestURI = request.getRequestURI ();
        //最后返回目标方法
        LogEntity logEntity = new LogEntity (user.getId (), actiondese,returnValue, ipAddress, requestURI);
        logEntityMapper.insertSelective (logEntity);
        log.info("logaop切面执行结束");
    }

    private static String getReturnValue(String[] paramNames, JoinPoint joinPoint){
        String typeName = null;
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        boolean clazzFlag = true;
        for(int k=0; k<args.length; k++){
            Object arg = args[k];
            sb.append(paramNames[k]+" ");
            // 获取对象类型
            if (arg==null){
                arg="null";
            }else{ typeName= arg.getClass().getTypeName();}

            for (String t : types) {
                if (t.equals(typeName)) {
                    sb.append("=" + arg+"; ");
                }
            }
            if (clazzFlag) {
                sb.append(getFieldsValue(arg));
            }
        }
        return sb.toString();
    }

    /**
     * 得到参数的值
     * @param obj
     */
    public static String getFieldsValue(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        String typeName = obj.getClass().getTypeName();
        for (String t : types) {
            if(t.equals(typeName)) {
                return "";
            }
        }
        StringBuilder sb = new StringBuilder();
        sb.append("【");
        for (Field f : fields) {
            f.setAccessible(true);
            try {
                for (String str : types) {
                    if (f.getType().getName().equals(str)){
                        sb.append(f.getName() + " = " + f.get(obj)+"; ");
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        sb.append("】");
        return sb.toString();
    }
    /**
     * 得到方法参数的名称
     * @param cls
     * @param clazzName
     * @param methodName
     * @return
     * @throws
     */
    private static String[] getFieldsName(Class cls, String clazzName, String methodName) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        //ClassClassPath classPath = new ClassClassPath(this.getClass());
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramNames.length; i++){
            paramNames[i] = attr.variableName(i + pos);    //paramNames即参数名
        }
        return paramNames;
    }
}
6.自定义过滤器

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

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

/**
 * 登录过滤器
 * @version 1.0 
 * 文件名称:LoginFilter.java  
 * 类说明:过滤不符合要求的请求,使请求跳转到登录页面
 */
@Configuration
@WebFilter("/*")
@Slf4j
public class LoginFilter implements Filter {

   // 白名单 url 集合,符合条件的请求不需要登录也可以访问目标资源
   private List<String> whiteUrlList = new ArrayList<String>();

   /**
    * 初始化方法
    * @方法名: init
    * @方法说明: 项目启动时会执行该方法,初始化处理写在这个方法中
    * @参数 @param filterConfig
    * @参数 @throws ServletException
    * @创建时间: 2018年6月8日
    */
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
      log.info("登陆过滤器初始化");

      // 添加白名单
      whiteUrlList.add("/resource/");
      whiteUrlList.add("/login");
      whiteUrlList.add("/index");
      whiteUrlList.add("/register");
      //支付 回调以及 后期需要添加 支付成功 或者失败页面
      whiteUrlList.add("/wxpay");
      whiteUrlList.add("/wxpaynotify");
      whiteUrlList.add("/alipayNotify");
      whiteUrlList.add("/order/my/wxpaysuccess");
      whiteUrlList.add("/order/my/wxpayfail");
      whiteUrlList.add("/order/my/alipaysuccess");
      whiteUrlList.add("/alirefund");
      whiteUrlList.add("/wxrefund");

   }

   /**
    * filter处理方法
    * @方法名: doFilter
    * @方法说明: 每次请求都会执行,在方法中判断用户是否可用访问目标资源
    * @参数 @param request 请求
    * @参数 @param response 响应
    * @参数 @param chain
    * @参数 @throws IOException
    * @参数 @throws ServletException
    * @创建时间: 2018年6月8日
    */
   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      HttpServletRequest httpRequest = (HttpServletRequest) request;
      HttpServletResponse httpResponse = (HttpServletResponse) response;

      // 判断是否在白名单列表中,如果在就直接放行
      String requestURI = httpRequest.getRequestURI();
      log.info("验证 requestURI = " + requestURI);
      for (String whiteUrl : whiteUrlList) {
         if (requestURI.indexOf(whiteUrl) >= 0) {
            log.info("属于白名单请求   " + whiteUrl + " ,验证通过");
            chain.doFilter(request, response);
            return;
         }
      }

      // 不在白名单列表中的请求需要验证,判断是否登陆,如果已经登陆直接放行
      HttpSession session = httpRequest.getSession();
      Object user = session.getAttribute(GlobalConstants.SessionKey.CURRENT_LOGIN_USER);
      log.info("验证 session 中的用户 = " + user);
      if (user != null) {
         chain.doFilter(request, response);
         return;
      }  
      
      // 登录验证不通过,跳转到登录页面    
      log.info("未通过登录验证,跳转到登录页面");
      httpResponse.sendRedirect("/login");
      
   
   }

   /**
    * 销毁方法
    * @方法名: destroy
    * @方法说明:销毁时执行 
    * @参数
    */
   @Override
   public void destroy() {
      log.info("登陆过滤器被销毁");
   }

}
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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值