瑞吉项目总结--针对后端技术点

ThreadLocal设置值

每次请求调用一个线程,我们可以在该线程中设置值,其底层相当于一个map,可以调用set和get方法,但是此处是静态对象,它只能存一个值,如下是其set方法

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

我们可以发现它是将自己作为建存入map,再将传过来的value作为值
具体操作:
1.创建
创建一个BaseContext类,内部创建一个静态对象ThreadLocal,并提供相对应的静态方法

public class BaseContext {
     //创造一个静态的ThreadLocal对象,并声明泛型类
    private static ThreadLocal<Long> threadLocal  = new ThreadLocal<>();
    //存入key值
    public static void setSessionId(Long id){
        threadLocal.set(id);
    }
    //获取key值
    public static Long getSessionId(){
        return threadLocal.get();
    }
}

2.1存入key值
因为每次请求都调用一个线程,那么我们可以在请求经过拦截器或过滤器的时候设置它的值
如下是一个登录过滤器,用于判断用户是否登录

public class LoginFilter implements Filter {
    //路径匹配,也会匹配通配符
    private static AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String requestURI = request.getRequestURI();
        //不需要拦截的路径
        String strs[] = new String[]{
                "/backend/**",
                "/front/**",
                "/employee/login",
                "/employee/logout",
                "/user/login",
                "/user/getcode"
        };
        boolean check = check(strs, requestURI);
        if (check){
            filterChain.doFilter(request,response);
            return;
        }
        Object employeeId = request.getSession().getAttribute("employee");
        Long userId = (Long) request.getSession().getAttribute("userID");
        //登录成功
        if (employeeId!=null){
            //BaseContext存入key值
            BaseContext.setSessionId((Long)employeeId);
            filterChain.doFilter(request,response);
            return;
        }
        if (userId!=null){
            BaseContext.setSessionId(userId);
            filterChain.doFilter(request,response);
            return;
        }
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
    }

    /**
     * 路径匹配,检查本次请求是否需要放行
     * @param urls
     * @param requestURI
     * @return
     */
    public boolean check(String[] urls,String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if(match){
                return true;
            }
        }
        return false;
    }
}

2.2获取key值
在存入key值后,且在这次请求中,就可以获取key值了
如下是要将seesionid存入数据库中的公共字段处理器

public class CommonField implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("进入自动装入公共字段");
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        //存入key值
        metaObject.setValue("createUser", BaseContext.getSessionId());
        metaObject.setValue("updateUser", BaseContext.getSessionId());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getSessionId());
    }
}

登录过滤器LoginFilter

用于判断用户是否登录

//urlPatterns : 设置要拦截的请求,此处是拦截所有请求
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
    public class LoginFilter implements Filter {
    //路径匹配,也会匹配通配符
    private static AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String requestURI = request.getRequestURI();
        //不需要拦截的路径
        String strs[] = new String[]{
                "/backend/**",
                "/front/**",
                "/employee/login",
                "/employee/logout",
                "/user/login",
                "/user/getcode"
        };
        boolean check = check(strs, requestURI);
        if (check){
        	//为true就放行
            filterChain.doFilter(request,response);
            return;
        }
        Object employeeId = request.getSession().getAttribute("employee");
        Long userId = (Long) request.getSession().getAttribute("userID");
        //登录成功
        if (employeeId!=null){
            //BaseContext存入key值
            BaseContext.setSessionId((Long)employeeId);
            filterChain.doFilter(request,response);
            return;
        }
        if (userId!=null){
            BaseContext.setSessionId(userId);
            filterChain.doFilter(request,response);
            return;
        }
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
    }

    /**
     * 路径匹配,检查本次请求是否需要放行
     * @param urls
     * @param requestURI
     * @return
     */
    public boolean check(String[] urls,String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if(match){
                return true;
            }
        }
        return false;
    }
}

公共字段自动填充

问题分析
在很多的对象中都有一些相同的字段,每次插入数据的时候都是必填,例如 创建日期,修改日期,创建人,修改人等等.
而手动填写很麻烦,我们就可以利用公共字段的技术实现每次插入或更新时自动填入
实现步骤
1、编写对象处理器,要实现MetaObjectHandler类,并且要使用@Component注解

@Component
@Slf4j
public class CommonField implements MetaObjectHandler {
	/**
     * 在插入时自动执行该方法
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("进入自动装入公共字段");
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        //存入key值
        metaObject.setValue("createUser", BaseContext.getSessionId());
        metaObject.setValue("updateUser", BaseContext.getSessionId());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getSessionId());
    }
}

2、在实体类的属性上加入@TableField注解,指定自动填充的策略。
例如员工类:

@Data
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;

    private Integer status;
	
	//在插入时执行FieldFill.INSERT方法
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

}

对象映射器

问题分析
当服务器将Long型数据传给前端页面时会造成精度缺失,此时就可以利用对象映射器处理该问题,(此处提供一种解决方法,核心就是将long型转为string类型)
步骤
1.编写JacksonObjectMapper类

public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

2.添加到WebMvcConfig 类中的extendMessageConverters方法中

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    /**
     * 添加我们自己的消息转换器,将我们自己的转换器放在最前面
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将我们自己的转换器放在最前面
        converters.add(0,messageConverter);
    }
}

全局异常处理器

针对某些特定的且常会产生的异常进行统一处理

//拦截annotations中,有标注这些注解的类或方法
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> handleException(SQLIntegrityConstraintViolationException ex){
        //从报错信息中获得是否是我们想要的异常错误
        if (ex.getMessage().contains("Duplicate entry")){
            String s = ex.getMessage().split(" ")[2];
            return R.error(s+"已经存在了");
        }
        return R.error("未知错误");
    }

    @ExceptionHandler(CustomerException.class)
    public R<String> handleException2(CustomerException ex){
        //从报错信息中获得是否是我们想要的异常错误
        return R.error(ex.getMessage());
    }
}

通用结果类R

将返回数据包装成类R返回给前端

@Data
public class R<T> implements Serializable {
	//前端会根据code这一字段的值进行后续的操作
    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息
	
	//前端所需要的数据
    private T data; //数据

    private Map map = new HashMap(); //动态数据

    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }
}

DTO(数据传输对象)

当前端所需要的数据不仅仅是对象中的字段,还需要一些对象之外的字段,这个时候就可以用dto扩展对象的字段
如下DishDto继承了Dish,还增加了一些其他的字段

@Data
public class DishDto extends Dish {

    private List<DishFlavor> flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}

分页插件

使用分页功能就要注册这个插件,是一个config类

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

配置静态资源路径映射

此操作就是让请求页面路径与我们自己的静态资源路径匹配

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

    /**
     * 配置静态资源路径映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("/backend/**")
                .addResourceLocations("classpath:/backend/" );
        registry.addResourceHandler("front/**")
                .addResourceLocations("classpath:/front/");
    }
}

将前端传过来的xx对象转为xxDto对象

例如将dish对象转为dishdto,期间要新添一些字段
利用BeanUtils.copyProperties(source,dest)方法,先将dish中有的字段拷贝给dishdto,并且利用stream流对每个字段进行操作

 dishDtos = list.stream().map((item) -> {
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(item,dishDto);
            LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(DishFlavor::getDishId, item.getId());
            List<DishFlavor> list1 = dishFlavorService.list(queryWrapper);
            dishDto.setFlavors(list1);
            return dishDto;
        }).collect(Collectors.toList());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值