Spring不止AOP与IOC:Spring生态详解

final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);

final Object bean = annotationConfigApplicationContext.getBean(“【定义的Bean名称】”);

复制代码

通过上面的两种方法可以将配置的类委托给IOC来进行管理;

注:在SpringBoot中直接通过Main方法可以自动扫描Main所在路径下(包含所有子路径下)的所有类;

结合上述内容,可以轻松地把需要的对象委托给IOC容器进行管理,那么委托给IOC容器的对象是如何进行处理的呢;这里需要了解一个Spring的重要知识点:BeanFactory(Bean工厂)

4.1)简单的示例

=========

  • 创建一个简单的接口;public interface IBean { void testDemoFunction1(); }

  • 创建一个实现类public class TestBean implements IBean {@Override public void testDemoFunction1(){ System.out.println(“testDemoFunction1”); } 复制代码}

  • 创建一个配置类,注意@Configuration与@Bean注解,当然你也可以@ComponentScan等注解用于描述一个将被扫描的路径;@Configuration public class TestConfig { @Bean public IBean testBean(){ return new TestBean(); } }

  • 创建启动类,运行看效果;public class TestMain { public static void main(String[] values){ final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(TestConfig.class); final IBean testBean = (IBean)annotationConfigApplicationContext.getBean(“testBean”); testBean.testDemoFunction1(); } }---------------最终打印结果--------------------- testDemoFunction1

4.2)简单的源码跟踪

===========

从4.1中可以看到通过getBean方法可以直接获取到我们定义的Bean对象;那么他是如何实现的呢?进入getBean的源码中看看

public Object getBean(String name) throws BeansException {

//啥也不说,先来一个断言,看看当前是否是活跃的,可用的;

this.assertBeanFactoryActive();

/*

  • 下面的代码可以看出这个方法,其实是一个门面包装方法,他自己本身不进行任何处理,而是直接交给另外一个对象进行处理;

  • 是直接调用的this.getBeanFactory()中的getBean方法来实现的

  • 这里先买个关子:这个getBean可以获取Bean也可以生产Bean,在以后有机会挑战源码的时候再说吧

*/

return this.getBeanFactory().getBean(name);

}

//这里getBeanFactory是获取的一个ConfigurableListableBeanFactory对象

public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

复制代码

来看看这个是ConfigurableListableBeanFactory个什么东西

很明显这类是BeanFactory的接口抽象;

从这个UML图中可以看出DefaultListableBeanFactory中仅仅是实现了ConfigurableListableBeanFactory,但是没有实现超类接口中的getBean方法,具体的getBean方法是在AbstractBeanFactory中实现的;

/*

  • 在AbstractBeanFactory中是直接调用的doGetBean方法进行的处理

  • 这里只能说一句,我真的很菜,真的看了很久没有看懂这个doGetBean中的具体实现;

  • 欢迎高手指教;

*/

@Override

public Object getBean(String name) throws BeansException {

return doGetBean(name, null, null, false);

}

复制代码

4.3)BeanFactory是什么

==================

BeanFactory是Spring对Bean管理的顶层核心接口,使用了简单工厂模式,负责生产Bean;

4.4)Bean的装配

===========

上面说了一堆,都是说得怎么获取到Bean,那么这些Bean是什么时候及怎么放进去的呢?在最前面有过介绍Bean的装载是由多种形式的,有XML(ClassPathXmlApplicationContext)、也有注解(AnnotationConfigApplicationContext)等多种方式,如果一种方式写一种加载模式,Spring的开发人员肯定要哭了,这么多的方式是通过什么办法统计进行处理的呢?

先来看看下面的一个例子:

业务人员拿到了某小区的销售信息,查询到张先生与李先生都购买了这个小区的房子,装修公司的销售找到了张先生与李先生,张先生需要装修而李先生不需要装修,销售带张先生先来到装修公司,张先生提出自己的需求,装修公司的设计师根据张三的需求出装修设计图纸,然后装修公司再找到工厂去下单生产,最后在张三的房子中安装完成装修;

在这个例子中翻译过来:

  • **张先生与装修:**可以看作2个类,张三需要依赖装修才能入住;

  • **装修公司:**可以看作上下文对象(ApplicationContext),由装修公司进行统一的管理;

  • **业务人员:**可以看作Bean定义读取对象(BeanDefinitionReader),用于加载配置信息;

  • **销售:**可以看作Bean定义扫描对象(BeanDefinitionSacnner),用于扫描所有对象;用于处理配置中的信息,确定哪些需要装载到容器,哪些不需要;

  • **设计师的图纸:**可以看作Bean定义对象(BeanDefinition),这里包含了Bean的所有描述信息,为工厂的生产提供依据;

  • **工厂:**可以看作BeanFactory用于生产装修需要的东西;

装修公司的业务人员拿到楼盘购买信息,销售人员根据业务人员提供的业主信息把所有业主的联系方式都找出来,最终找到了张先生,邀请张先生到装修公司来洽谈业务,装修公司根据张先生的装修的风格(是否单例)、灯具要求(是否懒加载)、墙面需求、地板需求等多方面的需要进行设计;设计好的图纸交给工厂,工厂根据设计的图纸进行生产;虽然张先生对房屋整装的需求很多,但最终都变成了可量化的图纸,而工厂不关心张先生还是李先生,只根据图纸进行生产,根据多源同根的原则解决了问题;

在Spring中也是如此操作及处理的,不管外部的源是什么,最终都会变成一个统一的Bean定义对象(BeanDefinition),最后Bean工厂(BeanFactory)根据这个Bean定义(BeanDefinition)进行生产;大致的流程为:读取配置——>筛选对象——>生成Bean定义——>注册Bean定义——>生产Bean;

对于外部配置而言,不管外面是什么样的场景,最终都是转换为对应的BeanDefinitionReader;

  • **AnnotatedBeanDefinitionReader:**用于读取注解的Bean定义配置;

  • **XmlBeanDefinitionReader:**用于读取XML的Bean定义配置;

在AbstractBeanDefinition中可以看到定义了如下几个关键的属性

/**

  • 用于保存bean组件的class对象

*/

@Nullable

private volatile Object beanClass;

/**

  • bean的作用范围

  • 默认是singleton,prototype

*/

@Nullable

private String scope = SCOPE_DEFAULT;

/**

  • 判断当前的bean是不是抽象的

*/

private boolean abstractFlag = false;

/**

  • 判断当前的bean是不是懒加载的

*/

private boolean lazyInit = false;

/**

  • 默认的注入模型是0不支持外部注入

*/

private int autowireMode = AUTOWIRE_NO;

/**

  • 默认的依赖检查模式

*/

private int dependencyCheck = DEPENDENCY_CHECK_NONE;

/**

  • 当前的bean创建 必须要要依赖哪个bean先被创建

*/

@Nullable

private String[] dependsOn;

/**

*设置为false的时候,这样容器在自动装配对象的时候,会

  • 不考虑当前的bean(他不会被看作为其他bean的依赖的bean,但是他依赖其他的bean是能够被自动装配进来的)

*/

private boolean autowireCandidate = true;

/**

*当发生自动装配的时候,假如某个bean会发现多个,那么标注了primary 为true的首先被注入

*/

private boolean primary = false;

复制代码

BeanDefinition也是Spring中的一个重要的核心接口,封装了生产Bean的一切信息;BeanDefinition是通过BeanDefinitionRegistry进行装载的;BeanDefinitionRegistry的装载方法如下:

/**

  • 注册一个Bean定义

  • 支持RootBeanDefinition与ChildBeanDefinition

  • @param beanName 需要注册的bean实例名称

  • @param beanDefinition 需要注册的bean的定义

  • @throws BeanDefinitionStoreException 如果注册的Bean无效或已经存在则抛出这个异常

  • @see GenericBeanDefinition

  • @see RootBeanDefinition

  • @see ChildBeanDefinition

*/

void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

throws BeanDefinitionStoreException;

复制代码

总结:一个类要变成一个Bean,首先要把类变成Bean定义,需要经过:ApplicationContext读取配置类——>根据配置类扫描满足加载条件的类——>将满足条件的类变成Bean定义(BeanDefinition)——>将Bing定义进行注册——>由BeanFactory进行生产——>最后变成我们的Bean;并且ApplicationContext可以调用BeanFactoryPostProcessor修改Bean定义,也可以调用BeanDefinitionRegistryPostProcessor来注册Bean定义;

五、Bean的生命周期

===========

上面已经把我们的Bean装载到IOC中了,那么一个Bean的周期是什么样的?需要经过哪些步骤?带着这些问题我们继续往下看;

5.1)实例化

=======

一般情况下是使用反射的方式进行处理,但是需要注意的是,这里仅仅只是Bean对象的实例化,Bean中的资源还没有解析(例如:@Autowired这些注解类型的资源),这个时候的Bean仅仅只是一个空壳而已;

还可以通过工厂方法来进行处理,我们在配置类中使用@Bean注解标识的方法就是一个工厂方法;

反射是由Spring自身的机制完成实例化的,而工厂方法是用户自己完成的,通俗的讲反射是Spring通过自己的算法来实现的(也就是上面说的没看明白的那个方法),而工厂方法是用户自己控制new方法来完成的,用户拥有初始化的完全掌控权;

我们常用的@Service、@RestController、@Component等都是通过反射的方式来进行实例化的;

反射没有工厂灵活

========

5.2)注入填充属性

==========

在这一步里才是真正的解析@Autowired、@Value等资源,开始将对象的资源进行注入;

在填充的时候会出现循环依赖问题,即:BeanA中@Autowired BeanB,BeanB中@Autowired BeanA。BeanA依赖BeanB,BeanB依赖BeanA,这个时候一个闭环就出现了,这就是循环依赖问题产生的原因,目前Spring已经解决了这个问题,是采用三级缓存来解决的,貌似是采用的标识方法来解决的,当初始化BeanA的时候标识为正在创建,当创建BeanB的时候发现依赖的BeanA被标识为正在创建,那么就会直接退出,从而解决了循环依赖的问题,这部分内容在后面的章节中再进行详细的阐述吧;

5.3)初始化

=======

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
mages/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值