SpringBoot进阶学习上篇
1、SpringBoot核心概念
1.1Spring优缺点
Spring优点:通过IOC容器统一管理Bean大大简化了开发;DI;AOP
Spring缺点:配置比较繁琐;jar包比较繁琐
1.2SpringBoot核心
SpringBoot核心思想,约定优于配置。
SpringBoot优点:
- 依赖模块化:通过Starter模块化依赖来简化依赖的导入
- 自动装配: springboot会自动将一些配置类的bean注册进ioc容器,我们可以需要的地方使用@autowired或者@resource直接注入使用。
1.3SpringBoot使用
1.3.1 HelloWorldDemo
POM
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!--MVC模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
启动类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
1.3.2SpringBoot测试与热部署
测试
首先需要在POM中添加测试starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
测试代码
//注意在使用SpringBootTest这个注解进行测试的时候包名和业务代码的报名保持一致
@RunWith(SpringRunner.class) //用于启动Spring项目的启动器
@SpringBootTest
public class TestHelloTest {
@Autowired
private DemoController demoController;
@Test
public void testDemo() throws Exception {
String demo = demoController.demo();
System.out.println(demo);
}
}
热部署
添加POM
<!-- 引入热部署依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
IDEA设置
Settings->compiler-> 勾选Build projectAutomatically
imageCtrl+shift+alt+/打开 Registry 搜索running勾选
[图片上传失败...(image-7768bf-1610005803327)]
1.3.3全局配置文件之properties
1、对于老属性的覆盖比如端口
server.port=8081 #这个是对老属性的覆盖
2、给pojo类型注入属性
@Data
@Component
//将配置文件中以person开头的属性注入到该类中 添加提示
@ConfigurationProperties(prefix = "person")
public class Person {
private int id; //id
private String name; //名称
private List hobby; //爱好
private String[] family; //家庭成员
private Map map;
private Pet pet; //宠物
}
@Data
public class Pet {
private String name;
private String type;
}
针对@ConfigurationProperties注解添加依赖,并且rebuild
<!--全局配置处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
针对不同的属性类型在properties中配置注入
person.id=1 #Integer类型
person.name=tom #String类型
person.hobby=吃饭,玩游戏 #集合类型
person.family=father,mather #数组类型
person.map.key1=value1 #Map类型
person.map.game=峡谷 #Map类型
person.pet.name=旺财 #pojo类型
person.pet.type=dog #pojo类型
1.3.4全局配置文件之yml
yml又叫做yaml,是xml的一种超级表现形式,更适合计算机解析,属性名: 进行解析。
- server:
- port: 8081
- path: /hello
同样对pojo进行依赖注入
- person:
- id: 2
- name: lucy
- family: [father,mother]
- hobby: [吃饭,睡觉]
- map: {key1: value2,game: LOL}
- pet: {name: 旺财,type: dog}
如果是数组类型的话,也可以
person:
hobby:
play,
read
但是不直观。
思考:如果项目中同时存在yml,yaml,properties三种配置文件,那么哪个配置文件会生效?
根据parentPom中可以看出加载SpringBoot对三种配置文件加载顺序是,yml、yaml、properties。也就是说yml中的配置最先生效,但是如果properties中也有跟yml一样的配置的话,会被覆盖,因此最终是properties中的会生效。
1.3.5属性注入
方式一: 通过@ConfigurationProperties(prefix = "person")注解,这个注解是查找配置文件中以person作为开头的的配置调用属性的set方法进行注入的。
需要引入下边的pom rebuild。
<!--全局配置处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
方式二:通过@Value(“{}直接从配置文件中取值,@Value是通过反射的方式对属性值进行设置,不需要set方法。
1.3.6自定义配置
自定义配置文件
在使用自定义配置文件的时候,导入使用@ConfigurationProperties进行导入。
使用Demo:
自定义一个配置类test.properties
- test.id=3
- test.msg=测试自定义配置文件
自定义pojo
@Data
@Component //注意必须加上@Component因为必须先被Spring扫描到
//导入自定义配置文件
@PropertySource("classpath:test.properties")
@ConfigurationProperties(prefix = "test")
public class TestPojo {
private Integer id;
private String msg;
}
自定义配置类
SpringBoot推荐使用贴有@Configuration注解的配置类,方便注入。@Configuration贴到类上就相当于Spring的applicationContext.xml配置类。
使用Demo:
@Configuration
public class MyConfig {
@Bean//默认使用方法名字作为类的key 可以通过name属性来自定义key
public TestPojo2 testPojo2(){
return new TestPojo2();
}
}
1.3.7配置文件生成随机数和参数之间的引用
通过配置文件生成随机数
my.secret=${random.value} # 配置随机值
my.number=${random.int} # 配置随机整数
my.bignumber=${random.long} # 配置随机long类型数
my.uuid=${random.uuid} # 配置随机uuid类型数
my.number.less.than.ten=${random.int(10)} # 配置小于10的随机整数
my.number.in.range=${random.int[1024,65536]} # 配置范围在[1024,65536]之间的随机整数
参数之间的引用
- app.name=MyApp
- app.description=${app.name} is a Spring Boot application
2、SpringBoot源码剖析
2.1 依赖管理
2.1.1引入父Pom之后,为啥不需要引入version
在实际使用中项目中会引入父工程。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
在这个父工程中指定了配置文件的加载以及加载了一些顺序,并且又引入了spring-boot-dependencies父工程。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
在这个父工程中指定了需要的各种pom并且指定了版本因此我们在引入spring-boot-starter-parent这个父工程之后不需要在指定版本了。
2.1.2 项目依赖的jar包怎么来的呢?
我们在构建项目的时候仅仅引入了父Pom,还有spring-boot-starter-web就可以运行了,那么里边依赖的那么的jar包怎么导入的呢 ?
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!--MVC模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
在spring-boot-starter-web Pom中引入了我们需要使用的各种依赖比如Tomcat、spring-webmvc等。换句话说spring-boot-starter-xx是对各种Pom集,封装了各种需要的依赖。
2.2自动装配
2.2.1@SpringBootApplication注解
SpringBoot自动化装配从@SpringBootApplication作为入口讲解
@Target(ElementType.TYPE) //注解的适用范围, Type表示注解可以描述在类、接口、注解或枚举
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时
@Documented //表示注解可以记录在javadoc中
@Inherited //表示可以被子类继承该注解
@SpringBootConfiguration //注解的传递性这个是配置注解 说明@SpirngBootApplication也是一个配置注解 修饰的类变成配置类
@EnableAutoConfiguration //开启自动化装配
//@Component注解扫描
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//配置一些属性省略
}
2.2.2@SpringBootConfiguration 配置注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//Spring的配置注解
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
) //是否使用CGLib代理 true使用false不使用
boolean proxyBeanMethods() default true;
}
@SpringBootConfiguration 这个注解只是封装了@Configuration注解并且暴露了proxyBeanMethods属性。
2.2.3@ComponentScan
Spring的注解用于扫描指定目录下的@Component的类,注册到IOC容器中。只不过在在这里过滤了自定义的AutoConfigurationExcludeFilter这个过滤器
▲▲2.2.4@EnableAutoConfiguration
开启自动化装配注解,非常核心的注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //扫描自动配置包
@Import(AutoConfigurationImportSelector.class) //导入自动配置selector
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* 需要排除哪些Class不被注入到ioc容器中
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be applied
* 排除哪些className不被注入到ioc容器中
*/
String[] excludeName() default {};
}
@AutoConfigurationPackage
这个注解的主要作用是自动配置包
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//主要导入了AutoConfigurationPackages. Registrar这个组件
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
/**
* Base packages that should be registered with {@link AutoConfigurationPackages}.
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages} for specifying the packages to be
* registered with {@link AutoConfigurationPackages}.
*/
Class<?>[] basePackageClasses() default {};
}
AutoConfigurationPackages. Registrar配置类说明
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
//扫描子包下所有的组件注册到IOC容器
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//startardAnnotationMetadata主要封装了启动类的信息,包路径,类名等
// new PackageImports主要作用是将启动类的包路径封装成一个数组
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
//确定导入的包
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
Registry方法
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//判断registry中是否已经注册了AutoConfigurationPackages 很明显在启动的时候没有
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
// GenericBeanDefinition 属于SpringFramework的一个类 作用是提供标准的BeanDefinition
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
//设置一个基础的beanClas 属于SpringBoot新建的类
beanDefinition.setBeanClass(BasePackages.class);
//获取到构造器,给构造器添加第一个参数 刚刚传递进来的packageNames constructorArgumentValues
//往constructorArgumentValues这个属性值中添加属性值
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
//设置角色为INFRASTRUCTURE 基础配置类型
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//调用DefaultListableBeanFactory中的registerBeanDefinition 将当前的beanDefinition注册到ioc容器中
//里边主要就是设置到beanDefinitionMap中
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
小结:这一步主要是将AutoConfigurationPackages的BeanDefinition注册到容器中。
导入AutoConfigurationImportSelector.class
将AutoConfigurationImportSelector这个类导入到ioc容器中。
先执行process方法
执行rocess方法
在实际启动项目的时候调用的是这个方法来处理需要导入的自动装配的类。
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
//校验deferredImportSelector 是否属于AutoConfigurationImportSelector默认是属于的
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
//获取到自动装配的配置类
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
//保存在autoConfigurationEntries这个属性中
this.autoConfigurationEntries.add(autoConfigurationEntry);
//缓存在保存到entiresMap中 key是classname value本身
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
getAutoConfigurationEntry
//获取到自动装配的配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获取到注解@ EnableAutoConfiguration的exclude、excludeName属性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//加载所有符合条件的组件
List<String> configurations = ①getCandidateConfigurations(annotationMetadata, attributes);
//去除重复的组件 通过set去重
configurations = removeDuplicates(configurations);
//很据配置的属性排除掉不被加载的组件
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//校验这些不被加载的属性名是否存在
checkExcludedClasses(configurations, exclusions);
//去除排除的组件
configurations.removeAll(exclusions);
//获取到对应的configuration的过滤器判断是否满足
configurations = ②getConfigurationClassFilter().filter(configurations);
//发布到Configuration事件
fireAutoConfigurationImportEvents(configurations, exclusions);
//返回进行装配。
return new AutoConfigurationEntry(configurations, exclusions);
}
①getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //通过工厂来加载组件
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
//加载组件
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName(); //如果不存在的话就给个默认
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;
}
Private String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;
try { //直接通过类加载器加载根目录下的META-INF/spring.factories配置文件
Enumeration<URL> urls = (classLoader != null ?
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);
//将URL读取为配置文件
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);
return result;
}
catch (IOException ex) {
}
}
②getConfigurationClassFilter
获取到过滤条件
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
//spring.factories中找出org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的组件的实现类
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
//运行通知方法执行初始化初始化一些classLoader、环境变量等参数
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
//获取到过滤器
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
[图片上传失败...(image-bcb5f-1610005803326)]
ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
//除了复制filter之外还加载了classPath下边的spring-autoconfigure-metadata.properties 配置文件
//此配置文件中包括实现类类名.过滤条件=过滤类
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
this.filters = filters;
}
加载好过滤器 执行过滤filter
这个过滤就是根据spring-autoconfigure-metadata.properties中配置是否存在进行过滤的,
ConditionalOnClass 只有存在class时才不会被过滤,
ConditionalOnBean 只有存在Bean时,才不会被过滤
ConditionalOnSingleCandidate 只有单例存在时才不会被过滤
这里的源码都在 ConfigurationClassFilter#filter方法中这里不做过多的讲解。
执行selectImports
public Iterable<Entry> selectImports() {
//如果配置为空的话 直接返回 从processor中处理的获取
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
//获取到所有排除的组件classname
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
//对configuration进行排序
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
总结:自动化装配整体通过SPI(Service Provider Interface 服务发现机制)机制来实现自动化装配,就是通过读取spring.factories来加载所有需要注入的组件然后去重,在通过spring-autoconfigure-metadata.properties这个配置文件中配置的默认过滤条件进行过滤 最后将组件注入到Spring容器中,我们使用的时候直接可以通过@Autowires或者@Resource。
2.3、自定义Starter
2.3.1 SpringBoot Starter概念
starter是SpringBoot非常重要的一部分,可以理解成一个可插拔的插件,正式因为有starter的存在,开发者不需要在关注各种依赖的处理,和配置的处理,都由SpringBoot自动通过classpath路径下的类发现需要的bean,并加载到容器中。
比如使用redis的话,只需要引入spring-boot-start-redis就可以直接使用。
2.3.2 starter命名规范
SpringBoot提供的starter以 spring-boot-starter-xxx 的方式命名的。
官方建议自定义的starter使用xxx-spring-boot-starter 命名规则。以区分SpringBoot生态提供的starter。
2.3.3为啥需要自定义starter?
在开发过程中,经常会有一些独立于业务之外的配置模块,如果我们将这些可独立于业务代码之外的功能配置模块封装一个个starter,复用的时候只需要将这些所在的pom中引用依赖即可,SpringBoot为我们完成自动装配。
2.3.4自定义starter实现
实现
POM
<description>
自定义Starter实现
</description>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0</version>
<groupId>com.springboot.demo</groupId>
<dependencies>
<!--自定义装配模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<!--全局配置处理器-代码提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.4.RELEASE</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
装配的demo类
@Data
@ConfigurationProperties(prefix = "mystarterdemo") //注意这理的前缀必须全部小写
public class MyStarterDemo {
private String name;
private String sex;
}
配置类
@EnableConfigurationProperties
@Configuration //这里只能使用Configuration
@ConditionalOnClass(MyStarterDemo.class) //classpath下边只有存在MyStarterDemo这个类的时候才会去加载
public class MyStarterConfig {
static {
System.out.println("myStart init");
}
@Bean
public MyStarterDemo myStarterDemo(){
return new MyStarterDemo();
}
}
装配的配置文件
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mystarter.springBoot.demo.config.MyStarterConfig
使用
引入POM
<dependency>
<groupId>com.springboot.demo</groupId>
<artifactId>_402-my-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
配置类
-
直接加载进来
- mystarterdemo:
- name: 张三
- sex: 男
3、SpringBoot运行源码解析
是基于2.3.4.RELEASE版本,不同版本之间代码可能不同但是 核心代码都是一样的。
3.1、HelloWorld启动代码
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
启动调用的是SpringApplication静态run方法,点击进去可以看到最终调用的是重载的run方法
//分成两步 第一步new SpringApplication 第二步 调用了run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return ①new SpringApplication(primarySources) ②.run(args);
}
3.2 new SpringApplication
调用重载的构造器 resourceLoader为null, primarySources为启动类DemoApplication的Class
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//赋值到属性上但是这里为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//将启动类的Class储存在primarySources属性中
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
this.webApplicationType = ①WebApplicationType.deduceFromClasspath();
//设置初始化器(Initializer),最后会调用这些初始化器
//初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
②setInitializers((Collection)
getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器(Listener)
③setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
this.mainApplicationClass = ④deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath
设置Web容器类型
static WebApplicationType deduceFromClasspath() {
//判断classPath是否包含DispatcherHandler这个类,并且不包含DispatcherServlet、ServletContainer类 属于reactive容器类型 //isPresent是通过递归去查找类是否存在
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// classPath中不包括 Servlet、ConfigurableWebApplicationContext 其中任一一个类怎是非Web容器类型设置为NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
//其他返回servlet类型
return WebApplicationType.SERVLET;
}
setInitializers
往容器中创建一些初始化器。
第一步getSpringFactoriesInstances 获取到工厂对象。
//调用重载方法, type= ApplicationListener.class,parameterTypes为空,args没有
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//从spring.factories配置文件中加载ApplicationListener.class这种类型的实现类
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//获取到classname通过反射的方式获取到构造器然后进行newInstance创建对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
配置文件
# Initializers 工厂实现类
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
第二步setInitializers 设置到属性
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers); //设置到initializers属性中
}
setListeners
设置监听器
- setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
先从spring.factories中获取到ApplicationListener.class这个类型的配置并且实例化跟上一步是调用一样的方法,加载的配置如下。
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
设置到listeners属性中
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
deduceMainApplicationClass
获取到当前main方法运行的类的class,并保存在mainApplicationClass这个属性中。
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
3.3run方法▲▲
public ConfigurableApplicationContext run(String... args) {
// StopWatch就是用来统计SpringBoot项目启动耗费的时长
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//声明一个上下文 这个就是Spring容器
ConfigurableApplicationContext context = null;
//创建一个异常报告集合
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 headless 属性 往系统属性中设置java.awt.headless属性为true 作用是系统可以没有鼠标、键盘、显示器(服务器)
configureHeadlessProperty();
//①获取并启动Listener
//从spring.factories中获取到SpringApplicationRunListener.class配置并且实例化返回 获取到的是EventPublishingRunListener这个监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//通过事件多播器将事件进行发布
listeners.starting();
try {
//创建一个ApplicationArguments参数对象来封装args
// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。 如:--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//②获取到运行环境的 配置
//创建并配置当前SpringBoot应用将要使用的Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置需要忽略的bean信息
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//③创建Spring容器
context = createApplicationContext();
//获得异常报告器 SpringBootExceptionReporter 数组 这个跟初始化监听器的步骤是一样的都是从spring.factories中获取到实现类并且进行实例化的 这个是获取到ConfigurableApplicationContext这个配置的类也就是FailureAnalyzers实例化之后放到异常报告器中
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//④Spring容器前置处理
//准备容器在刷新之前一些必要的参数,比如将启动类注入到容器中
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//⑤刷新容器
refreshContext(context);
//⑥Spring容器后置处理
afterRefresh(context, applicationArguments);
//停止计时并打印
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//⑦发出结束执行的事件通知
listeners.started(context);
//⑧执行Runners
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//⑨发布应用上下文就绪事件
listeners.running(context);
}
catch (Throwable ex) {
//如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
ConfigurableApplicationContext继承图
[图片上传失败...(image-fdabe2-1610005803326)]
ConfigurableApplicationContext实际上属于Spring-context的一个类,继承了ApplicationContext的接口。
②获取到运行环境的配置
加载外部化配置资源到environment;包括命令行参数、servletConfigInitParams、servletContextInitParams、systemProperties、sytemEnvironment、random、 application.yml(.yaml/.xml/.properties)等;初始化日志系统。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//获取或创建环境(存在就直接返回,不存在创建一个再返回) //启动的时候会根据web类型来初始化不同的环境
//如果是servlet环境会创建一个 StandardServletEnvironment环境,reactive创建一个StandardReactiveWebEnvironment环境,none创建一个StandardEnvironment环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境:配置PropertySources和active Profiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
//listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)。 循环遍历获取到的listeners并准备到环境值
listeners.environmentPrepared(environment);
//将环境绑定到SpringApplication 将环境中的一些属性赋值到Spring容器上
bindToSpringApplication(environment);
//如果非web环境,将环境转换成StandardEnvironment
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
ConfigurationPropertySources.attach(environment);
return environment;
}
将配置的属性附加到环境中
public static void attach(Environment environment) {
//先获取到属性源
MutablePropertySources sources = ((ConfigurableEnvironment) environment)
.getPropertySources();
//从属性中获取到需要附加的属性configurationProperties
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
//先删除需要附加的属性,在添加
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) { //添加第一个的位置上将附件属性
sources.addFirst(new ConfigurationPropertySourcesPropertySource(
ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
③创建Spring容器
protected ConfigurableApplicationContext createApplicationContext() {
// 根据 webApplicationType 类型,获得 ApplicationContext 类型
// 先判断有没有指定的实现类
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET: 【AnnotationConfigServletWebServerApplicationContext】
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE: 【AnnotationConfigReactiveWebServerApplicationContext】
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default: 【AnnotationConfigApplicationContext】
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
}
}
// 创建 ApplicationContext 对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
④ 准备容器
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//设置容器环境,包括各种变量 将获取到的环境设置到容器的属性中
context.setEnvironment(environment);
//设置上下文的 bean 生成器【internalConfigurationBeanNameGenerator】
//和资源加载器注册到容器中
postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
applyInitializers(context);
//获取到属性Listeners 【所有监听器】执行contextPrepared
listeners.contextPrepared(context);
// Add boot specific singleton beans
//将封装的启动参数的bean注册到ioc容器中
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
//将banner注册到spring容器中
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置是否允许BeanDefinition被覆盖默认为false
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
// 加载所有资源 从primartSource和source属性拼接出来所有的source
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
load(context, sources.toArray(new Object[0]));
//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}
这块会对整个上下文进行一个预处理,比如触发监听器的响应事件、加载资源、设置上下文环境等等。
执行容器中的Initializers
/*
getInitializers() 是直接从initializers属性中获取也就是我们在创建SpringApplication实例时设置的初始化器,
对他们,并调用initialize方法。
我们也可以自定义初始化器,实现initialize方法,然后放入META-INF/spring.factories配置文件中
Key为:org.springframework.context.ApplicationContextInitializer的value中,来进行初始化。
*/
protected void applyInitializers(ConfigurableApplicationContext context) {
// 1\. 从SpringApplication类中的initializers集合获取所有的ApplicationContextInitializer
for (ApplicationContextInitializer initializer : getInitializers()) {
// 2\. 循环调用ApplicationContextInitializer中的初始化方法initialize方法
// 校验 ApplicationContextInitializer 的泛型非空
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 初始化 ApplicationContextInitializer
initializer.initialize(context);
}
}
Load加载我们启动类
protected void load(ApplicationContext context, Object[] sources) {
// 创建 BeanDefinitionLoader 对象 new了一个
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
// 设置loader一些必要的属性
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// 执行 BeanDefinition 加载
loader.load();
}
public int load() {
int count = 0;
// 遍历 sources 数组,逐个加载
for (Object source : this.sources) {
count += load(source);
}
return count;
}
//根据不同类型的source进行load 我们在启动的时候传入的是class因此只看class即可其他的有兴趣的小伙伴可以自行研究
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
// 如果是 Class 类型,则使用 AnnotatedBeanDefinitionReader 执行加载
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
// 如果是 Resource 类型,则使用 XmlBeanDefinitionReader 执行加载
if (source instanceof Resource) {
return load((Resource) source);
}
// 如果是 Package 类型,则使用 ClassPathBeanDefinitionScanner 执行加载
if (source instanceof Package) {
return load((Package) source);
}
// 如果是 CharSequence 类型,则各种尝试去加载
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
// 无法处理的类型,抛出 IllegalArgumentException 异常
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
//根据Class进行loader
private int load(Class<?> source) {
// 判断是否是grovery 肯定不是咱们是java
if (isGroovyPresent()
&& GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
load(loader);
}
// 如果是 Component ,则执行注册
if (isComponent(source)) {
// AnnotatedBeanDefinitionReader#register方法 是Spring中的内容了就是将当前的class注册到ioc容器
this.annotatedReader.register(source);
return 1;
}
return 0;
}
▲▲刷新容器
private void refreshContext(ConfigurableApplicationContext context) {
// 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等)
refresh(context);
// 注册 ShutdownHook 钩子 默认为true
if (this.registerShutdownHook) {
try {
//向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
关机钩子的方法 abstractApplicationContext中的方法
创建一个线程并放入到shutdownHook中在关闭的时候执行start优雅关闭
public void registerShutdownHook() {
if (this.shutdownHook == null) {
this.shutdownHook = new Thread() {
public void run() {
synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
//doClose 销毁bean关闭工厂声明周期设置为close可以自行研究
AbstractApplicationContext.this.doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
Refresh方法
里边实际调用ApplicationContext的refresh的方法 实例化bean啥的都是在这一步进行的详情可以查看Spring源码。
protected void refresh(ApplicationContext applicationContext) {
// 断言,判断 applicationContext 是 AbstractApplicationContext 的子类
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// 启动(刷新) AbstractApplicationContext
((AbstractApplicationContext) applicationContext).refresh();
}
Spring容器后置处理
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}//该方法没有实现,可以根据需要做一些定制化的操作。
⑦ 发出结束执行事件通知
public void started(ConfigurableApplicationContext context) {
//执行所有SpringApplicationRunListener实现的started方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
⑧ 执行Runners
自定义的执行器XxxRunner类,为了在项目启动之后执行一些特定的程序,经常用来做一些业务方面的初始化操作, 可以通过实现SpringBoot提供的ApplicationRunner和CommandLineRunner这两种接口实现。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// 获得所有 Runner 们
List<Object> runners = new ArrayList<>();
// 获得所有 ApplicationRunner 实现类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 获得所有 CommandLineRunner 实现类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序 runners
AnnotationAwareOrderComparator.sort(runners);
// 遍历 Runner 数组,执行逻辑
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
⑨ 发布应用上下文就绪事件
使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext, 这样整个Spring Boot项目就正式启动完成了。
public void running(ConfigurableApplicationContext context) {
//触发所有 SpringApplicationRunListener 监听器的 running 事件方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
4、SpringBoot整合MyBatis、Jpa、Redis
4.1、整合MyBatis
POM
<dependencies>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--整合MyBatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--mysql驱动注册-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--测试相关的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置
# Mysql数据库连接配置 : com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#开启驼峰命名匹配映射
mybatis.configuration.map-underscore-to-camel-case=true
#配置mybatis的xml映射配置文件路径
mybatis.mapper-locations=classpath:mapper/*.xml
#配置mybatis映射配置文件中实体类别名
mybatis.type-aliases-package=com.springboot.demo.pojo
启动类
@SpringBootApplication
@MapperScan("com.springboot.demo.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
Pojo以及Mapper
@Data
public class Person {
private Integer id;
private String name;
private Integer petId;
}
@Mapper
public interface PersonMapper {
@Select("select * from person where id = #{id}")
Person getById(Integer id);
Person selectById(Integer id);
}
为了演示注解和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.springboot.demo._01mybatisDemo.mapper.PersonMapper">
<select id="selectById" resultType="com.springboot.demo._01mybatisDemo.pojo.Person">
select * from person where id =#{id}
</select>
</mapper>
总结 :在使用Mapper接口的时候可以在接口上贴@Mapper或者在启动类上贴@MapperScan。
4.2整合JPA
POM
<dependencies>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--SpringBoot整合jpa的包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--mysql驱动注册-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--测试相关的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置
# Mysql数据库连接配置 : com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Pojo以及接口
@Data
@Entity
@Table(name = "person")
public class Person {
@Id //表明映射主键id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "pet_id")
private Integer petId;
}
public interface PersonRepository extends CrudRepository<Person,Integer> {
}
4.3整合Redis
POM
<dependencies>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--redis整合包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--测试相关的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency>
</dependencies>
配置
-
redis服务器地址
- spring.redis.host=127.0.0.1
-
redis服务器连接端口
- spring.redis.port=6379
-
redis服务器连接密码
- spring.redis.password=
使用
方式1通过CRUD的方式使用
@AllArgsConstructor
@NoArgsConstructor
@Data
//指定存储在redis中的数据类型
@RedisHash(value = "dog")
public class Dog {
@Id // 用来标识实体类主键 字符串形式的hashkey标识唯一的实体类对象id
private String id;
@Indexed//生成二级索引
private String name;
}
public interface DogRepository extends CrudRepository<Dog,String> {
List<Dog> findByName(String name);
}
测试
@Autowired
private DogRepository dogRepository;
@Test
public void testDogRepository() throws Exception {
dogRepository.save(new Dog("dog:1","旺财"));
List<Dog> dogs = dogRepository.findByName("旺财");
System.out.println(dogs);
}
这种是将Redis作为DB来使用不推荐使用。
方式二
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Test
public void testRedisTemplate() throws Exception {
redisTemplate.opsForValue().set("helloDog", JSONObject.toJSONString(new Dog("1","2")));
String helloDog = redisTemplate.opsForValue().get("helloDog");
Dog dog = JSONObject.parseObject(helloDog, Dog.class);
System.out.println(dog);
}