栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

18.Springboot(2)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

18.Springboot(2)

七.Thymeleaf模板引擎 1.模板引擎

由于SpringBoot默认不支持 JSP(因为默认打包方式为jar包,jsp不支持在jar包内运行),需要引入第三方模板引擎技术实现页面渲染。

其实jsp就是一个模板引擎,还有用的比较多的freemarker、Thymeleaf。但再多的模板引擎,他们的思想都是一样的,什么样一个思想呢我们来看一下这张图:

模板引擎的作用:写一个页面模板,比如有些值是动态的,我们可以在这些值的地方写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。然后把这个数据和页面模板交给我们模板引擎,模板引擎按照我们这个数据帮你把表达式解析、填充到我们指定的位置,最终把这个数据生成一个我们想要的内容给我们写展现出去
SpringBoot推荐的是Thymeleaf模板引擎

2.Thymeleaf语法

1、表达式
表达式部分官方文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#standard-expression-syntax

表达式名字语法用途
变量取值 表达式${…}获取请求域、session域、对象等值
选择变量 表达式*{…}获取上下文对象值
消息 表达式#{…}消息(获取国际化等值)
链接 表达式@{…}生成适应当前项目根路径的链接
片段 表达式~{…}导入html片段,获取公共页面片段

2、字面量
文本值: `‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格

3、文本操作
字符串拼接: +
变量替换: |The name is ${name}|

4、数学运算
运算符: + , - , * , / , %

5、布尔运算
运算符: and , or
一元运算: ! , not

6、比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )

7、条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)

8、特殊操作
无操作: _

9、设置属性值: th:attr
设置单个值

设置多个值


18.Springboot(2)

以上两个的代替写法 th:xxxx


所有h5标签属性的兼容写法:(基本h5标签属性都能使用th:xxx代替)
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes

10、循环遍历
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#iteration


        Onions
        2.41
        yes


  Onions
  2.41
  yes

11、条件运算
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#conditional-evaluation

view

User is an administrator

User is a manager

User is some other thing

12、属性优先级
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#attribute-precedence

3.使用Thymeleaf

Thymeleaf官网使用文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#standard-expression-syntax

1、pom.xml中引入Thymeleaf模板引擎启动器依赖:


    org.springframework.boot
    spring-boot-starter-thymeleaf

或者在构建项目是,勾选Thymeleaf模板引擎,它会自动导入Thymeleaf模板引擎启动器

2、编写一个HelloController

@Controller
public class HelloController {

    @GetMapping("/hello")
    public String hello(){
        return "hello"; //classpath:/templates/test.html
    }
}

3、编写一个测试页面 hello.html 放在 templates 目录下




    
    Title


    

hello,Thymeleaf!

4、启动项目请求测试:

4.Thymeleaf自动配置原理

前面呢,我们已经引入了Thymeleaf,并成功通过controller方法跳转页面,其原理是什么?
按照SpringBoot的自动配置原理特点,肯定有个ThymeleafAutoConfiguration自动配置类来进行Thymeleaf模板引擎的自动配置。有一个ThymeleafProperties类绑定了配置文件,用于简单的自定义Thymeleaf配置修改

ThymeleafAutoConfiguration:

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration {

ThymeleafProperties:

@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
	private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
	public static final String DEFAULT_PREFIX = "classpath:/templates/";
	public static final String DEFAULT_SUFFIX = ".html";

我们可以在其中看到默认的前缀DEFAULT_PREFIX和后缀DEFAULT_SUFFIX
上面就说明了为什么只要导入Thymeleaf启动器,然后在resources/templates/下写html页面,thymeleaf就可以自动渲染,就可以通过controller方法转发/重定向到指定页面

总的来说:

  • 所有thymeleaf的配置值都在 ThymeleafProperties
  • 自动配置类配置好了SpringTemplateEngine、ThymeleafViewResolver
  • 我们只需要直接开发页面
5.练习

1、修改测试请求,增加数据传输;

@GetMapping("/t1")
    public String test1(Model model){
        //存入数据
        model.addAttribute("msg","test,Thymeleaf!");
        model.addAttribute("users", Arrays.asList("唐三","小舞","胖子"));

        return "test";  //classpath:/templates/test.html
    }

2、我们要使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。

我们可以去官方文档的中看一下命名空间拿来过来:

 xmlns:th="http://www.thymeleaf.org"

3、我们去编写下前端页面test.html




    
    Title


    
    

[[${msg}]]

[[${user}]]

4、启动测试!

我们看完语法,很多样式,我们即使现在学习了,也会忘记。所以我们在学习过程中,需要使用什么,根据官方文档来查询,才是最重要的,要熟练使用官方文档!

八.拦截器 1.实现

1、实现 HandlerInterceptor 接口
LoginInterceptor类:自定义用来拦截登录认证的拦截器

public class LoginInterceptor implements HandlerInterceptor {

	//目标方法执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        System.out.println("preHandle执行了");

        //登录检查逻辑
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("loginUser");

        if(loginUser != null){
            //放行
            return true;
        }

        //拦截住。未登录。跳转到登录页
        request.setAttribute("msg","请先登录");
//        re.sendRedirect("/");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

	//目标方法执行完成以后
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle执行了");
    }

	//页面渲染以后
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion执行了");
    }
}

2、在自定义的配置类中添加配置好的LoginInterceptor拦截器

  • 自定义的配置类必须实现WebMvcConfigurer接口
  • @Configuration注解该类,表示为一个配置类,会注册到spring容器中
  • 在自定义配置类重写addInterceptors方法
  • addInterceptor方法:添加自定义的LoginInterceptor拦截器
  • addPathPatterns方法:添加拦截的路径
  • excludePathPatterns方法:添加不拦截的路径
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("*.xml
        
        true
    

4、创建service业务层接口及实现类(这一步可以不要,mybatis整合就可以用了,只是为了规范mvc思想)

public interface BooksService {

    List getBooks();
}
@Service
public class BooksServiceImpl implements BooksService{

    @Autowired
    BooksMapper booksMapper;

    @Override
    public List getBooks() {
        return booksMapper.getBooks();
    }
}


5、测试(使用springboot test类模拟controller),能访问数据库OK

	@Autowired
    BooksServiceImpl booksService;

    @Test
    void contextLoads() throws SQLException {
        List books = booksService.getBooks();
        for (Books book : books) {
            System.out.println(book);
        }
    }

整个过程我们都没有使用mybatis的核心配置文件mybatis.xml,只用到了mapper接口的sql映射xxxmapper.xml文件
如果第3步mapper(dao)层接口和对应的xxxmapper.xml sql映射文件不在同一个包下,那我们又没有核心配置文件mybatis.xml去映射该xxxmapper.xml,怎么做?(比如下图这样,放在resources的mybatis目录下了)

mybatis的核心配置文件mybatis.xml全部功能都被整合到了application.properties(yml)配置文件中
所以上图解决办法,可以在配置文件中配置如下:

mybatis:
  mapper-locations: classpath:mybatis/*.xml

如果我们想开启驼峰命名映射和开启日志功能(选择标准日志输出),可以这样做(这就相当于mybatis的核心配置文件mybatis.xml中开启驼峰命名映射和开启日志功能)

mybatis:
  configuration:
    map-underscore-to-camel-case: true #开启驼峰命名映射功能
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启控制台打印日志功能

比如给pojo包下所有类取别名:(这样在sql中指定类型不用写全类名了)

mybatis:
  type-aliases-package: com.example.pojo
2.原理
  • 只要导入了MyBatis启动器,里面有个MybatisAutoConfiguration自动配置类,它会将SqlSessionFactory、SqlSessionTemplate(也就是SqlSession)自动配置好并注册为bean组件
  • MybatisAutoConfiguration自动配置类有个@import(AutoConfiguredMapperScannerRegistrar.class)
    它将AutoConfiguredMapperScannerRegistrar类注册到spring容器,并且该类功能:只要我们定义的mapper接口使用了@Mapper注解,就会被自动扫描进来
  • MybatisProperties.class: MyBatis绑定配置文件的类(可以在配置文件中修改以mybatis开始的所有属性)
@ConfigurationProperties(prefix = "mybatis")
public class MybatisProperties {
十三.JUnit5单元测试

SpringBoot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库作为最新版本的JUnit框架,集成在了spring-boot-starter-test启动器,JUnit5与之前版本的Junit框架有很大的不同,由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit VintageJUnit Platform

  • Junit Platform:是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
  • JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部包含了一个测试引擎,用于在Junit Platform上运行。
  • JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。

注意:SpringBoot 2.4 以上版本移除了默认对 JUnit Vintage 的依赖。如果需要兼容junit4需要自行引入(不能使用junit4的功能 @Test)
JUnit 5的s JUnit Vintage:依赖是从spring-boot-starter-test移除的,如果需要继续兼容junit4需要自行引入JUnit Vintage


    org.junit.vintage
    junit-vintage-engine
    test
    
        
            org.hamcrest
            hamcrest-core
        
    


现在springboot项目创建自带的一个测试类。以前版本是@SpringBootTest + @RunWith(SpringTest.class)

@SpringBootTest
class Boot05WebAdminApplicationTests {

    @Test
    void contextLoads() {

    }
}

@Transactional 标注测试方法,测试完成后自动回滚

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

断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。
JUnit 5 内置的断言可以分成如下几个类别:1.检查业务逻辑返回的数据是否合理;2.所有的测试运行结束以后,会有一个详细的测试报告;

1、简单断言(用来对单个值进行简单的验证)

方法功能
assertEquals判断两个对象或两个原始类型是否相等
assertNotEquals判断两个对象或两个原始类型是否不相等
assertSame判断两个对象引用是否指向同一个对象
assertNotSame判断两个对象引用是否指向不同的对象
assertTrue判断给定的布尔值是否为 true
assertFalse判断给定的布尔值是否为 false
assertNull判断给定的对象引用是否为 null
assertNotNull判断给定的对象引用是否不为 null

测试以下:

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

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

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

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

2、数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等

	@Test
    @DisplayName("array assertion")
    public void array() {
        Assertions.assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
    }
}

3、组合断言
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言

 	@Test
    @DisplayName("assert all")
    public void all() {
        Assertions.assertAll("Math",
                () -> Assertions.assertEquals(2, 1 + 1),
                () -> Assertions.assertTrue(1 > 0)
        );
    }

4、异常断言
在JUnit4时期,想要测试方法的异常情况时,需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用

	@Test
    @DisplayName("异常测试")
    public void exceptionTest() {
        ArithmeticException exception = Assertions.assertThrows(
                //扔出断言异常
                ArithmeticException.class, () -> System.out.println(2%0));
    }

5、超时断言
Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间

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

6、快速失败
通过 fail 方法直接使得测试失败

	@Test
    @DisplayName("fail")
    public void shouldFail() {
        Assertions.fail("This should fail");
    }
3.前置条件(assumptions)

JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。

@DisplayName("前置条件")
public class AssumptionsTest {
	 private final String environment = "DEV";
	 
	 @Test
	 @DisplayName("simple")
	 public void simpleAssume() {
	    assumeTrue(Objects.equals(this.environment, "DEV"));
	    assumeFalse(() -> Objects.equals(this.environment, "PROD"));
	 }
	 
	 @Test
	 @DisplayName("assume then do")
	 public void assumeThenDo() {
	    assumingThat(
	       Objects.equals(this.environment, "DEV"),
	       () -> System.out.println("In DEV")
	    );
	 }
}

assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止。

4.嵌套测试

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

@DisplayName("A stack")
class TestingAStackDemo {

    Stack stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, stack::pop);
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        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);
            }

            @Test
            @DisplayName("it is no longer empty")
            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());
            }
        }
    }
}
 
5、参数化测试 

参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。利用 @ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
使用@ParameterizedTest表示这个方法是一个参数化测试

  • @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
  • @NullSource: 表示为参数化测试提供一个null的入参
  • @EnumSource: 表示为参数化测试提供一个枚举入参
  • @CsvFileSource: 表示读取指定CSV文件内容作为参数化测试入参
    @MethodSource: 表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)

当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参。

@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(String string) {
    System.out.println(string);
    Assertions.assertTrue(StringUtils.isNotBlank(string));
}

@ParameterizedTest
@MethodSource("method")    //指定方法名
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(String name) {
    System.out.println(name);
    Assertions.assertNotNull(name);
}

static Stream method() {
    return Stream.of("apple", "banana");
}
十四.指标监控

官网文档:https://docs.spring.io/spring-boot/docs/2.4.11/reference/html/production-ready-features.html#production-ready
未来每一个微服务应用在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务应用快速引用Actuator即可获得生产级别的应用监控、审计等功能。
springboot2.0以上版本使用的是springboot Actuator2.0以上版本:

1.使用

1、引入actuator启动器依赖

	
        org.springframework.boot
        spring-boot-starter-actuator
    

2、访问 http://localhost:8080/actuator/**(发现只暴露了2个端点的监控指标)

3.以web页面方式暴露所有默认端点

management:
  endpoints:
    enabled-by-default: true #暴露所有端点信息,默认也是开启的
    web:
      exposure:
        include: '*'  #以web方式暴露所有端点

4.再访问 http://localhost:8080/actuator/**(所有默认端点都可以看到了)

查看env端点的项目环境:http://localhost:8080/MrL/actuator/env
查看metrics端点的cpu总数:http://localhost:8080/MrL/actuator/metrics/system.cpu.count
。。。。。。

2.Actuator端点

官网文档:https://docs.spring.io/spring-boot/docs/2.4.11/reference/html/production-ready-features.html#production-ready-endpoints
所有默认端点官网均有介绍,这里只列举常用的端点

ID描述
beans显示应用程序中所有Spring容器Bean的完整列表
caches暴露可用的缓存
conditions显示自动配置的所有条件信息,包括匹配或不匹配的原因
env暴露Spring的属性ConfigurableEnvironment
health(最常用)显示应用程序运行状况信息
info显示应用程序信息
loggers(最常用)显示和修改应用程序中日志的配置
metrics(最常用)显示当前应用程序的“指标”信息
mappings显示所有@RequestMapping路径列表
threaddump执行下载,线程转储文件
heapdump执行下载,返回hprof堆转储文件

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

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

2、metrics endpoint
提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到

  • 通过Metrics对接多种监控系统
  • 简化核心Metrics开发
  • 添加自定义Metrics或者扩展已有Metrics
3.端点管理

1、开启与禁用端点

  • 默认所有的endpoint除了shutdown都是开启的。
  • 需要开启或者禁用某个endpoint,可以按照配置模式为 management.endpoint.endpointName.enabled = true进行配置

比如开启beans端点

management:
  endpoint:
    beans:
      enabled: true

或者禁用所有的Endpoint然后手动开启指定的Endpoint
比如关闭所有端点,只开启beans和health端点

management:
  endpoints:
    enabled-by-default: false
  endpoint:
    beans:
      enabled: true
    health:
      enabled: true

2、暴露端点
支持的暴露方式

  • Web暴露方式:默认只暴露health和info端点
  • JMX暴露方式:默认暴露所有Endpoint
management:
    web:
      exposure:
        include: '*'  #以web方式暴露所有端点
        exclude: "env,beans"  #不暴露env,beans端点
4.自定义端点

1、定制 health端点信息

@Component
public class MyHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        int errorCode = check(); // perform some specific health check
        if (errorCode != 0) {
            return Health.down().withDetail("Error Code", errorCode).build();
        }
        return Health.up().build();
    }

}
management:
    health:
      enabled: true
      show-details: always #总是显示详细信息。可显示每个模块的状态信息
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {


    //真实的检查方法
    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        //mongodb。  获取连接进行测试
        Map map = new HashMap<>();
        // 检查完成
        if(1 == 2){
//            builder.up(); //健康
            builder.status(Status.UP);
            map.put("count",1);
            map.put("ms",100);
        }else {
//            builder.down();
            builder.status(Status.OUT_OF_SERVICE);
            map.put("err","连接超时");
            map.put("ms",3000);
        }
        builder.withDetail("code",100)
                .withDetails(map);
    }
}

2、定制info端点信息
常用两种方式
第一种方式:编写配置文件

info:
  appName: boot-admin
  version: 2.0.1
  mavenProjectName: @project.artifactId@  #使用@@可以获取maven的pom.xml文件值
  mavenProjectVersion: @project.version@

第二种方式:实现InfoContributor

import java.util.Collections;

import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

@Component
public class ExampleInfoContributor implements InfoContributor {

    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("example",
                Collections.singletonMap("key", "value"));
    }

}

访问http://localhost:8080/actuator/info 会显示以上两种方式定制的所有info信息

3、定制Metrics端点信息

class MyService{
    Counter counter;
    public MyService(MeterRegistry meterRegistry){
         counter = meterRegistry.counter("myservice.method.running.counter");
    }

    public void hello() {
        counter.increment();
    }
}

4、定制端点

@Component
@Endpoint(id = "container")
public class DockerEndpoint {

    @ReadOperation
    public Map getDockerInfo(){
        return Collections.singletonMap("info","docker started...");
    }

    @WriteOperation
    private void restartDocker(){
        System.out.println("docker restarted....");
    }
}
5.指标监控可视化平台

SpringBoot Admin是开源社区孵化的项目,用于对SpringBoot应用的管理和监控。

  • SpringBoot Admin分为服务端(spring-boot-admin-server)和客户端(spring-boot-admin-client)
  • 服务端和客户端之间采用http通讯方式实现数据交互
  • 单体项目中需要整合spring-boot-admin-client才能被服务端监控
  • 在SpringCloud项目中,spring-boot-admin-server是直接从注册中心抓取应用信息,不需要每个微服务应用整合spring-boot-admin-client就可以实现应用的管理和监控
1.SpringBoot Admin服务端的搭建

创建新的springboor工程项目,作为服务端(必须勾选一个web场景启动器)
引入spring-boot-admin-starter-server场景启动器(由于我们的springboot版本是2.4.11,所以选择2.4版本(这里我们现在2.4.4版本))。而且,spring-boot-admin-starter-server场景启动器已经包含了springboot actuator的依赖


    org.springframework.boot
    spring-boot-starter-web


   de.codecentric
   spring-boot-admin-starter-server
   2.4.4

在application.properties配置文件中进行配置端口和根目录访问路径

#指定服务器端口
server.port=8888
#项目根路径访问访问路径
server.servlet.context-path= /MrL

在主启动类上添加@EnableAdminServer注解,表示当前web应用作为springboot admin服务端

@EnableAdminServer  //开启 springboot admin服务端
@SpringBootApplication
public class AdminserverApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminserverApplication.class, args);
    }
}

访问:http://localhost:8888/MrL/applications。由于客户端还没被服务端监听,这里服务端可视化页面并没有获取到任何客户端应用信息

2.SpringBoot Admin客户端的搭建

创建新的springboor工程项目,作为客户端(必须勾选一个web场景启动器)
spring-boot-admin-starter-client场景启动器已经包含了springboot actuator的依赖,所以我们不用导入spring-boot-starter-actuator场景启动器了


    org.springframework.boot
    spring-boot-starter-web


    de.codecentric
    spring-boot-admin-starter-client
    2.4.4

在application.yml配置文件中配置

spring:
  application:
    name: admin-client  #给client应用取个名字
  boot:
    admin:
      client:
        url: http://localhost:8888/MrL #指定服务端的URL
#        instance:
#          prefer-ip: true		#如果出现注册不到服务端,可能是localhost没有映射为ip地址,这时再开启这里的prefer-ip: true,表示使用ip地址注册到服务端

#actuator监控的相关配置,打开并暴露全部端点信息,这样服务端就可以获得所以端点信息并可视化展示
management:
  endpoints:
    enabled-by-default: true
    web:
      exposure:
        include: '*'

当我们的客户端项目启动后,在服务端就能看到admin-client这个应用了

这里我们没有进行登录授权认证,进一步的改进可参考https://www.cnblogs.com/zeng1994/p/de3232ab0e1ce857b57eebb8e8922f0f.html

十五.Swagger

在前后端分离时代,前后端通过API接口(通熟点说就是controller方法)进行交互,所以前后端相对独立且松耦合。但同时产生了一个问题,前端需求变更(比如数据库多一个字段),后端无法及时知道这次变更去更改数据库,导致业务错误
解决方案:首先定义计划表,并实时跟踪最新的API接口,降低前后端不一致风险。
Swagger就是为了解决这个问题的,Swagger特点如下:

  • 号称世界上最流行的API框架
  • Restful Api 文档在线自动生成器 (API 接口文档 与API 接口定义同步更新)
  • 可以直接运行,在线测试API接口
  • 支持多种语言 (如:Java,PHP等)
1. 使用

导入Swagger依赖


    io.springfox
    springfox-swagger2
    2.9.2


   io.springfox
   springfox-swagger-ui
   2.9.2

编写一个swagger配置类,使用 @EnableSwagger2注解开启swagger服务

@Configuration
@EnableSwagger2     //开启swagger服务
public class Swagger2Config {
}

访问测试 :http://localhost:8080/swagger-ui.html(基本功能就能使用了)

这里我们可以找到处理/login的方法,点击测试

然后输入参数username和password


最后能看到响应内容

相较于传统的Postman或Curl方式测试API接口(controller方法),使用swagger简直就是傻瓜式操作。不需要额外说明文档(写得好本身就是文档)而且更不容易出错,只需要传入参数的值然后点击Execute。
Swagger是个优秀的工具,现在国内已经有很多的中小型互联网公司都在使用它,相较于传统的要先出Word接口文档再测试的方式,显然这样也更符合现在的快速迭代开发行情。当然了,提醒下大家在正式环境要记得关闭Swagger,一来出于安全考虑二来也可以节省运行时内存

2.功能扩展

Swagger通过注册Docket 类型bean组件,来替换默认的配置

1、配置info消息

可以通过Docket的apiInfo()方法配置文档信息

//配置文档信息
@Configuration
@EnableSwagger2     //开启swagger服务
public class Swagger2Config {

    @Bean
    public Docket docket(){
        Docket docket = new Docket(documentationType.SWAGGER_2);
        docket.apiInfo(new ApiInfo(
                "swagger2", //标题
                "API接口文档",  //描述
                "1.0",  //版本
                "urn:tos", //组织链接
                new Contact("LiQingfeng", "", "1164623618@qq.com"), //联系人消息
                "Apache 2.0", //许可
                "http://www.apache.org/licenses/LICENSE-2.0", //许可链接
                new ArrayList())); //扩展

        return docket;
    }
}

重启项目,访问测试 http://localhost:8080/MrL/swagger-ui.html 看下效果(发现红框内的内容发生了改变)

2、配置只扫描指定controller接口

通过select()方法配置扫描指定的接口包,RequestHandlerSelectors配置如何扫描接口

@Bean
public Docket docket() {
   Docket docket = new Docket(documentationType.SWAGGER_2);
     docket.select()
     .apis(RequestHandlerSelectors.basePackage("com.liqingfeng.controller"))
     .build();
      
   return docket;
}

重启项目测试,由于我们配置根据包的路径扫描接口,所以我们只能看到controller包下的controller及方法

3、配置Swagger开关

通过enable()方法配置是否启用swagger。false则swagger服务将不能在浏览器中访问了

@Bean
public Docket docket() {
    Docket docket = new Docket(documentationType.SWAGGER_2);
    docket.enable(false) ; //配置是否启用Swagger,如果是false,在浏览器将无法访问
    return docket;
}

再次访问:发现不能访问swagger服务了

4、配置项目处于test、dev环境时启动swagger服务,处于prod时不显示
@Bean
public Docket docket(Environment environment) {
   // 设置要显示swagger的环境
   Profiles of = Profiles.of("dev", "test");
   // 判断当前是否处于该环境"dev",或则"test"环境,处于返回true
   boolean b = environment.acceptsProfiles(of);
 
   Docket docket = new Docket(documentationType.SWAGGER_2);
   docket.enable(b) ; //配置是否启用Swagger,如果是false,在浏览器将无法访问
   return docket;
}
5、配置API分组

默认情况下只有default组
分组一般用与协同开发时,每个人有自己的一个组,可以在自己组中通过上面指定controller包,来查看不同组的API接口信息

通过groupName()方法以及注册多个Docket bean组件,即可配置多个分组

@Bean
public Docket docket1(){
   return new Docket(documentationType.SWAGGER_2).groupName("group1");
}
@Bean
public Docket docket2(){
   return new Docket(documentationType.SWAGGER_2).groupName("group2");
}
@Bean
public Docket docket3(){
   return new Docket(documentationType.SWAGGER_2).groupName("group3");
}

再次访问

6、配置实体类

在swagger服务中现models没有显示自己的User实体类

我们在User类使用一下注解

@ApiModel("用户实体")
public class User {
   @ApiModelProperty("用户名")
   public String username;
   @ApiModelProperty("密码")
   public String password;
}

只要这个User实体类在某个请求方法的返回值上,就能被swagger识别并放在Models项里

	@GetMapping("/user")
    public User user() {
        return new User();
    }
}

重启查看测试

注:并不是因为@ApiModel这个注解让实体显示在这里了,而是只要出现在接口方法的返回值上的实体都会显示在这里,而@ApiModel和@ApiModelProperty这两个注解只是为实体添加注释的。
Swagger常用注解:

  • @Api(tags = “xxx模块说明”) 作用在模块类上
  • @ApiOperation(“xxx接口说明”) 作用在controller方法上
  • @ApiModel(“xxxPOJO说明”) 作用在模型类上:如VO、BO
  • @ApiModelProperty(value = “xxx属性说明”,hidden = true) 作用在类方法和属性上,hidden设置为true可以隐藏该属性
  • @ApiParam(“xxx参数说明”) 作用在参数、方法和字段上,类似@ApiModelProperty我们也可以给请求的接口配置一些注释
7、配置swagget服务页面的风格

我们可以导入不同的包实现不同的皮肤定义:
1、默认的(访问 http://localhost:8080/MrL/swagger-ui.html)

 
   io.springfox
   springfox-swagger-ui
   2.9.2


2、bootstrap-ui (访问 http://localhost:8080/MrL/doc.html)


   com.github.xiaoymin
   swagger-bootstrap-ui
   1.9.1


3、mg-ui (访问 http://localhost:8080/MrL/document.html)


   com.zyplayer
   swagger-mg-ui
   1.0.6

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号