Spring5源码分析之启动类的相关接口和注解
一些基础但是核心的知识总结:
- Spring Boot项目启动的时候需要加@Configuration、 @ComponentScan
- @Configuration + @Bean 把第三方jar包注入到容器中。
- 内部的直接 @Service @Controller等等之类配合 @ComponentSscan 的就OK了
- @Scope可以实现单例
- 对于启动默认是饿汉式调用时候创建(但是项目启动时候比较耗费时间),另外一种是调用时候创建
- @ComponentScan有排除的用法,排除那个组件 需要哪个组件可以控制
- 在config类上面加 @ComponentScan然后去控制其他注解的注入情况
- 使用@Condition多条件注册bean对象,根据环境判断进行抉择
- @Import快速注入第三方bean对象
- SpringBoot Emablexxx开启原理
- 基于ImportBeanDefinitionRegister注册bean
- 基于FactoryBean注册Bean对象
Spring的扩展接口: condition
对于控制某个Bean,满足某个条件后才可以允许注册到容器中:
随便写个系统实体类吧:
public class OS {
private String name;
private Integer version; public OS() {
} @Override
public String toString() {
return "hello ********************* OS{" +
"name='" + name + '\'' +
", version=" + version +
'}';
}
}
配置文件(相当于xml)
@Configuration
public class MyConfig { @Bean
@Conditional(MyCondition.class)
public OS os(){
System.out.println("ttttttttttttttttttttt");
return new OS();
}
}
控制条件:
public class MyCondition implements Condition {
/**
*
* @param conditionContext 获取当前上下文
* @param annotatedTypeMetadata 获取到当前注解的细节
* @return
*/
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String osName = environment.getProperty("os.name");
if (osName.equals("Windows 7")){
return false;
}
if (osName.equals("Windows 10")){
return true;
}
return false;
}
}
测试类:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
}
OS osTest = (OS)applicationContext.getBean("os");
System.out.println(osTest);
}
}
结果:
2. @Import注解
思考 为啥使用@Import注解呢?
@Bean注解应用场景是什么呢? 注册加载外部的jar。如果有30个bean,逐个去写也是非常麻烦的啊
所以就用@Import简化操作,将外部的jar包注入到Spring ioc容器中。等同于@Bean。@Import注册的Bean对象,id为当前类的全路径。
配置类:
@Configuration
@Import(OS.class)
public class MyConfig {
}
测试方法:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
}
//Import的特殊性
OS osTest = (OS)applicationContext.getBean("com.toov5.config.beanTest.OS");
System.out.println(osTest);
}
}
@Import 和 @Bean的区别:
@Bean注册的bean的id是方法名称
@Import当前类完整地址
共同应用场景都是引用外部jar包。
3. @EnableXXX
开启某某功能。底层使用@Import注解实现的
底层调用的就是@Import注解
场景: 封装一个框架,把不支持springboot的,鼓捣成支持springboot的!
实体类:
public class PayEntity {
}
注解:
/**
* 启动时候加入该注解,开启功能,会将实体类注入到容器中
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({PayEntity.class})
public @interface EnablePayEntity { }
启动:
@Configuration
@EnablePayEntity
public class MyConfig {
}
测试:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
}
}
}
结果:
补充下: 可以@Import多个 ”,“解决。
4. ImportSelector接口 (跟 @Import注解一样的,只不过这个是原生api罢了)
实体类1:
public class MemberEntity {
}
实体类2:
public class PersonEntity {
}
原生接口的实现:
public class MyImportSeletcor implements ImportSelector {
/**
* 注解信息
* @param annotationMetadata
* @return
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.toov5.config.beanTest.MemberEntity", "com.toov5.config.beanTest.PersonEntity"};
}
}
启动类:
@Configuration
@Import(MyImportSeletcor.class)
public class MyConfig {
}
测试类:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
}
}
}
结果:
5. ImportBeanDefinitionRegister接口 手动往ioc 注入bean
Spring容器中 Bean的信息,都是由BeanDefinition描述的
可以看下:
各种方法,Bean的各种信息。
实体类:
public class PersonEntity {
}
接口实现,手动注入实体类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* 注解信息
* @param annotationMetadata
* @param beanDefinitionRegistry
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//手动注册到ioc容器中
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(PersonEntity.class);
//IOC源码里面肯定有这个的!!! 对象放到IOC容器中就叫注册 (底层是个map 线程安全的)
beanDefinitionRegistry.registerBeanDefinition("personEntity", rootBeanDefinition);
}
}
启动类:
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class MyConfig { }
测试类:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
}
}
}
运行结果:
6. 基于FactoryBean
FactoryBean也可以用来注册Bean
启动方式千万万,随便使用:
实体类:
public class PersonEntity {
}
FactoryBean:
public class MyFactoryBean implements FactoryBean<PersonEntity> {
@Override
public PersonEntity getObject() throws Exception {
return new PersonEntity();
} @Override
public Class<?> getObjectType() {
return PersonEntity.class;
} @Override
public boolean isSingleton() {
//默认情况下是true。单例
return true;
}
//往ioc容器中注入对象 }
启动类:
@Configuration
public class MyConfig { @Bean
public MyFactoryBean myFactoryBean(){
return new MyFactoryBean();
}
}
测试类:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
PersonEntity personEntity1 = (PersonEntity)applicationContext.getBean("myFactoryBean");
PersonEntity personEntity2 = (PersonEntity)applicationContext.getBean("myFactoryBean");
System.out.println(personEntity1 == personEntity2);
}
}
自己写“抄”个注解玩玩
自定义一个注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface toov5 {
}
实体类:
@toov5
public class Hello {
}
启动采用扫包的方式:扫包的范围
@Configuration
@ComponentScan("com.toov5.config.beanTest.entity")
public class MyConfig { }
测试:
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){
System.out.println(name);
}
}
}
测试结果:
小结: spring 注入bean到容器方式:
@Bean @Import ,一般用于外部的jar包
其他的 @Service @Repository 注入对象底层其实都一样,就是区分不同的场景。使用的时候需要@ComponentScan注解扫包
还有就是实现一系列相应的接口去实现注入Bean 的方式
如下:
注解
- @Bean
- @Import
- @EnableXXX(实质@Import)
接口
- condition接口 + @Conditional
- ImportSelector接口 + @Bean、@Import
- ImportBeanDefinitionRegister接口+@Bean、@Import
- FactoryBean接口+ @Bean、@Import
Spring5源码分析之启动类的相关接口和注解的更多相关文章
- 精尽Spring Boot源码分析 - SpringApplication 启动类的启动过程
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- Appium Server 源码分析之启动运行Express http服务器
通过上一个系列Appium Android Bootstrap源码分析我们了解到了appium在安卓目标机器上是如何通过bootstrap这个服务来接收appium从pc端发送过来的命令,并最终使用u ...
- Appium Android Bootstrap源码分析之启动运行
通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...
- Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...
- Spring5源码分析(1)设计思想与结构
1 源码地址(带有中文注解)git@github.com:yakax/spring-framework-5.0.2.RELEASE--.git Spring 的设计初衷其实就是为了简化我们的开发 基于 ...
- u-boot 源码分析(1) 启动过程分析
u-boot 源码分析(1) 启动过程分析 文章目录 u-boot 源码分析(1) 启动过程分析 前言 配置 源码结构 api arch board common cmd drivers fs Kbu ...
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)
http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...
- Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...
- JUC源码分析-其它工具类(一)ThreadLocalRandom
JUC源码分析-其它工具类(一)ThreadLocalRandom ThreadLocalRandom 是 JDK7 在 JUC 包下新增的随机数生成器,它解决了 Random 在多线程下多个线程竞争 ...
随机推荐
- 学习markdown(一)
转:https://www.jianshu.com/p/81e1608ea2d8 ----------------------------------------------------------- ...
- HDFS内存配置
下图是HDFS的架构: 从上图中可以知道,HDFS包含了NameNode.DataNode以及Client三个角色,当我们的HDFS没有配置HA的时候,那还有一个角色就是SecondaryNameNo ...
- linux 导出mysql数据库表及数据
导出表及结构 mysqldump -h地址ip -u用户名 -p密码 数据库名 > 数据库名.sql --连接数据库 mysql -h地址ip -u用户名 -p密码 --使用数据库 user ...
- [Svelte 3] Use await block to wait for a promise and handle loading state in Svelte 3
Most web applications have to deal with asynchronous data at some point. Svelte 3 apps are no differ ...
- 2019-2020-1 20199302《Linux内核原理与分析》第一周作业
2019.9.13 说明:可能是因为网速问题,笔记本上一直没有办法加载实验楼的学习界面,所以没有使用实验楼提供的环境,而是用的ubuntu进行的所有实验. 二.学习第二章,对shell命令有了一个大概 ...
- map json 字符串 对象之间的相互转化
1.对象与字符串之间的互转 将对象转换成为字符串 String str = JSON.toJSONString(infoDo); 字符串转换成为对象 InfoDo infoDo = JSON.pars ...
- C++ error C2015: too many characters in constant
错误原因:字符常量中的字符太多了. 错误分析: 单引号表示字符型常量. 一般的,单引号中必须有,也只能有一个字符(使用转义符时,转义符所表示的字符当作一个字符看待),如果单引号中的字符数多于4个,就会 ...
- UVA 11019 Matrix Matcher ( 二维字符串匹配, AC自动机 || 二维Hash )
题目: 传送门 题意: 给你一个 n * m 的文本串 T, 再给你一个 r * c 的模式串 S: 问模式串 S 在文本串 T 中出现了多少次. 解: 法一: AC自动机 (正解) 670ms 把模 ...
- 4-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案升级篇(远程升级WIFI内部程序)
https://www.cnblogs.com/yangfengwu/p/10360618.html 演示视频: https://www.bilibili.com/video/av54894356/ ...
- 数据科学速查手册(包括机器学习,概率,微积分,线性代数,python,pandas,numpy,数据可视化,SQL,大数据等方向)
介绍:https://redstonewill.com/2372/ 项目网址:https://github.com/FavioVazquez/ds-cheatsheets