开发技巧

连接数据库

spring:
application:
name: reggie
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sq1?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&userSSL=false
username: root
password: 168168

mybatisplus配置

mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: assign_id

 

1.配置热部署,不至于每次更改代码都要重新加载整个项目,可以手动配置导入devtools依赖,然后每次更改代码后按ctrl+f9构建项目。

也可以自动部署,有点麻烦

在yml文件中配置devtools下的exclude中可以配置热部署的范围。

 

2.springboot文件中的引用

boot引用单个属性的时候可以使用@value(${})//一级.二级.三级,不支持松散绑定(就是文件中的属性名可以使用驼峰命名也可以使用烤肉串形式,大写小写都可以,包括赋值的字段名都可以大小写

引用文件全部属性·的时候可以定义一个Environment 的变量使用自动加载然后用变量.getProperty(属性)//一级.二级.三级

引用文件的引用类型,可以使用@ConfigurationProperties(prefix=”属性名")//属性名为一级.二级  ,属性名只可以用全小写字母,下划线和数字形式,该注解支持松散绑定,此注解需要写在类上面,并且该类需要被spring管理,就是将此类变为bean对象,使用这个注解的时候需要导入一个spring-boot-configuration-processor依赖

 

3.springboot中jdk8提供的计量单位的使用

@Duration(ChronoUnit.Minutes)   //以分钟为单位。可以更改为hours或者其他

   private Duration Timeout;

@DataSizeUnit(DataUnit.MEGABYTES) //以兆为单位,可以更改其他单位

  private DataSize dataSize;

 

4.bean属性校验

导入两个依赖validation-api,hibernate-validator

在bean对象的类上开启@Validated注解,在需要校验的属性上添加具体的规则,比如@Max(value= ,meassage=“ ”),@Min()

 

5.yml文件中int支持二进制八进制和16进制,8进制以0开头(如果是0127恰巧所有的值都在0-7,那么就会转换为十进制87),16进制以0x开头,如果需要的是字符串直接用双引号“‘   防止进制转换出问题

 

6.测试属性

在@SpringbootTest注解中配置properties值,可以为当前的测试用例配置临时的属性。配置args属性值可以为当前测试用例添加临时命令行参数,执行优先级,args>properties>yml

@SpringBootTest(properties={"test."})

@SpringBootTest(args={"--test."})

 

7.在test类中配置临时的bean

用@Import(xxx.config.class)来导入外部的bean的config类和Spring一样

 

8.test中开启web环境

@SpringBootTest(webEnvironment=SpringBootTest.webEnvironment.Ran.....)

 

9.mybatisplus自动填充,在需要填充的字段上添上注解

@TableField(fill = FieldFill.INSERT_UPDATE)

private LocalDateTime updateTime;

  //此处代表插入和更新的时候使用该字段,也可以单写INSERT或者UPDATE,根据情况写

 

@Slf4j

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     *
     * MybatisPlus
     * 插入操作自动填充
     *做插入操作的时候会自动执行下面的语句
     * @param metaObject
     */

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        //这里的方法参数不支持Servletrequest,拿不到session或者request对象,因此可以通过线程,
        // 发送http时这三个类中的特定方法的线程是相同的所以才能调用
        metaObject.setValue("createUser", BaseContext.getCurrentID());
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser",  BaseContext.getCurrentID());
    }

    /**
     * 更新操作自动填充
     *
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser",  BaseContext.getCurrentID());
    }
}
//忽略这个字段,数据库表中不含这个字段
@TableField(exist = false)

10.消息转换(可以将long或者bigint类型的值转换为String后传输给前端处理,因为如果值过长js会省略后几位,也可以指定传输给前端的日期格式)

 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)))

//将BigInteger和Long类型转换为String
.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);
}
}

需要在MvcConfig中扩展消息转换器MvcConfig类需要继承WebMvcConfigurationSupport重写里面的extendMessageConverters()方法

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

 /**
     * springboot如果需要继承webmvc那么就必须配置静态资源映射,否则访问不了
     *
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//        前面的路径是前端访问路径,后面的是映射到本地哪个路径,/不能省略
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/static/front/");
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/static/backend/");
//        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }

    /**;
     * 扩展MVC框架的消息转换对象
     *
     *
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //创建消息转换对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用jackson将java对象转换为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中,其中还有别的转换对象,因此要放到第一个才会优先执行
        converters.add(0,messageConverter);
    }

}

 11. 上传下载数据(文件传输一定是MultipartFile类型)

 public R<String> upLoad(MultipartFile file) {

        //原始文件名
        String originalFilename = file.getOriginalFilename();
        //截取后缀名
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        //使用uuid重新生成文件名,防止文件名重复覆盖,每次执行的fileName结果都不相同
        String fileName = UUID.randomUUID().toString();

        fileName = fileName + suffix;
        //判断文件夹存不存在
        File pathname = new File(basePath);
        if (!pathname.exists()) {
            pathname.mkdirs();
        }

        //file是一个临时文件,需要转存到指定位置,否则本次请求完成之后临时文件会删除
        try {
            //将文件传到指定位置

            file.transferTo(new File(basePath + fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);

    }


    @GetMapping("/download")
    public void download(String name, HttpServletResponse response) {
        try {

            BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(basePath + name));
            //老师这里使用的是ServletOutputStream
            OutputStream outputStream = response.getOutputStream();
            //设置一下响应回去的是什么文件
            response.setContentType("image/jpeg");

            int len = 0;
            byte[] byt = new byte[1024];
            while ((len = inputStream.read(byt)) != -1) {
                outputStream.write(byt, 0, len);
            }

            inputStream.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
 12.spring提供的一个匹配器
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

PATH_MATCHER (arg0,arg1);
13.过滤器,放行不需要登录信息的页面(须在起动器上配置@ServletComponentScan注解)
@WebFilter(filterName = "logincheckfilter", urlPatterns = "/*")//filterName随便配置
@Slf4j
public class LoginCheckFilter implements Filter {
    //spring下的匹配器,支持通配符
    public static final 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;
        //1.获取本次请求的url
        String requestURI = request.getRequestURI();
        log.info("拦截到的请求{}", requestURI);
        //定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/user/login",
                "/common/**"
        };


        //2.判断本次请求是否需要处理,封装一个方法
        boolean check = check(urls, requestURI);


        //3.如果不需要处理,则直接放行
        if (check) {
            log.info("本次请求{}不需要处理", requestURI);
            filterChain.doFilter(request, response);
            return;
        }
        //4.1判断登陆结果,如果已登录,则直接放行

        if (request.getSession().getAttribute("employee") != null) {
            log.info("employee已登录,用户id为:{}", request.getSession().getAttribute("employee"));

            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);

            filterChain.doFilter(request, response);
            return;
        }
        log.info("employee未登录");
//
//        //再结合前端好好看看
//
//        //5.1如果未登录则返回未登录结果,结合前端内容通过输出流方式向客户端页面发送响应数据
//        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
//        return;
        //4.2判断登陆结果,如果已登录,则直接放行
        if (request.getSession().getAttribute("user") != null) {
            log.info("user已登录,用户id为:{}", request.getSession().getAttribute("user"));

            Long userId = (Long) request.getSession().getAttribute("user");
            BaseContext.setCurrentId(userId);

            filterChain.doFilter(request, response);
            return;
        }
        log.info("user未登录");

         //5.1和5.2合在一块,返回同样的结果
        //5.2如果未登录则返回未登录结果,结合前端内容通过输出流方式向客户端页面发送响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;


    }

    public boolean check(String[] urls, String requestURI) {
        for (String url : urls) {
            if (PATH_MATCHER.match(url, requestURI)) {
                return true;
            }
        }
        return false;
    }

}

14.拦截器,也可以配置拦截页面功能(需要配置两个类一个拦截器,第二个继承webmvcconfigurationSupport或者webmvcconfiger)

/**
 * 拦截器拦截页面
 */
//@Component
public class Interceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 2023/2/6 1.判断threadlocal中有没有用户信息
User currentUser = BaseContext.getCurrentUser();

if (currentUser == null) {
    // 2023/2/6 2.没有用户信息,拦截
    return false;
}
// 2023/2/6 3.有用户信息,放开
return true;

    }
}


 mvcconfig类

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Autowired
    private RefreshTokenInterceptor refreshTokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //登录拦截,order是拦截的优先级
        registry.addInterceptor(loginInterceptor)
        .excludePathPatterns(
                "/user/code",
                "/user/login",
                "/blog/hot",
                "/shop/**",
                "/shop-type/**",
                "/voucher/**",
                "/upload/**"
                ).order(1);
        //登录状态有效期刷新拦截,
        registry.addInterceptor(refreshTokenInterceptor).order(0);
    }
}

 


posted @ 2023-04-09 17:18  Bepowerful  阅读(25)  评论(0)    收藏  举报