SpringBoot框架入门及使用教程(微服务学习笔记)
#SpringBoot入门
1、Spring Boot 简介
简化Spring应用开发的一个框架;
整个Spring技术栈的一个大整合;
J2EE开发的一站式解决方案;
2、微服务简介
微服务:架构风格(服务微化)
一个应用应该是一组小型服务;可以通过HTTP的方式进行互通;
单体应用:ALL IN ONE
微服务:每一个功能元素最终都是一个可独立替换和独立升级的软件单元;
3、Spring Boot HelloWorld
- 构建maven项目
- 配置工程pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
- 编写主程序:启动Spring Boot应用
/**
* @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// Spring应用启动起来
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
- 编写相关的Controller、Service
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World!";
}
}
- 运行主程序测试
2018-09-30 10:34:56.756 WARN 776 --- [ main] ionWarningsApplicationContextInitializer :
** WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of the default package.
//异常原因:application.java 文件不能直接放在main/java文件夹下,必须要建一个包把他放进去
- 简化部署
<!-- 这个插件,可以将应用打包成一个可执行的jar包;-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
将这个应用打成jar包,直接使用java -jar的命令进行执行;
4、Hello World探究
1、POM文件
1、父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
他的父项目是
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
他负责管理Spring Boot应用里面的所有依赖版本;
Spring Boot的版本仲裁中心;
以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)
2、启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-web:
spring-boot-starter:spring-boot场景启动器之一;帮我们导入了web模块正常运行所依赖的组件;
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器
2、主程序类,主入口类
/**
* @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// Spring应用启动起来
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@SpringBootConfiguration:Spring Boot的配置类;
标注在某个类上,表示这是一个Spring Boot的配置类;
@Configuration:配置类上来标注这个注解;
配置类 ----- 配置文件;配置类也是容器中的一个组件;@Component
@EnableAutoConfiguration:开启自动配置功能;
以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@AutoConfigurationPackage:自动配置包
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
Spring的底层注解@Import,给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class;
将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;
@Import(EnableAutoConfigurationImportSelector.class);
给容器中导入组件?
EnableAutoConfigurationImportSelector:导入哪些组件的选择器;
将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-liyTHXAr-1605780851786)(./image/#自动配置加载的组件.png)]
有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
==Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;==以前我们需要自己配置的东西,自动配置类都帮我们;
J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-2.0.5.RELEASE.jar;
5、使用Spring Initializer快速创建Spring Boot项目
1、IDEA:使用 Spring Initializer快速创建项目
IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目;
选择我们需要的模块;向导会联网创建Spring Boot项目;
默认生成的Spring Boot项目;
- 主程序已经生成好了,我们只需要我们自己的逻辑
- resources文件夹中目录结构
- static:保存所有的静态资源; js css images;
- templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面);可以使用模板引擎(freemarker、thymeleaf);
- application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
2、STS使用 Spring Starter Project快速创建项目
~~~~~~~~~~~~~~~~
1、配置文件
1、配置文件概述
- SpringBoot使用一个全局的配置文件,配置文件名是固定的;
- application.properties
- application.yml
配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;
YAML(YAML Ain’t Markup Language)
YAML A Markup Language:是一个标记语言
YAML isn’t Markup Language:不是一个标记语言;
标记语言:
以前的配置文件;大多都使用的是 xxxx.xml文件;
YAML:以数据为中心,比json、xml等更适合做配置文件;
YAML:配置例子
server:
port: 8081
XML:
<server>
<port>8081</port>
</server>
2、YAML语法:
1、基本语法
k:(空格)v:表示一对键值对(空格必须有);
以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
server:
port: 8081
path: /hello
属性和值也是大小写敏感;
2、值的写法
字面量:普通的值(数字,字符串,布尔)
k: v:字面直接来写;
字符串默认不用加上单引号或者双引号;
“”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi
‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
对象、Map(属性和值)(键值对):
k: v:在下一行来写对象的属性和值的关系;注意缩进
对象还是k: v的方式
friends:
lastName: zhangsan
age: 20
行内写法:
friends: {lastName: zhangsan,age: 18}
数组(List、Set):
用- 值表示数组中的一个元素
pets:
- cat
- dog
- pig
行内写法
pets: [cat,dog,pig]
3、配置文件值注入
配置文件
person:
lastName: hello
age: 18
boss: false
birth: 2017/12/12
maps: {k1: v1,k2: 12}
lists:
- lisi
- zhaoliu
dog:
name: 小狗
age: 12
javaBean:
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
*
* 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
*
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
我们可以导入配置文件处理器,以后编写配置就有提示了
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
1、properties配置文件在idea中默认utf-8可能会乱码
调整
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P65CBI2r-1605780851788)(./image/1.properties配置文件在idea中设置.png)]
2、@Value获取值和@ConfigurationProperties获取值比较
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
配置文件yml还是properties他们都能获取到值;
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;
3、配置文件注入值数据校验
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
/**
* <bean class="Person">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//lastName必须是邮箱格式
@Email
//@Value("${person.last-name}")
private String lastName;
//@Value("#{11*2}")
private Integer age;
//@Value("true")
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
4、@PropertySource&@ImportResource&@Bean
@PropertySource:加载指定的配置文件;
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
*
* 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
* @ConfigurationProperties(prefix = "person")默认从全局配置文件中获取值;
*
*/
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
//@Validated
public class Person {
/**
* <bean class="Person">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//lastName必须是邮箱格式
// @Email
//@Value("${person.last-name}")
private String lastName;
//@Value("#{11*2}")
private Integer age;
//@Value("true")
private Boolean boss;
@ImportResource:导入Spring的配置文件,让配置文件里面的内容生效;
Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别;
想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上
@ImportResource(locations = {"classpath:beans.xml"})
导入Spring的配置文件让其生效
不来编写Spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloService" class="com.atguigu.springboot.service.HelloService"></bean>
</beans>
SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式
1、配置类**@Configuration**------>Spring配置文件
2、使用**@Bean**给容器中添加组件
/**
* @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
*
* 在配置文件中用<bean><bean/>标签添加组件
*
*/
@Configuration
public class MyAppConfig {
//将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
@Bean
public HelloService helloService02(){
System.out.println("配置类@Bean给容器中添加组件了...");
return new HelloService();
}
}
##4、配置文件占位符
1、随机数
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
2、占位符获取之前配置的值,如果没有可以是用:指定默认值
person.last-name=张三${random.uuid}
person.age=${random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.hello:hello}_dog
person.dog.age=15
5、Profile
1、多Profile文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml
默认使用application.properties的配置;
2、yml支持多文档块方式
server:
port: 8081
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: dev
---
server:
port: 8084
spring:
profiles: prod #指定属于哪个环境
3、**指定profile
1、在配置文件中指定 spring.profiles.active=dev
2、命令行:
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
可以直接在测试的时候,配置传入命令行参数
3、虚拟机参数;
-Dspring.profiles.active=dev
6、配置文件加载位置
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
–file:./config/
–file:./
–classpath:/config/
–classpath:/
优先级由高到底,高优先级的配置会覆盖低优先级的配置;
SpringBoot会从这四个位置全部加载主配置文件;互补配置;
我们还可以通过spring.config.location来改变默认的配置文件位置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
7、外部配置加载顺序
SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
1.命令行参数
所有的配置都可以在命令行上进行指定
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc
多个配置用空格分开; --配置项=值
2.来自java:comp/env的JNDI属性
3.Java系统属性(System.getProperties())
4.操作系统环境变量
5.RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内进行寻找;
优先加载带profile
6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
7.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
再来加载不带profile
8.jar包外部的application.properties或application.yml(不带spring.profile)配置文件
9.jar包内部的application.properties或application.yml(不带spring.profile)配置文件
aaa@qq.com注解类上的@PropertySource
11.通过SpringApplication.setDefaultProperties指定的默认属性
所有支持的配置加载来源;
~~~~~~~~~~~~~~~~
2、SpringBoot自动配置原理
配置文件到底能写什么?怎么写?自动配置原理
自动配置原理:
-
springboot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
-
@EnableAutoConfiguration的作用
- 利用AutoConfigurationImportSelector类给容器中导入了哪些内容
AutoConfigurationImportSelector.class 的方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
- 可以查看selectImports()方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//获取候选的配置
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
- List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
SpringFactoriesLoader.loadFactoryNames()方法
- 扫描所有jar包类路径下的 META-INF/spring.factories
- 把扫描到的这些文件的内容包装成properties对象
- 从properties对象中获取EnableAutoConfiguration.class类名对应的值,然后把它们添加到容器中
将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中
~~~properties
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都会被加到容器中;用他们来做自动配置;
- 每一个自动配置类进行自动配置功能
- 以**HttpEncodingAutoConfiguration(HTTP编码自动配置)**为例解释自动配置原理:
@Configuration //表示这是一个自动配置类,类似于之前的配置文件,也可以给容器中添加组件
@EnableConfigurationProperties({HttpEncodingProperties.class}) //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定;
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//spring底层的@Condition注解,根据不同的条件,如果满足了指定的条件,整个配置文件中的配置就会生效;判断当前应用是不是web应用,如果是,当前配置类生效
@ConditionalOnClass({CharacterEncodingFilter.class}) //判断当前项目有没有这个类
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)//判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
private final HttpEncodingProperties properties; 已经属性文件进行了映射
public class HttpEncodingAutoConfiguration {
//他已经和SpringBoot的配置文件映射了
private final HttpEncodingProperties properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
-------------------------------------------
@ConfigurationProperties(
prefix = "spring.http.encoding"
)
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET;
private Charset charset;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;
根据当前不同的条件判断,决定这个配置类是否生效?
一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取 的,这些类里面的每一个属性又是和配置文件绑定的;
- 所有在配置文件中能配置的属性都是在xxxProperties中封装的;配置文件能配置什么可以参照这个功能对应的Properties类
@ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件中获取指定的值和bean的属
性进行绑定
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF‐8");
精髓:
1)、SpringBoot启动会加载大量的自动配置类
2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这 些属性的值;
xxxxAutoConfigurartion:自动配置类; 给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
@Conditional注解
-
@Conditional派生注解(spring注解版原生的@Conditional作用)
- 作用:必须是@Conditional指定的条件成立,才能给容器中添加组件,配置类里面的内容才生效
@Conditional扩展注解 作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean;
@ConditionalOnMissingBean 容器中不存在指定Bean;
@ConditionalOnExpression 满足SpEL表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项
自动配置类需要在一定的条件下才能生效;
- 如何看自动配置类生效了
- 可以通过在配置文件(application.properties)中配置debug=true,在console中来查看自动配置类是否启用
~~~~~~~~~~~~~~~~
3、SpringBoot和日志
1、日志框架
市面上的日志框架:JUL,JCL,Jboss-logging,logback,log4j,log4j2,slf4j
日志门面 (日志的抽象层) | 日志实现 |
---|---|
|
JUL(java.util.logging) Log4j2 Logback |
左边选一个门面(抽象层)、右边选一个实现
日志门面:SLF4J
日志实现:Logback
SpringBoot:底层是spring框架,spring框架默认是用JCL
springboot选用SLF4J和logback
2、SLF4J的使用
1、如何在系统中使用SLF4Jhttps://www.slf4j.org
以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法; 给系统里面导入slf4j的jar和 logback的实现jar
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
图示:
每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文 件;
2、遗留问题
a(slf4j+logback): Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx 多框架使用的日志不相同
统一日志记录,统一使用slf4j进行输出
解决办法:jcl-over-slf4j.jar(真正的实现slf4j-api.jar)排除并替换其他日志框架
如何让系统中所以的日志输出都统一到slf4j:
- 将系统中其他日志框架排除掉;
- 用中间包来替换原有的日志框架;
- 导入slf4j其他的实现
3、SpringBoot日志关系
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter</artifactId>
</dependency>
SpringBoot使用它来做日志:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐logging</artifactId>
</dependency>
底层依赖关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSbm0cWg-1605780851794)(./image/3.日志.png)]
总结:
-
SpringBoot底层也是使用slf4j和logback的方式进行日志记录
-
SpringBoot也把其他的日志都替换成了slf4j
-
中间的替换包
-
如果我们要引入其他的框架,一定要把这个框架的默认日志依赖移除掉
- 比如Spring框架用的commons-logging:
dependency> <groupId>org.springframework</groupId> <artifactId>spring‐core</artifactId> <exclusions> <exclusion> <groupId>commons‐logging</groupId> <artifactId>commons‐logging</artifactId> </exclusion> </exclusions> </dependency>
SpringBoot能自动适配所以的日志,而且底层使用sjf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可;
4、日志使用
-
-
默认配置
SpringBoot默认帮我们配置好了日志;
//记录器 Logger logger = LoggerFactory.getLogger(getClass()); @Test public void contextLoads() { //System.out.println(); //日志的级别; //由低到高 trace<debug<info<warn<error //可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效 logger.trace("这是trace日志..."); logger.debug("这是debug日志..."); //SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root 级别 logger.info("这是info日志..."); logger.warn("这是warn日志..."); logger.error("这是error日志..."); } 日志输出格式: %d表示日期时间, %thread表示线程名, %‐5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息,x %n是换行符 ‐‐> %d{yyyy‐MM‐dd HH:mm:ss.SSS} [%thread] %‐5level %logger{50} ‐ %msg%n
SpringBoot修改日志的默认配置
logging.level.cn.starfish=trace #logging.path= # 不指定路径在当前项目下生成springboot.log日志 # 可以指定完整的路径; #logging.file=G:/springboot.log # 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件 logging.path=/spring/log # 在控制台输出的日志的格式 logging.pattern.console=%d{yyyy‐MM‐dd} [%thread] %‐5level %logger{50} ‐ %msg%n # 指定文件中日志输出的格式 logging.pattern.file=%d{yyyy‐MM‐dd} === [%thread] === %‐5level === %logger{50} ==== %msg%n
Logging.file Logging.path example Description (none) (none) 只在控制台输出 指定文件 (none) My.log 输出日志到my。log (none) 指定目录 /var/log 输出到指定目录的spring。log文件中 -
指定配置
给类路径下放上每个日志框架自己的配置文件即可;SpringBootj就不使用其他默认配置的了
logging system logback logback-spring.groovy , logback.xml or logback.groovy Log4j2 log4j2-spring.xml or log4j2.xml Jdk(java.util.logging) logging.properties - logback.xml直接就被日志框架识别
- logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot
<springProfile name="staging"> <!‐‐ configuration to be enabled when the "staging" profile is active ‐‐> 可以指定某段配置只在某个环境下生效 </springProfile>
-
-
5、切换日志框架
可以按照slf4j的日志配图,进行相关的切换
- slf4j+log4j的方式:
<dendency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback‐classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>log4j‐over‐slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dendency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j‐log4j12</artifactId>
</dependency>
- 切换为log4j2:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring‐boot‐starter‐logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐log4j2</artifactId>
</dependency>
~~~~~~~~~~~~~~~~
4、web开发
1、简介
使用SpringBoot;
1)、创建SpringBoot应用,选中我们需要的模块;
2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
3)、自己编写业务代码;
自动配置原理?
这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxxx
xxxxAutoConfiguration:帮我们给容器中自动配置组件;
xxxxProperties:配置类来封装配置文件的内容;
2、SpringBoot对静态资源的映射关系
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {
//可以设置和静态资源有关的参数,缓存时间等
WebMvcAuotConfiguration:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Integer cachePeriod = this.resourceProperties.getCachePeriod();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(
registry.addResourceHandler("/webjars/**")
.addResourceLocations(
"classpath:/META‐INF/resources/webjars/")
.setCachePeriod(cachePeriod));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
//静态资源文件夹映射
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(
this.resourceProperties.getStaticLocations())
.setCachePeriod(cachePeriod));
}
}
//配置欢迎页映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
ResourceProperties resourceProperties) {
return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
}
//配置喜欢的图标
@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration {
private final ResourceProperties resourceProperties;
public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
//所 //所有 **/favicon.ico
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
faviconRequestHandler()));
return mapping;
}
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new
ResourceHttpRequestHandler();
requestHandler
.setLocations(this.resourceProperties.getFaviconLocations());
return requestHandler;
}
}
-
所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;
webjars:以(maven坐标)或jar包的方式引入静态资源;
<!‐‐引入jquery‐webjar‐‐>在访问的时候只需要写webjars下面资源的名称即可 <de<dendency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.1</version> </d</dendency>
-
“/**”访问当前项目的任何资源。(静态资源的文件夹)
- 以下是能存放静态资源文件的文件夹路径
"classpath:/META‐INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" "/":当前项目的根路径
- localhost:8080/abc === 去静态资源文件夹里面找abc
-
(欢迎页):静态资源文件夹下的所有index.html页面;被“/**”映射;
- localhost;8080 默认访问 index.html
-
(图标):所有的**/favicon.ico都是在静态资源文件夹下找;
-
可以在application.properties通过spring.resources.static-loactions属性修改静态资源路径
3、模板引擎
JSP、Velocity、Freemarker、Thymeleaf
SpringBoot推荐的Thymeleaf; 语法更简单,功能更强大;
1、 引入thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐thymeleaf</artifactId>
</dependency>
<!‐‐切换版本‐‐>
<properties>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf‐layout‐dialect.version>2.2.2</thymeleaf‐layout‐dialect.version>
</properties>
- 注意事项: 布局功能的支持程序 thymeleaf3主程序需layout2以上版本
thymeleaf2需layout1
2、thymeleaf的使用
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF‐8");
private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;
3、thymeleaf的语法
- 使用thymeleaf 需要在HTML中导入名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
- 使用thymeleaf语法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF‐8">
<title>Title</title>
</head>
<body>
<h1>成功!</h1>
<!‐‐th:text 将div里面的文本内容设置为 ‐‐>
<!--会将${hello}的值覆盖div属性的值-->
<div th:text="${hello}">这是显示欢迎信息</div>
</body>
</html>
- 语法规则
- th:text:改变当前元素里面的文本内容
- 表达式(参照thymeleaf文档)
Simple expressions:(表达式语法)
Variable Expressions: ${...}:获取变量值;OGNL;
1)、获取对象的属性、调用方法
2)、使用内置的基本对象:
#ctx : the context object.
#vars: the context variables.
#locale : the context locale.
#request : (only in Web Contexts) the HttpServletRequest object.
#response : (only in Web Contexts) the HttpServletResponse object.
#session : (only in Web Contexts) the HttpSession object.
#servletContext : (only in Web Contexts) the ServletContext object.
${session.foo}
3)、内置的一些工具对象:
#execInfo : information about the template being processed.
#messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
#uris : methods for escaping parts of URLs/URIs
#conversions : methods for executing the configured conversion service (if any).
#dates : methods for java.util.Date objects: formatting, component extraction, etc.
#calendars : analogous to #dates , but for java.util.Calendar objects.
#numbers : methods for formatting numeric objects.
#strings : methods for String objects: contains, startsWith, prepending/appending, etc.
#objects : methods for objects in general.
#bools : methods for boolean evaluation.
#arrays : methods for arrays.
#lists : methods for lists.
#sets : methods for sets.
#maps : methods for maps.
#aggregates : methods for creating aggregates on arrays or collections.
#ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样;
补充:配合 th:object="${session.user}:
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
Message Expressions: #{...}:获取国际化内容
Link URL Expressions: @{...}:定义URL;
@{/order/process(execId=${execId},execType='FAST')}
Fragment Expressions: ~{...}:片段引用表达式
<div th:insert="~{commons :: main}">...</div>
Literals(字面量)
Text literals: 'one text' , 'Another one!' ,…
Number literals: 0 , 34 , 3.0 , 12.3 ,…
Boolean literals: true , false
Null literal: null
Literal tokens: one , sometext , main ,…
Text operations:(文本操作)
String concatenation: +
Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
Binary operators: + , ‐ , * , / , %
还有比较运算,条件运算(支持三元运算符),特殊操作(_)
4、 SpringMVC自动配置
[https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing- web-applications](https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing- web-applications)
If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration
class of type WebMvcConfigurerAdapter
, but without@EnableWebMvc
. If you wish to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
or ExceptionHandlerExceptionResolver
you can declare a WebMvcRegistrationsAdapter
instance providing such components.
1、Spring MVC auto-configuration
Springboot 自动配置好了springMVC
以下是springboot对springMVC的自动配置
- Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.- 自动配置了
ViewResolver
(视图解析器:根据方法的返回值的到视图对象(View),视图对象·决定如何渲染(重定向?转发?)) -
ContentNegotiatingViewResolver
:组合所以的视图解析器 - 如何定制:我们可以自己给容器中添加一个视图解析器,自动的将其组合进来
- 自动配置了
- Support for serving static resources, including support for WebJars (see below).
- 静态资源文件夹路径webjars
- Automatic registration of
Converter
,GenericConverter
,Formatter
beans.-
Converter
:转换器; public String hello(User user):类型转换使用Converter -
Formatter
:格式化器;2017.12.17===Date;
-
@Bean
@ConditionalOnProperty(prefix = “spring.mvc”, name = “date‐format”)//在文件中配置日期格
式化的规则
public Formatter dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
}
自己添加的格式化器,我们只需要放在容器中即可
- Support for
HttpMessageConverters
(see below).- HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User—Json
- HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter;
自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中 (@Bean,@Component)
- Automatic registration of
MessageCodesResolver
(see below). 定义错误代码生成规则 - Static
index.html
support. - Custom
Favicon
support (see below). - Automatic use of a
ConfigurableWebBindingInitializer
bean (see below).
我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)
ConfigurableWebBindingInitializer的作用:
初始化WebDataBinder
请求数据====JavaBean
2、扩展SpringMVC
原有xml配置文件的方式:
<mvc:view‐controller path="/hello" view‐name="success"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean></bean>
</mvc:interceptor>
</mvc:interceptors>
Springboot现有方式:编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc
既保留了所有的自动配置,也能用我们扩展的配置
@Configuration
public class MyMvcConfigure implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("success");
}
}
原理:
-
WebMvcAutoConfiguration
是springmvc的自动配置类‘ - 在做其他的自动配合的时会导入;@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
- EnableWebMvcConfiguration继承的父类DelegatingWebMvcConfiguration
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
public DelegatingWebMvcConfiguration() {
}
@Autowired(
required = false
)
//从容器中获取所有的WebMvcConfigurer并赋值到此类的成员变量上
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
//一个参考实现:所有的WebMvcConfigurer相关配置都一起调用
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
- 容器中所有的WebMvcConfigurer都会一起起作用
- 我们的配置类也会被调用
效果:springmvc的自动配置和我们自己的扩展配置的都会生效
spring:
mvc:
view:
suffix: .jsp
prefix: /WEB-INF/
3、@EnableWebMvc全面接管SpringMVC
@EnableWebMvc: 全部接管springMVC,所有配置都是我们自己配
- 使用 在我们的配置类里加上此注解
@EnableWebMvc
@Configuration
public class MyMvcConfigure implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("success");
}
}
原理:添加@EnableWebMvc注解后自动配置失效
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
public DelegatingWebMvcConfiguration() {
}
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//当容器中没有这个组件的时候,自动配置类才生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
- @EnableWebMvc将WebMvcConfigurationSupport组件导入进来
- 导入的WebMvcConfigurationSupport只是springMVC的基本功能(不包含视图解析器,拦截器等)
4、注意事项:Spring5.0和SpringBoot2.0中废弃了WebMvcConfigurerAdapter
类
- 解决方案
- 1 、直接实现
WebMvcConfigurer
(官方推荐) - 2 、直接继承
WebMvcConfigurationSupport
- 在实现
WebMvcConfigurationSupport
的时候自己在application.properties配置的视图映射路径会失效
- 1 、直接实现
5、 如何修改SpringBoot的默认配置
模式:
- springboot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean @Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件有多个,可以将用户配置的和自动配置的组合起来使用;
- 在springboot中有很多xxxConfigurer帮助我们进行扩展配置
- 在springboot中会有很多的xxxCustomizer帮助我们进行配置
6、RestfulCRUD
1、 默认访问首页
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
//方式一
/*aaa@qq.com
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}*/
//方式二
@Bean
public WebMvcConfigurerAdapter myWebMvcConfigurerAdapter() {
return new WebMvcConfigurerAdapter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}
};
}
}
2、国际化
- 编写国际化配置文件
- 使用ResourceBundleMessageSource管理国际化资源文件
- 在页面使用fmt:message取出内容
步骤:
-
编写国际化配置文件,抽取页面需要显示的国际化消息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cfJSKEzd-1605780851796)(./image/4.国际化–properties设置.png)]
-
SpringBoot自动配置好了国际化资源文件的内容
- 设置application.properties.basename属性
- spring.messages.basename=i18n.login
@Configuration
@ConditionalOnMissingBean(
value = {MessageSource.class},
search = SearchStrategy.CURRENT
)
@AutoConfigureOrder(-2147483648)
@Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class})
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = new Resource[0];
public MessageSourceAutoConfiguration() {
}
@Bean
@ConfigurationProperties(
prefix = "spring.messages"
)
@Bean
public MessageSource messageSource() {
MessageSourceProperties properties = this.messageSourceProperties();
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
// 设置国际化资源文件的基础名(去掉语言国家代码) --> basenae = "messages"//我们的配置文件可以直接放在类路径下的messages.properties 也可以在application.properties文件中修改spring.messages.basename来修改国际化资源文件的存放位置
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
}
- 去页面获取国际化的值
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../../../../favicon.ico">
<title>Signin Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="../../../../dist/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin">
<img class="mb-4" src="https://getbootstrap.com/assets/brand/bootstrap-solid.svg" th:src="@{/asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72">
<h1 th:text="#{login.tip}" class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="username" th:text="#{login.username}" class="sr-only">Username</label>
<input type="email" id="username"class="form-control" placeholder="Username" required autofocus>
<label for="inputPassword" th:text="#{login.password}" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" placeholder="Password" required>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"/> [[#{login.remember}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" th:text="#{login.sign}" type="submit">Sign in</button>
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
</form>
</body>
</html>
效果:根据浏览器的语言的信息切换国际化
原理:
-
国际化依赖Locale(区域信息对象);LocaleResolver(获取区域信息对象(Locale))
-
SpringBoot会根据请求头带来的区域信息获取Locale进行国际化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-keKOKGEQ-1605780851797)(./image/4.国际化–请求头.png)]
- 点击链接切换国际化功能
<a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
- 自定义 LocaleResolver对象实现LocaleResolver接口
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String l = request.getParameter("l");
Locale locale = request.getLocale();
if (!StringUtils.isEmpty(l)) {
String[] split = l.split("_");
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
- 将自定义的LocalResolver对象加入到容器中
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
}
3、 登录
开发期间模板引擎修改以后,要实时生效
- 禁用模板引擎的缓存
spring.thymeleaf.cache=false
- 页面修改完之后Ctrl+f9:重新编译
登录错误显示错误信息
- SpringMvc配置
@Controller
public class UserLoginController {
@PostMapping("/user/login")
public String login(@RequestParam(name="username") String username,
@RequestParam(name = "password") String password, Map<String,Object> map, HttpServletRequest request) {
if ("admin".equals(username) && "123456".equals(password)) {
request.getSession().setAttribute("loginUser",username);
return "redirect:/main.html";
} else {
map.put("msg", "用户名或者密码错误");
return "login";
}
}
}
- 静态页面设置
<p th:text="${msg}" style="color: red" th:if="${not #strings.isEmpty(msg)}" ></p>
4、 拦截器interceptor配置
- 自定义类实现HandlerInterceptor接口,重写preHandle方法
package com.example.demo.component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
if (loginUser == null) {
request.setAttribute("msg","没有权限请先登录");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
} else {
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- 自定义类继承WebMvcConfigurerAdapter重写addInterceptors方法
- SpringBoot2.0后此类作废,详情参照web开发–>4.SpringMVC自动配置–》4.注意事项
package com.example.demo.config;
import com.example.demo.component.LoginInterceptor;
import com.example.demo.component.MyLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
//方式二
@Bean
public WebMvcConfigurerAdapter myWebMvcConfigurerAdapter() {
return new WebMvcConfigurerAdapter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
registry.addViewController("/main.html").setViewName("dashboard");
}
};
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addInterceptor() 将自定义拦截器添加到容器中
//addPathPatterns() 添加拦截路径
//excludePathPatterns() 排除无需拦截的路径
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login");
}
}
5、 CRUD-员工列表
实验要求:
- RestfulCRUD:CRUD满足rest风格;
- URI:/资源名称/资源标识 HTTP请求方式区分对资源的CRUD操作
普通CRUD(uri未区分操作) | RestfulCRUD | |
---|---|---|
查询 | getEmp | emp—GET |
添加 | addEmp?xxx | emp—POST |
修改 | updateEmp?id=xxx&xxx=xx | emp/{id}—PUT |
删除 | deleteEmp?id=1 | emp/{id}—DELETE |
- thymeleaf公共页面抽取
1、抽取公共片段
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
2、引入公共片段
<div th:insert="~{footer :: copy}"></div>
~{templatename::selector}:模板名::选择器
~{templatename::fragmentname}:模板名::片段名
3、默认效果:
insert的公共片段在div标签中
如果使用th:insert等属性进行引入,可以不用写~{}:
行内写法可以加上:[[~{}]];[(~{})];
- 三种引入功能片段的th属性:
- th:insert 将公共片段整个插入到引用元素中
- th:include 将被引入的片段的内容包含进来
- th:replace 将声明引入的元素替换为公共片段
模板名::片段名
footer.html -->模板名称为footer
<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
引入方式
<div th:insert="footer :: copy"></div>
引入片段的时候传入参数:
6)、CRUD-员工添加
添加页面
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
效果
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>
============================================
模板名::选择器
footer.html -->模板名footer
<div id = "footer">
© 2011 The Good Thymes Virtual Grocery
</div>
<div th:replace="footer ::#footer">
</div>
引入片段的时候传入参数
emp_list.html 引入片段
<div th:replace="commons/bar :: leftbar(activeUri='emps')"></div>
dashboard.html 引入片段
<div th:replace="commons/bar :: leftbar(activeUri='dashboard')"></div>
bar.html 被引入片段
<li class="nav-item">
<!--根据引用传入的参数 改变样式-->
<a class="nav-link active" href="#" th:href="@{/main.html}"
th:class="${activeUri=='dashboard'?'nav-link active':'nav-link'}">
<span data-feather="home"></span>
Dashboard <span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<!--根据引用传入的参数 改变样式-->
<a class="nav-link" href="#" th:href="@{/emps}"
th:class="${activeUri=='emps'?'nav-link active':'nav-link'}">>
<span data-feather="users"></span>
员工列表
</a>
</li>
thymeleaf常用方法
<tr th:each="emp:${list}">
<td th:text="${emp.id}"></td>
<td th:text="${emp.lastName}"></td>
<td>[[${emp.email}]]</td>
<td th:text="${emp.gender==0?'男':'女'}"></td>
<td th:text="${emp.department.departmentName}"></td>
<td th:text="${#dates.format(emp.birth,'yyyy-MM-dd HH:mm')}"></td>
</tr>
th:attr标签可以生成delUrl属性
<!--自定义属性的值-->
<button th:attr="aaa@qq.com{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteEmp">删除</button>
<form id="delEmpForm" action="" method="post">
<input type="hidden" name="_method" value="delete">
</form>
$(function () {
$(".deleteEmp").click(function () {
$("#delEmpForm").attr("action", $(this).attr("delUrl")).submit();
});
});
6、修改Form表单提交方式
- SpringMVC配置HiddenHttpMethodFilter(SpringBoot自动配置了)
- 页面创建一个post表单
- 创建一个input项,name="_method",value值就是我们指定的请求方式
<input type="hidden" name="_method" value="put">
7、SpringBoot错误处理机制
1、 SpringBoot默认的错误处理机制
默认效果:
- 返回一个默认的错误页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qu0NOO1p-1605780851797)(./image/4.springboot默认错误页面.png)]
- 如果是其他客户端访问,返回json数据
原理:参照ErrorMvcAutoConfiguration:错误处理的自动配置
给容器添加了如下组件:
- DefaultErrorAttributes:
帮我们在页面*享信息
//页面能获取到的信息
timestamp:时间戳
status:状态码
error:错误提示exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里
- BasicErrorController:处理默认的error请求,根据请求头accept进行处理
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = "text/html") //产生HTML类型的页面,处理浏览器请求
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
//去哪个页面作为错误页面,包含页面地址和页面内容
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null ? modelAndView : new ModelAndView("error", model));
}
@RequestMapping
@ResponseBody //产生json数据,处理其他请求
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(body, status);
}
- ErrorPageCustomizer:
@Value("${error.path:/error}")
private String path = "/error";
//系统出现错误来到error请求进行处理,类似于(web.xml配置的错误处理机制)
- DefaultErrorViewResolver:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
//默认springboot去寻找页面 error/404
String errorViewName = "error/" + viewName;
//模板引擎可以解析,就用模板引擎解析
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
if (provider != null) {
//模板引擎可用的基础上返回errorViewName对应的页面
return new ModelAndView(errorViewName, model);
}
//模板引擎不可用的情况下,就在静态资源文件夹下errorViewName对应的页面
return resolveResource(errorViewName, model);
}
步骤:
- 一旦页面中出现了4xx或5xx,ErrorPageCustomizer就会生效(定制错误的响应规则);-
- 就会来到/error请求,就会被BasicErrorController处理
- BasicErrorController处理完会返回响应的ModelAndView或者json数据
- DefaultErrorViewResolver解析ModelAndView决定去哪个页面
2、如何定制错误响应
-
如何定制错误的页面
- 有模板引擎的情况下: error/状态码【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的error文件夹下】
- 我们可以用4xx或者5xx作为错误文件的文件名来匹配这种类型的错误(优先匹配错误码.html)
- 没有模板引擎的情况下(模板引擎未找到):会去类路径下的静态资源文件中获取
-
如何定制错误的json数据
- 自定义异常处理消息:
@ControllerAdvice public class MyExceptionHandler { @ResponseBody @ExceptionHandler(UserNotExistException.class) public Map<String,Object> handleException(Exception e){ Map<String,Object> map = new HashMap<>(); map.put("code","user.notexist"); map.put("message",e.getMessage()); return map; } } //没有自适应效果...
- 转发到/error进行自适应响应效果处理:(SpringBoot的BasicErrorController处理)
@ExceptionHandler(UserNotExistException.class) public String handleException(Exception e, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); //传入我们自己的错误状态码 4xx 5xx,否则就不会进入定制错误页面的解析流程 request.setAttribute("javax.servlet.error.status_code",500); map.put("code","user.notexist"); map.put("message",e.getMessage()); //转发到/error return "forward:/error"; } //无法携带自定义数据
-
携带自定义数据:
出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由
getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);
-
完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中;
-
页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;SpringBoot容器中通过DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的;
自定义ErrorAttributes
//给容器中加入我们自己定义的ErrorAttributes @Component public class MyErrorAttributes extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace); map.put("company","abc"); return map; }
总结:自定义异常处理注意事项:
- 通过/error转发springboot默认的异常处理controller
- 传入我们自己的错误状态码 4xx 5xx(不然无法进入定制错误页面的解析流程)
request.setAttribute("javax.servlet.error.status_code",500);
- 自定义ErrorAttributes将自定义数据携带出去,视图显示
8、配置嵌入式servlet容器
SpringBoot默认是用的嵌入式容器(Tomcat):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y9NgVzWA-1605780851798)(./image/4.springboot内置tomcat.png)]
1、如何定制和修改servlet容器的相关配置
- 修改和server有关的配置
server.port=8081
server.context‐path=/crud
server.tomcat.uri‐encoding=UTF‐8
//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx
- 编写一个EmbeddedServletContainerCustomizer实体加入到容器中:嵌入式的servlet容器的定制器,来修改配置
@Bean //一定要将这个定制器加入到容器中
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new EmbeddedServletContainerCustomizer() {
//定制嵌入式的Servlet容器相关的规则
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(8083);
}
};
}
- 若同时配置了properties和EmbeddedServletContainerCustomizer则前者会被覆盖掉
2、注册servlet的三大组件(servlet,listener,filter)
由于springboot默认是以jar包的方式启动嵌入式的servlet容器来启动springboot的web应用,没有web.xml文件
注册三大组件
- ServletRegistrationBean
//自定义Servlet
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello World");
}
}
@Configuration
public class MyServletConfig {
@Bean
public ServletRegistrationBean myServletRegistrationBean() {
return new ServletRegistrationBean(new MyServlet(), "/myServlet");
}
}
- ServletListenerRegistrationBean
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("项目被启动了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("项目被关闭了");
}
}
@Configuration
public class MyServletConfig {
@Bean
public ServletListenerRegistrationBean myListener() {
ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
bean.setListener(new MyListener());
return bean;
}
}
- FilterRegistrationBean
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("filter执行了");
chain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
@Configuration
public class MyServletConfig {
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new MyFilter());
bean.addUrlPatterns("/myServlet","/");
return bean;
}
}
springboot帮我们自动配置springMVC的时候,自动注册了springMVC的前端控制器:DispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean registration = new ServletRegistrationBean(
dispatcherServlet, this.serverProperties.getServletMapping());
//默认拦截:/ 所有请求,包含静态资源,但不拦截jsp; /*会拦截jsp
//可以通过server.servletPath来修改springboot前端控制器默认拦截的请求路径
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
3、springboot能不能支持其他的servlet容器
默认支持Tomcat(默认):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
引入web模块默认就是使用嵌入式的Tomcat作为Servlet容器;
</dependency>
切换为Jetty:
<!‐‐ 引入web模块 ‐‐>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring‐boot‐starter‐tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!‐‐引入其他的Servlet容器‐‐>
<dependency>
<artifactId>spring‐boot‐starter‐jetty</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
切换为Undertow :
<!‐‐ 引入web模块 ‐‐>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring‐boot‐starter‐tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!‐‐引入其他的Servlet容器‐‐>
<dependency>
<artifactId>spring‐boot‐starter‐undertow</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
4、嵌入式servlet容器自动配置
EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置?
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
//导入BeanPostProcessorsRegistrar:Spring注解版;给容器中导入一些组件
//导入了EmbeddedServletContainerCustomizerBeanPostProcessor:
//后置处理器:bean初始化前后(创建完对象,还没赋值赋值)执行初始化工作
public class EmbeddedServletContainerAutoConfiguration {
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })//判断当前是否引入了Tomcat依赖;
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search =
SearchStrategy.CURRENT)//判断当前容器没有用户自己定义EmbeddedServletContainerFactory:嵌入式的Servlet容器工厂;作用:创建嵌入式的Servlet容器
public static class EmbeddedTomcat {
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory()
{
return new TomcatEmbeddedServletContainerFactory();
}
}
/**
* Nested configuration if Jetty is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
WebAppContext.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search =
SearchStrategy.CURRENT)
public static class EmbeddedJetty {
@Bean
public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
return new JettyEmbeddedServletContainerFactory();
}
}
/**
* Nested configuration if Undertow is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search =
SearchStrategy.CURRENT)
public static class EmbeddedUndertow {
@Bean
public UndertowEmbeddedServletContainerFactory
undertowEmbeddedServletContainerFactory() {
return new UndertowEmbeddedServletContainerFactory();
}
}
1)、EmbeddedServletContainerFactory(嵌入式Servlet容器工厂)
public interface EmbeddedServletContainerFactory {
//获取嵌入式的Servlet容器
EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers);
}
2)、EmbeddedServletContainer:(嵌入式的Servlet容器)
3)、以TomcatEmbeddedServletContainerFactory为例
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
//创建一个Tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本环节
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
//将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器
return getTomcatEmbeddedServletContainer(tomcat);
我们对嵌入式容器的配置修改是怎么生效?
ServerProperties(也是一个定制处理器)、EmbeddedServletContainerCustomizer(定制处理器)
EmbeddedServletContainerCustomizer:定制器帮我们修改了Servlet容器的配置? 怎么修改的原理?
4)、容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor
//初始化之前
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//如果当前初始化的是一个ConfigurableEmbeddedServletContainer类型的组件
if (bean instanceof ConfigurableEmbeddedServletContainer) {
//
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(
ConfigurableEmbeddedServletContainer bean) {
//获取所有的定制器,调用每一个定制器的customize方法来给Servlet容器进行属性赋值;
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
this.beanFactory
//从容器中获取所有这葛类型的组件:EmbeddedServletContainerCustomizer
//定制Servlet容器,给容器中可以添加一个EmbeddedServletContainerCustomizer类型的组件
.getBeansOfType(EmbeddedServletContainerCustomizer.class,
false, false)
.values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
ServerProperties也是定制器
步骤:
1)、SpringBoot根据导入的依赖情况,给容器中添加相应的
EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】
2)、容器中某个组件要创建对象就会惊动后置处理器; EmbeddedServletContainerCustomizerBeanPostProcessor;
只要是嵌入式的Servlet容器工厂,后置处理器就工作;
3)、后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法
5、嵌入式Servlet容器启动原理
什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat; 获取嵌入式的Servlet容器工厂: 1)、SpringBoot应用启动运行run方法
2)、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个 组 件 】 ; 如 果 是 web 应 用 创 建 AnnotationConfigEmbeddedWebApplicationContext, 否 则 : AnnotationConfigApplicationContext
3)、refresh(context);刷新刚才创建好的ioc容器;
ublic void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post‐processing of the bean factory in context subclasses.
4)、 onRefresh(); web的ioc容器重写了onRefresh方法
5)、webioc容器会创建嵌入式的Servlet容器;createEmbeddedServletContainer();
6)、获取嵌入式的Servlet容器工厂:
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
从ioc容器中获取EmbeddedServletContainerFactory 组件;TomcatEmbeddedServletContainerFactory创建对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置;
7)、使用容器工厂获取嵌入式的Servlet容器:this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
8)、嵌入式的Servlet容器创建对象并启动Servlet容器;
先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;
IOC容器启动创建嵌入式的Servlet容器
9、使用外置的Servlet容器
嵌入式Servlet容器:应用打成可执行的jar
-
优点:简单、便携;
-
缺点:默认不支持JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义
EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂
外置的Servlet容器:外面安装Tomcat—以war的方式打包;
1)、步骤
- 必须创建一个war项目(利用idea创建好目录结构)
- 将内置Tomcat指定为provided
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐tomcat</artifactId>
<scope>provided</scope>
</dependency>
- 必须修改一个SpringBootServletInitializer的子类,并调用configure方法
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
//传入SpringBoot应用的主程序
return application.sources(SpringBoot04WebJspApplication.class);
}
}
- 启动服务器就可以使用了
2)、原理
- jar包:执行springboot主类的main方法,启动IOC容器,创建嵌入式的Servlet容器
- war包:启动服务器,服务器启动springboot应用,启动IOC容器
servlet3.0(Spring注解版):
8.2.4 Shared libraries / runtimes pluggability: 规则:
1)、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:
2)、ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为
javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
3)、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;
流程:
1)、启动Tomcat
2)、org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\META- INF\services\javax.servlet.ServletContainerInitializer:
Spring的web模块里面有这个文件:org.springframework.web.SpringServletContainerInitializer
3)、SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set>;为这些WebApplicationInitializer类型的类创建实例;
4)、每一个WebApplicationInitializer都调用自己的onStartup;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eciDP3Hr-1605780851799)(./image/4.WebApplicationInitializer实现.png)]
5)、相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法
6)、SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建容器
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
//1、创建SpringApplicationBuilder
SpringApplicationBuilder builder = createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
//调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
builder = configure(builder);
//使用builder创建一个Spring应用
SpringApplication application = builder.build();
if (application.getSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.getSources().add(getClass());
}
Assert.state(!application.getSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.getSources().add(ErrorPageFilterConfiguration.class);
}
//启动Spring应用
return run(application);
}
7)、Spring的应用就启动并且创建IOC容器
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新IOC容器
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
启动Servlet容器,再启动SpringBoot应用
3)、核心
根据Servlet3.0的标准服务器启动的时候,会加载SpringBootServletInitializer实现的实例,而在它的实例里面重写了configure方法,这个方法标识了主程序的位置,这样服务器启动的时候就会加载这个主程序
10、SpringBoot和jsp
1、使用外置的tomcat容器启动
1、创建web项目
- 打包方式为war的形式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f44b3BnM-1605780851800)(./image/4.创建web项目.png)]
- 修改webapp指向位置目录结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hq5JPdfT-1605780851801)(./image/4.%E4%BF%AE%E6%94%B9web%20app%E6%8C%87%E5%90%91%E7%9A%84%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84.png)]
- 创建jsp存储路径和jsp
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tY22gLYt-1605780851802)(./image/4.创建jsp存储文件目录.png)]
- 配置外置tomcat容器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WFreeFP2-1605780851802)(./image/4.添加外置tomcat容器.png)]
2、修改配置文件
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
3、编写Controller测试
@Controller
public class JspController {
@GetMapping("hello")
public String jsp() {
return "hello";
}
}
2、使用内置的tomcat容器启动
1、创建web项目
- Spring Initializer创建war打包方式的web项目,并在其基础上添加如下依赖
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
- 打包方式为war的形式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oUguTGah-1605780851803)(/Users/mac/Documents/#SpringBoot/image/4.创建web项目.png)]
- 修改webapp指向位置目录结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KQ80dEFR-1605780851804)(./image/4.修改web app指向的目录结构.png)]
- 创建jsp存储路径和jsp
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QFyuPYXT-1605780851805)(./image/4.创建jsp存储文件目录.png)]
2、修改配置文件
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
3、编写Controller测试
@Controller
public class JspController {
@GetMapping("hello")
public String jsp() {
return "hello";
}
}
~~~~~~~~~~~~~~~~
5、SpringBoot和Docker
1、简介
Docker是一个开源的应用容器引擎:
Docker支持将软件编译成一个镜像;然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使 用这个镜像
运行中的这个镜像称为容器,容器启动是非常快速的。
2、核心概念
- docker主机(host):
- docker客户端(client):
- docker仓库:用来保存打包好的镜像
- docker镜像:软件打包好的镜像,放在docker仓库中
- docker容器:镜像启动后生产的实例,称为一个容器;容器是独立的一个或一组应用
使用步骤
- 安装Docker
- 去Docker仓库找到这个软件对应的镜像
- 使用docker这个镜像,这个镜像会生成一个docker容器
- 对容器的启动体质就是对软件的启动停止
3、安装docker
1)安装linux虚拟机
-
VMWare、VirtualBox(安装);
-
导入虚拟机文件centos7-atguigu.ova;
-
双击启动linux虚拟机;使用 root/ 123456登陆
-
使用客户端连接linux服务器进行命令操作;
-
设置虚拟机网络;
桥接网络=选好网卡==接入网线;
-
设置好网络以后使用命令重启虚拟机的网络
service network restart
- 查看linux的ip地址
ip addr
- 使用客户端连接linux;
2)在Linux下安装docker
- 查看centos的版本(docker要求centos系统的内核版本高于3.10)
uname -r
- 升级软件包及内核(3.10版本以下进行升级)
yum update
- 安装docker
yum install docker
//查看版本号
docker -v
- 启动docker
systemctl start docker
//关闭docker
stop docker
- 将docker服务设为开机启动
systemctl enable docker
3)docker的常用镜像操作
docker search + 关键字(例如docker search mysql)//搜索
docker pull + 镜像:tag//下载,tag表示标签,多为软件的版本,默认是latest
docker images //查看所有本地镜像
docker rmi + 镜像名称 //删除镜像
4)docker常用的容器操作
软件镜像(QQ安装程序)—-运行镜像—-产生一个容器(正在运行的软件,运行的QQ);
步骤:
1.搜索镜像
docker search tomcat
2.拉取镜像
docker pull tomcat
3.根据镜像启动容器
docker run --name mytomcat -d tomcat:latest
4.查看运行中的容器
docker ps
5.停止运行中的容器
docker stop 容器ID 或者 docker stop容器名字
6. 查看所有的容器
docker ps -a
7. 停止容器
docker stop + 容器ID
8. 删除容器
docker rm 容器ID
9. 启动有个做了端口映射的tomcat
docker run -d -p 8888:8080 tomcat
-d:后台运行
-p:将主机的端口映射到容器的一个端口 主机端口:容器内部端口
10. 关闭Linux防火墙
service firewalld status:查看防火墙状态
service firewalld stop:关闭防火墙
11.查看容器日志
docker logs 容器名称/容器ID
12.进入容器内部 73f01be51ee8表示容器ID
docker exec -it 73f01be51ee8 /bin/bash
13.显示容器内文件和文件夹
aaa@qq.com:/usr/share/elasticsearch# ls -l
14.进入某一个文件夹
aaa@qq.com:/usr/share/elasticsearch# cd config/
15.编辑文件,由于docker下未安装vim/vi指令
aaa@qq.com:/usr/share/elasticsearch/config# vim elasticsearch.yml
bash: vim: command not found
安装指令
安装Vi:apt-get install vim,如果提示:Unable to locate package vim,则需要敲:apt-get update, 等更新完毕以后再敲命令: apt-get install vim
16.退出容器内部
exit
关于docker容器的操作,可以参照官方文档
一个镜像可以启动多个容器,而且每个容器都是独立的,互不干扰
1、安装MySQL
1.拉取镜像
docker pullmysql
2.启动mysql
[aaa@qq.com ~]# docker run ‐‐name mysql01 ‐e MYSQL_ROOT_PASSWORD=123456 ‐d mysql
3.查看错误日志
[aaa@qq.com ~]# docker logs 42f09819908b
注意事项:启动MySQL时:MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and
MYSQL_RANDOM_ROOT_PASSWORD;这个三个参数必须指定一个
2、docker中MySQL镜像的高级操作
docker run ‐‐name mysql03 ‐v /conf/mysql:/etc/mysql/conf.d ‐e MYSQL_ROOT_PASSWORD=123456
‐d mysql:tag
把主机的/conf/mysql文件夹挂载到 mysqldocker容器的/etc/mysql/conf.d文件夹里面
改mysql的配置文件就只需要把mysql配置文件放在自定义的文件夹下(/conf/mysql)
docker run ‐‐name some‐mysql ‐e MYSQL_ROOT_PASSWORD=123456 ‐d mysql:tag ‐‐character‐set‐
server=utf8mb4 ‐‐collation‐server=utf8mb4_unicode_ci
指定mysql的一些配置参数
5) docker镜像加速
$ docker pull registry.docker-cn.com/library/mysql
~~~~~~~~~~~~~~~~
6、SpringBoot与数据访问层
1、JDBC
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql‐connector‐java</artifactId>
<scope>runtime</scope>
</dependency>
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://192.168.15.22:3306/jdbc
driver‐class‐name: com.mysql.jdbc.Driver
效果:
默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源;
数据源的相关配置在DataSourceProperties
自动配置原理:
org.springframework.boot.autoconfigure.jdbc:
1、参考DataSourceConfiguration,根据配置创建数据源,默认使用Tomcat连接池;可以使用spring.datasource.type指定自定义的数据源类型;
2、SpringBoot默认可以支持:
org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource、
3、自定义数据源类型
/**
* Generic DataSource configuration.
*/
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
//使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
return properties.initializeDataSourceBuilder().build();
}
}
4 、 DataSourceInitializer:ApplicationListener;
作用:
1)、runSchemaScripts();运行建表语句;
2)、runDataScripts();运行插入数据的sql语句;
- 默认只需要将文件命名为:(存放在classpath路径下,即可在项目启动时被加载)
schema‐*.sql(建表)、data‐*.sql(插入数据)
默认规则:schema.sql,schema‐all.sql;
- 指定指特点位置;
spring:
datasource:
schema:
‐ classpath:department.sql
initialization-mode: always
5、操作数据库:自动配置了JdbcTemplate操作数据库
@Controller
public class HelloController {
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping("list")
@ResponseBody
public Map<String, Object> list() {
List<Map<String, Object>> maps = jdbcTemplate.queryForList("SELECT * FROM USERS ");
return maps.get(0);
}
}
2、整合Druid数据源
- 引入maven仓库
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
- 设置properties相关配置
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/springboot
# 下面为连接池的补充设置,应用到上面所有数据源中
initialSize: 5
minIdle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPrparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectProperties: druid.stat.mergeSql=true,druid.stat.slowSqlMills=500
logSlowSql: true
- 将Druid配置添加到容器中
@Configuration
public class DruidConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
//配置Druid的监控
//1、配置一个管理后台的Servlet
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin");
initParams.put("loginPassword", "123456");
initParams.put("allow", "");//默认就是允许所有访问
initParams.put("deny", "192.168.15.21");//不允许访问的设置
bean.setInitParameters(initParams);
return bean;
}
//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map<String, String> initParams = new HashMap<>();
initParams.put("exclusions", "*.js,*.css,/druid/*");
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
3、整合Mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--引入Druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
将Druid配置添加到容器中 参照2.3 将Druid配置添加到容器中
1、注解版
//指定这是一个操作数据库的mapper
//或者使用MapperScan进行批量扫描,被扫描目录下所有的接口相当于自动添加了@Mapper注解
@Mapper
public interface DepartmentMapper {
@Select("select * from department where id = #{id}")
public Department getDepatById(Integer id);
@Options(useGeneratedKeys = true,keyProperty = "id") //设置insert成功后返回主键ID
@Insert("insert into department(departmentName) values(#{departmentName})")
public int saveDepart(Department department);
@Delete("delete from department where id = #{id}")
public int deleteDepartById(Integer id);
@Update("update department department_name = #{departmentName} where id = #{id}")
public int updateDeprtById(Department department);
}
若想批量扫描接口为mybatis的mapper映射文件,可做如下配置
@SpringBootApplication
//com.example.demo.mapper下所有的接口相当于自动添加了@Mapper注解
//将接口扫描装备到容器中
//@MapperScan("com.example.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
支持匹配驼峰命名法:
- 数据库字段 department_name 映射到entity字段上的departmentName
@org.springframework.context.annotation.Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer(){
@Override
public void customize(Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
2、配置文件的方式
- mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration
<!--支持驼峰命名的方式-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
- mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.EmployeeMapper">
<select id="findById" resultType="com.example.demo.bean.Employee">
select * from employee where id = #{id}
</select>
<insert id="saveEmp">
insert into employee(lastName,email,gender,d_id) values (#{lastName},#{email},#{gender},#{did})
</insert>
</mapper>
- Mapper
@Mapper
//或者使用MapperScan进行批量扫描
public interface EmployeeMapper {
public Employee findById(Integer id);
public int saveEmp(Employee employee);
}
4、整合JPA
1、SpringData介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHCSjiGk-1605780851806)(./image/6.jpa模型.png)]
2、整合SpringDataJPA
JPA ORM(Object Relation Mapping):
1)编写一个实体类(bean)和数据表进行映射,并配置好映射关系
//使用JPA注解配置映射关系
@Entity //告诉JPA这是一个实体类(和数据表映射的类)
@Table(name = "user")//@Table来指定和哪个数据表对应;如果省略默认表名就是userEntity;
public class UserEntity {
@Id //这是一个主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //自动增长
@Column(name = "user_id")
private Integer id;
@Column(name = "last_name") //这是和数据库表对应的一个列,若省略name属性则列名及为属性名
private String lastName;
@Column(name = "sex")
private String sex;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
2)编写一个Dao接口来操作实体类对应的数据表(Repository)
//继承JpaRepository来完成对数据库的操作
public interface UserRepostory extends JpaRepository<UserEntity,Integer> {
}
3)基本的配置JpaProeprties
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot
jpa:
properties:
hibernate:
format_sql: true
show_sql: true
hibernate:
ddl-auto: update
4)测试
@RestController
public class UserController {
@Autowired
UserRepostory userRepostory;
@GetMapping("/user")
public UserEntity findById(Integer id) {
return userRepostory.findOne(id);
}
}
3、@Query注解自定义SQL
public interface UserRepostory extends JpaRepository<UserEntity,Integer> {
@Query(value = "select * from user where sex = ?1",nativeQuery = true)
public List<UserEntity> findList(String sex);
}
- @Query是用来配置自定义SQL的注解,后面参数nativeQuery = true表明了使用原生的sql,如果不配置,默认是false
4、@Query配合@Modifying
- 从名字上可以看到我们的@Query注解好像只是用来查询的,但是如果配合@Modifying注解一共使用,则可以完成数据的删除、添加、更新操作。
package com.example.demo.jpa;
import com.example.demo.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface UserRepostory extends JpaRepository<UserEntity,Integer> {
//根据用户名、性别删除一条数据
@Modifying
@Query(value = "delete from user where last_name = ?1 and sex = ?2", nativeQuery = true)
public boolean deleteByNameAndSex(String last_name, String sex);
}
- 异常TranscationRequiredException,意思就是你当前的操作给你抛出了需要事务异常,SpringDataJPA自定义SQL时需要在对应的接口或者调用接口的地方添加事务注解**@Transactional**,来开启事务自动化管理。下面我们在UserJPA内添加**@Transactional**注解
5、抽取BaseRepository
package com.example.demo.base;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
//@NoRepositoryBean 这个注解如果配置在继承了JpaRepository接口以及其他SpringDataJpa内部的接口的子接口时,子接口不被作为一个Repository创建代理实现类。
@NoRepositoryBean
public interface BaseRepository<T,PK extends Serializable> extends JpaRepository<T,PK> {
}
6、分页查询和排序
package com.yuqiyu.chapter13.base;
import java.io.Serializable;
public class BaseEntity implements Serializable{
/**
* <p>
* 分页页码,默认页码为1
* <p>
*/
protected int page = 1;
/**
* <p>
* 分页每页数量,默认20条
* <p>
*/
protected int size = 20;
/**
* <p>
* 排序列名称,默认为id
* <p>
*/
protected String sidx = "id";
/**
* <p>
* 排序正序
* <p>
*/
protected String sord = "asc";
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getSidx() {
return sidx;
}
public void setSidx(String sidx) {
this.sidx = sidx;
}
public String getSord() {
return sord;
}
public void setSord(String sord) {
this.sord = sord;
}
}
/**
* 分页查询测试
* @param page 传入页码,从1开始
* @return
*/
@RequestMapping(value = "/cutpage")
public List<UserEntity> cutPage(int page)
{
UserEntity user = new UserEntity();
user.setSize(2);
user.setSord("desc");
user.setPage(page);
//获取排序对象
Sort.Direction sort_direction = Sort.Direction.ASC.toString().equalsIgnoreCase(user.getSord()) ? Sort.Direction.ASC : Sort.Direction.DESC;
//设置排序对象参数
Sort sort = new Sort(sort_direction, user.getSidx());
//创建分页对象
PageRequest pageRequest = new PageRequest(user.getPage() - 1,user.getSize(),sort);
//执行分页查询
return userJPA.findAll(pageRequest).getContent();
}
7、SpringBoot validator
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ak4iCaD0-1605780851806)(./image/6.validator.png)]
5、SpringBoot项目下SpringDataJPA与QueryDSL框架整合
参考:https://www.jianshu.com/p/7379173e1970
~~~~~~~~~~~~~~~~
7、Springboot启动配置原理
几个重要的事件回调机制
配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
启动流程:
1、创建SpringApplication对象
initialize(sources);
private void initialize(Object[] sources) {
//保存主配置类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断当前是否一个web应用
this.webEnvironment = deduceWebEnvironment();
//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
2、运行run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
Banner printedBanner = printBanner(environment);
//创建ApplicationContext;决定创建web的ioc还是普通的ioc
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared();
//
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
//s刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
refreshContext(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回调finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成以后返回启动的ioc容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
3、事件监听机制
配置在META-INF/spring.factories
ApplicationContextInitializer
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
}
}
SpringApplicationRunListener
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
//必须有的构造器
public HelloSpringApplicationRunListener(SpringApplication application, String[] args){
}
@Override
public void starting() {
System.out.println("SpringApplicationRunListener...starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object o = environment.getSystemProperties().get("os.name");
System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextLoaded...");
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("SpringApplicationRunListener...finished...");
}
}
配置(META-INF/spring.factories)
org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run....");
}
}
CommandLineRunner
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
}
}
~~~~~~~~~~~~~~~~
8、SpringBoot自定义starters
starter:
1、这个场景需要使用到的依赖是什么?
2、如何编写自动配置
@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定的条件成立的情况下生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationProperties //结合响应的xxxPropeties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
自动配置类能加载:需要将启动就加载的自动配置类,配置在META-INF/spring.factories
模式:
**别人只需要引入启动器(starter);而启动器依赖自动配置;**
启动器只用来做依赖导入;
专门来写一个自动配置模块;
命名:
-mybatis-spring-boot-starter:自定义启动器命名 xxx-spring-boot-starter
自定义starter步骤如下:(demo参照百度云–>05.springboot自定义starter)
- 自定义starter启动器(maven工程的方式),并在pom文件中引入自动配置工程的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>hello-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--引入自动配置模块的maven坐标-->
<dependency>
<groupId>com.demo</groupId>
<artifactId>hello-spring-boot-configure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 自定义自动配置模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>hello-spring-boot-configure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>hello-spring-boot-configure</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.15.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies
<!‐‐引入spring‐boot‐starter;所有starter的基本配置‐‐>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
- 自定义自动配置类
@Configuration
//此应用为web应用时此类生效
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
@Autowired
HelloProperties helloProperties;
@Bean
public HelloService helloService() {
HelloService service = new HelloService();
service.setHelloProperties(helloProperties);
return service;
}
}
- 自定义自动配置properties
@ConfigurationProperties(prefix = "hello.say")
public class HelloProperties {
private String prefix;
private String suffix;
private String age;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
- 自定义初始化到容器中的实体
public class HelloService {
HelloProperties helloProperties;
public String sayHello(String name) {
return helloProperties.getPrefix() + "-" + helloProperties.getSuffix() +"-"+name +"-"+ helloProperties.getAge();
}
public HelloProperties getHelloProperties() {
return helloProperties;
}
public void setHelloProperties(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}
}
- 在resources中创建META-INF/spring.factoris文件,并将自定义的自动配置类添加到springboot自动配置进行初始化自动装配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.demo.HelloAutoConfiguration
- 定义测试web工程(idea快速创建springboot项目是勾选web即可)
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello() {
return helloService.sayHello("张三");
}
}
================================
application.properties
hello.say.prefix=HELLO
hello.say.suffix=WORLD
hello.say.age=20
~~~~~~~~~~~~~~~~
9、SpringBoot与缓存
1、SpringBoot默认缓存机制
spring缓存抽象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nouuooAH-1605780851809)(./image/9.缓存概念.png)]
Cache、CacheManager
快速体验缓存
重点
1.开启基于注解的缓存 @EnableCaching
2.标注缓存注解即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E4c1qKB3-1605780851810)(./image/9.缓存注解.png)]
- @Cacheable: 将方法的运行结果进行缓存,以后要相同的数据,直接从缓存中获取,不用调方法
CacheManager管理多个cache组件的,对缓存的真正crud操作在cache组件中,每个缓存组件都有自己唯一的一个名字
几个属性:
cacheNames/value:指定缓存组件的名字;
key:缓存数据使用的key:可以用它类指定。默认是使用方法参数的值 --> 1-方法的返回值
编写spEL #id 参数ID的值 等价于 #a0,#p0,#root.args[0]
keyGenetator:key的生成器:可以自己指定key的生成器的组件id
key、keyGenetator:二选一使用
cacheManager:指定缓存管理器,或者cacheResolver指定获取解析器
condition:指定符合条件的情况下才缓存;
unless:否定缓存,当unless指定的条件为true,方法的返回值就不会被缓存,可以获取到结果进行判断 unless="#result == null"
sync:是否使用异步模式 -->unless不能使用
- @CachePut:既调用方法,也更新缓存数据
修改了数据库中的某个数据,同时更新缓存
运行时机:
1. 先调用目标方法
2. 将目标方法的结果缓存起来
- @CacheEvict:缓存清除
key:指定要清楚的额数据
allEntries=true:指定清除这个缓存中所有的数据
beforeInvocation=false:缓存的清除是否在方法之前执行(默认是在方法执行之后执行,如果出现异常就不会清除)
beforeInvocation=true:在方法运行之前执行,无论方法是否出现异常,缓存都清除
- @Caching 组合注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
- @CacheConfig
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
String[] cacheNames() default {};
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
}
- 案例
@Service
@CacheConfig(cacheNames = "emp") //抽取公共属性
public class EmpService {
@Autowired
EmployeeMapper employeeMapper;
//缓存key为id,lastName,email的属性
@Caching(
cacheable = {
@Cacheable(key = "#lastName")
}, put = {
@CachePut(key = "#result.id"),
@CachePut(key = "#result.email")
}
)
public Employee findByLastName(String lastName) {
return employeeMapper.findByLastName(lastName);
}
}
- 自定义KeyGenerator
@Configuration
public class CacheConfig {
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
return method.getName()+"["+Arrays.asList(objects).toString()+"]";
}
};
}
}
//引用keyGenerator
@Cacheable(cacheNames = "emp",keyGenerator = "myKeyGenerator")
public Employee findOne(Integer id) {
System.out.println("查询员工信息");
return employeeMapper.findById(id);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jU1SAJoQ-1605780851811)(./image/9.cache–SpEL.png)]
启动时默认生效的缓存配置类:SimpleCacheConfiguration 给容器中注册了CacheManager组件:ConcurrentMapCacheManager
可以获取和创建ConcurrentMapCache类型的缓存组件,他的作用是将数据保存到ConcurrentMap中
运行流程:
使用@Cacheable执行流程:
1.方法在运行之前,先查询Cache(缓存组件),按照cacheNames指定的名字获取;
(CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建
2.去Cache中查找缓存的内容,使用一个key,默认是方法的参数
key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key
SimpleKeyGenerator生成key的策略
如果没有参数: key = new SimpleKey();
如果有一个参数:key=参数的值
如果有多个参数:key= newSimpleKey(params)
3.没有查询到缓存就调用目标方法
4.将目标方法返回的结果,放进缓存中
@Cacheable标注的方法执行之前先检查缓存中国有没有数据,默认按照参数的值作为key去查询缓存,如果没有就执行目标方法,将目标方法返回的结果放入缓存,以后再来调用就可以直接使用缓存中的数据;
核心:
1.使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurentMapCache】组件
2.key使用keyGenerator生成的,默认是SimpleKeyGenerator
2、SpringBoot整合redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1、Redis安装(Docker镜像的方式 )
1.启动docker
[aaa@qq.com ~]# systemctl start docker
2.下载Redis镜像
[aaa@qq.com ~]# docker pull registry.docker-cn.com/library/redis
3.查看docker中的镜像
[aaa@qq.com ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.docker-cn.com/library/redis latest e1a73233e3be 45 hours ago 83.4 MB
4.启动Redis镜像,创建实例
[aaa@qq.com ~]# docker run -d -p 6379:6379 --name myredis registry.docker-cn.com/library/redis
-d 表示后台运行 -p 表示端口映射(Redis端口:服务器或者虚拟机的端口)
5.查看启动的镜像实例 详情
[aaa@qq.com ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
30072cf1139a registry.docker-cn.com/library/redis "docker-entrypoint..." 2 minutes ago Up 2 minutes 0.0.0.0:6379->6379/tcp myredis
2、Java操作Redis数据库
- StringRedisTemplate
stringRedisTemplate.opsForValue(); [操作string字符串]
stringRedisTemplate.opsForList(); [操作list集合]
stringRedisTemplate.opsForHash(); [操作hash散列]
stringRedisTemplate.opsForSet(); [操作set集合]
stringRedisTemplate.opsForZSet() [操作zset有序集合]
- RedisTemplate
redisTemplate.opsForValue(); [操作string字符串]
redisTemplate.opsForList(); [操作list集合]
redisTemplate.opsForHash(); [操作hash散列]
redisTemplate.opsForSet(); [操作set集合]
redisTemplate.opsForZSet() [操作zset有序集合]
- 测试demo
package com.example.demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void contextLoads() {
stringRedisTemplate.opsForValue().append("a", "hello");
String a = stringRedisTemplate.opsForValue().get("a");
System.out.println(a);
}
}
3、自定义RedisTemplate通过序列化存储Json对象
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Employee> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Employee>(Employee.class));
return template;
}
}
- 引入Redis的starter,容器中保存的是RedisCacheManager
- RedisCacheManager帮助我们创建RedisCache 来作为缓存组件,RedisCache操作Redis缓存
- 默认报错数据K-V都市object:利用序列化保存
- 如何保存json
- 引入Redis的starter,cacheManager变为RedisCacheManager
- 默认创建的
RedisCacheManager
操作Redis的时候使用的是RedisTemplate<Obeject,Object>
-
RedisTemplate<Obeject,Object>
是默认使用JDK的序列化机制
4、CacheManager配置
- SpringBoot1.x版本
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setUsePrefix(true);
//设置过期时间
cacheManager.setDefaultExpiration(3000);
return cacheManager;
}
-
通过配置Spring的CacheManager为redis,即可指定使用redis做缓存,具体的配置方式跟1.0也有所不同,在1.0中使用RedisTemplate即可实例化一个RedisCacheManager:
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
,在2.0中删除了这个构造器,同时也不可以通过之前的setDefaultExpiration方法设置默认的缓存过期时间等,在新版本中可以通过以下的两种方式构造一个RedisCacheManager: -
SpringBoot2.x版本
-
通过RedisCacheManager的静态方法create:
@Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheManager cacheManager = RedisCacheManager.create(factory); return cacheManager; }
-
通过Spring提供的RedisCacheConfiguration类,构造一个自己的redis配置类,从该配置类中可以设置一些初始化的缓存命名空间、及对应的默认过期时间等属性,再利用RedisCacheManager中的builder.build()的方式生成cacheManager:
@Bean public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { //设置默认缓存的有效期以及key和value的序列化方式 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(60)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class))) .disableCachingNullValues(); // 设置一个初始化的缓存空间set集合 Set<String> cacheNames = new HashSet<>(); cacheNames.add("employee"); cacheNames.add("depart"); // 对每个缓存空间应用不同的配置 Map<String, RedisCacheConfiguration> configMap = new HashMap<>(); configMap.put("employee", config); configMap.put("depart", config.entryTtl(Duration.ofSeconds(600))); RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory) .cacheDefaults(config).initialCacheNames(cacheNames).withInitialCacheConfigurations(configMap) .transactionAware() .build(); return redisCacheManager; }
-
~~~~~~~~~~~~~~~~
10、SpringBoot与消息(RabbitMQ)
1、概述
- 点对点
- 消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移除队列
- 消息只有唯一的发送者和接受者,但不是说只能有一个接收者
- 发布订阅式
- 发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达时同时收到消息
- JMS(Java Message Service) Java消息服务:
- 基于JVM消息代理的规范。ActiveMQ、HornetMQ是JMS实现的
- AMQP(Advanced Message Queuing Protocol)
- 高级消息队列协议,也是一个消息代理的规范,兼容JMS
- RabbitMQ是AMQP的实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vy4c5WNv-1605780851812)(./image/10.JMS和AMQP对比.png)]
2、Spring支持
- spring-jms提供了对JMS的支持
- spring-rabbit提供了对AMQP的支持
- 需要ConnectionFactory的实现来连接消息代理
- 提供JmsTemplate、RabbitTemplate来发送消息
- @JmsListener(JMS)、@RabbltListener(AMQP)注解在方法上监听消息代理发布消息
- @EnableJms、@EnableRabbit开启支持
3、SpringBoot-MQ自动配置
- JmsAutoConfiguration
- RabbitAutoConfiguration
1、RabbitMQ简介
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZpKVkoQ-1605780851813)(./image/10.rabbitMQ简介.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AzGbHPYn-1605780851814)(./image/10.rabbitMQ简介2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCK0pAEC-1605780851814)(./image/10.rabbitMQ简介3.png)]
交换器:决定消息发送到哪,根据路由键将交换器和消息队列进行绑定
2、RabbitMQ的Docker镜像安装
1.下载rabbitmq镜像
[aaa@qq.com ~]# docker pull registry.docker-cn.com/library/rabbitmq:3-management
2.查看镜像ID
[aaa@qq.com ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.docker-cn.com/library/redis latest e1a73233e3be 2 days ago 83.4 MB
registry.docker-cn.com/library/rabbitmq latest d502f687f36a 2 days ago 125 MB
3.根据镜像ID生成并启动实例
[aaa@qq.com ~]# docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq d502f687f36a
4.查看运行状态
[aaa@qq.com ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6ab564be385e d502f687f36a "docker-entrypoint..." 7 minutes ago Up 7 minutes 4369/tcp, 0.0.0.0:5672->5672/tcp, 5671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp myrabbitmq
30072cf1139a registry.docker-cn.com/library/redis "docker-entrypoint..." 15 hours ago Up 15 hours 0.0.0.0:6379->6379/tcp myredis
注释 : d502f687f36a --> rabbitmq的镜像ID
3、RabbitMQ的界面化操作
参照: https://www.bilibili.com/video/av23478787?t=626&p=88
1)登录RabbitMQ
-
http://172.16.38.150:15672/
-
用户名 guest
-
密码guest
2)添加exchange交换器
- 如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-idwpagQa-1605780851815)(./image/10.rabbitMQ的exchange添加.png)]
3) 添加消息队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wOQai4Ek-1605780851816)(./image/10.rabbitMQ添加消息queue.png)]
4)将交换器和 队列进行绑定
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vf30PHjV-1605780851817)(./image/10.exchange绑定topic类型.png)]
- direct : 根据路由键匹配对应的队列
- fanout:发送消息到所以的队列
- topic: 根据定义的匹配规则,匹配相应的队列
4、SpringBoot集成RabbitMQ
-
RabbitMQ的几个核心
-
RabbitAutoConfiguration
自动配置类 -
ConnectionFactory
自动配置了连接工厂 -
RabbitProperties
封装了rabbitMQ的配置 -
RabbitTemplate
给rabbitMQ发送和接收消息 -
AmqpAdmin
rabbitMQ系统管理功能组件,创建和删除Queue,Exchange,Binding -
@RabbitListener
+@EnableRabbit
监听消息队列的内容
-
-
引入maven坐标(或用idea创建时勾选rabbitMQ)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 配置属性文件(application.yml)
spring:
rabbitmq:
addresses: 172.16.38.150
username: guest
password: guest
# 默认为 /
# virtual-host:
- 编写测试文件
package com.example.demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void contextLoads() {
//对象被默认序列化后发送
rabbitTemplate.convertAndSend("exchange.direct","hello","这是第一个消息");
}
@Test
public void demo() {
Object hello = rabbitTemplate.receiveAndConvert("hello");
System.out.println(hello);
}
}
- 默认的序列化方式是以JDK序列化规则进行序列化(二进制数据存储)
- 修改序列化方式为json,如下
package com.example.demo.config;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* ========================
* Created with IntelliJ IDEA.
* package: com.example.demo.config
* author:kehong
* className: RabbitConfig
* projectName: springboot-rabbitmq
* Date:2018-09-18
* Time:下午3:46
* description: ${DESCRIPTION}
* ========================
*/
@Configuration
public class RabbitConfig {
@Bean
public MessageConverter myMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
- Springboot中RabbitMQ生效配置
-
@RabbitListener
+@EnableRabbit
监听消息
-
package com.example.demo.service;
import com.example.demo.bean.Book;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* ========================
* Created with IntelliJ IDEA.
* package: com.example.demo.service
* author:kehong
* className: BookService
* projectName: springboot-rabbitmq
* Date:2018-09-18
* Time:下午4:01
* description: ${DESCRIPTION}
* ========================
*/
@Service
public class BookService {
//队列有内容,方法recieve会执行
//反序列化数据到book对象中
@RabbitListener(queues = "hello.news")
public void recieve(Book book) {
System.out.println(book);
}
@RabbitListener(queues = "hello.emps")
public void recieve(Message message) {
System.out.println(message.getBody());
System.out.println(message.getMessageProperties());
}
}
===========================================================
package com.example.demo;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableRabbit //开启基于注解的RabbitMQ
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- SpringBoot中系统管理功能操作
package com.example.demo;
import com.example.demo.bean.Book;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
AmqpAdmin amqpAdmin;
@Test
public void amqpAdmin() {
//创建交换器
amqpAdmin.declareExchange(new DirectExchange("amqpAdmin.exchange"));
//创建队列
amqpAdmin.declareQueue(new Queue("amqpAdmin.queue",true));
//创建绑定规则
amqpAdmin.declareBinding(new Binding("amqpAdmin.queue",Binding.DestinationType.QUEUE,"amqpAdmin.exchange","amqp.news",null));
}
~~~~~~~~~~~~~~~~
11、SpringBoot与搜索
1、Docker下安装elasticsearch
1.搜索镜像
[aaa@qq.com ~]# docker search elasticsearch
2.下载镜像
[aaa@qq.com ~]# docker pull registry.docker-cn.com/library/elasticsearch
3.查看镜像ID
[aaa@qq.com ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.docker-cn.com/library/elasticsearch latest 362c5cb1669b 2 days ago 486 MB
registry.docker-cn.com/library/redis latest e1a73233e3be 2 days ago 83.4 MB
registry.docker-cn.com/library/rabbitmq 3-management 2888deb59dfc 3 days ago 149 MB
4.根据镜像ID生成容器
[aaa@qq.com ~]# docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES01 362c5cb1669b
3ed338952f83a9503bac88aa64c358415116f3d8f72b9c45c59d5e65c2032e6d
5. 查看运行状态
[aaa@qq.com ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
71d635e2e017 362c5cb1669b "/docker-entrypoin..." 14 seconds ago Up 13 seconds 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp ES01
eb38f708564e 2888deb59dfc "docker-entrypoint..." 7 hours ago Up 7 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp myrabbitmq
30072cf1139a registry.docker-cn.com/library/redis "docker-entrypoint..." 23 hours ago Up 23 hours 0.0.0.0:6379->6379/tcp myredis
- 9200端口用于外部访问,9300用于内部节点之间的通讯
2、查看elasticsearch是否安装成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FCOhmRrq-1605780851817)(./image/11.elasticsearch访问.png)]
3、 elasticsearch概念
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5LMQufK3-1605780851818)(./image/11.elasticsearch概念图.png)]
-
ElasticSearch学习文档参考
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
-
ElasticSearch的数据存储风格包括条件查询都是以json的形式实现的
4、elasticsearch的基本操作
1)通过restful风格往elasticsearch中存入数据
- 每个雇员索引一个文档,包含该雇员的所有信息。
- 每个文档都将是
employee
类型 。 - 该类型位于 索引
megacorp
内。 - 该索引保存在我们的 Elasticsearch 集群中。
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G1aaegRl-1605780851819)(./image/11.elasticsearch:postman PUT操作.png)]
- 注意,路径
/megacorp/employee/1
包含了三部分的信息:
megacorp
索引名称
employee
类型名称
1
特定雇员的ID
2)通过restful风格检索文档
GET /megacorp/employee/1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CkA1XBoW-1605780851820)(./image/11.elasticsearch:postman GET操作.png)]
- 将 HTTP 命令由
PUT
改为GET
可以用来检索文档,同样的,可以使用DELETE
命令来删除文档,以及使用HEAD
指令来检查文档是否存在。如果想更新已存在的文档,只需再次PUT
。
HEAD /megacorp/employee/1 //若检索存在返回Status:200 , 检索不存在则返回Status:404
DELETE /megacorp/employee/1
3)轻量搜索
- 搜索所有雇员
GET /megacorp/employee/_search
- 根据last_name=Smith检索
GET /megacorp/employee/_search?q=last_name:Smith
4)使用查询表达式搜索
POST /megacorp/employee/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2a8zslye-1605780851821)(./image/11.elasticsearch:postman POST操作.png)]
5)复杂搜索
- 检索 last_name 和age大于30的员工
POST /megacorp/employee/_search
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
6)全文检索
- 全文检索会根据关键字进行分词搜索
POST /megacorp/employee/_search
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
7)短语搜索
- 根据关键字进行匹配搜索
POST /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}
}
8)高亮搜索
POST /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
结果:
{
...
"hits": {
"total": 1,
"max_score": 0.23013961,
"hits": [
{
...
"_score": 0.23013961,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
},
"highlight": {
"about": [
"I love to go <em>rock</em> <em>climbing</em>"
]
}
}
]
}
}
- 更多搜索方式 请参照官方文
5、SpringBoot整合elasticsearch
1)springboot默认支持俩种技术和ES进行交互
- Jest:默认不生效
- 需导入Jest的工具包(io.searchbox.client.JestClient)
- SpringData ElacticSearch
- TransportClient:节点信息clusterNodes:clusterName
2)Jest方式整合es
- 导入jest坐标依赖(注意版本问题)
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
<version>5.3.4</version>
</dependency>
- 设置application.yml
spring:
elasticsearch:
jest:
uris: http://172.16.38.150:9200/
- 使用JestClient
package com.example.demo.bean;
import io.searchbox.annotations.JestId;
/**
* ========================
* Created with IntelliJ IDEA.
* package: com.example.demo.bean
* author:kehong
* className: Book
* projectName: springboot-elasticsearch
* Date:2018-09-25
* Time:下午4:55
* description: ${DESCRIPTION}
* ========================
*/
public class Book {
//标示主键
@JestId
private Integer id;
private String name;
private double price;
private String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
package com.example.demo;
import com.example.demo.bean.Book;
import com.google.gson.Gson;
import io.searchbox.client.JestClient;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private JestClient jestClient;
@Test
public void contextLoads() {
//给es种索引(保存)一个文档
Book book = new Book();
book.setId(11);
book.setName("平凡的世界");
book.setPrice(20.0);
book.setAuthor("路遥");
//构建一个索引功能
Index index = new Index.Builder(book).index("hello").type("book").build();
try {
//执行
jestClient.execute(index);
} catch (IOException e) {
e.printStackTrace();
}
}
//测试搜索
@Test
public void search(){
String json = "{\n" +
" \"query\" : {\n" +
" \"match\" : {\n" +
" \"author\" : \"路遥\"\n" +
" }\n" +
" }\n" +
"}";
Search search = new Search.Builder(json).addIndex("hello").addType("book").build();
try {
SearchResult searchResult = jestClient.execute(search);
System.out.println(searchResult.getJsonString());
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(search);
}
}
-
更多jest操作参考
3)spring data elasticsearch整合es
-
spring data elasticsearch对应elasticsearch安装版本参考
版本对应
-
安装对应版本的elastic search
[aaa@qq.com ~]# docker pull registry.docker-cn.com/library/elasticsearch:5.5.0
[aaa@qq.com ~]# docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES02 519c56205eb
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
6、 @Document
@Document注解里面的几个属性,类比mysql的话是这样:
index –> DB
type –> Table
Document –> row
加上@Id注解后,在Elasticsearch里对应的该列就是主键了,在查询时就可以直接用主键查询。其实和mysql非常类似,基本就是一个数据库。
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Document {
String indexName();//索引库的名称,个人建议以项目的名称命名
String type() default "";//类型,个人建议以实体的名称命名
short shards() default 5;//默认分区数
short replicas() default 1;//每个分区默认的备份数
String refreshInterval() default "1s";//刷新间隔
String indexStoreType() default "fs";//索引文件存储类型
}
7、 @Field
加上了@Document注解之后,默认情况下这个实体中所有的属性都会被建立索引、并且分词。
通过@Field注解来进行详细的指定,如果没有特殊需求,那么只需要添加@Document即可。
@Field注解的定义如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface Field {
FieldType type() default FieldType.Auto;//自动检测属性的类型
FieldIndex index() default FieldIndex.analyzed;//默认情况下分词
DateFormat format() default DateFormat.none;
String pattern() default "";
boolean store() default false;//默认情况下不存储原文
String searchAnalyzer() default "";//指定字段搜索时使用的分词器
String indexAnalyzer() default "";//指定字段建立索引时指定的分词器
String[] ignoreFields() default {};//如果某个字段需要被忽略
boolean includeInParent() default false;
}
8、 ElasticsearchRepository
//不需要加@Component,直接可以@Autowared
package com.example.demo.repository;
import com.example.demo.bean.Book;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface BookRepository extends ElasticsearchRepository<Book,Integer> {
/*
* 功能描述:
* @method: findByNameAndPrice
* @param: [name, price]
* @return: java.util.List<com.example.demo.bean.Book>
* @date: 2018/9/30 下午6:23
* @author: mac
* 等价于
* 发送一下json形式的查询表达式
{ "bool" :
{ "must" :
[
{ "field" : {"name" : "?"} },
{ "field" : {"price" : "?"} }
]
}
}
*/
//查询方式一
public List<Book> findByNameAndPrice(String name, Integer price);
//查询方式二
@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
Page<Book> findByName(String name, Pageable pageable);
}
package com.example.demo;
import com.example.demo.bean.Book;
import com.example.demo.repository.BookRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
BookRepository bookRepository;
@Test
public void contextLoads() {
Book book = new Book();
book.setId(1);
book.setBookName("西游记");
book.setPrice(35.0);
bookRepository.index(book);
}
}
异常:NoNodeAvailableException[None of the configured nodes are available: [{#transport#-1}{QD96g9OlRsGdO50_Yv6UqA}{172.16.38.150}{172.16.38.150:9200}]]
~~~~~~~~~~~~~~~~
12、SpringBoot异步任务
- 开启异步任务注解
@EnableAsync //开启异步任务支持注解
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- 标示异步方法
package com.example.demo.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
@Async //此注解表示在开启一条线程执行此方法,告诉Spring这是一个异步方法
public void hello() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行hello方法");
}
}
- 测试
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
//无需等待service方法执行完
@RequestMapping("/hello")
public String hello() {
helloService.hello();
return "调用成功";
}
}
~~~~~~~~~~~~~~~~
13、SpringBoot定时任务
- 开启定时任务注解
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling //开启定时任务支持注解
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- 标示定时方法
package com.example.demo.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class ScheduledService {
/*
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。按顺序依次为:
秒(0~59)
分钟(0~59)
3 小时(0~23)
4 天(0~31)
5 月(0~11)
6 星期(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
年份(1970-2099)
---------------------
,:枚举
-:区间
斜杠:步长
?:日/星期冲突匹配
L:代表最后
W:代表工作日
C:和calendar联系后计算过的值
#:星期,4#2,第二个星期四
每隔5秒执行一次:/5 * * * ?
每隔1分钟执行一次:0 /1 * * ?
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
“0 0 12 * * ?” 每天中午12点触发
“0 15 10 ? * *” 每天上午10:15触发
“0 15 10 * * ?” 每天上午10:15触发
“0 15 10 * * ? *” 每天上午10:15触发
“0 15 10 * * ? 2005” 2005年的每天上午10:15触发
“0 * 14 * * ?” 在每天下午2点到下午2:59期间的每1分钟触发
“0 0/5 14 * * ?” 在每天下午2点到下午2:55期间的每5分钟触发
“0 0/5 14,18 * * ?” 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
“0 0-5 14 * * ?” 在每天下午2点到下午2:05期间的每1分钟触发
“0 10,44 14 ? 3 WED” 每年三月的星期三的下午2:10和2:44触发
“0 15 10 ? * MON-FRI” 周一至周五的上午10:15触发
“0 15 10 15 * ?” 每月15日上午10:15触发
“0 15 10 L * ?” 每月最后一日的上午10:15触发
“0 15 10 ? * 6L” 每月的最后一个星期五上午10:15触发
“0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最后一个星期五上午10:15触发
“0 15 10 ? * 6#3” 每月的第三个星期五上午10:15触发
*/
@Scheduled(cron = "0/5 * * * * *")
public void scheduled() {
System.out.println("定时任务执行了。。。");
}
}
~~~~~~~~~~~~~~~~
14、SpringBoot邮件任务
- 引入mail启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
- 配置application.yml
spring:
mail:
host: smtp.163.com
username: aaa@qq.com
password: ke0826
properties:
-mail.smtp.auth: true
-mail.smtp.starttls.enable: true
-mail.smtp.starttls.required: true
- 测试邮件发送
package com.example.demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.test.context.junit4.SpringRunner;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
JavaMailSenderImpl mailSender;
@Test
public void contextLoads() {
//简单邮件发送
/*SimpleMailMessage message = new SimpleMailMessage();
message.setText("测试邮件");
message.setTo("aaa@qq.com");
message.setFrom("aaa@qq.com");*/
//复杂邮件发送
MimeMessage mimeMessage = mailSender.createMimeMessage();
try {
//发送复杂的消息邮件 参数二:true表示是否上传附件
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
//邮件设置
helper.setSubject("通知,今晚开会!");
//true表示发送内容为html
helper.setText("<b style='color:red'>今天7:30开会</b>",true);
helper.setTo("aaa@qq.com");
helper.setFrom("aaa@qq.com");
//上传文件
//helper.addAttachment("1.jpg",new File("d:\\1.jpg"));
//helper.addAttachment("2.jpg",new File("d:\\2.jpg"));
mailSender.send(mimeMessage);
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
~~~~~~~~~~~~~~~~
15、SpringBoot与安全验证
1)、引入SpringSecurity:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2)、编写SpringSecurity的配置类:
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
}
3)、控制请求的访问权限:
package com.example.demo.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* ========================
* Created with IntelliJ IDEA.
* package: com.example.demo.config
* author:kehong
* className: MySecurityConfig
* projectName: springboot-security
* Date:2018-09-27
* Time:上午11:39
* description: ${DESCRIPTION}
* ========================
*/
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
//定制请求的授权规则
//permitAll 表示所有人都可以访问 此处的/代表首页
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("VIP1")
.antMatchers("/level2/**").hasRole("VIP2")
.antMatchers("/level3/**").hasRole("VIP3");
/*
*开启自动配置的登录功能 效果:如果没有登录,没有权限就会来到登录页面
*
*功能描述:
* 1、自动生成登录页面 /login
* 2、登录失败:重定向到/login?error
* 3、默认的post形式的/login代表处理登录,get形式的/login代表跳转到登录页面
* 4、一旦自己定制loginPage,那么loginPage的post请求就是登录处理
* 也可通过 loginProcessingUrl()指定登录处理路径
* 5、usernameParameter 代表请求用户名参数名称设置 passwordParameter 代表请求密码参数设置
*
*/
http.formLogin().loginPage("/userlogin").usernameParameter("user").passwordParameter("pass");
/*
*开启自动配置的注销
*
*功能描述:
* 1、访问 /logout 表示用户注销
* 2、清空session
* 3、logoutSuccessUrl 注销成功后 返回到哪里 此处的/代表首页
*/
http.logout().logoutSuccessUrl("/");
//开启记住我功能
//登录成功后,将cookie发送给浏览器保存,以后登录带上这个cookie,只要通过安全检查就可以免登录
//默认请求参数的名称为remember-me
http.rememberMe().rememberMeParameter("remember");
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//super.configure(auth);
//auth.jdbcAuthentication() -->在数据库里查用户
auth.inMemoryAuthentication()
.withUser("zhangsan").password("123456").roles("VIP1", "VIP2")
.and()
.withUser("lisi").password("123456").roles("VIP1", "VIP3")
.and()
.withUser("wangwu").password("123456").roles("VIP2", "VIP3");
}
}
4)、thymeleaf支持springsecurity
<properties>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
<thymeleaf-extras-springsecurity4.version>3.0.2.RELEASE</thymeleaf-extras-springsecurity4.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
</dependencies>
5)、html中使用springsecurity
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div sec:authorize="!isAuthenticated()">
游客您好,您尚未登录<a th:href="@{/login}">请登录</a>
</div>
<div sec:authorize="isAuthenticated()">
<h2><span sec:authentication="name"></span>,您好,您的角色有:
<span sec:authentication="principal.authorities"></span>
</h2>
<form th:action="@{/logout}" method="post">
用户名:<input name="user"><br/>
密码:<input name="pass"><br/>
<input type="checkbox" name="remember"> 记住我<br/>
<input type="submit" value="注销">
</form>
</div>
<div sec:authorize="hasRole('VIP1')">
权限为VIP1 显示内容
</div>
<div sec:authorize="hasRole('VIP2')">
权限为VIP2 显示内容
</div>
<div sec:authorize="hasRole('VIP3')">
权限为VIP3 显示内容
</div>
</body>
</html>
~~~~~~~~~~~~~~~~
16、分布式应用
1)dubbo+zookeeper的模式
- docker下安装和启动zookeeper
[aaa@qq.com ~]# docker pull registry.docker-cn.com/library/zookeeper
[aaa@qq.com ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/zookeeper latest 89f7884dcc4e Less than a second ago 148 MB
registry.docker-cn.com/library/elasticsearch latest 362c5cb1669b 3 days ago 486 MB
registry.docker-cn.com/library/redis latest e1a73233e3be 3 days ago 83.4 MB
registry.docker-cn.com/library/rabbitmq 3-management 2888deb59dfc 3 days ago 149 MB
[aaa@qq.com ~]# docker run --name zk01 -p 2181:2181 --restart always -d 89f7884dcc4e
d06afa17e60a281f1d6d938bf20ecd0493fc70d7bdc383640c469f88fef73024
[aaa@qq.com ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d06afa17e60a 89f7884dcc4e "/docker-entrypoin..." 3 minutes ago Up 3 minutes 2888/tcp, 0.0.0.0:2181->2181/tcp, 3888/tcp zk01
71d635e2e017 362c5cb1669b "/docker-entrypoin..." 12 hours ago Up 12 hours 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp ES01
eb38f708564e 2888deb59dfc "docker-entrypoint..." 20 hours ago Up 20 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp myrabbitmq
30072cf1139a registry.docker-cn.com/library/redis "docker-entrypoint..." 36 hours ago Up 36 hours 0.0.0.0:6379->6379/tcp
-
provider配置
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
server:
port: 8081
dubbo:
application:
name: customer-dubbo
registry:
address: zookeeper://172.16.38.150:2181
scan:
base-packages: com.example.demo.service
package com.example.demo.service;
public interface ProviderService {
public void dubbo();
}
package com.example.demo.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.example.demo.service.ProviderService;
import org.springframework.stereotype.Component;
@Service
@Component
public class ProviderServiceImpl implements ProviderService {
public void dubbo() {
System.out.println("dubbo-service执行了");
}
}
- customer配置
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<!--引入provider 打包到本地仓库的坐标依赖-->
<dependency>
<groupId>com.example</groupId>
<artifactId>provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
dubbo:
application:
name: customer-dubbo
registry:
address: zookeeper://172.16.38.150:2181
server:
port: 8080
package com.example.demo.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.example.demo.service.ProviderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
//@Reference
ProviderService providerService;
@RequestMapping(value = "dubbo")
public void dubbo() {
providerService.dubbo();
}
}
2)SpringBoot+SpringCloud的模式
1、SpringCloud概述
- Spring Cloud是一个分布式的整体解决方案,Spring Cloud为开发者提供了在分布式系统(配置管理,服务发现,熔断,路由,微代理,控制总线,一次性token,全局锁,leader选举,分布式session,集群状态)中快速构建的工具,使用Spring Cloud的开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。
- Spring Cloud分布式开发的五大常用组件
- 服务发现—Netflix Eureka 等价于zookeeper
- 客户端负载均衡—Netflix Ribbon
- 断路器—Netflix Hystrix
- 服务网关—Netflix Zuul
- 分布式配置—Spring Cloud Confi
2、SpringCloud注册中心Eureka
- 创建配置注册中心Eureka
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x4D0WrP8-1605780851822)(./image/16.springcloud eureka创建.png)]
1、 配置eureka相关信息
server:
port: 8761
eureka:
instance:
hostname: eureka-server #eureka实例的主机名
client:
#不将自己注册到eureka上
register-with-eureka: false
#不从eureka上获取服务的注册信息
fetch-registry: false
service-url:
#默认值为 http://localhost:8761/eureka/
defaultZone: http://localhost:8761/eureka/
2、开启@EnableEurekaServer
注解
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
//注册中心
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3、启动并访问http://localhost:8761/观察eureka是否配置正确
- 创建生产者或消费者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DaTnjgi1-1605780851823)(./image/16.springclod 生产者或消费者创建.png)]
- 生产者配置
package com.example.providerticket.service;
import org.springframework.stereotype.Service;
@Service
public class TicketService {
public String getTicket() {
return "《厉害了,我的国》";
}
}
==================================================================
package com.example.providerticket.controller;
import com.example.providerticket.service.TicketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TicketController {
@Autowired
TicketService ticketService;
@GetMapping("ticket")
public String getTicket() {
return ticketService.getTicket();
}
}
server:
port: 8001
spring:
application:
name: provider-ticket
eureka:
instance:
#注册服务时,使用服务的IP地址
prefer-ip-address: true
client:
service-url:
#默认值为 http://localhost:8761/eureka/
defaultZone: http://localhost:8761/eureka/
- 消费者配置
package com.example.customeruser.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class UserController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/buy")
public String buyTicket(String name) {
String s = restTemplate.getForObject("http://PROVIDER-TICKET/ticket", String.class);
return name + "购买了:" + s;
}
}
==============================================================================================
package com.example.customeruser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient //开启发现服务功能
public class CustomerUserApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerUserApplication.class, args);
}
@Bean
@LoadBalanced //使用负载均衡机制
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
server:
port: 8002
spring:
application:
name: customer-user
eureka:
instance:
#注册服务时,使用服务的IP地址
prefer-ip-address: true
client:
service-url:
#默认值为 http://localhost:8761/eureka/
defaultZone: http://localhost:8761/eureka/
- 异常
Invocation of destroy method failed on bean with name 'scopedTarget.eurekaClient': org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'eurekaInstanceConfigBean': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
解决办法 : 开始在创建工程时未引入stater-web启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
~~~~~~~~~~~~~~~~
17、SpringBoot开发热部署
- 引入maven坐标
- idea下 ctrl+f9
- eclipse下 ctrl+s
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
~~~~~~~~~~~~~~~~
18、SpringBoot与监控管理
- 通过引入spring-boot-starter-actuator,可以通过Spring Boot为我们提供准生产环境下应用监控和管理功能,我们可以通过HTTP JMX SSH协议来进行操作,自动得到审计、健康及指标信息等。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nvaKITBd-1605780851825)(./image/18.actuator引入.png)]
- SpringBoot1.x版本
#server.port=8081
#1、management.security.enabled默认为true,无访问权限的
#2、默认启用所有的监控端点
management.security.enabled=false
#开启远程关闭 post请求
# endpoints.shutdown.enabled=true
#关闭所有的端点
#endpoints.enabled=false
#开启beans端点
#endpoints.beans.enabled=true
- SpringBoot2.x版本
#1、默认端点 path 前面多了一级 /actuator 。
#2、同时注意只有端点/health和/info端点是暴露的。
#3、通过如下配置开启所有监控端点
management:
endpoints:
web:
exposure:
include: "*"
#关闭所有端点
#enabled-by-default: false
server:
#管理端点端口号
port: 10111
servlet:
context-path: /
ssl:
enabled: false
endpoint:
#开启远程关闭,post请求
#shutdown:
# enabled: true
health:
show-details: always
#开启beans端点
#beans:
# enabled: true
-
management.endpoint.health.show-details
的值除了always
之外还有when-authorized
、never
,默认值是never
。 - SpringBoot1.x版本和SpringBoot2.x版本actuator不同参照
- https://blog.csdn.net/qq_35915384/article/details/80203768
- 监控和管理端点
端点名 | 描述 |
---|---|
autoconfig | 所有自动配置信息 |
auditevents | 审计事件 |
beans | 所有的bean信息 |
configprops | 所有配置信息 |
dump | 线程状态信息 |
env | 当前环境信息 |
health | 应用状态信息(含引入的starter) |
info | 当前应用信息 |
metrics | 应用的各项指标 |
mappings | 应用@RequestMapping映射路径 |
shutdown | 关闭当前应用(默认关闭) |
trace | 追踪信息(最新的http请求) |
1、健康检查的原理
Spring boot的健康信息都是从ApplicationContext
中的各种HealthIndicator
Beans中收集到的,Spring boot框架中包含了大量的HealthIndicators
的实现类,当然你也可以实现自己认为的健康状态。
默认情况下,最终的spring boot应用的状态是由HealthAggregator
汇总而成的,汇总的算法是:
- 设置状态码顺序:
setStatusOrder(Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN);
。 - 过滤掉不能识别的状态码。
- 如果无任何状态码,整个spring boot应用的状态是
UNKNOWN
。 - 将所有收集到的状态码按照 1 中的顺序排序。
- 返回有序状态码序列中的第一个状态码,作为整个spring boot应用的状态。
- Spring boot框架自带的
HealthIndicators
目前包括在此路径下:- spring-boot-actuator/2.0.5.RELEASE/spring-boot-actuator-2.0.5.RELEASE.jar!/org/springframework/boot/actuate/health
你可以通过management.health.defaults.enabled
这个配置项将它们全部禁用掉,也可以通过management.health.xxxx.enabled
将其中任意一个禁用掉。
2、自定义 HealthIndicator 健康检查
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@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();
}
}
另外,除了Spring boot定义的几个状态类型,我们也可以自定义状态类型,用来表示一个新的系统状态。在这种情况下,你还需要实现接口 HealthAggregator
,或者通过配置 management.health.status.order
来继续使用HealthAggregator
的默认实现。
例如,在你自定义的健康检查HealthIndicator
的实现类中,使用了自定义的状态类型FATAL
,为了配置该状态类型的严重程度,你需要在application的配置文件中添加如下配置:
management.health.status.order=FATAL, DOWN, OUT_OF_SERVICE, UNKNOWN, UP
在做健康检查时,响应中的HTTP状态码反应了整体的健康状态,(例如,UP
对应 200, 而 OUT_OF_SERVICE
和 DOWN
对应 503)。同样,你也需要为自定义的状态类型设置对应的HTTP状态码,例如,下面的配置可以将 FATAL
映射为 503(服务不可用):
management.health.status.http-mapping.FATAL=503
如果你需要更多的控制,你可以定义自己的
HealthStatusHttpMapper
bean。
下面是内置健康状态类型对应的HTTP状态码列表:
Status | Mapping |
---|---|
DOWN | SERVICE_UNAVAILABLE (503) |
OUT_OF_SERVICE | SERVICE_UNAVAILABLE (503) |
UP | No mapping by default, so http status is 200 |
UNKNOWN | No mapping by default, so http status is 200 |
~~~~~~~~~~~~~~~~
19、SpringBoot使用 Lombok
1、引入 Lombok
- maven坐标的方式
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
- 快速创建的形式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhPzQKIV-1605780851826)(./image/19.添加Lombok依赖.png)]
- 添加idea支持
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unQywLDa-1605780851827)(./image/19.idea安装Lombok支持插件.png)]
2、 Lombok有哪些注解
- @Setter
- @Getter
- @Data
- @Log(这是一个泛型注解,具体有很多种形式)
- @AllArgsConstructor
- @NoArgsConstructor
- @EqualsAndHashCode
- @NonNull
- @Cleanup
- @ToString
- @RequiredArgsConstructor
- @Value
- @SneakyThrows
- @Synchronized
3、 部分注解演示
package com.example.demo.entity;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* ========================
*
*/
//@Getter
//@Setter 在使用注解时,会默认生成一个无参构造。和对应的getter、setter方法
//@ToString
@Data //该注解使用在类上,该注解会提供getter、setter、equals、canEqual、hashCode、toString方法。
@AllArgsConstructor //该注解使用在类上,该注解提供一个全参数的构造方法,默认不提供无参构造。
@NoArgsConstructor //该注解使用在类上,该注解提供一个无参构造
//@Value 这个注解用在 类 上,会生成含所有参数的构造方法,get 方法,此外还提供了equals、hashCode、toString 方法。注意:没有setter
public class UserEntity {
private String name;
private String sex;
private String height;
}
package com.example.demo;
import com.example.demo.entity.UserEntity;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
//@Slf4j
@Log
public class DemoApplicationTests {
@Test
public void contextLoads() {
UserEntity entity = new UserEntity();
entity.setName("张三");
entity.setHeight("175cm");
entity.setSex("男");
System.out.println(log.getClass());
log.info(entity.toString());
//System.out.println(entity);
}
}
-------------------------------------------------------------
class java.util.logging.Logger
2018-10-08 16:53:33.559 INFO 1054 --- [ main] com.example.demo.DemoApplicationTests : UserEntity(name=张三, sex=男, height=175cm)
4、关于log注解
注解在 类 上。有如下可选择可用:
//@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
//@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
//@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
//@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
//@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
//@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
//@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);