springBoot构建
parent
spring boot 继承了spring-boot-dependencies 定义了很多包的版本,在使用的时候无需指定包版本,直接使用,免去了人工依赖导致版本冲突,依赖的管理完全取决于spring boot 的大版本,继承parent的形式也可以采用引用依赖的形式效果
starter
spring boot 中常见的项目名称,定义了当前项目使用的所有依赖坐标,以达到减少配置的目的
引导类
@SpringBootApplication
public class ExceltoolApplication {
public static void main(String[] args) {
SpringApplication.run(ExceltoolApplication.class, args); // 返回值就是一个启动spring容器
}
}
@SpringBootApplication // 默认扫描包为引导类所在的包及其子包放入容器@CompoentScan规则
内置tomcat
spring-boot-starter-tomcat ===>内嵌了tomcat核心 tocat-embed-core
运行的时候封装成tomcat对象,放到spring容器中
可以指定其他服务器 jetty weblogic等 在web工程中排除tomcat 依赖
获取bean的方式
获取容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml")
1、根据id获取
ioc.getBean("helloword")
2、根据类型获取
ioc.getBean(Helloword.class)
3、根据id和类型获取
ioc.getBean("helloword",Helloword.class)
4、我们还是可以通过接口Person来获取到Student的bean。也就是说在IOC容器中配置了一个bean后,我们是可以通过它所继承的父类或所实现的接口来获取
springBoot配置
配置文件加载顺序和级别
application.properties >application.yml >application.yaml
1级:file :config/application.yml // 最高
2级:file :application.yml
3级:classpath: config/application.yml
4级:classpath: application.yml // 最低
读取配置文件
baseDir: c:\user
tempDir:${baseDir}\temp
datasource.driver= com.mysql.jdbc.Driver
datasource.url = jdbc:mysql://localhost/springboot
datasource.username = root
datasource.password = 123456
@Value("${key: 默认值}")
@Value("${tempDir}")
private String tempDir;
Environment 对象
@Autowired
private Environment env; //所有属性都在这个对象中
env.getProperty("temDir")
@ConfigurationProperties("datasource")
// 首先得是一个spring管控的bean
@Compoent
@ConfigurationProperties("datasource") // 配置前缀
pulic class MyDatasource{
private String dirver;
private String url;
private String username;
private String password;
}
多环境开发
spring:
profiles:
active: pro // 当前激活环境
---
server:
port: 80
spring:
config:
activate:
on-profile:pro
---
server:
port: 81
spring:
config:
activate:
on-profile:dev
上述多环境在一个配置文件中,可以分成三个多环境配置文件application.yml(主配置文件), application-pro.yml,application-dev.yml
#主配置文件 指定环境
spring:
profiles:
active: pro
#application-pro.yml文件
server:
port: 80
spring:
config:
activate:
on-profile:pro
#application-dev.yml文件
server:
port: 81
spring:
config:
activate:
on-profile:dev
多环境开发包含文件使用group(include 2.4以前版本)属性设置配置文件分组,便于线上管理注意:如果是properties 只支持多文件版本,不支持---单一文件配置
maven profille的级别大于配置文件profile
<!--pom-->
<profiles>
<profile>
<id>env_dev</id>
<properties>
<profile.active>dev</profile.active>
</properties>
<activation>
<!--默认启动dev-->
<actoveByDefault>true</actoveByDefault>
</activation>
</profile>
<profile>
<id>env_pro</id>
<properties>
<profile.active>pro</profile.active>
</properties>
</profile>
</profiles>
yml 读取pom环境(读取maven配置属性值 @..@)
spring:
profiles:
active: @profile.active@ # 读取maven配置属性值 @..@
group:
"dev": pro,aa
"pro": dev,bb
配置高级
@ConfigurationProperties可以为第三方bean绑定属性
@SpringBootApplication
@EnableConfigurationProperties // 可以将使用@ConfigurationProperties注解对应的类加入spring容器
public class SpringBootDemo{
@Bean
@ConfigurationProperties(perfix = "datasource")
public DruidDataSource datasource(){
return new DruidDataSource();
}
}
// @EnableConfigurationProperties 与@Compoent 不能同时使用
解除使用@ConfigurationProperties注释警告
Spring Boot Configuration Annotation Processor not configured
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
@ConfigurationProperties松散绑定
servers:
ip_adress:192.168.0.143
@ConfigurationProperties("servers")
public class Test(){
private String ipAdress; // 能够匹配配置中的ip-adress 常量、中划线模式、下划线等 只有@ConfigurationProperties注解能用,@Value就不能用
}
常用计量单位
servers:
serverTimeOut:3 #默认s
data-size: 10
@ConfigurationProperties("servers")
public class Test(){
@DurationUnit(ChronoUnit.MINUTES) //支持多种形式
private Duration serverTimeOut; //JDK8提供的计量单位
@DataSizeUnit(DataUnit.Bytes)
private DataSize datasize; // 容量大小
}
数据校验JSR303
<!--导入校验规范-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!--添加校验器实现-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
@ConfigurationProperties("servers")
// 开启对当前bean属性注入校验
@Validated
public class Test(){
@DurationUnit(ChronoUnit.MINUTES)
private Duration serverTimeOut;
// 设置校验规则
@Max(value=8888,message="最大值不能超过8888")
@DataSizeUnit(DataUnit.Bytes)
private DataSize datasize;
}
配置需注意进制转换
dataSource:
password: 0127 # 读到的属性为 8进制 为87 方式为 "0127"
springBoot 开发
异常处理器
在表现层 controller处理异常即可,所有异常都会抛到表现层
@RestControllerAdvice // springMVC异常处理器
public class ProjectExceptionAdvice{
// 拦截所有异常信息
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
// 记录日志
ex.printStackTrace();
return new Result(500,"服务器故障") // 返回统一结果集
}
}
临时属性设置
java -jar springboot.jar --server.port = 80
多个属性之间用空格连接(命令行参数) --属性名 = 值 命令行属性 > 配置属性
日志
public class XxxController{
// 日志 导包 导入日志门面 slf4j
private static final Logger log = LoggerFactory.getLogger(XxxController.class);
public String getXxx(){
// 级别由低到高
log.debug("debug...");
log.info("info...");
log.warn("warn...");
log.error("error...");
}
}
// 默认系统日志为info级别
调整日志级别配置 debug:true
logging.level.root = info // 项目路径下的日志级别
// 注解形式日志
基于lombok提供的@Slf4j注解为类快速添加日志对象
日志输出格式
时间 级别PID---[所属线程] 当前记录位置(包名过长会简化包名等): 日志信息
设置日志输出格式
# %p代表级别
#%5n 占5个位置 换行
#%clr显示颜色
#%16t 显示线程
#%40c 显示类型 %-40.40c -40左对齐 40.40 包名.类名
#%m 显示消息
logging:
pattern:
#console: "%d - %m %n"
#console: "%d %clr(%5p) --- [%16t] %-40.40c :%m %5n"
日志输出到文件
logging:
file:
name: server.log # 位置在项目同级目录
logback:
rollingpolicy: #设置滚动日志
max-file-size :4kB #最大文件大小 超过大小在来一个新文件
file-name-pattern:server.%d{yyyy-mm-dd}.%i.log #文件名 %d 时间 %i index 0开始
单独日志配置文件 src/main/resources/logback-spring.xml
<?xml version="1.0" encoding="utf-8" ?>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,
默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 定义日志文件 输入位置 -->
<property name="logPath" value="./excel_log" />
<!-- 日志最大的历史 30天 -->
<property name="maxHistory" value="30"/>
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<!-- layout代表输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</layout>
</appender>
<!-- 日志输出文件 -->
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</encoder>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender-->
<!-- 滚动策略,它根据时间来制定滚动策略.既负责滚动也负责触发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 输出路径 -->
<fileNamePattern>${logPath}/info/%d.log</fileNamePattern>
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6,
则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除-->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
</appender>
<!-- 特殊记录Error日志 -->
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 只记录ERROR级别日志,添加范围过滤,可以将该类型的日志特殊记录到某个位置 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/error/%d.log</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>
<!--定义日志输出级别-->
<logger name="com.baomidou.mybatisplus" level="INFO" />
<logger name="com.neuxa.egi.exceltool.controller" level="INFO"/>
<root level="info">
<!-- 引入控制台输出规则 -->
<appender-ref ref="consoleLog" />
<appender-ref ref="fileInfoLog" />
<appender-ref ref="fileErrorLog" />
</root>
</configuration>
热部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
注意:手动开启热部署 Ctrl+F9 重新构建 热部署不加载jar资源,只加载自身开发资源
自动启动热部署
- settings=>Complier => Build Project automatically【勾选】
- Ctrl +Alt+ Shift +/ =>Registry => complier.automake.allow.when.app.running【勾选】
- 规则:自动原则,当编译器失去焦点5s之后自动构建
热部署范围
默认不触发重启的目录列表
/META-INF/maven
/META-INF/resources
/resources
/static
/public
/templates// 设置不参与热部署的范围
spring.devtools.restart.exclude = static/**,public/**,config/application.yml关闭热部署
spring.devtools.restart.enable = false
测试
加载测试专用属性
@springBootTest(args ={"--test.arg = testValue"},properties = {"test.arg = testvalue1"})
@Import(MsgConfig.class) // 仅仅在局部测试生效
public class PropertiesTest{
@Value("${test.arg}")
private String msg;
@Autoeired
private String ttmsg;
@Test
void testArgs(){
System.out.println(msg);
System.out.println(ttmsg);
}
}
测试web环境虚拟调用
@springBootTest(WebEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) //指定随机端口
// 开启虚拟MVC调用
@AutoConfigureMockMVc
public class PropertiesTest{
@Test
void test(@Autowired MockMvc mvc) throws Exception{
// 创建了一个虚拟请求 ,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books")
// 执行请求
ResultActions action =mvc.perform(builder)
//设置执行状态匹配器
StatusResultMatchers status = MockMvcResultMatchers.status();
// 设置预计成功状态 200
ResultMatcher ok = status.isOk();
// 添加预计值匹配
action.addExpect(ok);
}
}
// 测试web环境端口测试
数据库层测试事务回滚
@springBootTest
@Transactional // 加上它标识事务不提交 springboot发现它是测试类,所以不提交事务
@Rollback(false) // 设置不回滚 提交事务
public class DaoTest{
}
数据层解决方案
sql解决方案(mysql sqlserver oracle)
spring boot 内嵌3种数据库 H2 HSQL Derby
spring boot 内嵌3中数据源对象
HikariCP:默认内置数据源对象
Tomcat提供的DataSource:HikariCP不可用的时候,且在web环境中将使用tomcat提供的数据源对象
Commons DBCP: HikariCP不可用,Tomcat提供的DataSource也不可用,使用dbcp数据源
通过数据源对象获取数据库链接
spring:
datasource:
url:jdbc:mysql://localhost:3306/ssm?serverTimeZone=UTC
hikari:
driver-class-name:com.mysql.cj.jdbc.Driver
username:root
password:root
maximun-pool-size:50
---
spring:
datasource:
url:jdbc:mysql://localhost:3306/ssm?serverTimeZone=UTC
druid:
driver-class-name:com.mysql.cj.jdbc.Driver
username:root
password:root
maximun-pool-size:50
nosql解决方案(redis、mongo、es)
整合redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
host: localhost
port: 6379
操作redis
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
@SpringBootTest
class Springboot16RedisApplicationTests {
//用这个属性就能操作Redis
@Autowired
private RedisTemplate redisTemplate;
//下面测试类运行的时候,一定要确保Redis是开启的状态
@Test
void set() {
// String类型
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("age","41");
}
@Test
void get() {
// 取值
ValueOperations valueOperations = redisTemplate.opsForValue();
Object age = valueOperations.get("age");
System.out.println(age);
}
//hash
@Test
void hSet() {
HashOperations ho = redisTemplate.opsForHash();
// key是info
ho.put("info","a","aa");
}
@Test
void hGet() {
//获取值
HashOperations ho = redisTemplate.opsForHash();
Object p = ho.get("info","a");
System.out.println(p);
}
}
发现问题,redisTemplate操作的行为,在命令行不同步,具体原因 RedisTemplate<k,V> 带有泛型,不给,默认以对象的方式操作的redis,可以使StringRedisTemplate方式操作redis
import org.springframework.data.redis.core.ValueOperations;
@SpringBootTest
public class StringRedisTemplateTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void get(){
ValueOperations<String,String> ops = stringRedisTemplate.opsForValue();
String name = ops.get("name");
System.out.println(name);
}
}
jedis操作redis
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
spring:
redis:
host: localhost
port: 6379
client-name: jedis
运行之前的代码,一样的效果,jedis和lettuce 都是springboot的内部实现,外层不表现,默认使用lettuce,如果使用jedis,引入依赖,配置使用
整合mogodb
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring:
data:
mongodb:
uri: mongodb://localhost/test
操作mogo
@SpringBootTest
class Springboot17MongodbApplicationTests {
@Autowired
private MongoTemplate mongoTemplate;
@Test
void save() {
Book book = new Book();
book.setId(111);
book.setType("1");
book.setDescription("perfect");
mongoTemplate.save(book);
}
@Test
void find(){
List<Book> all = mongoTemplate.findAll(Book.class);
System.out.println(all);
}
}
整合es
es 分布式全文检索,java语言编写,安装默认带一个高版本jdk,方便后续拓展,.kibana客户端操作连接es,根据关键字得到id,在通过id查到数据,称为倒排索引,分词器(ik存储数据),建立索引映射规则,加分词器,添加文档,_doc/1 都是以请求的方式添加
- Lucene:底层的API,工具包
- Solr:基于Lucene开发的企业级的搜索引擎产品
- Elasticsearch:基于Lucene开发的企业级的搜索引擎产品
<!--低级别客户端引入方式-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
#第二种方法:Druid专用配置 推荐
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT
username: root
password: admin
#低级别配置方式
elasticsearch:
rest:
uris: http://localhost:9200
<!--高级别引入推荐-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
// 手动开启客户端,不是springboot维护
private RestHighLevelClient client;
@Test
void testCreateClient() throws IOException{
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
// 创建名称为books的索引
CreateIndexRequest request = new CreateIndexRequest("books");
client.indices().create(request,RequestOptions.DEFAULT)
// 关闭客户端
client.close();
}
缓存(数据临时存储介质)
springboot 提供了缓存技术,还可以对其他缓存技术进行整合,统一接口,方便缓存技术的开发管理
Generic
JCache
EhCache
Hazelcast
Infinispan
Couchbase
Redis
Caffenine
Simple(默认 内存级)
memcached 不是springboot内置的
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
@SpringBootApplication
@EnableCaching // 开启缓存功能
public class ExceltoolApplication {
private static final Logger log = org.slf4j.LoggerFactory.getLogger(ExceltoolApplication.class);
public static void main(String[] args) {
SpringApplication.run(ExceltoolApplication.class, args);
}
}
@Cacheable(value="cacheSpace",key="#id")
public Book getById(Integer id){
return bookDto.selectById(id);
}
数据淘汰策略
- LRU 挑选最近最少使用的数据淘汰
- LFU 挑选最近使用次数最少的数据淘汰
- TTL 挑选要过期的淘汰
- RANDOM 随机淘太
使用redis做缓存
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
cache:
type:redis
use-key-prefix:true #是否使用前缀
cache-null-values:false #是否缓存空值
key-prefix:sys_ #指定key的前缀 必须使用 以防乱套
tiem-to-live:10s # 设置缓存过期时间
redis:
host:localhost
port:6379
#直接使用即可,缓存数据存储到redis
使用memcached做缓存
1、下载安装
2、客户端选择
Memcached Client for java :最早期的客户端,稳定可靠,用户群广
SpyMemcached :效率更高
XMemcached: 并发处理更好 #推荐选择
<!--选择客户端-->
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmencached</artifactId>
<version>2.4.7</version>
</dependency>
@Configuration
public class XMemcachedConfig{
@Bean
public MemcacheClient getMemcacheClient() throws IOException{
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBilder("localhost:11211");
MemcacheClient memcacheClient = memcachedClientBuilder.build();
return memcacheClient;
}
}
使用memcache
// expire 0代表永不过期 放入缓存
memcacheClient.set(key,expire,value)
// 取出缓存
memcacheClient.get(key);
定时任务
java本身的Timer可以实现定时任务(java.util.Timer),其他定时任务,封装了Timer,进行了专业封装 比如 quartz
Timer timer = new Timer();
TimerTask task = new TimerTask(){
public void run{
System.out.println("XXXX")
}
//2s一次
timer.schedule(task,0,2000);
}
整合quartz
相关概念
-
工作(job):用于具体执行的工作
-
工作明细(JobDetail):用于描述定时工作相关的信息
-
触发器(Tigger):用于描述触发工作的规则,通常使用cron表达式定义调度
-
调度器(Scheduler):描述工作明细和触发器的对应关系
关系 任务job绑定 工作明细 ,工作明细绑定触发器 并指定Scheduler
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quarz</artifactId>
</dependency>
public class MyQuartz extends QuartzJobBean{
// 具体任务job
protected void executeInternal(JobExecutionContext context) throws JobExectionException{
System.out.println("xxxx")
}
}
@Configuration
public class QuartzConfig{
//工作明细bean 要绑定具体的工作任务
@Bean
public JobDetail printJobDetail(){
return JobBuilder.newJob(MyQuartz.class).storeDurably().build();
}
// 触发器Bean
@Bean
public Trigger printTigger(){
// 绑定具体的工作明细
ScheduleBuilder schedBuilder = CronScheduleBuilder.cronSchedule("0/15 * * * * ?);
return TriggerBuilder.newTrigger().forJob(printJobDetail).withSchedule(schedBuilder).build();
}
}
整合Spring Task
@SpringBootApplication
@EnableScheduling // 开启定时任务开关
public class ExceltoolApplication {
public static void main(String[] args) {
SpringApplication.run(ExceltoolApplication.class, args);
}
}
@Component
public class testSchedule{
@Scheduled(cron = "0 0 * * * ?")
public void print(){
System.out.println("123213");
}
}
发送邮件
概念
- SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
- POP3(Post Office Protocol - Version 3 ):用于接收电子邮件的标准协议
- IMAP (Internet Mail Access Protocol): 互联网消息协议,是POP3的替代协议
springBoot整合JavaMail
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
配置
spring:
mail:
host:smtp.qq.com
username:jock
# 邮件服务器提供的授权码
password:ewdrtfgjaskd
发送邮件
public class Send{
@Autowired
private JavaMailSender javaMailSender;
// 发送人
private String from = "XXX@213432.com";
// 接收人
private String to = "xxx@.com";
// 标题
private String subject = "测试邮件";
// 正文
private String context = "测试邮件正文" ;
private File f1 = new File("C:\\XXX");
private File f2 = new File("C:\\XXX");
public void sendSimpleMessage(){
//发送简单消息
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setFrom(from);
simpleMailMessage.setTo(to);
simpleMailMessage.setSubject(subject);
simpleMailMessage.setText(context);
javaMailSender.send(simpleMailMessage);
}
public void sendMail(){
// 发送复杂邮件
MimeMessage message = javaMailSender.createMimeMessage();
// true 代表可以发送附件
MimeMessageHelper helper = new MimeMessageHelper(meaage,true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(context); // 可以发送图片 setText(context,true); true为html
// 添加附件
helper.addAttachment(f1.getName(),f1);
helper.addAttachment(f2.getName(),f2)
javaMailSender.send(message);
}
}
消息处理
业务系统接到请求之后,不是马上执行,而是将消息推送到消息队列(mq),消费者在订阅消息,处理业务
企业中广泛使用的三种异步消息传递技术
JMS规范 ActiveMQ RabbitMQ RocketMQ(没有完全遵守)
AMQP协议 一种协议,高级消息队列协议,规范了网络交换的数据格式,兼容JMS 跨平台,消费者可以使用不同语言来实现 RabbitMQ RocketMQ
MQTT规范 消息队列遥测传输,专为小设备,专用于物联网
整合RabbitMQ
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
rabbitmq:
host: localhost
port:5672
@Configuration
public class RabbitConfigDirect{
// 队列
@Bean
public Queue directQueue(){
return new Queue("direct_queue");
}
// 交换机
@Bean
public DirectExchange directExChange(){
return new DirectExchange("directExChange");
}
// 队列绑定交换机
@Bean
public Binding bindingDirect(){
return BindingBuilder.bind(directQueue()).to(directExChange()).with("direct"); // 改为topic 代表topic类型
}
}
public class sendMq{
@Autowired
private AmqpTemplate amqpTemplate;
public void sendMessage(String id){
amqpTemplate.convertAndSend("directExChange","direct",id);
}
}
--------listener
@Componemt
public class MessageListener{
@RabbitListener(queues="direct_queue")
public void receive(String id){
System.out.println("已经完成发送业务"+ id);
}
}
监控
监控的意义
监控服务状态是否宕机
监控服务运行指标(内存、虚拟机、线程、请求等)
监控日志
管理服务(服务下线)
可视化监控平台 Spring -boot-admin(不是spring-boot官方维护的)
服务端
<!--服务端-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.5.4</version> // 一定要和springboot版本一致 否则无法使用
</dependency>
// 服务端
@SpringBootApplication
@EnableAdminServer // 开启监控
public class ExceltoolApplication {
public static void main(String[] args) {
SpringApplication.run(ExceltoolApplication.class, args);
}
}
客户端
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.5.4</version> // 一定要和springboot版本一致 否则无法使用
</dependency>
spring:
boot:
admin:
client:
url: http://localhost:8080
#查看健康指标 和开启全部的信息监控
management:
endpoint:
htalth:
show-details: always
endpoints:
web:
exposure:
inlude: "*"
监控原理
- Actuator提供了SpringBoot生产就绪功能,通过端点的配置与访问,获取端点信息,
- 端点描述了一组监控信息,SpringBoot提供了多个内置端点,也可以根据需求自定义端点信息
- 访问当前应用所有端点信息 /actuator
- 访问端点详细信息 /actuator/端点名称
springBoot 原理
Bean的加载方式
ApplicationContext.xml 注册bean
<bean id = "cat" class = "com.default.Cat"/>
<bean class="com.default.Dog"/>
<!--声明第三方bean-->
<bean id = datasource class = "com.alibaba.druid.pool.DruidDataSource"/>
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.getBeanDefinitionNames(); // 拿到所有bean定义的名称
ctx.getBean("cat"); // 通过名称获取bean
ctx.getBean(Dog.class); // 通过类型取
ctx.getBean(dog,Dog.class) // 通过类型和名称
注解注册Bean
@Component("tom") // spring注解
public class Cat{
}
<!--指定加载Bean的位置,组件 component 扫描包放入容器中 与@ComponenScan类似-->
<context:component-scan base-package="com,default"/>
加载第三方bean
@Configuration
public class dbConfig(){
@Bean
public DruidDataSource datasource(){
return new DruidDataSource();
}
}
配置类注册bean
// @Configuration配置项如果不用于被扫描可以省略不加
@ComponentScan({"com.default"}) //配置包扫描放入容器 扫描@Component 衍生注解 @Configuration 加入容器
public class SpringConfig{
}
ApplicationContext ctx = new AnnotationConfigApplicationContext("SpringConfig.class");
FactoryBean接口实现了FactoryBean<V>接口的类,造出的对象不是本身,而是泛型上类型的对象
@Bean
public DogFactoryBean dog{
return new DogFactoryBean();
}
// bean 名不是DogFactoryBean 而是 dog ,可以做环境检测和初始化工作,所以提供了工厂接口
导入资源注册Bean
@ImportResource("applicationContext.xml") // 导入资源 整合 系统迁移
public class SpringConfig{
}
// 冲突问题,如果两个配置文件中都配置了相同名称的bean ,那么 XMl与XMl冲突 后加载的留下 XML与注解冲突,XML留下
@Configuration proxyBeanMethods
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
@Configuration
public class Test{
@Bean
public Cat cat{
return new Cat();
}
}
对象结果
com.default.Test$$EnhanceBySpringCGLIB$$363d9609@21a1947fe
test 对象 其实是proxyBeanMethods代理对象 如果 @Configuration(proxyBeanMethods =false)为正常对象
//如果proxyBeanMethods为true的时候 创建的对象的时候只创建一次,在每次调用在容器中拿到现有对象,保证只有一个对象必须有@Bean,去容器中拿对象,如果没有@Bean 跟@Component效果一样,每次重新创建,如果为false,则每次调用创建一个对象
@Import配置Bean
@Import(Cat.class) // 只需要一个注解 就可以加入spring容器
public class SpringConfig{
}
ApplicationContext ctx = new AnnotationConfigApplicationContext("SpringConfig.class");
@Import创建的bena是全路径类名
上下文对象注册bean
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerBean("tom",Cat.class,0);
ctx.registerBean("tom",Cat.class,1);
ctx.registerBean("tom",Cat.class,2);
ctx.register(Mouse.class);
// 注册多个bean的只保留2,后边的对象覆盖前边的对象
实现ImportSelector注册Bean
public class myImportSelector implements ImportSelector{
public String [] selectImports(AnnotationMetadata importingClassMetadata){
// selectImports 选择器 可以做一系列判定,决定是否加载Bean
return new String[]{"com.defautlt.Cat"};
}
}
@Import(myImportSelector.class)
public class SpringConfig{
}
ApplicationContext ctx = new AnnotationConfigApplicationContext("SpringConfig.class");
// 可以做自定义创建Bean 能够通过元数据AnnotationMetadata 获取类的所有信息
实现ImportBeanDefinitionRegistrar接口注册Bean
// 该接口内部全是默认方法
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
public class MyRegister implements ImportBeanDefinitionRegistrar{
// 元数据对象 通过registry对象注册对象
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//1、 使用元数据对象判断
//2、创建 BeanDefinition对象
BeanDefinition beanDefinition = BeanDefinitionBulider.rootBeanDefinition(Dog.class).getBeanDefinition();
//3、注册bean
registry.registerBeanDefinition("yellow",beanDefinition);
}
}
@Import(MyRegister.class)
public class SpringConfig{
}
ApplicationContext ctx = new AnnotationConfigApplicationContext("SpringConfig.class");
实现BeanDefinitionRegistryPostProcessor注册Bean
// bean注册完以后
public class MyRegister implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// bean定义对象
BeanDefinition beanDefinition = BeanDefinitionBulider.rootBeanDefinition(Dog.class).getBeanDefinition();
//3、注册bean
registry.registerBeanDefinition("yellow",beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
@Import(MyRegister.class)
public class SpringConfig{
}
ApplicationContext ctx = new AnnotationConfigApplicationContext("SpringConfig.class");
//bean后处理器,做保底对像,可以覆盖之前定义的对象
Bean加载控制
使用@Conditional选择性加载Bean
public class SpringConfig{
@ConditionalOnMissingClass("com.default.Cat")
public Cat tom(){
return new Cat();
}
}
Bean属性设置
1、通过读取配置实现属性的注入
@ConfigurationProperties("test")
2、单独创建属性类 自动注入属性对象,构造器注入
public Test(Myproperties myproperties){
cat = new Cat;
cat.setName(myproperties.getCat().getName()?myproperties.getCat().getName():"tom")
}
自动配置原理
1、初始化springBoot基础环境,加载用户自定义的bean和导入的坐标依赖,形成初始化环境
2、将技术集包含的所有技术定义出来,在spring/springBoot启动时默认全部加载
3、将具有使用条件的技术约定出来,设置成条件加载,由开发者决定是否使用该技术
4、将设计集作为默认参数配置,(约定大于配置)减少配置工作量
5、开放设计集配置覆盖接口,由开发者根据自身需要决定是否覆盖默认配置
如果类实现XXXAware接口表示可以使用该资源发现的资源 @oreder 对应bean加载顺序,可能50号bean依赖30号bean 值越小越先加载
定义拦截器
//定义拦截器
public class MyInteceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
}
//将拦截器配置上
@Configuration
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {
/**
* 添加默认跳转路径
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getMyInteceptor()).addPathPatterns("/**");
}
@Bean
public MyInteceptor getMyInteceptor(){
return new MyInteceptor();
}
}
启动流程
1、初始化各种属性,加载成对象
读取环境配置(Environment)
系统配置(spring.factories)
参数(Arguments、application.properties)
2、创建Spring容器对象ApplicationContext,加载各种配置
3、在容器创建前,通过监听器机制,应对不同阶段加载数据、更新数据的需求
4、容器初始化过程中追加各种功能,例如统计时间、输出日志等
源码分析
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 提升变量作用域
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 变成集合数据类型操作去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 一系列判断 返回加载的容器类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 读取spring.factories工厂配置初始化 加载系统级引导文件ApplicationContextInitializer实例
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 初始化监听器的实例对初始化过程和阶段进行干预 不带监听泛型每次都运行,如果干预的话传入泛型,
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 初始化了引导类类名信息,备用
this.mainApplicationClass = deduceMainApplicationClass();
}
// 初始化容器,返回上下文对象
public ConfigurableApplicationContext run(String... args) {
// 初始化属性
// 计时器
StopWatch stopWatch = new StopWatch();
// 计时器开始
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//模拟输入输出信号,避免因缺少外设导致信号传输失败引发错误,模拟显示器 键盘 鼠标...
configureHeadlessProperty();
// 获取当前所有可运行的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//监听器启动
listeners.starting();
try {
// 命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 系统环境变量
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 图标
Banner printedBanner = printBanner(environment);
// 创建spring容器 根据判定类型决定
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 对容器进行设置,参数来源于前期设定
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器环境
refreshContext(context);
// 刷新完毕之后做的后处理
afterRefresh(context, applicationArguments);
// 计时器结束
stopWatch.stop();
if (this.logStartupInfo) {
// 输出日志信息包括启动时间
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 将容器加入监听器,可以干预
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}