JAVA-第十部分-Spring-Boot

306 阅读6分钟

写在前面

Spring-Boot

  • 简化spring开发
  • 约定优于配置
  • 并不是对spring的增强,而是快速开发spring项目的方式
  • jar的打包方式
  • 业务代码编写方式完全一样

起步依赖

  • spring-boot-starter-parent控制依赖版本
  • spring-boot-starter-web继承依赖了了Spring-web和mvc

引导类

  • springBoot项目的入口
@SpringBootApplication
public class DemoApplication {

   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }

}

配置文件

yaml

  • 文件名只能为application
  • .yml/.yaml,类似.properties 设置默认值
  • 实现配置层级的关系,使用缩进表示层级关系,只允许使用空格,idea中tab可以;#表示注释
//.properties
server:
  port: 8082

//.yml/.yaml
server:
  port: 8082
  • 多种配置文件,加载顺序为.properties/.yml/.yaml
  • 使用
# 行内写法
person: {name: zhangsan}

# 数组
adress:
  - bejing
  - shanghai
age: [18,20]

# 纯量/常量
msg1: 'hello \n world' # 单引号忽略转义文字
msg2: "hello \n world" # 双引号识别转义文字
  • 参数引用,行内写法不能进行参数引用
# 参数引用
sex: man
people:
  sex: ${sex}

读取配置文件

Value方式

@Value("${sex}")
private String sex;

// 参数引用
@Value("${people.sex}")
private String sex;

//数组
@Value("${adresses[0]}")
private String adress;

environment

@Autowired
private Environment environment;
//调用
environment.getProperty("people.sex") + environment.getProperty("adresses[0]")

ConfigurationProperties

//类设置
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private int age;
    private String[] adresses;
    ...
    getter/setter
}

//.yml
person:
  name: ${name}
  age: 20
  adresses:
    - bejing
    - shanghai

//调用时直接注入
@Autowired
private Person person;

profile

动态配置切换

#激活 application-dev.properties
spring.profiles.active=dev

.yaml多文档

---
server:
  port: 8081

spring:
  config:
    activate:
      on-profile: dev
---
server:
  port: 8082

spring:
  config:
    activate:
      on-profile: test
---
server:
  port: 8083

spring:
  config:
    activate:
      on-profile: pro
---
# 激活
spring:
  profiles:
    active: dev

虚拟机参数设置

-Dspring.profiles.active=test

命令行参数

--spring.profiles.active=pro
  • 生成jar包后,在命令台运行,执行参数
java -jar demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=pro

内部配置加载顺序

  • 优先级从上至下
file:../config/ 当前项目
file../
classpath:/config/ resources目录下
classpath:/
  • 初始访问
server.servlet.context-path=/hello

整合其他框架

整合junit

@SpringBootTest
class DemoApplicationTests {
   @Autowired
   private UserService userService;
   @Test
   void serviceTest() {
      userService.add();
   }
}
  • 如果跟要测试的包不在同一个包或者子包下,要写明classes属性
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)

整合Redis

  • .yml配置
spring:
  redis:
    host: 127.0.0.1 # redis主机ip
    port: 3435
  • 使用
@Autowired
private RedisTemplate redisTemplate;

@Test
public void testSet() {
   //存入数据
   redisTemplate.boundValueOps("name").set("zhangsan");
}

@Test
public void testGet() {
   //取出
   Object name = redisTemplate.boundValueOps("name").get();
   System.out.println(name);
}

整合Mybatis

  • 只需要配置dataSource
# dataSource
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///dbtest2
    username: root
    password: 123456789
  • 如果使用xml配置mapper,需要配置mybatis
# mybatis
mybatis:
  # mapper映射文件
  mapper-locations:
  # 核心配置文件
  config-location:
  # 扫描实体
  type-aliases-package:
  • 调用
@Autowired
private AccountMapper accountMapper;
@Test
public void testFindAll() {
   List<Account> accountList = accountMapper.findAll();
   for (Account account : accountList) {
      System.out.println(account);
   }
}

condition 自动配置

  • 条件配置
  • 获取Bean
//获取springboot容器
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//获取Bean
Object redisTemplate = context.getBean("redisTemplate");
System.out.println(redisTemplate);

手动实现

//配置类获取user bean
@Configuration
public class UserConfig {
    @Bean
    //实现Condition接口,重现方法实现判断
    @Conditional(ClassCondition.class)
    public User user() {
        return new User();
    }
}
//ClassCondition 实现 Condition
public class ClassCondition implements Condition {
    @Override
    //conditionContext 上下文对象 获取环境 IOC容器 classloader对象
    //metadata 注解原对象,获取注解对应属性值
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //导入jedis坐标后创建bean
        boolean b = true;
        try {
            //判断jedis字节码文件是否存在
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            b = false;
        }
        return b;
    }
}

动态实现

//自定义注解
//加载什么上面 类/方法
@Target({ElementType.TYPE, ElementType.METHOD})
//生效的时间
@Retention(RetentionPolicy.RUNTIME)
//生成文档
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
    String[] value();
}

//通过获取注解内容达到动态实现
public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //导入jedis坐标后创建bean
        //获取注解属性值
        Map<String, Object> map = annotatedTypeMetadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        String[] value = (String[]) map.get("value");
        boolean b = true;
        try {
            for (String s : value) {
                //判断字节码文件是否存在
                Class<?> cls = Class.forName(s);
            }
        } catch (ClassNotFoundException e) {
            b = false;
        }
        return b;
    }
}

//调用
@Configuration
public class UserConfig {
    @Bean
    //自定义注解
    @ConditionOnClass({"redis.clients.jedis.Jedis"})
    public User user() {
        return new User();
    }
}

切换内置web服务器

  • 排除依赖
<!--排除tomcat依赖-->
<exclusions>
   <exclusion>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <groupId>org.springframework.boot</groupId>
   </exclusion>
</exclusions>
  • 导入jetty依赖
<dependency>
   <artifactId>spring-boot-starter-jetty</artifactId>
   <groupId>org.springframework.boot</groupId>
</dependency>

Enable注解

  • 用于动态启用某些功能,底层原理使用@Import注解导入一些配置类,实现动态加载
  • @Import加载类,这些类都会被Spring创建并放入IOC容器
@Import(UserConfig.class)
  • 自定义Enable注解进行封装
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}

Import注解

  • 导入Bean
@Import(User.class)
  • 导入配置类
@Import(UserConfig.class)
  • 导入ImportSelector实现类
//调用
@Import(MyImportSelector.class)

//实现类
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //具体的类可以通过配制文件加载
        return new String[]{"com.java.domain.User"};
    }
}
  • 导入ImportBeanDefinitionRegistrar实现类
//调用
@Import(MyImportBeanDefinitionRegistrar.class)

//实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        //注册了user bean
        registry.registerBeanDefinition("user",beanDefinition);
    }
}

@EnableAutoConfiguration

  • 内部使用 @Import({AutoConfigurationImportSelector.class})加载配置类
  • 加载META-INF/spring.factories文件中的配置类,在springBoot启动时,根据Conditional条件进行选择性的加载Bean

自定义starter

  • 创建spring-boot-autoConfig模块
  • 创建RedisAutoConfig类,加载Bean
@Configuration
//properties文件,加载相关配置
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfig {
    //提供jedis bean
    @Bean
    public Jedis jedis(RedisProperties redisProperties) {
        return new Jedis(redisProperties.getHost(),redisProperties.getPort());
    }
}
  • 创建RedisProperties类,加载配置
//在properties文件中设置时的引用 类似 redis.port=6666;
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
    //默认值
    private String host = "localhost";
    private int port = 6379;
}
  • 创建META-INF/spring.factories文件,springBoot会自动扫描加载
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.java.redis.config.RedisAutoConfig
  • 创建redis-spring-boot-starter模块,最终导入这个模块使用

监听

  • 监听事件,监听对象属性的变化

CommandLineRunner和ApplicationRunner

  • 当项目启动后执行run方法,如提前加载数据库的数据,数据预热‘
  • 效果一样
  • 参数通过命令行导入 name=java
  • ApplicationRunner
@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("MyApplicationRunner...run");
        System.out.println(args.getSourceArgs()[0]);
    }
}
  • CommandLineRunner
@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("MyCommandLineRunner...run");
        System.out.println(Arrays.asList(args));
    }
}

ApplicationContextInitializer和SpringApplicationRunListener

  • 配置 META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=com.java.listen.MyApplitaionCentextInitializer
org.springframework.boot.SpringApplicationRunListener=com.java.listen.MySpringApplicationRunListener
  • ApplicationContextInitializer环境准备好后,执行
@Component
public class MyApplitaionCentextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("MyApplitaionCentextInitializer...");
    }
}
  • SpringApplicationRunListener 要自己配置构造方法,不能注入
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
    //application 事件源
    public MySpringApplicationRunListener(SpringApplication application, String[] args) {
    }
    ......
    starting...项目启动 
    environmentPrepared...环境对象开始准备 
    contextPrepared...上下文开始准备
    contextLoaded...上下文对象开始加载 
    started...上下文对象加载完成 
    running...项目启动完成,开始运行 
    failed...项目启动失败
}

启动流程

  • 初始化,加载模块
  • 启动应用,启动计时器,启动监听器
  • 启动监听模块,配置环境模块,应用上下文模块(加载bean)
  • 刷新上下文
  • 关闭监听,记录启动结束时间

监控

  • Acuator
  • 访问http://localhost:8080/actuator
{
    "_links":{
        "self":{
            "href":"http://localhost:8080/actuator",
            "templated":false
        },
        "health":{
            "href":"http://localhost:8080/actuator/health",
            "templated":false
        },
        "health-path":{
            "href":"http://localhost:8080/actuator/health/{*path}",
            "templated":true}}}
  • 查看完整健康信息
# 开启健康检查的完整信息
management.endpoint.health.show-details=always

{"status":"UP",
"components":{
    "diskSpace":{ //磁盘信息
        "status":"UP",
        "details":{ 
            "total":250685575168,
            "free":27977404416,
            "threshold":10485760, //阈值
            "exists":true}},
        "ping":{
            "status":"UP"}}}

开启其他监控信息

# 将所有的监控endpoint暴露
management.endpoints.web.exposure.include=*

Admin

  • 图形化处理监控信息
  • 服务端加载admin-server提供UI界面查看
  • 客户端加载admin-client,并配置
# 指定admin.server地址
spring.boot.admin.client.url=http://localhost:9000
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=*

部署

  • jar包 命令行执行 java -jar spring-boot-admin-client-0.0.1-SNAPSHOT.jar

war包

  • 引导类修改,继承SpringBootServletInitializer
@SpringBootApplication
public class SpringBootAdminClientApplication extends SpringBootServletInitializer {
   public static void main(String[] args) {
      SpringApplication.run(SpringBootAdminClientApplication.class, args);
   }
   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
      return builder.sources(SpringBootAdminClientApplication.class);
   }
}
  • 放在tomcat的webapps文件夹下,访问路径要加包名http://localhost:8080/spring-war/user/findAll
  • build标签指定包名
<finalName>spring-war</finalName>
  • 设置打包方式
<packaging>war</packaging>