SpringBoot学习总结

文章目录

    • 1.基础篇
    • 2.运维实用篇
    • 3.开发实用篇
      • 1.热部署
      • 2.配置高级
      • 3.测试
      • 4.数据层解决方案
      • 5.整合第三方技术
      • 6.监控
    • 4.原理篇
      • 1.bean的加载方式
      • 2.bean的加载控制
      • 3.bean依赖属性配置
      • 4.自动配置
      • 5.自定义starter
      • 6.核心原理
    • 5.补充扩展

1.基础篇

用来简化spring应用的初始搭建以及开发过程

  • 基于阿里云创建项目:https://start.aliyun.com
  • 基于spring官网创建项目:https://start.spring.io/

快速启动(cmd命令行)

  • 对springboot项目打包package
  • 执行启动指令:java -jar springboot-demo.jar

jar支持命令行启动需要maven插件的支持:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

配置文件优先级:

application.properties > application.yml > application.yaml

引导类:

  • springboot工程提供引导类来启动程序

    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  • springboot工程启动后创建并初始化spring容器,扫描引导类所在包加载bean

常用依赖:

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

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

lombok:

  • @Data包含了@Getter、@Setter、@ToString

项目整合mybatis-plus:

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_
      id-type: auto		#开启主键id自增
  configuration:
    #开启日志详细过程,方便调试
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

业务异常处理:

//作为springmvc的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {

    //拦截所有异常信息
    @ExceptionHandler(Exception.class)
    public R doException(Exception e) {
        //记录日志,通知运维,通知开发
        e.printStackTrace();
        return new R("服务器故障,请稍后再试!");
    }
}

业务消息统一返回:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class R {
    private Boolean flag;
    private Object data;
    private String msg;

    public R(Boolean flag) {
        this.flag = flag;
    }

    public R(Boolean flag, Object data) {
        this.flag = flag;
        this.data = data;
    }

    public R(Boolean flag, String msg) {
        this.flag = flag;
        this.msg = msg;
    }

    public R(String msg) {
        this.flag = false;
        this.msg = msg;
    }
}

2.运维实用篇

工程打包与运行:

  • SpringBoot工程可以基于java环境下独立运行jar文件启动服务
  • SpringBoot工程执行mvn命令package进行打包
  • 执行jar命令:java -jar 工程名.jar

打包插件:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

windows端口被占用

# 查询端口
netstat -ano
# 查询指定端口
netstat -ano |findstr "端口号"
# 根据进程PID查询进程名称
tasklist |findstr "进程PID号"
# 根据PID杀死任务
taskkill /F /PID "进程PID号"
# 根据进程名称杀死任务
taskkill -f -t -im "进程名称"

在Linux中:

  • 安装好合适的jdk和数据库
  • 将打包好的jar项目放在/usr/local/app目录下
  • 在centos上创建数据库
  • 启动项目
#后台挂载运行.jar项目
nohup java -jar xxx.jar > server.log 2>&1 &
[1]30669
#server.log为项目执行过程的日志

#查看运行着的jar项目PID
ps -ef|grep "java -jar"

#结束进程
kill -9 30669

临时属性: 带属性数启动springboot,携带多个属性用空格分隔

java -jar xxx.jar --server.port=8080 --spring.datasource.druid.password=123
//在开发环境下设置临时属性
public static void main(String[] args) {
    String[] arg = new String[1];
    arg[0] = "--server.port=8081";
    SpringApplication.run(Application.class, arg);
}

配置文件分类:

  • SpringBoot中4级配置文件
    1级 : file(jar目录) : config/application.yml【最高】(运维经理)
    2级 : file : application.yml(运维人员)
    3级 : classpath: config/application.yml(项目经理)
    4级 : classpath: application.yml【最低】(开发人员)
  • 作用:
    1级与2级留做系统打包后设置通用属性,1级常用于运维经理进行线上整体项目部署方案调控
    3级与4级用于系统开发阶段设置通用属性,3级常用于项目经理进行整体项目属性调控

自定义配置文件:通过启动参数加载指定文件路径下的配置文件时可以加载多个配置

--spring.config.name=ebank
--spring.config.location=classpath:/ebank.yml,classpath:/ebank-server.yml //后面的配置覆盖前面的

多环境开发:

  1. 多环境开发需要设置若干种常用环境,例如开发、生产、测试环境
  2. yaml格式中设置多环境使用 — 区分环境设置边界
  3. 每种环境的区别在于加载的配置属性不同
  4. 启用某种环境时需要指定启动时使用该环境
#应用环境,公共配置
spring:
  profiles:
    active: dev
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test1?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_
      id-type: auto
#  configuration:
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
---
#设置环境
#开发环境
server:
  port: 8080
spring:
  config:
    activate:
      on-profile: dev

---
#生产环境
server:
  port: 81
spring:
  profiles: pro

---
#测试环境
server:
  port: 82
spring:
  profiles: test

多环境开发多配置文件格式:

  1. 主启动配置文件application.yml
    spring:
    profiles:
    active: dev

  2. 环境分类配置文件application-pro.yml
    server:
    port: 80

  3. 环境分类配置文件application-dev.yml
    server:
    port: 81

  4. 环境分类配置文件application-test.yml
    server:
    port: 82

多环境分组管理:

1.根据功能对配置文件中的信息进行拆分,并制作成独立的配置文件,命名规则如下

  • application-devDB.yml
  • application-devRedis.yml
  • application-devMvC.yml

2.使用include属性在激活指定环境的情况下,同时对多个环境进行加载使其生效,多个环境间使用逗号分隔

spring:
  profiles:
    active: dev
      include: devDB,devRedis,devMvc

3.使用group属性替代include属性,降低了配置书写量

spring:
  profiles:
    active: dev
    group: 
      "dev": devDB,devRedis,devMvc
      "pro": proDB,proRedis,proMvc

3.开发实用篇

1.热部署

  • 开启开发者工具

     <!--热部署-->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-devtools</artifactId>
     </dependency>
    
  • 激活热部署:CTRL+F9

  • 重启(restart):自定义开发代码,包含类、页面、配置文件等,加载位置restart类加载器

热部署范围配置:

#设置不热部署的目录,自定义不参与重启排除项
devtools:
    restart:
      exclude: static/**,config/application.yml

关闭热部署:

public static void main(String[] args) {
        System.setProperty("spring.devtools.restart.enabled","false");
        SpringApplication.run(Application.class, arg);
    }

2.配置高级

@ConfigurationProperties(prefix = “xxx”):为第三方bean绑定属性,支持宽松绑定,如驼峰模式(ipAddress)、下划线模式(ip_address)、中划线(ip-address)和常量模式(IP_ADDRESS)

@Component
@Data
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
    private String ipAddress;
    private int port;
    private long timeout;//超时时间
}


@Bean
@ConfigurationProperties(prefix = "datasource")
public DruidDataSource datasource {
    DruidDataSource ds=new DruidDataSource();
    return ds;
}


servers:
  ipAddress: 192.168.0.1
  port: 8081
  timeout: -1
datasource:
  driverClassName: com.mysql.jdbc.Driver

@Value(“${xxx.yyy}”)也可以进行属性绑定,但是不支持松散绑定

数据校验:

  • 导入JSR303规范

    <dependency>
       <groupId>javax.validation</groupId>
       <artifactId>validation-api</artifactId>
     </dependency>
     <!--实用hibernate框架提供的校验器做实现类-->
     <dependency>
       <groupId>org.hibernate.validator</groupId>
       <artifactId>hibernate-validator</artifactId>
     </dependency>
    
  • 开启对当前bean的属性注入校验

    @Validated
     public class ServerConfig {
         private String ipAddress;
         @Max(value = 8888,message = "最大值不能超过8888")
         private int port;
         private long timeout;//超时时间
     }
    

3.测试

加载测试专用属性:用于小范围测试环境

  • 在启动测试环境时可以通过properties参数设置测试环境专用的属性,仅对当前测试类有效

    @SpringBootTest(properties = {"test.prop=testValue1"})
    class SpringbootPlusApplicationTests {
    
        @Value("${test.prop}")
        private String msg;
    
        @Test
        void contextLoads() {
            System.out.println(msg);
        }
    }
    
  • 在启动测试环境时可以通过args参数设置测试环境专用的传入属性

    @SpringBootTest(args = {"--test.arg=testValue2"})
    class SpringbootPlusApplicationTests {
    
        @Value("${test.arg}")
        private String msg;
    
        @Test
        void contextLoads() {
            System.out.println(msg);
        }
    
    }
    

加载测试专用配置:

  • 使用@Import注解加载当前测试类专用的配置

    @SpringBootTest
    @Import(MsgConfig.class)
    class SpringbootPlusApplicationTests {
    
        @Autowired
        private String msg;
    
        @Test
        void contextLoads() {
            System.out.println(msg);
        }
    }
    

web环境模拟测试

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
    @Test
    void testWeb(@Autowired MockMvc mvc) throws Exception {
        //创建虚拟请求,当前访问/books
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        //执行请求
        mvc.perform(builder);
    }

    @Test
    void testStatus(@Autowired MockMvc mvc) throws Exception {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值,与真实值进行比较
        //定义本次调用的预期值
        StatusResultMatchers status = MockMvcResultMatchers.status();
        //预计本次调用成功的:状态200
        ResultMatcher ok = status.isOk();
        //添加预期值到本次调用过程中进行匹配
        action.andExpect(ok);
    }

    @Test
    void testBody(@Autowired MockMvc mvc) throws Exception {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);

        //定义执行结果匹配器
        ContentResultMatchers content = MockMvcResultMatchers.content();
        //定义执行结果
        ResultMatcher result = content.string("springboot");
        //添加预期值到本次调用过程中进行匹配
        action.andExpect(result);
    }

    @Test
    void testRandomPort() {

    }
}

4.数据层解决方案

内置数据源

  • SpringBoot提供了3种内嵌的数据源对象供开发者选择
    ~ HikariCP:默认内置数据源对象
    ~ Tomcat提供DataSource: HikariCP不可用的情况下,且在web环境中,将使用tomcat服务器配置的数据源对象
    ~ Commons DBCP: Hikari不可用,tomcat数据源也不可用,将使用dbcp数据源
  • 通用配置无法设置具体的数据源配置信息,仅提供基本的连接相关配置,如需配置,在下一级配置中设置具体

设定

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test1?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    hikari:
      maximum-pool-size: 50

内置持久化解决方案——JdbcTemplate

JdbcTemplate是 Spring 对 JDBC 的封装,目的是使JDBC更加易于使用,JdbcTemplate是Spring的一部分


  org.springframework.boot
  spring-boot-starter-jdbc



@Repository
public class BookDaoImp implements BookDao{
 
    //按类型、自动注入了JdbcTemplate对象
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    @Override
    public void add(Book book) {
 
        //创建添加的sql语句
        String addsql = "insert into Book values(?,?,?)";
        //调用jdbcTemplate.update实现添加,book.getUserid()、book.getUsername()、book.getUstatus()对应values(?,?,?)里的三个问号
        Object[] args = {book.getUserid(), book.getUsername(), book.getUstatus()};
        int update = jdbcTemplate.update(addsql,args);
        //返回值update代表添加了几行
        System.out.println(update);
    }
 
    @Override
    public void update(Book book) {
        //根据id修改username ustatus
        String updatesql = "update Book set username=?,ustatus=?where user_id=?";
        Object[] args = {book.getUsername(), book.getUstatus(),book.getUserid(),};//注意参数顺序
        int update = jdbcTemplate.update(updatesql,args);
    }
 
    @Override
    public void delete(String id) {
        //根据user_id删除
        String deletesql = "delete from Book where user_id=?";
        int update = jdbcTemplate.update(deletesql,id);
    }
}

内嵌数据库——H2

  • 导入H2相关坐标

    <dependency>
       <groupId>com.h2database</groupId>
       <artifactId>h2</artifactId>
       <scope>runtime</scope>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-jpa</artifactId>
     </dependency>
    
  • 设置当前项目为web工程,并且配置H2管理控制台参数

    server:
      port: 80
    spring:
      h2:
        console:
          path: /h2
          enabled: true
    
  • 启动

NoSQL解决方案:

  • Redis:一款key-value存储结构的内存级NoSQL数据库
  • MongoDB:操作结构化数据且执行速度快的文档型数据库
  • Elasticsearch(ES):分布式全文搜索引擎

Redis:

  • 支持多种数据存储格式;支持持久化;支持集群
  • 服务端启动:redis-server.exe
  • 客户端启动:redis-cli.exe

springboot整合redis:

  • 导入坐标:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  • 相关配置

    spring:
      redis:
        host: localhost
        port: 6379
        client-type: lettuce
    
  • 使用操作接口RedisTemplate进行操作,注意:RedisTemplate以对象作为key和value,内部对数据进行序列化;StringRedisTemplate以字符串作为key和value,与Redis客户端操作等效

    @SpringBootTest
    public class RedisTest {
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Test
        void test() {
            ValueOperations ops = redisTemplate.opsForValue();
            ops.set("age", 23);
            Object age = ops.get("age");
            System.out.println(age);
    
            HashOperations opsForHash = redisTemplate.opsForHash();
            opsForHash.put("info","name","springboot");
            opsForHash.put("info","age",22);
            Object name = opsForHash.get("info", "name");
            System.out.println(name);
        }
    }
    

MongoDB

  • 应用场景
    1. 淘宝用户数据
      存储位置:数据库
      特征:永久性存储,修改频度极低
    2. 游戏装备数据、游戏道具数据
      存储位置:数据库、Mongodb
      特征:永久性存储与临时存储相结合、修改频度较高
    3. 直播数据、打赏数据、粉丝数据
      存储位置:数据库、Mongodb
      特征:永久性存储与临时存储相结合,修改频度极高
    4. 物联网数据
      存储位置:Mongodb
      特征:临时存储,修改频度飞速
  • 启动
    1. 服务端:mongod --dbpath=…\data\db
    2. 客户端:mongo --host=127.0.0.1 --port=27017
  • springboot整合mongodb:
    1. 导坐标

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
      </dependency>
      
    2. 配置mongodb访问uri

      spring:
        data:
          mongodb:
            uri: mongodb://localhost/test
      
    3. 提供操作mongodb接口对象MongoTemplate

Elasticsearch:

  • 分词,索引,倒排索引,创建文档,使用文档

  • IK分词器,设置索引创建规则

  • 文档操作

    1. 创建文档
      POST http://localhost:9200/books/_doc #使用系统生成id
      POST http://localhost:9200/books/_create/1 #使用指定id
      POST http://localhost:9200/books/_doc/1 #使用指定id,不存在创建,存在更新

    2. 查询文档
      GET http://localhost:9200/books/_doc/1 #查询单个文档
      GET http://localhost:9200/books/_search #查询全部文档

    3. 条件查询
      GET http://localhost:9200/books/_search?q=name:springboot

    4. 删除文档
      DELETE http://localhost:9200/books/_doc/1

    5. 修改文档(全量修改)
      PUT http://localhost:9200/books/_doc/1

    6. 修改文档(部分修改)
      POST http://localhost:9200/books/_update/1

  • 整合springboot

    1. 导坐标
    <!--低级别-->
    <!--<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>-->
    
    <dependency>
      <groupId>org.elasticsearch.client</groupId>
      <artifactId>elasticsearch-rest-high-level-client</artifactId>
    </dependency>
    
    1. 编写操作
    @SpringBootTest
     public class ElasticSearchTest {
     //    低级别
     //    @Autowired
     //    private ElasticsearchRestTemplate template;
     
         private RestHighLevelClient client;
     
         @BeforeEach
         void setUp() {
             HttpHost host = HttpHost.create("http://localhost:9200");
             RestClientBuilder builder = RestClient.builder(host);
             client = new RestHighLevelClient(builder);
         }
     
         @AfterEach
         void tearDown() throws IOException {
             client.close();
         }
     
         @Test
         void test() {
     
         }
     }
    

5.整合第三方技术

缓存

  • 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质

  • 使用缓存可以有效的减少低速数据读取过程的次数,提高系统性能

  • spring boot使用缓存

    1. 启用缓存
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-cache</artifactId>
     </dependency>
     
     @EnableCaching
    
    1. 设置进入缓存的数据
    @Cacheable(value = "cacheSpace",key = "#id")
    public Book getById(Integer id){
        return bookDao.selectById(id);
    }
    
    1. 设置读取缓存的数据
    2. 手机验证码案例:
    @Component
    public class CodeUtils {
        private String[] patch = {"000000", "00000", "0000", "000", "00", "0", ""};
    
        public String generator(String tel) {
            int hash = tel.hashCode();
            int encryption = 20206666;
            long result = hash ^ encryption;
    
            long nowTime = System.currentTimeMillis();
            result = result ^ nowTime;
            long code = result % 1000000;
            code = code < 0 ? -code : code;
            String codeStr = code + "";
            int len = codeStr.length();
            return patch[len] + codeStr;
        }
    
        @Cacheable(value = "smsCode", key = "#tel")
        public String get(String tel) {
            return null;
        }
        /*public static void main(String[] args) {
            System.out.println(new CodeUtils().generator("12345678765"));
        }*/
    }
    
    @Service
    public class SMSCodeServiceImpl implements SMSCodeService {
        @Autowired
        private CodeUtils codeUtils;
    
        @Override
        @CachePut(value = "smsCode", key = "#tel")
        public String sendCodeToSMS(String tel) {
            return codeUtils.generator(tel);
        }
    
        @Override
        public boolean checkCode(SMSCode smsCode) {
            String code = smsCode.getCode();
            String cacheCode = codeUtils.get(smsCode.getTel());
            return code.equals(cacheCode);
        }
    }
    
    @RestController
    @RequestMapping("/sms")
    public class SMSCodeController {
    
        @Autowired
        private SMSCodeService smsCodeService;
    
        @GetMapping
        public String getCode(String tel) {
    
            return smsCodeService.sendCodeToSMS(tel);
        }
    
        @PostMapping
        public boolean checkCode(SMSCode code) {
    
            return smsCodeService.checkCode(code);
        }
    
    }
             
    

jetcache

  • jetCache对SpringCache进行了封装,在原有功能基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能
  • jetCache设定了本地缓存与远程缓存的多级缓存解决方案
    1. 本地缓存( local)
      LinkedHashMap;Caffeine
    2. 远程缓存(remote)
      Redis;Tair

任务

  • Quartz是一个定时任务调度框架,比如你遇到这样的问题:
    比如淘宝的待支付功能,后台会在你生成订单后24小时后,查看订单是否支付,未支付则取消订单
    比如vip的每月自动续费功能

    想定时在某个时间,去做某件事

  • Quartz是一套轻量级的任务调度框架,只需要定义了 Job(任务),Trigger(触发器)和 Scheduler(调度器),即可实现一个定时调度能力。支持基于数据库的集群模式,可以做到任务幂等执行。

  • springboot整合Quartz

    1. 相关概念
      • 工作(Job)∶用于定义具体执行的工作
      • 工作明细(JobDetail)∶用于描述定时工作相关的信息
      • 触发器(Trigger):用于描述触发工作的规则,通常使用cron表达式定义调度规则
      • 调度器( Scheduler) :描述了工作明细与触发器的对应关系
    2. 配置
    --1.导坐标--
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    
    --2.实现类--
    public class MyQuartz extends QuartzJobBean {
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            System.out.println("quartz task run");
        }
    }
    
    --3.配置信息--
    @Configuration
    public class QuartzConfig {
        @Bean
        public JobDetail printJobDetail() {
            //绑定具体工作
            return JobBuilder.newJob(MyQuartz.class).storeDurably().build();
        }
    
        @Bean
        public Trigger printTrigger() {
            //绑定对应的工作明细
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
            return TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(scheduleBuilder).build();
        }
    }
    
  • springboot整合task

    --1.在主启动类上添加注解,开启定时任务功能--
    @EnableScheduling
    
    --2.在具体任务上添加cron表达式,设置定时执行的任务,并设定执行周期--
    @Component
    public class MyBean {
    
        @Scheduled(cron = "0/1 * * * * ?")
        public void print() {
            System.out.println("spring task run...");
        }
    }
          
    

邮件

  • springboot整合JavaMail

    --1.导坐标--
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    
    --2.配置--
    spring:
      mail:
        host: smtp.qq.com
        username: 1474150648@qq.com
        password: orqbnhnhpietgjii
        
    --3.发送简单邮件--
    @Service
    public class SendMailServiceImpl implements SendMailService {
        @Autowired
        private JavaMailSender sender;
    
        //发送人
        private String from = "[email protected]";
        //接收人
        private String to = "[email protected]";
        //标题
        private String subject = "测试邮件";
        //正文
        private String context = "测试邮件正文内容";
    
        @Override
        public void sendMail() {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(from);
            message.setTo(to);
            message.setSubject(subject);
            message.setText(context);
            sender.send(message);
        }
    }
    
    --3.发送多部件邮件--
    @Service
    public class SendMailServiceImpl2 implements SendMailService {
        @Autowired
        private JavaMailSender sender;
    
        //发送人
        private String from = "[email protected]";
        //接收人
        private String to = "[email protected]";
        //标题
        private String subject = "测试邮件";
        //正文
        private String context = "点开有惊喜";
    
        @Override
        public void sendMail() {
    
            try {
                MimeMessage message = sender.createMimeMessage();
                MimeMessageHelper helper = new MimeMessageHelper(message, true);
                helper.setFrom(from + "派大星");
                helper.setTo(to);
                helper.setSubject(subject);
                helper.setText(context, true);
    
                //添加附件
                File file = new File("xxxx");
                helper.addAttachment(file.getName(), file);
    
                sender.send(message);
            } catch (MessagingException e) {
                e.printStackTrace();
            }
        }
    }
    

消息

  • 消息发送方——生产者;消息接收方——消费者

  • JMS:一个规范,提供了与消息服务相关的API接口

    • 消息模型:点对点模型(消息只能被一个消费者消费);发布订阅模型(消息可以被多个消费者消费)
  • AMQP:一种协议,规范了网络交换的数据格式,兼容JMS

  • ActiveMQ

    --1.导坐标--
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
    
    --2.做配置--
    spring:
      activemq:
        broker-url: tcp://localhost:61616
      jms:
        template:
          default-destination: springboot
          
    --3.使用模板进行操作--
    @Service
    public class MessageServiceImpl implements MessageService {
    
        @Autowired
        private JmsMessagingTemplate messagingTemplate;
    
        @Override
        public void sendMessage(String id) {
            System.out.println("待发送短信的订单已纳入处理队列,id:" + id);
            messagingTemplate.convertAndSend(id);
        }
    
        @Override
        public String doMessage() {
            String id = messagingTemplate.receiveAndConvert(String.class);
            System.out.println("已完成短信发送业务,id:" + id);
    
            return id;
        }
    }
    
  • RabbitMQ:基于Erlang语言编写,需要进行安装

  • RocketMQ

6.监控

监控的意义

  • 监控服务状态是否宕机
  • 监控服务运行指标(内存、虚拟机、线程、请求…)
  • 监控日志
  • 管理服务(服务下线)

可视化监控平台——springbootadmin

    --Admin服务端--
    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-server</artifactId>
      <version>2.3.1</version>
    </dependency>
    server:
      port:8080
    
    @EnableAdminServer
    
    --Admin客户端--
    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-client</artifactId>
      <version>2.3.1</version>
    </dependency>
    注意:admin的版本号必须与springboot的一致
    spring:
      boot:
        admin:
          client:
            url: http://localhost:8080
    management:
      endpoint:
        health:
          show-details: always
        #端点功能信息开启
        info:
          enabled: true
      #配置通过web端读取监控信息
      endpoints:
        web:
          exposure:
            include: "*"
        #端点功能信息开启
        enabled-by-default: true

监控原理

  • Actuator提供了SpringBoot生产就绪功能,通过端点的配置与访问,获取端点信息
  • 端点描述了一组监控信息,SpringBoot提供了多个内置端点,也可以根据需要自定义端点信息
  • 访问当前应用所有端点信息: /actuator
  • 访问端点详细信息:/actuator/端点名称

自定义监控指标

  • info端点指标控制

    --方法1:yml--
    info:
      author: xin
      appName: @project.artifactId@
      version: @project.version@
    --方法2:实现InfoContributor接口--
    @Component
    public class AppInfoContributor implements InfoContributor {
        @Override
        public void contribute(Info.Builder builder) {
            Map infoMap = new HashMap();
            infoMap.put("buildTime", "2023");
            builder.withDetail("company", "itheima");
            builder.withDetails(infoMap);
        }
    }
    
  • health端点指标控制

    @Component
    public class HealthConfig extends AbstractHealthIndicator {
        @Override
        protected void doHealthCheck(Health.Builder builder) throws Exception {
            Map infoMap = new HashMap();
            infoMap.put("buildTime", "2023");
            builder.withDetail("company", "itheima");
            builder.withDetails(infoMap);
            builder.status(Status.UP);
        }
    }
    
  • 自定义端点

    @Component
    @Endpoint(id = "pay", enableByDefault = true)
    public class PayEndpoint {
        @ReadOperation
        public Object getPay() {
            //调用业务操作,获取支付相关信息结果,最终return出去
            Map payMap = new HashMap();
            payMap.put("level1", 200);
            payMap.put("level2", 300);
            return payMap;
        }
    }
    

4.原理篇

1.bean的加载方式

方式一:XML方式声明bean

<beans>
  <!--声明自定义bean-->
  <bean id="bookService" class="com.xin.service.impl.BookServiceImpl"/>
  <!--声明第三方bean-->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
</beans>

方式二:

  • 使用@Component以及衍生注解@Controller、@Service、@Repository定义bean

    @Service
    public class OrderServiceImpl implements OrderService {}
    
  • 使用@Bean定义第三方bean,并将所在类定义为配置类或Bean

    //@Component
    @Configuration(proxyBeanMethods = true)
    public class MPConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return interceptor;
        }
    }
    

    proxyBeanMethods可以保障用此方法得到的对象是从容器中获取的而不是重新创建的,@Configuration默认的proxyBeanMethods就为true

  • 通过xml组件扫描bean

      <!--扫描基于注解的方式配置bean-->
      <context:component-scan base-package="com.xin.service.impl"/>
    

方式三: 注解方式声明配置类代替了配置文件XML

@Configuration
@ComponentScan("com.xin")
public class SpringConfig {}

方式四:

  • 使用@Import注解导入要注入的bean对应的字节码

    @Import(Book.class)
    public class SpringConfig{
    }
    
  • 被导入的bean无需使用注解声明为bean

    public class Book{}
    

方式五:使用上下文对象在容器初始化完毕后注入bean

 public class AppImport {
      public static void main(String[] args) {
          ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
          //手工加载bean
          ctx.register(Book.class);
      }
  }

方式六:导入实现了ImportSelector接口的类,实现对导入源的编程式处理

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Import");
        if (flag) {
            return new String[]{"com.xin.pojo.Book"};
        }
        return new String[]{"com.xin.pojo.People"};
    }
}

方式七:导入实现了ImportBeanDefinitionRegistrar接口的类,通过BeanDefinition的注册器注册实名bean,实现对bean的裁定

public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //1.使用元数据去做判定

        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Book.class).getBeanDefinition();
        registry.registerBeanDefinition("book", beanDefinition);
    }
}

方式八: 导入实现了BeanDefinitionRegistryPostProcessor接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的最终裁定

public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

2.bean的加载控制

根据特定情况对bean进行选择性加载以达到适用于项目的目标

  • bean的加载方式五、六、七、八可以达到控制(编程式)

    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata metadata) {
            Class<?> flag = Class.forName("com.xin.pojo.Cat);
            if (flag!=null) {
                return new String[]{"com.xin.pojo.Book"};
            }
            return new String[]{"com.xin.pojo.People"};
        }
    }
    
  • 注解式:

    1. 匹配指定类

      public class SpringConfig {
           @Bean
       //    @ConditionalOnClass(Book.class)
           @ConditionalOnClass(name = "com.xin.pojo.Book")
           public Book book01() {
               return new Book();
           }
       }
      
    2. 未匹配指定类

      public class SpringConfig {
          @Bean
          @ConditionalOnMissingClass("com.xin.pojo.Book")
          public Book book01() {
              return new Book();
          }
        }
      
    3. 匹配指定类型的bean

      @Import(Book.class)
      public class SpringConfig {
          @Bean
          @ConditionalOnBean(Book.class)
          public Book book01() {
              return new Book();
          }
      }
      

3.bean依赖属性配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
  • 将业务功能bean运行需要的资源抽取成独立的属性类(…Properties),设置读取配置文件信息

    @Data
    @ConfigurationProperties(prefix = "cartoon")
    public class CartoonProperties {
        private Cat cat;
        private Dog dog;
    }
    
  • 配置文件中使用固定格式为属性类注入数据

    cartoon:
      cat:
        name: tom
        age: 5
      dog:
        name: merry
        age: 4
    
    
  • 使用 @EnableConfigurationProperties 注解设定使用属性类时加载bean

    @Component
    @Data
    //根据条件加载bean:当加载该类时去加载读取CartoonProperties类
    @EnableConfigurationProperties(CartoonProperties.class)
    public class Cartoon {
    
        private Cat cat;
        private Dog dog;
    
        @Autowired
        private CartoonProperties cartoonProperties;
    
       /* public Cartoon() {
            cat = new Cat();
            cat.setName("tom");
            cat.setAge(5);
            dog = new Dog();
            dog.setName("merry");
            dog.setAge(4);
        }*/
    
        public void play() {
            //System.out.println(cat.getAge() + "岁的" + cat.getName() + "和" + dog.getAge() + "岁的" + dog.getName() + "打了一架");
            System.out.println(cartoonProperties.toString());
        }
    }
    

4.自动配置

配置过程:

  1. 先开发若干种技术的标准实现
  2. springboot启动时加载所有的技术实现对应的自动配置类
  3. 检测每个配置类的加载条件是否满足并进行对应的初始化
  4. 先加载所有的外部资源,然后根据外部资源进行条件比对
 @SpringBootApplication
 /*  @SpringBootConfiguration
          @Configuration
          @Indexed
      @EnableAutoConfiguration
          @AutoConfigurationPackage
              @Import({AutoConfigurationPackages.Registrar.class}) 设置当前配置所在的包作为扫描包,后续要针对当前包进行扫描
          @Import({AutoConfigurationImportSelector.class})
      @ComponentScan(
              excludeFilters = {@ComponentScan.Filter(
                      type = FilterType.CUSTOM,
                      classes = {TypeExcludeFilter.class}
              ), @ComponentScan.Filter(
                      type = FilterType.CUSTOM,
                      classes = {AutoConfigurationExcludeFilter.class}
              )}
      )*/

底层配置流程:

  • SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
  • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
  • 生效的配置类就会给容器中装配很多组件
  • 只要容器中有这些组件,相当于这些功能就有了
  • 定制化配置
    • 用户直接自己@Bean替换底层的组件
    • 用户去看这个组件是获取的配置文件什么值就去修改。
  • xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties

5.自定义starter

  • 业务功能开发

    public class IpCountService {
        //计数集合
        private Map<String, Integer> ipCountMap = new HashMap<>();
    
        @Autowired
        //当前的request对象的注入工作由使用当前starter的工程提供自动装配
        private HttpServletRequest httpServletRequest;
    
        public void count() {
            //每次调用当前操作,就记录当前访问的IP,然后累加访问次数
            //1.获取当前操作的IP地址
            String ip = httpServletRequest.getRemoteAddr();
            System.out.println("---------------------------" + ip);
            //2.根据IP地址从Map中取值,并递增
            ipCountMap.put(ip, ipCountMap.getOrDefault(ip, 0) + 1);
        }
    }
    
  • 自动配置类

    //@Import(IpCountService.class)
    public class IpAutoConfiguration {
        @Bean
        public IpCountService ipCountService() {
            return new IpCountService();
        }
    }
    
  • 配置(/META-INF/spring.factories),开启自动配置功能,项目启动自动加载

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      cn.itcast.autoconfig.IpAutoConfiguration
    
  • 在其他项目中引入该自定义starter依赖,并在控制层进行模拟调用

    @RestController
    @RequestMapping("/books")
    public class BookController {
        @Autowired
        private IpCountService ipCountService;
    
        @GetMapping
        public String test() {
            ipCountService.count();
            return "ok";
        }
    }
    
  • 使用属性配置设置功能参数

    1. 定义属性类,加载对应属性
    @ConfigurationProperties("tools.ip")
    @Data
    public class IpProperties {
        /**
         * 日志显示周期
         */
        private Long cycle = 5L;
        /**
         * 是否周期内重置数据
         */
        private Boolean cycleReset = false;
    
        /**
         * 日志输出模式 detail:详细模式;simple:极简模式
         */
        private String model = LogModel.DETAIL.value;
    
        public enum LogModel {
            DETAIL("detail"),
            SIMPLE("simple");
            private String value;
    
            LogModel(String value) {
                this.value = value;
            }
    
            public String getValue() {
                return value;
            }
        }
    }
    
    1. 设置加载Properties类为bean
    @EnableConfigurationProperties(IpProperties.class)
    @EnableScheduling
    public class IpAutoConfiguration {
        @Bean
        public IpCountService ipCountService() {
            return new IpCountService();
        }
    }
    
    1. 根据配置切换设置
    @Scheduled(cron = "0/5 * * * * ?")
        public void print() {
    
            if (ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())) {
                System.out.println("        ip访问监控");
                System.out.println("+-----ip-address-----+--num--+");
                for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
                    String key = entry.getKey();
                    Integer value = entry.getValue();
                    System.out.println(String.format("|%18s  |%5d  |", key, value));
                }
                System.out.println("+--------------------+-------+");
    
            } else if (ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())) {
                System.out.println("     ip访问监控");
                System.out.println("+-----ip-address-----+");
                for (String key : ipCountMap.keySet()) {
                    System.out.println(String.format("|%18s  |", key));
                }
                System.out.println("+--------------------+");
    
            }
    
            if (ipProperties.getCycleReset()) {
                ipCountMap.clear();
            }
        }
    

6.核心原理

1.SpringBoot启动流程

  • 初始化各种属性,加载成对象
    1. 读取环境属性(Environment)
    2. 系统配置(spring.factories)
    3. 参数(Arguments、application.properties )
  • 创建Spring容器对象ApplicationContext,加载各种配置
  • 在容器创建前,通过监听器机制,应对不同阶段加载数据、更新数据的需求
  • 容器初始化过程中追加各种功能,例如统计时间、输出日志等

2.容器类型选择

3.监听器

5.补充扩展

  • @Component和@Bean的区别
    1. 作用对象不同:@Component注解作用于类,而@Bean注解作用于方法
    2. @Component通常是通过路径扫描来自动检测以及自动装配到Spring容器中,@Bean告诉spring这是某个类的实例,当需要它时给我
    3. @Bean比@Component的自定义性更强,很多地方我们只能通过@Bean来注册bean,如当我们引用第三方库中的类需要装配到spring容器时,只能通过@Bean来实现
  • 开启yml提示功能
  1. 导坐标

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
      </dependency>
    
  2. 将target/classes/META-INF/spring-configuration-metadata.json复制到项目/META-INF下,移除processor依赖即可

  3. 自定义提示功能开发

     "hints": [
        {
           "name": "tools.ip.model",
           "values": [
             {
               "value": "detail",
               "description": "明细模式"
             },
             {
               "value": "simple",
               "description": "极简模式"
             }
           ]
         }
       ]
    

你可能感兴趣的:(Java开发,spring,boot,学习,java)