使用springboot怎么实现自动配置

这篇文章将为大家详细讲解有关使用springboot怎么实现自动配置,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

springboot是什么

springboot一种全新的编程规范,其设计目的是用来简化新Spring应用的初始搭建以及开发过程,SpringBoot也是一个服务于框架的框架,服务范围是简化配置文件。

首先创建一个springboot工程用来测试,然后在pom文件中引入springboot-starter-redis的启动器依赖

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
</dependencies>

然后,在application.properties中配置redis属性

spring.redis.port=6379
spring.redis.host=localhost
spring.redis.database=0

然后,在启动类中注入redisTemplate类,redisTemplate为spring官方提供的对redis底层开发包(例如jedis)进行了深度封装的组件,使用redisTemplate可以优雅的操作redis。我在启动类中写了一个测试方法,向redis写入一条数据

@RequestMapping("/redistest")
publicStringtest(){
redisTemplate.opsForSet().add("aaaaa","123456");
return"OK";
}

运行这个方法,打开redis客户端可以看到值已经写入了

使用springboot怎么实现自动配置

先抛开这里的键和值让人看不懂的问题,大家是不是觉得springboot整合redis要比普通的springmvc整合redis简单多了?我只配置了redis的连接地址,端口号,注入了redisTemplate,就能开始操作redis了,那么springboot底层到底做了些什么使得整合变得如此的简单了呢。

首先我们来看,springboot启动类上都有一个@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@interfaceSpringBootApplication

可以看到SpringbootApplication这个注解是由一系列的注解组合而成,这其中最重要的是@EnableAutoConfiguration和@ComponentScan,@ComponentScan的意思就是组件扫描注解,这个注解会自动注入所有在主程序所在包下的组件。比@ComponentScan注解更重要的就是@EnableAutoConfiguration注解了,这个注解的含义就是开启自动装配,直接把bean装配到ioc容器中,@EnableAutoConfiguration也是一个组合注解,我们点进去看一下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public@interfaceEnableAutoConfiguration

这个地方我们主要看@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)两个注解,首先来看@AutoConfigurationPackage注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public@interfaceAutoConfigurationPackage{

}

这个注解主要是获取我们注解所在包下的组件去进行注册,大家看到这个@Import注解,那么这个注解是什么含义呢,

@Import注解用来导入@Configuration注解的配置类、声明@Bean注解的bean方法、导入ImportSelector的实现类或导入ImportBeanDefinitionRegistrar的实现类,这里这个AutoConfigurationPackages.Registrar.class就是ImportBeanDefinitionRegistrar的实现类,来看下源码

staticclassRegistrarimplementsImportBeanDefinitionRegistrar,DeterminableImports{

//metadata是注解的元信息registry是bean定义的注册器
		@Override
		publicvoidregisterBeanDefinitions(AnnotationMetadatametadata,BeanDefinitionRegistryregistry){
//把注解所在的包下所有的组件都进行注册
			register(registry,newPackageImport(metadata).getPackageName());
		}

		@Override
		publicSet<Object>determineImports(AnnotationMetadatametadata){
			returnCollections.singleton(newPackageImport(metadata));
		}

	}




publicstaticvoidregister(BeanDefinitionRegistryregistry,String...packageNames){
		//首先判断这个bean有没有被注册
		if(registry.containsBeanDefinition(BEAN)){
			//获取bean定义
			BeanDefinitionbeanDefinition=registry.getBeanDefinition(BEAN);
			//通过bean定义获取构造函数值
			ConstructorArgumentValuesconstructorArguments=beanDefinition.getConstructorArgumentValues();
			//给构造函数添加参数值
			constructorArguments.addIndexedArgumentValue(0,addBasePackages(constructorArguments,packageNames));
		}
		else{
			//一个新的bean定义
			GenericBeanDefinitionbeanDefinition=newGenericBeanDefinition();
			//设置beanClass为beanPackages类型
			beanDefinition.setBeanClass(BasePackages.class);
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,packageNames);
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			//bean注册
			registry.registerBeanDefinition(BEAN,beanDefinition);
		}
	}

接下来就是@Import(AutoConfigurationImportSelector.class)这个注解,我们来看看AutoConfigurationImportSelector这个类,这个类是我们自动装配的导入选择器,首先看这个类的第一个方法,其实也就是这个类的核心方法

@Override
	publicString[]selectImports(AnnotationMetadataannotationMetadata){
		if(!isEnabled(annotationMetadata)){
			returnNO_IMPORTS;
		}
		//加载元数据
		AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		//获得自动装配的实体
		AutoConfigurationEntryautoConfigurationEntry=getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
protectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,
			AnnotationMetadataannotationMetadata){
		if(!isEnabled(annotationMetadata)){
			returnEMPTY_ENTRY;
		}
		//获得属性
		AnnotationAttributesattributes=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);
		returnnewAutoConfigurationEntry(configurations,exclusions);
	}

在这部分中,核心方法是getCandidateConfigurations,我们来看下这个方法

protectedList<String>getCandidateConfigurations(AnnotationMetadatametadata,AnnotationAttributesattributes){
		//从工厂中获取自动配置类
		List<String>configurations=SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		//这句断言很重要,告诉了我们工厂是去哪里找自动配置类的,这里显然META-INF/spring.factories是一个路径
		Assert.notEmpty(configurations,"NoautoconfigurationclassesfoundinMETA-INF/spring.factories.Ifyou"
				+"areusingacustompackaging,makesurethatfileiscorrect.");
		returnconfigurations;
	}

那我们就找一下这个路径,去哪里找呢,我们看到这个类的包是org.springframework.boot.autoconfigure;那我们就到这个包的位置去找这个spring.factories,果不其然,我们点开这个文件

使用springboot怎么实现自动配置

使用springboot怎么实现自动配置

我们看到文件中有一行注释这Auto configure,表示这些都是自动配置相关的类,这里我们不得不说spring框架真的是强大,这里面居然有100多个自动配置类,我们找到redis有关的自动配置类

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\

这里我们需要的肯定是第一个自动配置类,我们点进去看看

@Configuration
//条件注解,某个class位于类路径上,才会实例化一个Bean,这个类是redis操作的类
@ConditionalOnClass(RedisOperations.class)
//使得@ConfigurationProperties注解的类生效,这个类是配置redis属性的类
@EnableConfigurationProperties(RedisProperties.class)
//导入一些配置
@Import({LettuceConnectionConfiguration.class,JedisConnectionConfiguration.class})
publicclassRedisAutoConfiguration{

	@Bean
	//仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean,这个就是spring默认的redisTemplate
	@ConditionalOnMissingBean(name="redisTemplate")
	publicRedisTemplate<Object,Object>redisTemplate(RedisConnectionFactoryredisConnectionFactory)
			throwsUnknownHostException{
		RedisTemplate<Object,Object>template=newRedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		returntemplate;
	}

	@Bean
	@ConditionalOnMissingBean
	publicStringRedisTemplatestringRedisTemplate(RedisConnectionFactoryredisConnectionFactory)
			throwsUnknownHostException{
		StringRedisTemplatetemplate=newStringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		returntemplate;
	}

}

我们在application.properties中配置的redis属性,其实就是设置到了这个类中

//前缀spring.redis
@ConfigurationProperties(prefix="spring.redis")
publicclassRedisProperties{

	/**
	*Databaseindexusedbytheconnectionfactory.
	*/
	privateintdatabase=0;

	/**
	*ConnectionURL.Overrideshost,port,andpassword.Userisignored.Example:
	*redis://user:password@example.com:6379
	*/
	privateStringurl;

	/**
	*Redisserverhost.
	*/
	privateStringhost="localhost";

	/**
	*Loginpasswordoftheredisserver.
	*/
	privateStringpassword;

	/**
	*Redisserverport.
	*/
	privateintport=6379;

	/**
	*WhethertoenableSSLsupport.
	*/
	privatebooleanssl;

	/**
	*Connectiontimeout.
	*/
	privateDurationtimeout;

	privateSentinelsentinel;

	privateClustercluster;

	privatefinalJedisjedis=newJedis();

	privatefinalLettucelettuce=newLettuce();
}

我们前面说了,用了spring默认的redisTemplate操作redis的话,存到redis里的数据对我们的阅读不友好,我们看不懂,那是因为redisTemplate中默认用了jdk自带的序列化器

使用springboot怎么实现自动配置

要想让数据变成我们能看得懂的样子,我们需要替换掉redisTempalte默认的序列化器,现在我就来实操一下,写一个配置类

@Configuration
publicclassRedisConfig{
//这里的上下文已经有了自定义的redisTemplate,所以默认的redisTemplate不会生效
@Bean
publicRedisTemplate<Object,Object>redisTemplate(RedisConnectionFactoryredisConnectionFactory){
RedisTemplate<Object,Object>redisTemplate=newRedisTemplate<>();
//设置自定义序列化器
redisTemplate.setDefaultSerializer(newJackson2JsonRedisSerializer<Object>(Object.class));
redisTemplate.setConnectionFactory(redisConnectionFactory);
returnredisTemplate;
}
}

然后我改写一下测试方法,一起来看结果

publicStringtest(){
redisTemplate.opsForSet().add("ffffff","55555555");
return"OK";
}

使用springboot怎么实现自动配置

关于使用springboot怎么实现自动配置就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

发布于 2021-05-10 20:34:15
收藏
分享
海报
0 条评论
162
上一篇:python3如何获取文件中url内容并下载 下一篇:Vue中保存用户登录状态的示例分析
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码