**时代在变化(Spring全家桶时代):**J2EE ->SSH ->SSM ->Spring全家桶
Spring Boot的简介:Spring Boot来简化Spring应用开发,去除J2EE笨重的开发、繁多的配置、低下的开发效率、复杂的部署流程、减轻第三方技术的集成难度。(Spring的升级版)
一句话:自动装配,约定大于配置
Spring Boot学习文档:https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/using-spring-boot.html#using-boot
<groupId>com.jsongroupId>
<artifactId>hello_worldartifactId>
<version>0.0.1-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.9.RELEASEversion>
parent>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>1.5.9.RELEASEversion>
<relativePath>../../spring-boot-dependenciesrelativePath>
parent>
spring-boot-dependencies管理Spring Boot应用中所有的依赖的版本,导入依赖默认是不需要版本,因为spring-boot-dependencies中存放jar的版本号
注意:没有在dependencies中管理的依赖自然需要声明版本号
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
spring-boot-starter:spring boot场景启动器,spring boot 将J2EE中应用的模块整合到一个个starter中。例如用到web模块,导入spring-boot-starter-web即可导入所需的jar依赖组件
要用什么功能就导入什么场景的启动器
…省略
https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/html/using-spring-boot.html#using-boot-starter
/**
* @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
*/
package com.json;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloWorldApplication {
public static void main(String[] args) {
//Spring Boot应用启动
SpringApplication.run(HelloWorldApplication.class, args);
}
}
@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用
// @interface SpringBootApplication
@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 的配置类,配置类也是容器中的一个组件@Component
//@interface SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
//@interface Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@EnableAutoConfiguration:开启自动配置功能(核心注解)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@AutoConfigurationPackage中的@Import注解导入静态内部类Registrar.class,其register()方法将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//getPackageName获取主类包的名称,扫描该包及该包下的所有组件到SpringIOC容器中
register(registry, new PackageImport(metadata).getPackageName());
}
@Import(AutoConfigurationImportSelector.class)给容器中导入一个组件AutoConfigurationImportSelector类,该类会加载SpringBoot所需的自动配置类,将所有需要导入自动配置类以全类名的方式返回,这些组件就会被添加到容器中
public class AutoConfigurationImportSelector {
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取所有候选的自动配置类的全类名,并存在List集合中
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
//返回AutoConfigurationEntry(封装了存放自动配置类的List和Set集合)
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
//定义自动配置类的全类名的路径
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//加载工厂的名称
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
//在FACTORIES_RESOURCE_LOCATION路径下加载配置文件
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//将FACTORIES_RESOURCE_LOCATION路径下的配置文件封装到properties类中
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
//将所有配置类的全类名存放到Map集合中
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
//返回Map
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
总结:AutoConfigurationImportSelector类会给容器中导入非常多的自动配置类(xxxAutoConfifiguration),就是给容器中导入这个场景需要的所有组件, 并配置好这些组件,这些自动配置类容纳了J2EE所有的场景
这些自动配置类存放在spring-boot-autoconfigure\2.2.5.RELEASE\spring-boot-autoconfigure.jar中
Spring Boot使用一个全局的配置文件,配置文件名是固定的
配置文件的作用:修改SpringBoot自动配置的默认值
.yml是YAML (YAML Ain’t Markup Language)语言的文件,以数据为中心,比json、xml等更适合做配置文件
参考语法规范 http://www.yaml.org/
对比:
properties
#设置端口号
server.port=8888
yml
#设置端口号
server:
port: 8888
xml
<server>
<port>8888port>
server>
注意:
字面量: 普通的值(数字、字符串、布尔值)
对象、Map
方式一:使用缩进写法
student:
name: json
age: 22
方式二:行内写法(使用大括号,属性用逗号隔开)
student: {name: jack , age: 22}
数组集合(List、Set)
方式一:使用缩进写法,用**“ - ”**表示数组中的一个元素
pets:
- cat
- dog
- pig
方式二:行内写法(使用中括号,元素用逗号隔开)
pets: [cat,dog,pig]
yml
person:
name: json
age: 23
boss: false
birthday: 1995/7/12
maps: {k1: v1,k2: v2}
list:
- element1
- element2
- element3
pet:
name: dog
age: 3
properties
person.last-name=叶俊杰
person.list=list1,list2,list3
person.maps.key1=value1
person.maps.key2=value2
person.maps.key3=value3
person.pet.name=pig
person.pet.age=4
JavaBean
/*
将配置文件中配置的每一个属性的值,映射到这个组件中
@ConfigurationProperties:
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
prefix = "person":配置文件中哪个下面的所有属性进行一一映射
只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name ;
private Integer age ;
private Boolean boss ;
private Date birthday ;
private Map<String,Object> maps ;
private List<Object> list ;
private Pet pet ;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", boss=" + boss +
", birthday=" + birthday +
", maps=" + maps +
", list=" + list +
", pet=" + pet +
'}';
}
}
public class Pet {
private String name ;
private Integer age ;
@Override
public String toString() {
return "Pet{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
输出结果:
@ConfigurationProperties:将配置文件中配置的每一个属性的值映射到指定的JavaBean,prefix属性指定JavaBean与配置文件中的哪个属性进行绑定(prefix=“xxx”)
注意:只要是Spring容器中的JavaBean才能使用@ConfigurationProperties进行绑定
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
导入该依赖在绑定时有相应的提示(鸡肋,可以忽略)
解决properties配置文件中文乱码问题
properties
person.last-name=json
yml
person:
LAST_NAME: json
JavaBean
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName ;
使用注解@Value也可以给指定一个属性赋值(可以从配置文件中动态获取)
yml
person:
LAST_NAME: json
JavaBean
@Component
public class Person {
@Value("${person.last-name}")
private String lastName ;
@Value("#{1*10}")
若properties、yaml、@Value都给属性注入了值,优先级为:
properties > yaml > @Value
使用场景:
@Value获取值和@ConfigurationProperties获取值比较
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定注入 |
松散语法 | 支持 | 不支持 |
EL表达式 | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
JSR303数据检验
使用**@Validated**进行检验,非法数据则会抛出异常
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
//判断是否为邮箱类型
@Email
private String lastName ;
private Integer age ;
private Boolean boss ;
//判断日期的格式化
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday ;
校验结果:
@ConfigurationProperties为全局配置,自定义properties配置文件绑定JavaBean赋值使用**@PropertiesSource**注解
@Component
@PropertySource("classpath:person.properties")
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName ;
private Integer age ;
private Boolean boss ;
private Date birthday ;
private Map<String,Object> maps ;
private List<Object> list ;
private Pet pet ;
person.properties配置文件
person.last-name=json
person.list=list1,list2,list3
person.maps.key1=value1
person.maps.key2=value2
person.maps.key3=value3
person.pet.name=pig
person.pet.age=4
注意:
@Value也可以与@PropertiesSource联合使用,若在application.properties或.yml中有定义,优先级为**.properties>.yml>@PropertiesSource加载的Properties配置文件**
@Component
@PropertySource("classpath:person.properties")
public class Person {
@Value("person.last-name")
private String lastName ;
private Integer age ;
private Boolean boss ;
private Date birthday ;
private Map<String,Object> maps ;
private List<Object> list ;
private Pet pet ;
person.properties
person.last-name=json
person.list=list1,list2,list3
person.maps.key1=value1
person.maps.key2=value2
person.maps.key3=value3
person.pet.name=pig
person.pet.age=4
yml
person:
last-name: marry
此时会注入marry
@PropertiesSource只是加载局部的配置文件到项目中,无法直接赋值,需要通过@Value或
@ConfigurationProperties赋值或绑定
Spring Boot中没有Spring的配置文件(.xml),自定义的配置文件需要使用注解**@ImportResource**加载(在主启动类上标注)
自定义配置文件conf.xml
<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="pet" class="com.json.pojo.Pet" scope="prototype">
<property name="name" value="cat" />
<property name="age" value="23" />
bean>
beans>
@ImportResource加载配置文件
@SpringBootApplication
@ImportResource(locations = "classpath:conf.xml")
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}
SpringBoot推荐给容器中添加组件的方式为使用全注解的方式
/*
@Configuration:指明当前类是一个配置类
就是来替代之前的xml配置文件
*/
@Configuration
public class MyConfiguration {
//将方法的返回值添加到容器中,容器中这个组件默认的id就是方法名
@Bean
public Pet getPet(){
Pet pet = new Pet();
pet.setName("pig");
pet.setAge(4);
return pet ;
}
}
person:
last-name: json${random.uuid}
age: ${random.int}
list:
- ${person.last-name}
- ${person.hello:hello}
官方定义配置文件中可以配置的所有属性:https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#common-application-properties
SpringBoot中存在大量的xxxAutoConfiguration的自动配置类,SpringBoot在启动时会加载这些自动配置类,SpringBoot的自动配置是依赖这些自动配置类实现的。
自动配置类的加载过程:
SpringBoot启动时加载主配置类,开启自动配置功能**@EnableAutoConfiguration**
@EnableAutoConfiguration的作用:
@Import(AutoConfigurationImportSelector.class)导入了一个自动配置导入选择器的类
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
AutoConfigurationImportSelector给容器中导入一些自动配置类
调用getAutoConfigurationEntry()方法中getCandidateConfigurations方法获取所有的候选的自动配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取所有的候选的自动配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations()方法中使用SpringFactoriesLoader类加载器加载配置文件中的自动配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//SpringFactoriesLoader类加载类加载配置文件的自动配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
loadFactoryNames方法加载具体的自动配置类的全类名,将这些类名包装成Properties对象,然后添加到Spring容器中
public final class SpringFactoriesLoader {
//自动配置类的配置文件路径
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//得到EnableAutoConfiguration.class的名称
String factoryTypeName = factoryType.getName();
//返回加载完的自动配置类的List集合
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//将加载的自动配置类的信息保存在 urls 中
Enumeration<URL> urls = (classLoader != null ?
//类加载器加载FACTORIES_RESOURCE_LOCATION路径的配置文件(MATA-INF/spring.factories)
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
//判断urls是否还有元素(迭代器模式)
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//将自动配置类封装到Properties类中
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
//添加在Map集合中
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
//返回Map集合
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
SpringBoot中所有的自动配置类都存放在spring-boot-autoconfigure-.jar\ META-INF\spring.factories路径下的配置文件
有了这些自动配置类,就可以自动配置了
每一个这样的 xxxAutoConfifiguration类都是容器中的一个组件,都加入到容器中,用他们来做自动配置,自动配置类也会给容器中添加组件
以HttpEncodingAutoConfiguration自动配置类为例,剖析自动配置原理:
//表示为一个配置类
@Configuration(proxyBeanMethods = false)
/*
开启ConfigurationProperties的功能
将配置文件中对应的值和HttpEncodingProperties绑定起来
并把 HttpProperties加入到ioc容器中
*/
@EnableConfigurationProperties(HttpProperties.class)
//判断是否为Web应用,如果是Web应用,该配置类才能生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断是有指定的类
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
@EnableConfigurationProperties(HttpProperties.class)表示开启@ConfigurationProperties的功能,@ConfigurationProperties注解将配置文件中属性的值和HttpProperties.class该类绑定起来,并把HttpProperties类加入到Spring容器中
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
private boolean logRequestDetails;
private final Encoding encoding = new Encoding();
public static class Encoding {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private Charset charset = DEFAULT_CHARSET;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
所以在配置文件可以配的属性以prefix=spring.http为前缀,HttpProperties类中的属性都可以在配置文件中指定,这些属性可以设置默认值,如果想自定义就可以在配置文件重新赋值
spring.http.encoding.charset=utf-8
spring.http.log-request-details=true
spring.http.encoding.enabled=true
spring.http.encoding.force=true
根据当前不同的条件判断,决定这个HttpEncodingAutoConfiguration配置类是否生效,一旦生效
HttpEncodingAutoConfiguration自动配置类给Spring容器添加组件CharacterEncodingFilter类和LocaleCharsetMappingsCustomizer类,CharacterEncodingFilter类中所需要的属性从HttpProperties类中获取
public class HttpEncodingAutoConfiguration {
private final HttpProperties.Encoding properties;
/*
HttpProperties类作为形参构造HttpEncodingAutoConfiguration自动配置类
@EnableConfigurationProperties又将HttpProperties加入到Spring容器
因此创建HttpEncodingAutoConfiguration配置类时,会从Spring容器中获取
HttpProperties实例作为形参
*/
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
/*
给Spring容器中添加CharacterEncodingFilter类
*/
@Bean
//判断容器中是否存在该Bean,如果不存在,才加入Spring容器
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
//设置filter的属性都是从HttpProperties.Encoding中获取
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
每一个xxxAutoConfiguration自动配置类都有对应的xxxProperties类,xxxAutoConfiguration若能生效,会通过**@Bean注解给Spring容器中导入一些组件,这些组件需要的属性的值都在xxxProperties获取,而xxxProperties类标注@ConfigurationProperties**注解,与配置文件绑定起来,xxxProperties类中的属性可以在配置文件(.properties,.yml)中配置
WebMvcAutoConfiguration自动配置类与WebMvcProperties、ResourceProperties类
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
//Web自动配置类
public class WebMvcAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
//开启ConfigurationProperties绑定功能,给容器导入WebMvcProperties和ResourceProperties类
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
//Web静态内部类
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
@Bean
@ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
public static RequestContextFilter requestContextFilter() {
return new OrderedRequestContextFilter();
}
}
//与配置文件绑定,prefix = "spring.mvc",该类的属性均可以在配置文件中配置
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
private DefaultMessageCodesResolver.Format messageCodesResolverFormat;
private Locale locale;
private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;
private String dateFormat;
//与配置文件绑定,prefix = "spring.resources",该类的属性均可以在配置文件中配置
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
HttpEncodingAutoConfiguration自动配置类与HttpProperties类
@Configuration(proxyBeanMethods = false)
//开启ConfigurationProperties绑定功能,给容器导入HttpProperties类
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
//HttpEncoding自动配置类
public class HttpEncodingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
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;
}
}
//与配置文件绑定,prefix = "spring.http",该类的属性均可以在配置文件中配置
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
private boolean logRequestDetails;
private final Encoding encoding = new Encoding();
public static class Encoding {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private Charset charset = DEFAULT_CHARSET;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,自动配置类里面的所有内容才生效;
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的Java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
自动配置类必须在一定的条件下才能生效
可以在配置文件中配置debug=true
,让控制台打印自动配置报告
生效的自动配置类
没生效的自动配置类
精髓:
通用模式
– xxxAutoConfiguration:自动配置类
– xxxProperties:属性配置类
– yml/properties文件中能配置的值就来源于[属性配置类]
Web自动配置规则
使用SpringBoot开发Web
创建SpringBoot应用,选中需要的模块
SpringBoot默认将场景配置好了,只需要在配置文件中指定少量的配置就可以运行起来
xxxxAutoConfiguration:给容器中自动配置组件
xxxxProperties:Properties配置类来封装配置文件的内容
静态资源的映射规则:
方式一:
WebMvcAutoConfiguration中的静态内部类WebMvcAutoConfigurationAdapter
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//处理请求路径为/web/jars/下所有请求
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
// 如果发送的请求路径为/web/jars/下所有请求都会自动映射到classpath:/META-INF/resources/webjars/
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
发送的请求路径为/web/jars/下所有请求都会自动映射到classpath:/META-INF/resources/webjars/
webjars官方:https://www.webjars.org/
方式二:
请求路径为:/**
访问当前项目的任何资源,都去(静态资源的文件夹)找映射
WebMvcAutoConfiguration中的静态内部类WebMvcAutoConfigurationAdapter
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
//静态资源的访问路径为/**
private String staticPathPattern = "/**";
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
//获取静态资源的访问路径(/**)
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
//将/**的访问路径映射到getStaticLocations的路径下
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
//静态资源的路径默认的路径
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
//静态资源的路径
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
private boolean addMappings = true;
private final Chain chain = new Chain();
private final Cache cache = new Cache();
//获取静态资源的路径
public String[] getStaticLocations() {
return this.staticLocations;
}
请求路径为:**/ **** 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
比如:发送请求**/hello.html会映射到默认的静态资源路径下即(classpath:/static/hello.html)**
可以在配置文件中修改默认的静态资源文件路径
ResourceProperties属性配置类
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].
*/
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
properties配置文件
#自定义静态资源的路径映射
spring.resources.static-locations=classpath:/html
private Optional<Resource> getWelcomePage() {
//getStaticLocations获取静态资源的路径
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
//加载首页的路径,即静态资源的路径下的index.html
return this.resourceLoader.getResource(location + "index.html");
}
例如:访问localhost:8888/映射到静态资源路径下的index.html,即(classpath:/static/index.html)
只要index.html欢迎页放在任意的静态资源路径下都能被识别
只需将图标定义为favicon.ioc,放在任意静态资源路径下就可被SpringBoot识别
Spring官网说明文档:https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/html/spring-boot-features.html#boot-features-developing-web-applications
官网上说明:SpringMVC自动配置默认以下功能:
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
Spring Boot 默认给Spring MVC 自动配置有以下功能:
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
ContentNegotiatingViewResolver解析所有的视图解析器,返回视图时使用
WebMvcAutoConfiguration向容器中注入ContentNegotiatingViewResolver
@Bean
//当ViewResolver视图解析器存在时向容器注入ContentNegotiatingViewResolver
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
ContentNegotiatingViewResolver解析所有视图,返回一个视图
dispatcherServlet则跳转到得到的视图
//ContentNegotiatingViewResolver解析所有视图,然后返回视图
public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
implements ViewResolver, Ordered, InitializingBean {
//调用方法解析视图名字,返回一个视图,dispatcherServlet则跳转到得到的视图
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
if (requestedMediaTypes != null) {
//主要使用两个方法实现
//1、获取候选的所有视图
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
//2、得到最好的视图,返回
View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
...
}
//1、获取候选的所有视图
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
throws Exception {
List<View> candidateViews = new ArrayList<>();
//如果视图解析器不为空
if (this.viewResolvers != null) {
Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
//遍历所有的视图解析器
for (ViewResolver viewResolver : this.viewResolvers) {
//解析resolveViewName得到view视图
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
candidateViews.add(view);
}
for (MediaType requestedMediaType : requestedMediaTypes) {
List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
for (String extension : extensions) {
String viewNameWithExtension = viewName + '.' + extension;
view = viewResolver.resolveViewName(viewNameWithExtension, locale);
if (view != null) {
//将每一个视图加入到候选的视图集合中
candidateViews.add(view);
}
}
}
}
}
...
//返回该候选的视图集合
return candidateViews;
}
//2、从候选的视图集合获取最好的视图
@Nullable
private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) {
//遍历候选的视图集合
for (View candidateView : candidateViews) {
if (candidateView instanceof SmartView) {
SmartView smartView = (SmartView) candidateView;
if (smartView.isRedirectView()) {
return candidateView;
}
}
}
}
viewResolvers视图集合的来源
@Override
protected void initServletContext(ServletContext servletContext) {
//从容器中获取类型ViewResolver.class的所有bean并放到集合matchingBeans中
Collection<ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
if (this.viewResolvers == null) {
//根据matchingBeans集合的长度创建viewResolvers
this.viewResolvers = new ArrayList<>(matchingBeans.size());
//遍历matchingBeans每一个视图解析器,并加入到ViewResolver中
for (ViewResolver viewResolver : matchingBeans) {
if (this != viewResolver) {
this.viewResolvers.add(viewResolver);
}
}
}
...
}
由源码可知,springboo会从容器中获取所有的视图解析器ViewResolver,若需要扩展视图解析器:
只需自定义一个类实现ViewResolver接口,并加入容器中即可
@SpringBootApplication
public class SpringbootWebRestfulCrudApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebRestfulCrudApplication.class, args);
}
//注入容器
@Bean
public ViewResolver getViewResolver(){
return new MyViewResolver();
}
//定义一个视图解析器类
static class MyViewResolver implements ViewResolver{
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}
}
BeanNameViewResolver解析beanName,返回json格式时使用
Support for serving static resources, including support for WebJars
默认情况下,Spring Boot对静态资源访问映射到目录**/static(或/public或/resources或/META-INF/resources)**
可以通过使用自定义静态资源位置spring.resources.static-locations属性替换默认值的列表目录位置
spring.resources.static-locations=classpath:/html
Automatic registration of Converter
, GenericConverter
, and Formatter
beans.
自定义格式化器:
添加的格式化器转换器,只需要放在容器中即可
@Bean
public Formatter formatter(){
return new MyFormatter();
}
class MyFormatter implements Formatter<Date>{
@Override
public Date parse(String text, Locale locale) throws ParseException {
return null;
}
@Override
public String print(Date object, Locale locale) {
return null;
}
}
Support for HttpMessageConverters
.
HttpMessageConverter:SpringMVC用来转换Http请求和响应的;
Object–Json
String(ModelAndView)–视图
默认情况下,字符串进行编码UTF-8
HttpMessageConverters:获取容器中所有的HttpMessageConverter
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//从容器中获取所有的Converter,调用addAll方法添加所有Converter到List集合中
this.messageConvertersProvider
.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}
若需要自定义:
只需实现HttpMessageConverter接口并@Bean或@Component放入容器中即可
Automatic registration of MessageCodesResolver
.
Static index.html
support.
Custom Favicon
support.
Automatic use of a ConfigurableWebBindingInitializer
bean.
Spring Boot自动配置基本的Spring MVC所需的功能,但若还需扩展
实现拦截器功能
@Configuration
public class MyConfig implements WebMvcConfigurer {
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
...
}
WebMvcConfigurer里面具有所有SpringMVC的功能的空方法,想要扩展哪个功能就重写哪个方法
既保留了所有的自动配置,也能自定义扩展的配置
为什么可以同时使用Spring Boot和自定义的Web配置
Spring Boot自动配置SpringMVC和自定义SpringMVC的原理
WebMvcConfiguration类时SpringBoot自动配置类
使用注解@Import导入了EnableWebMvcConfiguration类
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
@Configuration(proxyBeanMethods = false)
//导入EnableWebMvcConfiguration类
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
...
}
//继承DelegatingWebMvcConfiguration
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final WebMvcRegistrations mvcRegistrations;
private ResourceLoader resourceLoader;
//加载xxxProperties类里的默认属性(resourceProperties、mvcProperties)
public EnableWebMvcConfiguration(ResourceProperties resourceProperties,
ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ListableBeanFactory beanFactory) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}
...
}
EnableWebMvcConfiguration又继承了DelegatingWebMvcConfiguration
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
//参数为WebMvcConfigurer的List集合
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
//WebMvcConfigurer的List集合添加到Web配置中
this.configurers.addWebMvcConfigurers(configurers);
}
}
...
}
从上述可看出,SpringBoot加载Web自动配置类时,使用了所有的WebMvcConfigurer类,包括SpringBoot默认的配置类与自定义的配置类
全面接管SpringMVC
为何不能标注@EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
可以发现@EnableWebMvc导入了一个类@Import**(DelegatingWebMvcConfiguration.class**),该类为EnableWebMvcConfiguration类的父类
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
...
}
该类继承了WebMvcConfigurationSupport类
WebMvcAutoConfiguration自动配置类上标有注解**@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)**,若容器中有WebMvcConfigurationSupport类则该配置类不会生效
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//若容器中没有WebMvcConfigurationSupport类,则该类生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
总结:
spring-boot-starter-jdbc和mysql驱动
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
配置数据源信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/client?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root123
即可使用
测试
@SpringBootTest
class SpringbootWebRestfulCrudApplicationTests {
@Autowired
private DataSource dataSource ;
@Autowired
private JdbcTemplate jt ;
@Test
void contextLoads() throws Exception {
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
@Test
public void testQueryList(){
List<Map<String, Object>> maps = jt.queryForList("select * from `person`");
System.out.println(maps);
}
}
执行结果:
Spring Boot默认使用HikariDataSource数据源
也可以手动设置使用的数据源
只需在yml中配置spring.datasource.type
指定数据即可
有关配置DataSource疯转在DataSourceProperties类中
官网介绍:https://www.alibabacloud.com/help/zh/doc-detail/72987.html
Druid应用场景
导入jar
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
配置druid相关配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/client?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root123
#选择Druid数据源
type: com.alibaba.druid.pool.DruidDataSource
#配置Druid相关配置
druid:
# 连接池的配置信息
# 初始化大小,最小,最大
initial-size: 5
min-idle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
# DruidFilterConfiguration
filter:
slf4j:
enabled: true
wall:
enabled: true
stat:
enabled: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
# 配置DruidStatFilter
web-stat-filter:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
# 配置DruidStatViewServlet
stat-view-servlet:
url-pattern: "/druid/*"
# IP白名单(没有配置或者为空,则允许所有访问)
# allow: 127.0.0.1,192.168.46.120
# IP黑名单 (存在共同时,deny优先于allow)
# deny: 192.168.46.121
# 禁用HTML页面上的“Reset All”功能
reset-enable: false
# 登录名
login-username: admin
# 登录密码
login-password: 123456
然后访问URL为/druid,默认进入druid监控页面,登录可以查看监控
注意:若访问/druid为404,有可能为jar版本过高
导入mybaits-spring-boot-starter.jar
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.0version>
dependency>
配置mybatis
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #指定全局配置文件的位置
mapper-locations: classpath:mappers/*.xml #指定sql映射文件的位置
type-aliases-package: com.json.springboot.pojo #定义实体类的别名
标注@MapperScan
@MapperScan(basePackages="com.json.springboot.dao")
@SpringBootApplication
public class SpringbootWebRestfulCrudApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebRestfulCrudApplication.class, args);
}
}
mapper.xml
<mapper namespace="com.json.springboot.dao.PersonMapper">
<select id="queryAll" resultType="Person">
select * from person
select>
mapper>
测试:
public interface PersonMapper {
@Select("select * from person")
List<Person> queryAll();
}
即可
导入jar
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.2.5version>
dependency>
配置PageHelper
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
returnPageInfo: check
使用
@RestController
public class ContentController {
@Autowired
private ContentMapper contentMapper;
@GetMapping("/queryAll")
public PageInfo queryAll(){
//分页
PageHelper.startPage(1,10);
List<Content> contents = contentMapper.queryAll();
//使用PageInfo对象封装数据
PageInfo pageInfo = new PageInfo(contents);
return pageInfo;
}
}
导入jar
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
配置redis
spring:
redis:
database: 0
host: localhost
post: 6379
测试
@SpringBootTest
class SpringbootWebRestfulCrudApplicationTests {
@Qualifier("redisTemplate")
@Autowired
RedisTemplate rt ;
@Test
public void test(){
rt.opsForValue().set("name","json");
}
}
后端:控制层,业务层,持久层
前端:前端控制层,视图层
使用Vue+SpringBoot
前后端交互:
API
前端 <———> 后端
需要提供API文档供前后端团队交互
Swagger
Swagger配置主要向容器导入Docket类,配置该类,Docket类封装了Swagger的生成规则
导入依赖
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.9.2version>
dependency>
配置swagger
@Configuration
@EnableSwagger2//开发Swagger功能
public class SwaggerConfig {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2);
}
}
使用默认的Swagger配置信息
编写接口
@RestController
public class HelloController {
@GetMapping("/get")
public Map getMap(){
return new HashMap();
}
@PutMapping("/put")
public String putMap(Map map){
return "hello";
}
@PostMapping("/post")
public String postMap(String name){
return name;
}
@DeleteMapping("/delete")
public List deleteMap(){
return new ArrayList();
}
}
访问/swagger-ui.html,进入在线文档
可以发现:API文档可以详细的说明,当然也可以测试
API信息被封装为Docket中的ApiInfo类中
源码:
public class ApiInfo {
public static final Contact DEFAULT_CONTACT = new Contact("", "", "");
//默认的信息配置
public static final ApiInfo DEFAULT = new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos",
DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>());
private final String version;
private final String title;
private final String description;
private final String termsOfServiceUrl;
private final String license;
private final String licenseUrl;
private final Contact contact;
private final List<VendorExtension> vendorExtensions;
/**
* Deprecated in favor of richer contact object
* @deprecated @since 2.4.0
*
* @param title 标题
* @param description 描述
* @param version 版本
* @param termsOfServiceUrl 团体的地址URL
* @param contactName contact name
* @param license licence text
* @param licenseUrl license url
*/
/**
* Default contstructor
* @param title 标题
* @param description 描述
* @param version 版本
* @param termsOfServiceUrl 团体的地址URL
* @param contact contact
* @param license license
* @param licenseUrl license url
* @param vendorExtensions vendor extensions
*/
public ApiInfo(
String title,
String description,
String version,
String termsOfServiceUrl,
Contact contact,
String license,
String licenseUrl,
Collection<VendorExtension> vendorExtensions) {
this.title = title;
this.description = description;
this.version = version;
this.termsOfServiceUrl = termsOfServiceUrl;
this.contact = contact;
this.license = license;
this.licenseUrl = licenseUrl;
this.vendorExtensions = newArrayList(vendorExtensions);
}
自定义ApiInfo类和Docket类
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("json组")
//是否开启
.enable(true)
//指定扫描的包
.select().apis(RequestHandlerSelectors.basePackage("com.json.springboot.controller"))
.build()
//信息
.apiInfo(getApiInfo());
}
private ApiInfo getApiInfo(){
return new ApiInfo("API文档",
"Swagger在线文档",
"2.0",
"https:www.baidu.com",
new Contact("json", "", ""),
"",
"",
new ArrayList<VendorExtension>());
}
}
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("用户id")
private Integer id ;
@ApiModelProperty("用户姓名")
private String name ;
@ApiModelProperty("用户年龄")
private Integer age ;
@RestController
public class HelloController {
@ApiOperation("get请求")
@GetMapping("/get")
public Map getMap(User user){
return new HashMap();
}
@ApiOperation("put请求")
@PutMapping("/put")
public String putMap(@ApiParam("map集合") Map map){
return "hello";
}
@ApiOperation("post请求")
@PostMapping("/post")
public String postMap(@ApiParam("姓名")String name){
return name;
}
@ApiOperation("delete请求")
@DeleteMapping("/delete")
public List deleteMap(){
return new ArrayList();
}
}
可以发现:swagger API文档已经生成相应的注释
可以在线测试接口的准确性
异步任务:开启多线程
无需导入jar,Spring Boot默认集成
步骤:
异步任务
@EnableAsync
@SpringBootApplication
public class SpringbootJobApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootJobApplication.class, args);
}
}
service
@Service
public class AsyncJobService {
@Async
public void sleep(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
contrller
@RestController
public class HelloController {
@Autowired
private AsyncJobService asyncJobService ;
@RequestMapping("hello")
public String hello(){
asyncJobService.sleep();
return "success" ;
}
}
若没有开启异步,访问/hello会执行完sleep方法才响应成功,影响体验
开启了异步,访问/hello会立刻响应成功,开启另一个线程执行sleep方法
导入jar
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
配置application.properties
#服务器类型
spring.mail.host=smtp.qq.com
#发送人
[email protected]
#激活码
spring.mail.password=xxxxxxxxxxx
#QQ固有ssl加密
spring.mail.properties.mail.smtp.ssl.enable=true
发送邮件
@Autowired
private JavaMailSender javaMailSender ;
@Test
void contextLoads() {
//简单邮件
SimpleMailMessage message = new SimpleMailMessage();
//收件人
message.setTo("[email protected]");
//发件人
message.setFrom("[email protected]");
//邮件标题
message.setSubject("交个朋友呗");
//邮件内容
message.setText("你好!");
//发送消息
javaMailSender.send(message);
}
@Test
public void send() throws MessagingException {
//创建复杂邮件
MimeMessage message = javaMailSender.createMimeMessage();
//使用复杂邮件助手MimeMessageHelper
MimeMessageHelper helper = new MimeMessageHelper(message,true);
//收件人
helper.setTo("[email protected]");
//发件人
helper.setFrom("[email protected]");
//邮件标题
helper.setSubject("交个朋友呗");
//邮件内容,html超文本
helper.setText("百度一下",true);
//附件
helper.addAttachment("图片", new File("D:\\Users\\333\\Desktop\\微信图片_20200414140517.jpg"));
//发送消息
javaMailSender.send(message);
}
注意:
创建MailMessageHelper对象需要带上multipart参数为true
源码:
//空参multipart默认为null
public MimeMessageHelper(MimeMessage mimeMessage) {
this(mimeMessage, (String)null);
}
public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart) throws MessagingException {
this(mimeMessage, multipart, (String)null);
}
//若multipart为null,抛异常
public final MimeMultipart getRootMimeMultipart() throws IllegalStateException {
if (this.rootMimeMultipart == null) {
throw new IllegalStateException("Not in multipart mode - " +
"create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag " +
"if you need to set alternative texts or add inline elements or attachments.");
}
return this.rootMimeMultipart;
}
主类
@EnableScheduling
@SpringBootApplication
public class SpringbootJobApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootJobApplication.class, args);
}
}
调度方法
@Service
public class ScheduleService {
/**
* Seconds Minutes Hours Day Month DayOfWeek (Year)
* 秒 分 时 日 月 星期 年
* 通配符 *
* 匹配符 ?
*/
@Scheduled(cron = "")
public void sendTime(){
System.out.println("每隔两秒执行一次");
}
}
cron表达式:
字段 | 允许值 |
---|---|
秒(Seconds) | 0~59的整数 |
分(Minutes) | 0~59的整数 |
小时(Hours) | 0~23的整数 |
日期(Day) | 1~31的整数(但是你需要考虑你月的天数) |
月份(Month) | 1~12的整数或者 JAN-DEC |
星期(DayOfWeek) | 0~7的整数或者 SUN-SAT (0和7都代表星期日) |
年(可选,留空)(Year) | 1970~2099 |
注意:
***** :代表任意
?:只能用在Day和DayOfWeek两个域,代表匹配,因为会相互影响。所以“?”根据另一个字段匹配
例如:
1、在每月的20日触发调度,那星期DayOfWeek就不能为 * ,只能根据月份的20日匹配
2、在每个星期日触发调度,那日期Day就不能为 * ,只能根据 星期 去匹配
Dubbo 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:
角色:
Dubbo推荐注册中心使用Zookeeper(Dubbo+Zookeeper标配)
下载Zookeeper,解压
Zookeeper运行在Linux上,压缩包为gz,但Windows也可运行
修改配置文件
注意几个重要位置:
dataDir=/tmp/zookeeper 临时数据存储的目录(可写相对路径)
clientPort=2181 zookeeper的端口号
修改完成后再次启动zookeeper
启动Zookeeper
启动bin目录下的zkServer.cmd
启动zkCli.cmd客户端测试
连接成功
下载dubbo-admin,监控服务的注册与发现
github:https://github.com/apache/dubbo-admin/tree/master
可以发现:dubbo-admin是一个Spring Boot应用
配置信息如下
server.port=7001
spring.velocity.cache=false
spring.velocity.charset=UTF-8
spring.velocity.layout-url=/templates/default.vm
spring.messages.fallback-to-system-locale=false
spring.messages.basename=i18n/message
spring.root.password=root
spring.guest.password=guest
dubbo.registry.address=zookeeper://127.0.0.1:2181
使用maven打包为jar
mvn clean package -Dmaven.test.skip=true
#打包,跳过测试
得到dubbo-admin-0.0.1-SNAPSHOT.jar,可以在命令行运行
运行dubbo-admin
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
先启动zookeeper服务端,再运行dubbo-admin.jar
访问监控页面
默认用户和密码为root、root
可以发现监控中心可以运行了,注册的服务在此发现
使用 Spring Boot + Dubbo + zookeeper注册服务
注意:注册服务一定要启动zooke服务端,dubbo-admin监控可以不启动
导入jar
配置dubbo
编写注册的服务(标注@Service和@Component)
注意:@Service为dubbo包下的注解
导入jar
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.1version>
dependency>
<dependency>
<groupId>com.101tecgroupId>
<artifactId>zkclientartifactId>
<version>0.10version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
配置dubbo
#注册项目名称
#项目名不能使用中文
dubbo.application.name=provider-server
#指定扫描的包
dubbo.scan.base-packages=com.json.springboot.provider
#指定注册中心zookeeper的ip+post
dubbo.registry.address=zookeeper://127.0.0.1:2181
#指定项目的端口号
server.port=8889
编写注册的服务
//服务接口
public interface Provider {
String sellTicket(int count);
}
//服务具体实现
@Service//最好使用@Component区别
@org.apache.dubbo.config.annotation.Service
public class DubboProvider implements Provider{
/**
* 卖票服务
*/
public String sellTicket(int count){
return "出售了"+count+"张票";
}
}
启动zookeeper服务端与springboot应用,即可注册
注意:注册的服务必须实现接口
启动顺序:zookeeper服务端 -> springboot提供方 -> dubbo-admin
访问7001:
可以发现:服务已注册成功,还可以查看服务的具体信息
导入jar
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.1version>
dependency>
<dependency>
<groupId>com.101tecgroupId>
<artifactId>zkclientartifactId>
<version>0.10version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
配置dubbo
#指定项目的端口号
server.port=8888
#消费者的名称
dubbo.application.name=consumer-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
调用注册中心的方法
@SpringBootTest
class DubboZookeeperConsumerApplicationTests {
@Reference//注意为dubbo的注解
Provider provider ;
@Test
void contextLoads() {
String string = provider.sellTicket(3);
System.out.println(string);
}
}
若为容器中的对象,使用Autowired,若远程调用使用@Reference
调用结果:
注意:消费者端调用远程方法,从语法上也需要写提供方的接口,包的目录要一致
例如:Provider provider,但无需具体实现,因为调用远程的实现方法
public interface Provider {
String sellTicket(int count);
}
只要注册中心Zookeeper关闭,提供者端与消费者端立即报错
前后端分离大势所趋
CORS就是为了解决SOP问题而生的,当然CORS不是唯一的解决方案
CORS简介: CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。它的通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX/Fetch通信没有差别,代码完全一样。浏览器一旦发现请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信
在springboot的controller中添加@CrossOrigin
@CrossOrigin(origins = "http://localhost:8080")
@RestController
public class ContentController {
@Autowired
private ContentMapper contentMapper;
@GetMapping("/queryAll")
public List<Content> queryAll(){
return contentMapper.queryAll();
}
}
当然,也可以在controller方法中添加@CrossOrigin
lements Provider{
/**
* 卖票服务
*/
public String sellTicket(int count){
return "出售了"+count+"张票";
}
}
启动zookeeper服务端与springboot应用,即可注册
注意:注册的服务必须实现接口
启动顺序:zookeeper服务端 -> springboot提供方 -> dubbo-admin
访问7001:
[外链图片转存中...(img-rOoiw2aL-1589729019299)]
可以发现:服务已注册成功,还可以查看服务的具体信息
[外链图片转存中...(img-yyH6Pn9a-1589729019300)]
### 10.2.4 消费者端调用服务
* 导入jar(与提供端一样)
* 配置dubbo
* 编写消费者(标注@Reference远程调用)
> 导入jar
```xml
org.apache.dubbo
dubbo-spring-boot-starter
2.7.1
com.101tec
zkclient
0.10
org.apache.curator
curator-framework
2.12.0
org.apache.curator
curator-recipes
2.12.0
org.apache.zookeeper
zookeeper
3.4.14
org.slf4j
slf4j-log4j12
配置dubbo
#指定项目的端口号
server.port=8888
#消费者的名称
dubbo.application.name=consumer-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
调用注册中心的方法
@SpringBootTest
class DubboZookeeperConsumerApplicationTests {
@Reference//注意为dubbo的注解
Provider provider ;
@Test
void contextLoads() {
String string = provider.sellTicket(3);
System.out.println(string);
}
}
若为容器中的对象,使用Autowired,若远程调用使用@Reference
调用结果:
[外链图片转存中…(img-QZVfzXkx-1589729019301)]
注意:消费者端调用远程方法,从语法上也需要写提供方的接口,包的目录要一致
例如:Provider provider,但无需具体实现,因为调用远程的实现方法
public interface Provider {
String sellTicket(int count);
}
只要注册中心Zookeeper关闭,提供者端与消费者端立即报错
Spring Boot应用场景与整合:
内容概要:
SpringBoot开发文档:
前后端分离大势所趋
CORS就是为了解决SOP问题而生的,当然CORS不是唯一的解决方案
CORS简介: CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。它的通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX/Fetch通信没有差别,代码完全一样。浏览器一旦发现请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信
在springboot的controller中添加@CrossOrigin
@CrossOrigin(origins = "http://localhost:8080")
@RestController
public class ContentController {
@Autowired
private ContentMapper contentMapper;
@GetMapping("/queryAll")
public List<Content> queryAll(){
return contentMapper.queryAll();
}
}
当然,也可以在controller方法中添加@CrossOrigin