SpringBoot自动装配实现流程分析及阿里云短信自动装配实现
- SpringBoot自动装配实现流程分析
- 阿里云短信自动装配实现
这里以Redis的自动装配为例
SpringBoot自动装配实现流程分析
流程解析:
1.启动类上有@SpringBootApplication注解,进入此注解,会发现这个注解类上会有如下三个注解:
//与Configuration一样,把当前类标识为配置类
@SpringBootConfiguration
//包扫描,SpringBoot默认会扫描启动类所在包及其所在包中类上的注解
@ComponentScan
//开启自动配置
@EnableAutoConfiguration
2.进入@EnableAutoConfiguration注解
//@Import作用是导入其他的配置类
@Import({AutoConfigurationImportSelector.class})
//AutoConfigurationImportSelector实现了DeferredImportSelector,而DeferredImportSelector继承了ImportSelector (最终父类)
public interface ImportSelector {
//String[]数组中返回的就是配置类的类全名
String[] selectImports(AnnotationMetadata var1);
}
3.SpringBoot是从那里读取这些全类名的呢?
AutoConfigurationImportSelector实现类父接口类ImportSelector 重写了selectImports()方法。
//AutoConfigurationImportSelector重写的selectImports()方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//获取自动配置类的全类名,并将集合转换为数组
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
4.获取自动配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
5.进入getCandidateConfigurations方法获取配置类列表
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;
}
6.会发现配置类的全类名是由这个文件下获取的:
META-INF/spring.factories
进入文件后
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
7.进入RedisAutoConfiguration
//标识当前类为配置类
@Configuration(
proxyBeanMethods = false
)
// 当给定的类名在类路径上存在,则实例化当前Bean
//这个注解通俗的说就是Spring工程中引用了RedisOperations,才会构建这个bean
@ConditionalOnClass({RedisOperations.class})
//使使用 @ConfigurationProperties 注解的类生效。
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
//将方法的返回值制作成SpringIOC容器的Bean对象
@Bean
//它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员。
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
8.进入RedisProperties
//读取application.yml文件配置和redis相关信息,读到RedisProperties 对象中
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
}
阿里云短信自动装配实现
对照RedisTemplate的实现步骤
1.在项目的配置类包下的resources下创建META-INF/spring.factories,对照上方redis的spring.factories文件中内容进行修改如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jacob.autoconfig.SmsAutoConfiguration
//com.jacob.autoconfig为包名
//SmsAutoConfiguration为类名
2.定义SmsAutoConfiguration类
@EnableConfigurationProperties(SmsProperties.class)
public class SmsAutoConfiguration {
@Bean
public SmsTemplate smsTemplate(SmsProperties prpos){
return new SmsTemplate(prpos);
}
}
3.定义SmsProperties 类,读取yml文件中的配置
@Data
@ConfigurationProperties(prefix = "message.sms")
public class SmsProperties {
private String accessKeyId;
private String accessKeySecret;
private String signName;
private String templateCode;
private String endpoint;
}
4.定义SmsTemplate 类(发送短信业务类),可从阿里云官方导出并进行修改。
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
public class SmsTemplate {
private SmsProperties smsProperties;
public SmsTemplate(SmsProperties smsProperties) {
this.smsProperties = smsProperties;
}
/**
* 发送验证码短信
*
* @param mobile
* @param code
*/
public void sendSms(String mobile, String code) {
try {
Config config = new Config()
.setAccessKeyId(smsProperties.getAccessKeyId())
.setAccessKeySecret(smsProperties.getAccessKeySecret());
config.endpoint=smsProperties.getEndpoint();
// 访问的域名
//config.endpoint = "dysmsapi.aliyuncs.com";
Client client = new Client(config);
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(mobile)
.setSignName(smsProperties.getSignName())
.setTemplateCode(smsProperties.getTemplateCode())
.setTemplateParam("{\"code\":\"" + code + "\"}");
// 复制代码运行请自行打印 API 的返回值
SendSmsResponse response = client.sendSms(sendSmsRequest);
if ("OK".equals(response.getBody().getCode())) {
System.out.println("验证码发送成功");
}
System.out.println(response.getBody().getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.在application.yml文件中编写配置
message:
sms:
accessKeyId: xxxxxxxxx
accessKeySecret: xxxxxxxxx
signName: xxxxxxxxx
templateCode: xxxxxxxxx
endpoint: dysmsapi.aliyuncs.com
6.编写测试类进行发送
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AutoConfigApplication.class)
public class SendMessage {
@Autowired
private SmsTemplate smsTemplate;
@Test
public void send(){
smsTemplate.sendSms("131xxxxxxxx","xxxx");
}
}