SpringBoot

目录

WEB开发

拦截器

文件上传

Web原生组件注入(Servlet、Filter、Listener)

数据访问

数据源的自动配置

使用Druid数据源

druid官方github地址

整合MyBatis-Plus

分页插件

单元测试

JUnit5常用注解

断言

 指标监控

配置

最常用的Endpoint


目录

WEB开发

拦截器

文件上传

Web原生组件注入(Servlet、Filter、Listener)

数据访问

数据源的自动配置

使用Druid数据源

druid官方github地址

整合MyBatis-Plus

分页插件

单元测试

JUnit5常用注解

断言


WEB开发

拦截器

拦截器配置

@Configuration
public class AdminWeConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).
                addPathPatterns("/**").//所有请求都被拦截
                excludePathPatterns("/","/login","/css/**","/fonts/**","/js/**","/images/**");//放行的请求
    }
}

重写拦截器

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        log.info("拦截请求的路径:"+requestURI);
        //登陆检查逻辑
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("loginUser");
        if(loginUser!=null){
            //放行
            return true;
        }
        //拦截住,跳转到登录页
        request.setAttribute("msg","请先登录");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     * 目标方法执行完之后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

文件上传

前端固定写法:请求方式method="post"  ;enctype="multipart/form-data"

<form method="post" action="/upload" enctype="multipart/form-data">
    <input type="file" name="file"><br>
    <input type="submit" value="提交">
</form>
/**
 * 文件上传测试
 */
@Slf4j
@Controller
public class FormTestController {

    @GetMapping("/form_layouts")
    public String form_layouts(){
        return "form/form_layouts";
    }

    /**
     * MultipartFile 自动封装上传过来的文件
     */
    @PostMapping("/upload")
    public String upload(@RequestParam("email") String email,
                         @RequestParam("username") String username,
                         @RequestPart("headerImg") MultipartFile headerImg,
                         @RequestPart("photos") MultipartFile[] photos) throws IOException {

        log.info("上传的信息: email={}, username={}, headerImg={}, photos={}",
                email,username,headerImg.getSize(),photos.length);
        if(!headerImg.isEmpty()){
            //保存到文件服务器,OSS服务器
            String originalFilename = headerImg.getOriginalFilename();
            headerImg.transferTo(new File("D:\\"+originalFilename));
        }
        if(photos.length>0){
            for (MultipartFile photo : photos) {
                if(!photo.isEmpty()){
                    String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("D:\\"+originalFilename));
                }
            }
        }

        return "main";
    }
}

Web原生组件注入(Servlet、Filter、Listener)

@ServletComponentScan(basePackages = "com.gr.boot") :指定原生Servlet组件都放在那里

@WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器

@WebFilter(urlPatterns={"/css/*","/images/*"})

@WebListener

主类加注释 

@ServletComponentScan(basePackages = "com.gr.boot")
@SpringBootApplication
public class Boot05WebAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(Boot05WebAdminApplication.class, args);
    }
}

servlet

@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("6666");
    }
}

filter

@Slf4j
@WebFilter(urlPatterns={"/css/*","/images/*"})
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("MyFilter初始化完成");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("MyFilter工作");
        chain.doFilter(request,response);
    }
    @Override
    public void destroy() {
        log.info("MyFilter销毁");
    }
}

Listener

@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("MyServletContextListener监听到项目初始化完成");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyServletContextListener监听到项目销毁");
    }
}

数据访问

数据源的自动配置

引入JDBC依赖

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

引入数据库依赖

<!--默认版本:<mysql.version>8.0.22</mysql.version>-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--<version>5.1.49</version>-->
        </dependency>
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、重新声明版本(maven的属性的就近优先原则)
    <properties>
        <java.version>1.8</java.version>
        <mysql.version>5.1.49</mysql.version>
    </properties>

application.yml中修改配置项

spring:
  datasource:
    url: jdbc:mysql://localhost:13306/mybatis_plus?useSSL=false&server.Timezone=UTC
    username: root
    password: "abc123"
    driver-class-name: com.mysql.jdbc.Driver

使用Druid数据源

druid官方github地址

GitHub - alibaba/druid: 阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池

引入依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>

application.yml中设置配置项  (示例)

    druid:
      aop-patterns: com.gr.boot.* #监控SpringBean
      filters: stat,wall          # 底层开启功能,stat(sql监控),wall(防火墙)
      stat-view-servlet:          # 配置监控页功能
        enabled: true               #开启监控页
        login-username: admin       #设置账号
        login-password: abc123      #设置密码
        reset-enable: false         #是否开启重置功能
      web-stat-filter:               # 监控web
        enabled: true                  #是否开启
        url-pattern: /*                #拦截请求前缀
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'    #放行请求
      filter:
        stat:                      # 对上面filters里面的stat的详细配置
          slow-sql-millis: 1000
          log-slow-sql: true
          enabled: true
        wall:
          enabled: true

整合MyBatis-Plus

加入依赖:

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

Mapper类继承BaseMapper<>类即可实现基本CRUD功能

public interface UserMapper extends BaseMapper<User> {
}

@TableField(exist = false)注解表示表中不存在该属性 

    @TableField(exist = false)
    private String userName;
    @TableField(exist = false)
    private String password;

分页插件

model传入分页属性内容page

    @GetMapping("/dynamic_table")
    public String dynamic_table(@RequestParam(value = "pn",defaultValue = "1")Integer pn, Model model){
        List<User> list = userService.list();
//        model.addAttribute("Users",list);
        //分页查询数据
        Page<User> userPage = new Page<>(pn, 2);//Page(long current 当前页面页数, long size 每页显示几条数据)
        //分页查询的结果
        Page<User> page = userService.page(userPage, null);//page(E page, Wrapper<T> queryWrapper)
        model.addAttribute("page",page);
        return "table/dynamic_table";
    }

IOC中添加分页插件:

@Configuration
public class MyBatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInnerInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInnerInterceptor.setMaxLimit(500L);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}

查询的list数据实现分页功能

单元测试

JUnit5常用注解

JUnit5的注解与JUnit4的注解有所变化

JUnit 5 User Guide

  • @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
  • @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
  • @RepeatedTest :表示方法可重复执行,下方会有详细介绍
  • @DisplayName :为测试类或者测试方法设置展示名称
  • @BeforeEach :表示在每个单元测试之前执行
  • @AfterEach :表示在每个单元测试之后执行
  • @BeforeAll :表示在所有单元测试之前执行
  • @AfterAll :表示在所有单元测试之后执行
  • @Tag :表示单元测试类别,类似于JUnit4中的@Categories
  • @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
  • @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
  • @ExtendWith :为测试类或测试方法提供扩展类引用

断言

引入静态类

import static org.junit.jupiter.api.Assertions.*;

简单断言

方法

说明

assertEquals

判断两个对象或两个原始类型是否相等

assertNotEquals

判断两个对象或两个原始类型是否不相等

assertSame

判断两个对象引用是否指向同一个对象

assertNotSame

判断两个对象引用是否指向不同的对象

assertTrue

判断给定的布尔值是否为 true

assertFalse

判断给定的布尔值是否为 false

assertNull

判断给定的对象引用是否为 null

assertNotNull

判断给定的对象引用是否不为 null

@Test
@DisplayName("simple assertion")
public void simple() {
     assertEquals(3, 1 + 2, "simple math");
     assertNotEquals(3, 1 + 1);

     assertNotSame(new Object(), new Object());
     Object obj = new Object();
     assertSame(obj, obj);

     assertFalse(1 > 2);
     assertTrue(1 < 2);

     assertNull(null);
     assertNotNull(new Object());
}

数组断言

    @Test
    @DisplayName("数组断言")
    void array() {
        assertArrayEquals(new int[]{2, 1}, new int[]{1, 2}, "数组内容不相等");
    }

组合断言

   //所有断言全部需要成功
    @Test
    @DisplayName("组合断言")
    void all() {
        assertAll("test",
                () -> assertEquals(2, 1 + 1),
                () -> assertTrue(1 < 0,"2号出错了")
        );
    }

异常断言

    @Test
    @DisplayName("异常断言")
    void testException(){
        assertThrows(ArithmeticException.class,
                ()->{int i = 1/0;},
                "业务逻辑居然正常运行");
    }

超时断言

@Test
@DisplayName("超时测试")
public void timeoutTest() {
    //如果测试方法时间超过1s将会异常
    Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}

快速失败

    //快速失败
    @Test
    @DisplayName("快速失败")
    void testFail(){
        if( 1 == 2){
            fail("测试失败");
        }
    }

前置条件

    //测试前置条件
    @Test
    @DisplayName("测试前置条件")
    void testAssumptions(){
        Assumptions.assumeTrue(false,"结果不满足");
        System.out.println(111);
    }

嵌套测试

JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。

@DisplayName("嵌套测试")
public class TestingAStackDemo {
    Stack<Object> stack;

    @Test
    @DisplayName("创建一个栈Stack")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested //嵌套测试类
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach  //在所有测试之前执行
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("栈是否为空")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("抛出EmptyStackException(栈为空)异常")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, stack::pop);
        }

        @Test
        @DisplayName("抛出EmptyStackException(栈为空)异常")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, stack::peek);
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement); //给栈中放入元素anElement
            }

            //内层的Test可以驱动外层的Before(After)Each/All之类的方法提前/之后运行
            @Test
            @DisplayName("判断栈中不为空")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}

参数化测试

@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型

@NullSource: 表示为参数化测试提供一个null的入参

@EnumSource: 表示为参数化测试提供一个枚举入参

@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参

@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)

    @ParameterizedTest
    @DisplayName("参数化测试")
    @ValueSource(ints = {1,2,3,4,5})
    void testParameterized(int i){
        System.out.println(i);
    }

    @ParameterizedTest
    @DisplayName("参数化测试")
    @MethodSource("stringProvider")
    void testParameterized2(String i){
        System.out.println(i);
    }

 指标监控

配置

添加依赖

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

yml文件中配置暴露所有监控信息为HTTP

management:
  endpoints:
    enabled-by-default: true #暴露所有端点信息
    web:
      exposure:
        include: '*'  #以web方式暴露

yml文件中配置选择暴露监控信息为HTTP 

management:
  endpoints:
    enabled-by-default: false #是否暴露所有端点信息
    web:
      exposure:
        include: '*'  #以web方式暴露
  endpoint:
    info:
      enabled: true
    beans:
      enabled: true
    metrics:
      enabled: true

访问 htpp://localhost:8080/actuator/*

 

最常用的Endpoint

  • Health:监控状况
  • Metrics:运行时指标
  • Loggers:日志记录

ID

描述

auditevents

暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件

beans

显示应用程序中所有Spring Bean的完整列表。

caches

暴露可用的缓存。

conditions

显示自动配置的所有条件信息,包括匹配或不匹配的原因。

configprops

显示所有@ConfigurationProperties

env

暴露Spring的属性ConfigurableEnvironment

flyway

显示已应用的所有Flyway数据库迁移。
需要一个或多个Flyway组件。

health

显示应用程序运行状况信息。

httptrace

显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository组件。

info

显示应用程序信息。

integrationgraph

显示Spring integrationgraph 。需要依赖spring-integration-core

loggers

显示和修改应用程序中日志的配置。

liquibase

显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。

metrics

显示当前应用程序的“指标”信息。

mappings

显示所有@RequestMapping路径列表。

scheduledtasks

显示应用程序中的计划任务。

sessions

允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。

shutdown

使应用程序正常关闭。默认禁用。

startup

显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup

threaddump

执行线程转储。

 如果是Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点:

ID

描述

heapdump

返回hprof堆转储文件。

jolokia

通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core

logfile

返回日志文件的内容(如果已设置logging.file.namelogging.file.path属性)。支持使用HTTPRange标头来检索部分日志文件的内容。

prometheus

以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus

Health Endpoint

健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。

重要的几点:

  • health endpoint返回的结果,应该是一系列健康检查后的一个汇总报告
  • 很多的健康检查默认已经自动配置好了,比如:数据库、redis等
  • 可以很容易的添加自定义的健康检查机制

yml文件中配置查看详细信息 :

management:
  endpoints:
    enabled-by-default: true #暴露所有端点信息
    web:
      exposure:
        include: '*'  #以web方式暴露
  endpoint:
    health:
      show-details: always

Profile功能

application.yml   不带后缀的为默认配置文件,任何时候都会被加载

server.port=8080
#指定激活的环境
spring.profiles.active=test

application-test.yml   指定环境配置文件,其中配置当前环境下的配置内容  (可以有多个,后缀内容为默认配置选定的条件)

person:
  name: test-张三
server:
  port: 7000

@Profile条件装配功能

@Profile注解后加配置文件后缀,选为当前配置下生效

@Profile("test")
@ConfigurationProperties("person")
@Component
@Data
public class Worker implements Person {
    private String name;
    private Integer age;
}

profile分组

数组前为自定义名称,可选定多个配置文件

server.port=8080
#指定激活的环境
spring.profiles.active=myprod

spring.profiles.group.myprod[0]=ppd
spring.profiles.group.myprod[1]=prod
spring.profiles.group.mytest[0]=test

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值