java基础巩固-宇宙第一AiYWM:为了维持生计,Spring全家桶_Part1-1(Spring左膀右臂中的左膀IOC第一篇~全是概念,Spring为啥辣么6)~整起

我Java学的好好的,为什么要学spring框架呀【一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合】?或者说,成天喊简化开发,spring是如何简化开发的?或者说,这样,先瞅瞅Spring的特点(别人为你为啥要用spring,把这些搬出来吓唬一下他)
在这里插入图片描述

  • 也可以说是咱们使用Spring的优点或者说好处是什么,作为上图的补充:
    • Spring 是轻量的,基本的版本大约2MB
    • Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们
    • 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开
    • 容器:Spring 包含并管理应用中bean对象的生命周期和配置
    • MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品
    • 事务管理:Spring 提供一个持续的事务管理接
    • Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。口,可以扩展到上至本地事务下至全局事务(JTA)

通俗的说就是:spring这个哥们大体上就是两个方面,倚天剑与屠龙刀嘛

  • 倚天剑:就是没用框架之前,咱们写代码时,比如controller\service\dao三层,我是不是得先写个接口,然后写个子实现类去实现这个接口,从而实现咱们的功能或者说各层中类之间的调用关系。但是用了spring框架之后,咱们不用自己去写实现类去实现各种接口,只需要在配置好配置文件,哪些是bean呀,是什么类型的bean呀,bean们之间或者叫类们之间有啥互相依赖关系呢【将对象之间的相互依赖关系交给 IoC 容器来管理】,我要通过@Autowired或者@Qualified等注解进行注入【由 IoC 容器完成对象的注入】,然后是要通过byname还是betype等方式拿出来给伸手要调用的那个懒汉呢【相当于当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的】,就可以实现功能了,就算是一种松耦合 【就是没用框架之前咱们很多时候为了实现功能类之间不得不进行相互依赖,使得咱们有时候实现很多不必要的方法或者类,但是用了框架之后咱们就可以用什么时候咱们把什么注入到需要的类中进行调用,相比较而言比之前的依赖程度能低一点了】,很灵活
    • 在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
  • 屠龙刀:另外一点,Spring可以运行对自身进行修改,并且支持 集成比如Mybatis以及Redis等技术或者框架可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好】,感觉整个Spring的生态就很好,很灵活。

或者说,我个人觉得,一个不太贴切的例子,额感觉spring就是一个大型简历模板。咱们大家面试呀、复试呀都会用到简历模板参照参照,网上或者其他地方搞来一个个人使用的那种小模板。而Spring(为了解决企业级应用开发的复杂性而生),额觉的他就像一个大型的简历模板,用来给大型企业使用的一份简历模板。想想咱们个人使用的那种小简历,里面是不是就是一个大杂烩呢,那Spring这份大杂烩简历里面包含了很多内容,你的校园经历呀、你的各项技能呀等,框架给了咱们以后,就是根据自己的实际情况按照简历模板的条条框框向里面填充七十二般变化呗。

  • 首先呢,和咱们之前学东西一样,当时有一句话我自己时常提醒自己:有的东西是自己一遍学不会的,需要不断的重复学习,学习查漏补缺
  • 同时的,到了框架这里,还是有一句话,与诸君共勉:虽然说有说法是有了spring这个框架,我只需要做一些很简单的xml等配置,就可以进行开发并开发出一些东西。但是呀,过犹不及,虽然spring的组件代码是很轻量级的,但是配置多了后,配置就是重量级的了

开唠~
刚开始咱们学了封装继承之后,觉得用个extend就可以实现某个方面的代码复用性,确实很方便。但是很容易出现这样一种情况你想想,愣用继承extends,有啥问题没,点点看
所以呢,就有了咱们基于多态的面向抽象编程或者叫面向接口编程

......
public class UserService{
	//也相当于在UserService类中调UserDao的子实现类(用了多态写法,我可以使用或者说在这里调用UserDao不同的子类呀,不就显示出了面向抽象的方便了吗),就叫做面向抽象(接口)编程,
	UserDao userDao = new UserDaoImpl()
	
	......
	
	public void add(User u){
		this.userDao.save(u)}
}

public UserDaoImpl implements UserDao{

	......
	
	public void save(User u ){
		......
	}
}

public Interface UserDao{
	......
}

这算是一种好的改进吧。但是呢,来个更大更大的简历模板,咱们不就可以继续躺平到更深的层次了嘛,企业级大简历模板Spring就被搬出来了。

当咱们用框架时,那到底框架帮咱们干了啥呢,人家简历还很清晰呢,什么校园经历呀、教育经历呀、技能呀…

  • 我个人觉得,框架就是在咱们上面说的那个面向抽象编程或者叫面向接口编程的一个拔高。
    在这里插入图片描述
    你看,当咱们在UserService中想跨层调用UserDao的某个子实现类UserDaoImplX(X可以为任意的一个正整数)中的抽象方法时(为什么样这么说呢,因为UserDaoImplX中的重写了的方法是重写的人家UserDao接口中的抽象方法,他俩是有一定关系的,UserDao相当于一个大管家,管着手底下众多的子实现类),咱是不是要在UserService中这样玩:
UserDao userDao = new UserDaoImpl()

好了,现在我用了框架之后,我想要一个userDao这个对象引用去调用UserDaoImpl中的方法,我现在不想自己new UserDaoImpl了,反正我要userDao的时候你框架必须搞出来一个塞到我手里,就这。

  • 所以我觉的spring这个框架就做了这件事,不管谁要对象,你要userDao我框架帮你new出来,不用你自己去new,new好之后谁要我框架给谁就行,按需分配。当然澄清两点:
    • 人家spring可不是说只会干着一件事。
      • Spring 包含了多个功能模块,其中最重要的是 Spring-Core(主要提供 IoC 依赖注入功能的支持) 模块, Spring 中的其他模块(比如 Spring MVC)的功能实现基本都需要依赖于该Spring-Core模块
        • 比如Spring-MVC,Spring MVC 是 Spring 中的一个很重要的模块Spring-MVC主要赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,Spring MVC核心思想是通过将业务逻辑、数据、显示分离来组织代码
    • spring框架人家是总领,怎么可能自己事事亲历亲为呢,人家是派自己的左膀右臂中的左膀,也是集团中的二当家,名叫IOC容器的家伙去干这件事的。(后来呢,经过一代又一代的发展,这个userDao这种对象引用被叫做bean,规范化嘛,谁要bean谁就找二当家IOC吱一声
      • 啥是Spring IOC 容器:装载这一堆bean对象的,并帮助我们管理对象们之间的依赖关系,然后在需要时通过依赖注入的方式(也就是咱们之前没有用控制反转时,当咱们需要用到某个对象时,需要咱们自己去new去创建,然后得自己处理对象们之间的继承实现等依赖关系,要是对象很多这种依赖关系维护起来就很麻烦,有了Spring框架提供的控制反转这个概念,我们就可以将对象的创建、配置等操作或者说对象的控制权交给Spring,由Spring容器去管理,咱们要用对象时伸手找Spring要就行了),并将对象进行装载提供给我们的一种容器
        • IoC(Inverse of Control:控制反转) 是一种设计思想【将对象创建(实例化、管理)的权力控制权交给外部环境(Spring 框架、IoC 容器)】,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。不过, IoC 并非 Spring 特有,在其他语言中也有应用。
          • 在实际应用中,Spring通过配置文件(xml或者properties)指定需要实例化的java类(类名的完整字符串),包括这些java类的一组初始化值,通过加载读取配置文件,用Spring提供的方法(getBean())就可以获取到我们想要的根据指定配置进行初始化的实例对象
        • IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象
      • Spring 框架的核心是 Spring IOC容器。容器创建对象并将对象们装配在一起,配置它们并管理它们的完整生命周期。Spring 容器使用依赖注入来管理组成应用程序的组件。容器通过读取提供的配置元数据来接收对象进行实例化,配置和组装的指令。该元数据可以通过 XML,Java 注解或 Java 代码提供。(我们不用自己创建实例对象,这些都交给Spring的bean工厂帮我们创建管理这也是Spring的核心思想,通过面向接口编程的方式来是实现对业务组件的动态依赖
        在这里插入图片描述
        • DI:DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现
          • DI这个依赖注入其实和IOC大致相同,只不过是同一个概念使用了不同的角度去阐述罢了。
            • DI描述的重点在于依赖,IOC的核心功能就是在于,在程序运行时动态的向某个对象提供其他的依赖对象,而这个“在程序运行时动态的向某个对象提供其他的依赖对象”功能就是靠DI去完成的。比如我们需要注入一个对象 A,而这个对象 A 依赖一个对象 B,那么我们就需要把这个对象 B 注入到对象 A 中,这就是依赖(B)注入(到对象A中)
    • 说下面重点内容之前,先看下,你这个容器不可能跟个桶或者岗一样人一提就拿走去盛东西,这个Spring容器很灵活,他可以启动,然后【就是在 ClassPath 中寻找 xml 配置文件,根据 xml 文件内容来构建 ApplicationContext。】自己去按照自己的蓝图(配置信息搞出来的XXXDefinition)去创建bean然后按需分配,那不得先说一下启动相关,创建bean分配bean下面都有,自取…
      • 启动 Spring 容器:
        public static void main(String[] args) {
        	//利用配置文件来启动一个 Spring 容器,其实就是在 ClassPath 中寻找 xml 配置文件,根据 xml 文件内容来构建 ApplicationContext。除了 ClassPathXmlApplicationContext 以外,我们也还有其他构建 ApplicationContext 的方案可供选择,往下翻翻就有哦
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
        }
        
        //咱们一般都是maven项目了,所以可以直接在 dependencies 中加上以下依赖即可,【尽量不要不知道要添加什么依赖,然后把 Spring 的所有相关的东西都加进来,pom.xml配置文件也太乱了】,咱们现在只是启动Spring容器,所以依赖的话,只需要一个spring-context【spring-context 会自动将 spring-core、spring-beans、spring-aop、spring-expression 这几个基础 jar 包带进来。要不说人家这个容器强大呢,这容器里面包了多少东西呢】,如下:
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.3.11.RELEASE</version>
        </dependency>
        
        
    • spring主要提供了两种IOC容器,一种是BeanFactory,还有一种是 ApplicationContext。两者主要的区别就是:BeanFactory只提供了最基本的实例化对象和用getBean()方法拿对象的功能,而ApplicationContext是继承了BeanFactory所派生出来的产物,也就是说ApplicationContext是BeanFactory的子类,ApplicationContext的作用更加强大,比如ApplicationContext支持注解注入、国际化等功能
      在这里插入图片描述
      在这里插入图片描述
      • 两种IOC容器之一:BeanFactory【生产 bean 的工厂或者说 BeanFactory 是 装Bean 容器,负责bean的具体创建、生产和管理各个 bean 实例。(因为ApplicationContext相当于外部容器,具体的bean创建是由BeanFactory进行的),依赖注入和初始化
        在这里插入图片描述
        在这里插入图片描述
        • 而咱们一直念念叨叨的 Spring的bean这个东西就是BeanDefinition,Bean 在代码层面上可以简单认为是 BeanDefinition 的实例【BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。】。 我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition 存在于 Spring 的 BeanFactory 中
          在这里插入图片描述
        • BeanFactory 接口相关的主要的继承结构:
          在这里插入图片描述
          • ApplicationContext 继承了 ListableBeanFactory和 HierarchicalBeanFactory
            • ListableBeanFactory中的这个 Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的
            • HierarchicalBeanFactory表示可以在应用中起多个 BeanFactory,然后可以将各个 BeanFactory 设置为父子关系。
          • AutowireCapableBeanFactory是用来自动装配 Bean 用的,虽然ApplicationContext没有继承这个,但是别忘了不使用继承,不代表不可以使用组合【ApplicationContext 接口定义中的最后一个方法就是 getAutowireCapableBeanFactory()
          • ConfigurableListableBeanFactory继承了第二层所有的三个接口
        • BeanFactory的优缺点:
          • 优点:应用启动的时候占用资源很少,对资源要求较高的应用,比较有优势
          • 缺点:运行速度会相对来说慢一些。而且有可能会出现空指针异常的错误,而且通过Bean工厂创建的Bean生命周期会简单一些
      • 两种IOC容器之一:ApplicationContext【 ApplicationContext 其实就是一个 BeanFactory,但是ApplicationContext 继承自 BeanFactory应该说ApplicationContext 内部持有一个实例化的 BeanFactory(DefaultListableBeanFactory【为什么是这个而不是BeanFactory其他的子类,是因为DefaultListableBeanFactory是ConfigurableListableBeanFactory 唯一的实现类,DefaultListableBeanFactory他爸爸ConfigurableListableBeanFactory 手很长,ConfigurableListableBeanFactory 实现了BeanFactory 下面一层的所有三个接口,这还不手长,人家你祖父BeanFactory的东西都快让你摸完了。DefaultListableBeanFactory除了集成他爸爸的手长特性从而可以多摸BeanFactory的东西之外,实现类 DefaultListableBeanFactory 还通过实现右边的 AbstractAutowireCapableBeanFactory 通吃了右路,相当于BeanFactory的所有子类这个DefaultListableBeanFactory都能触及都能摸到,所以可以说最底下这个家伙 DefaultListableBeanFactory 基本上是最牛的 BeanFactory 了,这也是为什么这边会使用这个类来实例化的原因。】)。以后所有的 BeanFactory 相关的操作其实是委托给这个实例来处理的。
        • Spring通过应用上下文( ApplicationContext)装载bean的定义并把它们组装起来。Spring应用上下文全权负责对象的创建和组装。Spring自带了多种应用上下文的实现或者说多种容器的实现,它们之间主要的区别仅仅在于 如何加载配置
          在这里插入图片描述
        • ApplicationContext的三个实现类:【虽然下面结构是这样,但是因为 Spring 为了适应各种使用场景,提供的各个接口都可能有很多的实现类。对于我们来说,就是揪着一个完整的分支看完。】
          在这里插入图片描述
          • XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息。
          • FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。FileSystemXmlApplicationContext 的构造函数需要一个 xml 配置文件在系统中的路径,意思就是这个货你得给他传入一个路径这货才能正常工作,正所谓工欲善其事,必先利其器,其他和 ClassPathXmlApplicationContext 基本上一样。
          • ClassPathXmlApplicationContext:把上下文文件当成类路径资源。告诉spring,你必须按照我这个application-xxx.xml配置文件中的配置信息给我创建出来一个合适的spring容器,然后我就可以通过getBean等方法从这个合适的spring容器中取出我要的bean对象来,从而就可以调用你这个对象中的方法了嘛
            UserService//先模拟出来一个接口
            public interface UserService{
                String sayHello();
            }
            
            //再模拟一个接口的实现类
            public class UserServiceImpl implements UserService{
                public String sayHello(){
                    return "hello , 2035的hhb&ywm"
                }
            }
            
            //那下来要在resources目录下新建一个叫做application-xxx.xml配置文件,然后让spring把咱们定义的bean扫描到容器中,按需分配
            <?xml version="1.0" encoding="UTF-8" ?>
            <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.springframework.org/schema/beans"
                   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">
            
                <bean id="userService" class="com.hhbywm.example.UserServiceImpl"/>
            </beans>
            
            //然后模拟一下容器启动,在main中运行一下
            public class App {
                public static void main(String[] args) {
                    // 用我们的配置文件来启动一个 ApplicationContext。ApplicationContext 启动过程中,会负责创建实例 Bean,往各个 Bean 中注入依赖等。
                    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application-xxx.xml");
                    System.out.println("context 启动成功");
                    // 从context容器中取出我们的Bean,而不是用new UserServiceImpl()这种方式了
                    UserService userService = context.getBean(UserService.class);
                    /**
                    * 当然也可以使用注解,比如像下面,这就是ApplicationContext的不同实现类,启动容器的写法也有所不同
                    * AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
                    * UserService userService = applicat ionContext.getBean( name:"userService", UserService.class);
                    */
                    
                    // 这句将输出: hello , 2035的hhb&ywm
                    System.out.println(userService.sayHello());
                }
            }
            
            • getBean(beanName) 方法我们经常用来 从 BeanFactory 中获取一个 Bean,而初始化的过程也封装到了这个方法里。已经初始化过了的bean就从容器中直接返回,否则就先初始化这个bean再返回这个bean。
          • AnnotationConfigApplicationContext :AnnotationConfigApplicationContext 是基于注解来使用的,它不需要配置文件,采用 java 配置类和各种注解来配置,是比较简单的方式,也是大势所趋吧。
            • AnnotationConfigApplicationContext:我们此时给spring提的创建容器的要求不是xml配置文件了而是一个java类,这个java类中是通过注解(@bean、@ComponentScan、@Configuration…)等形式配置的你spring创建容器时需要遵守的要求。
              在这里插入图片描述
        • ApplicationContext的优缺点
          • 优点:所有的Bean在启动的时候都进行了加载,系统运行的速度快;在系统启动的时候,可以发现系统中的配置问题
          • 缺点:把费时的操作放到系统启动中完成,所有的对象都可以预加载,缺点就是内存占用较大
        • ApplicationContext的refresh流程
          • 所有的ApplicationContext都有一个共同的父类就是org.springframework.context.support.AbstractApplicationContext, 这个AbstractApplicationContext抽象类中有一个方法就是refresh,然后这个抽象类有一个咱们经常用的ClassPathXmlApplicationContext,ClassPathXmlApplicationContext的一个有参构造中…
            在这里插入图片描述
          • refresh()方法中有12个步骤:【 ApplicationContext 建立起来以后可以通过调用 refresh() 这个方法重建的,refresh() 会将原来的旧的 ApplicationContext 销毁,然后再重新执行一次初始化操作。】
            @Override
            public void refresh() throws BeansException, IllegalStateException {
               // 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
               synchronized (this.startupShutdownMonitor) {
            
                  // 第一步:做好准备工作,也就是创建和准备了一个Environment,为spring容器的后续运行准备一些键值信息,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
                  prepareRefresh();
             
                  //第二步:进一步创建新的或者说获取已经有的BeanFactory对象【此时BeanFactory对象和上一步的Environment对象是一个组合的关系】,这步完成后,配置文件就会解析成一个个 Bean 定义,然后将beanName与beanDefinition的map集合关系注册到 BeanFactory 中【注册也只是将这些信息都保存到了注册中心,其实就是一个 beanName<-> beanDefinition 的 map映射)】
                  // 当然,这里将会初始化 BeanFactory、加载 Bean、注册 Bean 等等,这步结束后,Bean 并没有完成初始化,Bean 实例并未在这一步生成,只是配置信息都提取出来了
                  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            
                  // 第三步:设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
                  prepareBeanFactory(beanFactory);
            
                  try {
                     // 【Bean 如果实现了BeanFactoryPostProcessor 接口,那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】
                     // 第四步:具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事,相当于这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化【调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法】
                     postProcessBeanFactory(beanFactory);
                     //第五步: 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 回调方法
                     invokeBeanFactoryPostProcessors(beanFactory);
            
                     // 注册 BeanPostProcessor 的实现类,BeanFactoryPostProcessor有一定的BeanPostProcessor  区别
                     // BeanPostProcessor接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
                     // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
                     //第六步:
                     registerBeanPostProcessors(beanFactory);
            
                     // 第七步:初始化当前 ApplicationContext 的 MessageSource,添加国际化这个功能
                     initMessageSource();
            
                     // 第八步:初始化当前 ApplicationContext 的事件广播器,用来发事件,第10步的registerListeners用于收事件
                     initApplicationEventMulticaster();
            
                     // 第九步:这一个方法就相当于是留给子类扩展的一个空实现,具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前),从方法名就可以知道,典型的模板方法(钩子方法)
                     onRefresh();
            
                     // 第十步:注册事件监听器,监听器需要实现 ApplicationListener 接口,用来收事件,第八步的initApplicationEventMulticaster用来发事件。
                     registerListeners();
            
                     // 第十一步:初始化所有非延迟的单例bean。【延迟bean指的是在第一次调用时才会创建实例的bean】,在创建每个非延迟bean的实例的过程中就会返回在第六步准备好的bean后处理器的作用,执行这些bean后处理器的各个扩展功能,对bean进行扩展。
                     finishBeanFactoryInitialization(beanFactory);
            
                     // 第十二步:广播事件,ApplicationContext 初始化完成。在ApplicationContext中创建一个lifecycleProcessor生命周期处理器(不是指初始化、销毁这些,这里的生命周期指的是启动停止等)。发布事件发布器发布的ContextRefreshed事件。
                     finishRefresh();
                  }
            
                  catch (BeansException ex) {
                     if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                              "cancelling refresh attempt: " + ex);
                     }
            
                     // Destroy already created singletons to avoid dangling resources.
                     // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
                     destroyBeans();
            
                     // Reset 'active' flag.
                     cancelRefresh(ex);
            
                     // 把异常往外抛
                     throw ex;
                  }
            
                  finally {
                     // Reset common introspection caches in Spring's core, since we
                     // might not ever need metadata for singleton beans anymore...
                     resetCommonCaches();
                  }
               }
            }
            
            在这里插入图片描述
            • 1.prepareRefresh():做好准备工作,也就是创建和准备了一个Environment,为spring容器的后续运行准备一些键值信息【这个Environment就是为咱们的spring容器后期的运行**提供并管理一些键值信息**,JAVA_HOME\jdk自带配置\从properties中读取键值配置信息】,在咱们后续值注入时不是有个@Value嘛,那这个注解的值从哪里来呀,不就是从Environment中来的嘛,也就是说Enviroment对象的作用之一就是读取系统的键值信息和咱们自定义的配置文件中的键值信息为后续的@Value注解在值注入时提供键值
              在这里插入图片描述
              在这里插入图片描述
              在这里插入图片描述

            • 2.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory() :进一步创建新的或者说获取已经有的BeanFactory对象【此时BeanFactory对象和上一步的Environment对象是一个组合的关系】,BeanFactory的作用看上面。(obtainFreshBeanFactory这步执行完成后,配置文件就会解析成一个个 Bean 定义,然后将beanName与beanDefinition的map集合关系注册到 BeanFactory 中【注册也只是将这些信息都保存到了注册中心,其实就是一个 beanName<-> beanDefinition 的 map映射)】当然,这里将会初始化 BeanFactory、加载 Bean、注册 Bean 等等,这步结束后,Bean 并没有完成初始化,Bean 实例并未在这一步生成,只是配置信息都提取出来了)【2-6都是在完善咱们的BeanFactory
              在这里插入图片描述
              在这里插入图片描述
              在这里插入图片描述
              在这里插入图片描述
              在这个loadBeanDefinitions类中就是用刚刚初始化的 XmlBeanDefinitionReader 开始来加载 xml 配置,每个文件可以看作是一个 resource,最后统计返回总共加载了多少的 BeanDefinition。 然后在doRegisterBeanDefinitions(root)方法中从 xml 根节点开始解析文件,用一个 ThreadLocal 来存放配置文件资源,之后将 xml 文件转换为 Document 对象,最终要 把xml配置文件转换成为一颗DOM树
              在doRegisterBeanDefinitions(Element root)方法中,首先BeanDefinitionParserDelegate parent = this.delegate;获得这个负责解析 Bean 定义的BeanDefinitionParserDelegate。 因为 内部是可以定义 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 节点。另外,在doRegisterBeanDefinitions(Element root)方法中有个parseBeanDefinitions(root, this.delegate);这块就涉及到了咱们default namespace 涉及到的就四个标签 、、 和 ,其他的属于 custom 的,比如我们经常会使用到的 、、、等。【如果需要使用上面这些 ”非 default“ 标签,那么上面的 xml 头部的地方也要引入相应的 namespace 和 .xsd 文件的路径,并且源码中需要提供相应的 parser 或者说XxxNamespaceHandle这个处理类来解析,如 Mvc NamespaceHandler、Task NamespaceHandler、Context NamespaceHandler、Aop NamespaceHandler 等。比如咱们要是碰到 <dubbo /> 这种标签,那么就应该搜一搜是不是有 DubboNamespaceHandler 这个处理类。】。而这个doRegisterBeanDefinitions(Element root)方法中就是来解析 default namespace 下面的几个元素和解析其他 namespace 的元素【命名空间嘛,就是XML文件最上面写的那些http://…,比如http://www.springframework.org/schema/beans】
              咱们可以看看咱们经常见到的的处理过程:processBeanDefinition 解析 标签:
              在这里插入图片描述
              在processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)方法中首先会将 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中【BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);】,那究竟是怎么解析 bean 元素,是怎么转换到 BeanDefinitionHolder 的呢?或者说将 节点转换为 BeanDefinitionHolder
              就是这样的:将 name 属性的定义按照 “逗号、分号、空格” 切分,形成一个别名列表数组, 如果没有指定id, 那么用别名列表的第一个名字作为beanName, 根据 <bean …>… 中的配置创建 BeanDefinition,然后把配置中的信息都设置到实例中,直到执行到这一句:AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);就说明一个 BeanDefinition 实例就出来了。也就是说到这里,整个 标签就算解析结束了,一个 BeanDefinition 就形成了。
              如果咱们已经根据一个 标签产生了一个 BeanDefinitionHolder 的实例【这个实例里面也就是一个 BeanDefinition 的实例和它的 beanName、aliases 这三个信息
              在这里插入图片描述
              当然,这个过程中最重要的还是咱们的BeanDefinition。咱们做的说实话不就是 初始化了 Bean 容器,<bean /> 配置也相应的转换为了一个个 BeanDefinition,然后注册了各个 BeanDefinition 到注册中心,并且发送了注册事件
              在这里插入图片描述

              • BeanFactory中有一个BeanDefinition对象,这个BeanFinition对象作为bean的设计蓝图,规定了bean的特征,比如单例多例、依赖关系、初始销毁方法等【BeanDefinition的来源有很多种,可以通过XML获得、也可以通过配置类获得、也可以通过组件扫描获得、也可以是编程添加】
                在这里插入图片描述
                在这里插入图片描述
            • 3.prepareBeanFactory(beanFactory);这一步其实就是准备BeanFactory的成员变量或者说属性,也就是这几个属性(beanExpressionResolver,EL表达式的解析器【用来解析EL表达式】、propertyEditorRegistrars【注释类型转换器,并用ApplicationContext提供的Environment完成${}解析】、resolvableDependencies【特殊bean指beanFactory以及ApplicationContext,通过registerResolvableDependency来注册这两个特殊bean】、内置的beanPostProcessors【用来解析Aware接口】)
              在这里插入图片描述

              • Spring 把我们在 xml 配置的 bean 都注册以后,会"手动"注册一些特殊的 bean。Spring 也很 “智能” 就是因为它会帮我们默认注册一些有用的 bean,我们也可以选择覆盖
                在这里插入图片描述
            • 4.postProcessBeanFactory:是一个空实现,主要就是留着给子类进行实现,从事对BeanFactory这些spring容器进行扩展。相当于子类你可以对BeanFactory进行扩展
              在这里插入图片描述

            • 5.invokeBeanFactoryPostProcessors:但是咱们实际使用中不可能每次都搞个子类去实现postProcessBeanFactory方法对容器进行扩展,而是通过这种后置处理器、前置处理器对容器的功能进行扩展。

              • org.springframework.context.annotation.ConfigurationClassPostProcessor:这个后处理器可以帮助我们解析配置类相关的一些注解,比如@Configuration、@Bean、@PropertySource等注解,这些注解都是在这一步由这个beanFactory的后处理器进行解析的。
                在这里插入图片描述
              • Bean的后处理器和beanFactory这个bean工厂的后处理器是不一样的,第五步中的beanFactory的后处理器是来解析那些注解的。bean后处理器是在bean的创建过程中,也就是第十一步中通过调用finishBeanFactoryInitialization进行初始化单例bean并执行bean的后处理器的扩展这个过程中,对bean进行的增强。
            • 6.registerBeanPostProcessors:BeanPostProcessors这个和上面的对BeanFactory增强不一样,这个是在bean的创建过程中对bean进行增强的【bean后处理器,充当bean的扩展点,可以工作在bean的实例化、依赖注入、初始化阶段】,但是这一步只是找出来有哪些bean后处理器加入到集合中,但是这些后处理器发挥作用是在bean创建过程中的常见的bean后处理器有下面三个

              • AutowiredAnnotationBeanPostProcessor. class【AutowiredAnnotationBeanPostProcessor功能有:解析@Autowired, @Value注解】
              • CommonAnnotationBeanPostProcessor. class【CommonAnnotationBeanPostProcessor功能有:解析@Resource, @PostConstruct, @PreDestroy】
              • AnnotationAwareAspectJAutoProxyCreator. class【AnriotationAwareAspectJAutoProxyCreator功能有:为符合切点的目标bean自动创建代理】
            • 7.initMessageSource:给MessageSource赋值,主要就是添加国际化这个功能的,【BeanFactory这个父类是相当于一个内部容器,那这种外交型功能他肯定是没有的了。所以7以后是ApplicationContext对他的父类BeanFactory的一些扩展,比如这一步就是扩展了一个国际化功能】这个也肯定是先在beanFactoryMap中找,看有没有MessageSource,如果有则把找到的这个作为MessageSource接口的实现,如果没有则创建一个空的MessageSource实现
              在这里插入图片描述

            • 8.initApplicationEventMulticaster:添加事件广播(发布)器,用来发事件,第10步的registerListeners用于收事件。这个也肯定是先在beanFactoryMap中找,看有没有ApplicationEventMulticaster,如果有则把找到的这个作为ApplicationEventMulticaster接口的实现,如果没有则创建一个默认的ApplicationEventMulticaster实现
              在这里插入图片描述

              • 可以调用ApplicationContext.publishEvent(事件对象)来发布事件
            • 9.onRefresh:是一个空实现,留给子类扩展。SpringBoot其实就是扩展了这个onRefresh方法(SpringBoot中的子类可以在这里准备WebServer,也就是内嵌Web容器),从而实现了内嵌服务器,比如Tomcat

            • 10.。registerListeners:添加事件监听器,用来收事件,第八步的initApplicationEventMulticaster用来发事件。

              • 一部分监听器是事先编程添加的、另一部分监听器来自容器中的bean、还有一部分来自于@EventListener的解析
              • 实现ApplicationListener接口,重写其中onApplicationEvent(E R)方法即可
            • 11.finishBeanFactoryInitialization:【finishBeanFactoryInitialization这个巨头会负责初始化所有的 singleton beans】初始化所有非延迟的单例bean【延迟bean指的是在第一次调用时才会创建实例的bean】,在创建每个非延迟bean的实例的过程中就会返回在第六步准备好的bean后处理器的作用,执行这些bean后处理器的各个扩展功能,对bean进行扩展。

              • 类型转换:用两个方法结合,共同完成类型转换。【conversionService也是一套转换机制,作为对PropertyEditor的补充】
                在这里插入图片描述
              • 解析 $ 这个占位符符号的【内嵌值解析器用来解析@Value中的${},借用的是Environment的功能】:之前第一步,咱们Enviroment对象的作用之一就是读取系统的键值信息和咱们自定义的配置文件中的键值信息为后续的@Value注解在值注入时提供键值,这一步其实就是做了一层封装,间接调用这个Environment对象来实现功能而已
              • singletonObjects:singletonObjects就是单例池,单例池用来缓存所有单例对象,对象的创建都分三个阶段,每一阶段都有不同的bean后处理器参与进来扩展功能 【BeanFactory先找到所有的非延迟的单例对象(延迟对象指的是第一次用到时才会去创建),然后根据beanDefinition的信息,看是单例呀?有没有初始化方法呀?有没有依赖注入呀?把bean的实例对象创建出来放到singletonObjects:singletonObjects这个单例池中,然后别人再从单例池中byName或者byType去拿,创建过程中beanPostProcessors这些后置处理器或者叫增强器就会通过切面、切点等对bean对象进行增强】
            • 12.finishRefresh:在ApplicationContext中创建一个lifecycleProcessor生命周期处理器(不是指初始化、销毁这些,这里的生命周期指的是启动停止等)。发布事件发布器发布的ContextRefreshed事件。
              在这里插入图片描述

              • 用来控制容器内需要生命周期管理的bean
              • 如果容器中有名称为lifecycleProcessor的bean就用它,否则创建默认的生命周期管理器
              • 调用context的start,即可触发所有实现LifeCycle接口bean的start
              • 调用context的stop, 即可触发所有实现L ifeCycle接口bean的stop
      • BeanFactory 和 ApplicationContext的区别:
        • BeanFactory 是Spring IOC容器,是用来承载对象的
        • FactoryBean是一个接口,为Bean提供了更加灵活的方式,通过代理一个Bean对象对方法前后做一些操作。
          在这里插入图片描述
      • 其实看了上面那两种spring容器,也没什么大不了的嘛,咱们也可以自己写一个DemoByHuApplicationContext,然后里面提供好有参无参构造方法,然后在给你对外提供一个getBean方法给你返回一个bean对象,不就完了
        在这里插入图片描述
        在这里插入图片描述
        • 你想呀,咱们上面定义的胡式容器你想实例化出来然后调里面的getBean方法搞来自己的一个对象【当然,你搞出来胡氏容器之后,你想要调用容器的getBean方法搞出bean,那此时咱们自己手写你不得写些类来模拟一下,这些类,你也知道spring中必须用四个注解中@Component、@Controller、@Service、@Repository的一个标识出这个类是哪一层的,那你是不是得模仿下面的@ComponentScan在写一个标识这个类是哪一层的注解,然后才能放在这个类上;并且如果咱们要指定一个类的作用于的话,那你是不是还得模仿着写一个@Scope(“prototype”)出来呀】,那你是不是第一步得按照构造方法传入一个参数实例化出来这个胡式容器,那你看咱们要传入的这个参数是个啥呀,不就是一个全身充斥着配置信息的配置类嘛。
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          • 其实spring中实现,比如prototype、singleton这种,就是搞了个单例池map<beanName, bean对象>,通过传入的beanName可以得到一个bean对象,当然了,这个单利池的实现肯定是在咱们的容器DemoByApplicationConterxt中实现的嘛
            在这里插入图片描述
          • 其实说到底,你spring去解析,不就是去解析带了某个注解【比如标识你是哪一层的四个注解之一、标识作用欲的等等】的java类嘛,spring解析类之后,其实就是生成了一个beanDefinition
            在这里插入图片描述
            在这里插入图片描述
            你要是判断是单例bean,你可以从单例池中拿或者放到池子中,玩这种操作,但是如果是prototype,那你是不是就得自己创建bean对象呀
            在这里插入图片描述
        • 那再回过头来看看,咱自己写不能说像上面那样就在有参构造中this.configClass = configClass,你光写个这能成功才怪,你写了注解类AppConfig传进来之后,之前是人家spring帮咱们解析,现在是咱们自己手写,那你不把解析配置类的代码写一下进行解析一下,能行?
          • 咱们RPC那里也有过对一个类进行解析,但是那里是通过反射,将类名、方法名、方法中的参数类型以及参数列表解析出来。而咱们spring这里肯定是解析类上面的各种spring提供的注解之类的,本地调用嘛。
            在这里插入图片描述
            在这里插入图片描述
        • 继续往下说,咱们得到了路径之后,咱们是怎样得到这个路径下或者说组件扫描扫到的包里面的各个类的呢?----->这里肯定是通过类加载器实现的呀。扫到类之后看看扫到的类上写没写标识你是哪个层的四个注解某一个,写了说明你可以作为spring中的一个bean,然后不就说明已经得到这个bean了嘛。
          在这里插入图片描述
          在这里插入图片描述
        • 但是人家正儿八经的组件扫描中的包路径人家是com.xxx.xxx这种,一是很短,而是中间是.不是/,这你不处理一下:
          在这里插入图片描述
      • 那先把@ComponentScan(com.xxx…)中写的要被组件扫描的包路径中的各个类扫进来之后,在通过类加载器加载进来,不就可以得到这个类了嘛,得到类了咱们不就可以根据Class类得到bean对象去玩了嘛。
      • 此外,如果咱们要指定一个类的作用于的话,那你是不是还得模仿着写一个@Scope(“prototype”)出来呀
        在这里插入图片描述
        • 除了上面无尽的解析【解析过程就是,识别哪些类上面有哪些注解呀(因为咱们现在是通过注解方式而不是xml配置文件方式,所以就都是注解),我需要按你注解的不同标识,把你们这帮类加载到我spring的容器中,然后其中具体解析过程就是上面,自己看。解析完了后就可以按照getBean得到bean对象,你去使用就完了嘛】过程。你看咱们之前用spring框架时,除了上面关于标识属于哪个层的四个注解以及标识作用域的注解,还有就是这个@Autowired:
          • 当然,这个注解咱们要和上面一样,自己定义呗:
            在这里插入图片描述
          • 你光写个下面这个依赖注入,就成了?肯定不是嘛,之前有人家spring帮咱们实现底层原理,但是咱们现在模仿spring的话,你肯定@Autowired底层的原理你也得自己写呀:@Autowired,去spring容器中byname找到一个bean对象,然后赋值给@Autowirde注解的属性或者叫对象
            在这里插入图片描述
            在这里插入图片描述
        • 除了上面关于标识属于哪个层的四个注解、标识作用域的注解以及这个@Autowired,还有就是:
  • spring 提供了哪些配置方式?【我感觉配置方式就是,咱们作为工程师,客户给咱们提的需求说,你给我造的这个篮子呀,材料必须...,里面必须...,外面必须...,然后客户说,你得把你的设计方案发给我我得先看看,我说,那给你发邮件还是发微信还是发QQ,就相当于咱们展示给Spring这个客户的设计方案一样,你,Spring,必须按照我XML配置文件\注解\JavaAPI配置中的配置信息,帮我造一个装满bean的容器(篮子)出来
    • 基于 xml 配置:bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签开头
    • 基于注解配置:可以通过在相关的类,方法或字段声明上使用注解,将 bean 配置为组件类本身,而不是使用 XML 来描述 bean 装配。默认情况下,Spring 容器中未打开注解装配。因此,您需要在使用它之前在 Spring 配置文件中配置注解驱动以及组件扫描等启用它
    • 基于 Java API 配置:Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现
      • @Bean 注解扮演与 <bean /> 元素相同的角色
        • 标签中可以定义哪些属性:
          在这里插入图片描述
          在这里插入图片描述
      • @Configuration 类允许通过简单地 调用同一个类中 的其他 @Bean 方法来定义 bean 间依赖关系
        在这里插入图片描述
        • @Configuration一般用来声明配置类可以使用 @Component注解替代,不过使用@Configuration注解声明配置类更加语义化

人家二当家IOC很聪明,给门口放了个厚厚的记事本,叫做“XML配置文件”(xml用来管理bean(就是在xml配置文件中写一个又一个bean))。你们哪个使用框架的顾客想要一个bean,来在记事本子上登记bean的详细信息

  • 要哪种类型的bean呀,class=xxx.xxx.xxx…
    • 举个例子,如果我想要一个鸟类,那我是不是class = com.xxx…Bird,也可以说我要一个鸟那种类型的bean
  • 你要的bean叫啥名字呀,id =…
    • 当然啦,哪个客户把自己想要的那个bean的信息登记的越详细,哪个客户要的这个bean中人家有什么属性什么方法什么引用等,IOC这个二当家肯定能更快的找到呀,这是当然呀。比如咱们贴寻人启示,这个丢的人 姓甚名谁,几点几分穿什么颜色衣服到过哪…肯定警察叔叔能帮咱们更快找到呀。
      在这里插入图片描述

登记好了之后,人家IOC这个二当家会在每次开店门(初始化)时先读取记事本(读取配置文件),然后按照登记信息找到各种各样的bean放到篮子里,谁要给谁就行
在这里插入图片描述

  • 整天说(自动)装配,什么是 spring 装配?也就是什么叫把bean们放进篮子里
    • 当 bean 们在 Spring 容器中组合在一起时,它被称为装配或 bean 装配(Spring 容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起,同时装配 bean)【将bean放入或者说放进Spring容器中有哪些方式?】
      在这里插入图片描述
      • @Configuration + @Bean
        • @Configuration用来声明一个配置类,然后使用 @Bean 注解,用于声明一个bean,将其加入到Spring容器中
          在这里插入图片描述
      • @Componet + @ComponentScan
        • @Componet中文译为组件,放在类名上面,然后@ComponentScan放置在我们的配置类上【之前咱们用XML方式写代码时不是在配置文件中也配置过组件扫描、静态资源加载等项嘛】,然后可以指定一个路径,进行扫描带有@Componet注解的bean,然后加至容器中。
          @Component
          public class Person{
          	private String name;
          	
          	//get/set/toString等方法
          }
          
          @ComponentScan(basePackage = "com.xxx.ooo.*")
          public class Demo1{
          	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
          	Person bean = applicationContext.getBean(Person.class);
          	System.out.println(bean);
          }
          
          //结果输出:Person{name='null'}。表示成功将Person放置在了IOC容器中
          
      • @Import注解导入:
        • 在进行Spring扩展时经常会用到@Import注解,它经常搭配自定义注解进行使用,然后往容器中导入一个配置文件
          在这里插入图片描述
        • @Import注解有四种使用方式:
          • @Import直接导入类:我们的Person类上 就不需要任何的注解了,直接导入即可。使用@Import导入了一个类,然后自动的就被放置在IOC容器中了。
            public class Person{
            	private String name;
            	
            	//get/set/toString等方法
            }
            
            @Import(Person.class)
            public class Demo1{
            	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
            	Person bean = applicationContext.getBean(Person.class);
            	System.out.println(bean);
            }
            
            //结果输出:Person{name='null'}。表示成功将Person放置在了IOC容器中
            
          • @Import + ImportSelector:实现一个ImportSelector的接口,然后实现其中的方法,进行导入。自定义了一个 MyImportSelector 实现了 ImportSelector 接口,重写selectImports 方法,然后将我们要导入的类的全限定名写在里面即可
            @Import(MyImportSelector.class)
            public class Demo1 {
             
                public static void main(String[] args) {
                    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
                    Person bean = applicationContext.getBean(Person.class);
                    System.out.println(bean);
                }
            }
             
            class MyImportSelector implements ImportSelector {
                @Override
                public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                    return new String[]{"com.springboot.pojo.Person"};
                }
            }
            
          • @Import + ImportBeanDefinitionRegistrar:需要我们实现 ImportBeanDefinitionRegistrar 接口中的方法。需要去实现接口,然后进行导入。先有bean的元数据BeanDefinition【BeanDefinition也是需要放在IOC容器中进行管理的】,applicationContext再根据bean的元数据去创建Bean
            @Import(MyImportBeanDefinitionRegistrar.class)
            public class Demo1 {
             
                public static void main(String[] args) {
                    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
                    Person bean = applicationContext.getBean(Person.class);
                    System.out.println(bean);
                }
            }
             
            class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
             
                @Override
                public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                    // 构建一个beanDefinition, 关于beanDefinition我后续会介绍,可以简单理解为bean的定义.
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
                    // 将beanDefinition注册到Ioc容器中.
                    registry.registerBeanDefinition("person", beanDefinition);
                }
            }
            
          • @Import + DeferredImportSelector:DeferredImportSelector 它是 ImportSelector 的子接口,所以实现的方法和第二种无异。只是Spring的处理方式不同,它和Spring Boot中的自动导入配置文件 延迟导入有关。当然它还可以搭配@Configuration注解使用,用于导入一个配置类。
            @Import(MyDeferredImportSelector.class)
            public class Demo1 {
                public static void main(String[] args) {
                    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
                    Person bean = applicationContext.getBean(Person.class);
                    System.out.println(bean);
                }
            }
            class MyDeferredImportSelector implements DeferredImportSelector {
                @Override
                public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                    // 也是直接将Person的全限定名放进去
                    return new String[]{Person.class.getName()};
                }
            }
            
      • 使用FactoryBean接口
        • FactoryBean接口和BeanFactory从名字其实可以大概的区分开,FactoryBean, 后缀为bean,那么它其实就是一个bean, BeanFactory,顾名思义 bean工厂,它是IOC容器的顶级接口,这俩接口都很重要。
          在这里插入图片描述
      • 使用 BeanDefinitionRegistryPostProcessor:这种方式也是利用到了 BeanDefinitionRegistry,在Spring容器启动的时候会执行 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法。大概意思就是等beanDefinition加载完毕之后,对beanDefinition进行后置处理,可以在此进行调整IOC容器中的beanDefinition,从而干扰到后面进行初始化bean
        public class Demo1 {
            public static void main(String[] args) {
                AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
                MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
                applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
                applicationContext.refresh();
                Person bean = applicationContext.getBean(Person.class);
                System.out.println(bean);
            }
        }
         
        class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
         
            @Override
            public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
                //手动向beanDefinitionRegistry中注册了person的BeanDefinition
                registry.registerBeanDefinition("person", beanDefinition);
            }
            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
         
            }
        }
        
    • Spring 容器能够自动装配 bean。也就是说,可以通过检查 BeanFactory 的内容让 Spring 自动解析 bean 的协作者自动装配的不同模式有如下四种:【当 bean 在 Spring 容器中组合在一起时,它被称为装配或 bean 装配。 Spring 容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起,同时装配 bean,可以通过检查 BeanFactory 的内容让 Spring 自动解析 bean 的协作者。】
      • no 这是默认设置,表示没有自动装配。应使用显式 bean 引用进行装配
      • byName - 它根据 bean 的名称注入对象依赖项。它匹配并装配其属性与 XML 文件中由相同名称定义的 bean
      • byType - 它根据类型注入对象依赖项。如果属性的类型与 XML 文件中的一个 bean 名称匹配,则匹配并装配属性
      • 构造函数 - 它通过调用类的构造函数来注入依赖项。它有大量的参数
      • autodetect - 首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配
  • 自动装配有什么局限?
    • 覆盖的可能性 - 您始终可以使用 和 设置指定依赖项,这将覆盖自动装配
    • 基本元数据类型 - 简单属性(如原数据类型,字符串和类)无法自动装配
    • 令人困惑的性质 - 总是喜欢使用明确的装配,因为自动装配不太精确

此时,有顾客燥了,找上门来说,你把所有的bean都放在一个篮子中,你IOC帮我们找到再分给我们的时候肯定慢呀,我不管,我要自己独占一个篮子。这IOC人家也不是傻子(心里想,你不给钱,一点点钱还要这要那,能的你),各位客官稍安勿躁,我们肯定不是这样干的啦。请看具体过程:
我们主要的过程分为两步:

  • 第一步:把bean放到IOC的篮子里(自动装配:咱们不用创建对象,只要在配置文件中描述如何创建它们以及哪些组件需要哪些服务。由 IoC 容器将它们装配在一起。spring会在上下文中自动寻找,然后自动给bean装配属性,也叫做 spring中的依赖注入可以通过三种方式完成
    在这里插入图片描述
    • 依赖注入可以通过三种方式完成,三种注入方式的比较
      • 接口注入。从注入方式的使用上来说,接口注入是现在不甚提倡的一种方式,基本处于“退役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。而构造方法注入和setter方法注入则不需要如此。
      • 构造方法注入。
        • 这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用
        • 缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在Java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。
        • 构造函数(方法)注入和 setter方法注入的区别:
          在这里插入图片描述
      • setter方法注入。 因为方法可以命名,所以setter方法注入在描述性上要比构造方法注入好一些。另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。
        • 缺点当然就是 对象无法在构造完成后马上进入就绪状态。综上所述,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使用最多的注入方式:而接口注入因为侵入性较强,近年来已经不流行了。
          在这里插入图片描述
    • 第一种放bean(注入)进篮子里的方式(set方法注入)的补充
      在这里插入图片描述
      在这里插入图片描述
    • 第二种放bean(注入bean)进篮子里的方式(构造方法(两种常用的实现方式))的补充
      根据下标去赋值
      根据下标去赋值
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 第三种放bean(注入bean)进篮子里的方式(p命名空间注入:对应的是set方法注入)的补充
      在这里插入图片描述
      在这里插入图片描述
    • 第四种放bean(注入bean)进篮子里的方式(c命名空间注入:对应的是构造器注入,所以你肯定得有构造函数)的补充
      在这里插入图片描述
  • 第二步:把篮子里的bean拿去给你们顾客
    在这里插入图片描述
    在这里插入图片描述

两个过程瞅完了后,再回顾一下,人家二当家给门口放的那个记事本:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

上面IOC二当家底下的小兵们不乐意了,光考虑顾客们的意见也不知道减轻一下我们拾捡bean到篮子里这些人员的负担。IOC作为二当家察觉到了小的们脸上的不满。
对着小的们说到,咱们最近引进了一批自动化拾捡手臂,你们只需要给不同种类的bean贴上常见原生的标签注解),自动化手臂就会帮你们去把bean拾到篮子里,你们就可以躺着刷会手机了。这不美了嘛。大家先看看下面的说明书学习一下哈:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • IOC手底下的小兵们 注入bean的注解 有哪些?
    • Spring的bean的注入方式有哪些 ?Spring注入bean的注解都有哪些:一般我们用配置文件或者注解类标识成可用于 @Autowired 、@Resource、@Inject等注解自动装配的 bean 的类,然后使用@Autowired 、@Resource、@Inject注解自动装配 bean【具体标识用的四个注解见下面】,那咱们不先看看,把被标识之后的bean进行自动装配的几个注解的详细情况???【下面这三个都可以都可以用于注入 Bean】
      在这里插入图片描述
      在这里插入图片描述
      • Spring内置的 org.springframework.bean.factory包下的@Autowired
        • @Autowired是spring的注解【,有人伸手要类咯,赶紧让Spring帮咱们自动导入或者叫装配bean对象到类中,被注入进的类同样要被 Spring 容器管理】, 是spring2.5版本引入的, @Autowired只根据type进行注入【@Autowired默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)】不会去匹配name。
          在这里插入图片描述
          • 当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写) 如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰。【建议通过 @Qualifier 注解来显示指定名称而不是依赖变量的名称。】
      • JDK 内置的javax.下的 @Resource 和 @Inject
        • @Resource
          • @Resource是Java自己的注解,@Resource默认注入方式为 byName。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType
          • @Resource有两个属性是比较重要的,分是name和typeSpring将@Resource 注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType 自动注入策略如果既不指定name也不指定type 属性,这时将通过反射机制使用byName自动注入策略
            在这里插入图片描述
      • @Autowired和@Resource注解的区别:@Autowired 和 @Resource 都是用来实现依赖注入的注解(在 Spring/Spring Boot 项目中),但二者却有着 5 点不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。
        在这里插入图片描述
        在这里插入图片描述
        • Autowired 属于 Spring 内置的注解,Autowired 默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)
          • 当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。。【建议通过 @Qualifier 注解来显示指定名称而不是依赖变量的名称。】
            在这里插入图片描述
        • @Resource属于 JDK 提供的注解,默认注入方式为 byName。如**果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType**。
          在这里插入图片描述
          • 如果仅指定 name 属性则注入方式为byName,如果仅指定type属性则注入方式为byType,如果同时指定name 和type属性(不建议这么做)则注入方式为byType+byName
            在这里插入图片描述
    • 将一个类声明【把类标识成可用于 @Autowired 等注解自动装配的 bean 的类】为 Bean 的注解有这四个:(一般使用 @Autowired 等注解自动装配 bean,要想把类标识成可用于 @Autowired 等注解自动装配的 bean 的类,采用以下注解可实现标识(也就是说,你哪个类想要能被人家@Autowired等注解自动装配(也就是当作一个bean塞到SpringIOC容器中,然后谁要拿出去给谁),你就得用下面四个注解写在自己类上面,表示,我是,哪一层下的bean哦,controller层bean、service层bean、dao层bean呢,不知道是哪一层就用@Component))。【这四个注解的本质都是一样的,只是为了在使用上区分不同的bean的应用分层】
      • @Component :通用的注解可标注任意类为 Spring 组件如果一个Bean不知道属于哪个层,可以使用@Component 注解标注
        • @Component 和 @Bean 的区别:
          • @Component 注解作用于类,而@Bean注解作用于方法
          • @Component通常是通过类路径扫描自动扫描出以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中 找出标识了需要装配的类并自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该@Bean注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
            在这里插入图片描述
          • @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现
            @Configuration
            public class AppConfig {
                @Bean
                public TransferService transferService() {
                    return new TransferServiceImpl();
                }
            
            }
            
            
            /*
            *上面通过@Bean配置的的效果就相当于下面通过xml配置
            */
            <beans>
            	<bean id = "transferService" class = "com.xxx.TransferServiceImpl"/>
            <beans>
            
            在这里插入图片描述
      • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作
      • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层
      • @Controller : 对应 Spring MVC 控制层主要用户接受用户请求并调用 Service 层返回数据给前端页面
        • @Controller 注解有什么用:@Controller 注解标记一个类为 Spring Web MVC 控制器 ControllerSpring MVC 会将扫描到该注解的类,然后扫描这个类下面带有 @RequestMapping 注解的方法,根据注解信息,为这个方法生成一个对应的处理器对象,而这个处理器对象就是SpringMVC中咱们HandlerAdapter帮咱们适配到的Handler处理器Controller
          • 当然,除了添加 @Controller 注解这种方式以外,你还可以实现 Spring MVC 提供的 Controller 或者 HttpRequestHandler 接口,对应的实现类也会被作为一个处理器对象
          • @RequestMapping 注解有什么用?
            • 配置处理器的 HTTP 请求方法,URI等信息,这样才能将请求和方法进行映射。这个注解可以作用于类上面,也可以作用于方法上面,在类上面一般是配置这个控制器的 URI 前缀
              在这里插入图片描述
        • 全局处理 Controller 层异常。比如咱们现在如果方法参数不对的话就会抛出MethodArgumentNotValidException,我们来处理这个异常
          @ControllerAdvice
          @ResponseBody
          public class GlobalExceptionHandler {
          
              /**
               * 请求参数异常处理
               */
              @ExceptionHandler(MethodArgumentNotValidException.class)
              public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
                 ......
              }
          }
          
        • @RestController注解是@Controller和@ResponseBody的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,或者说controller层返回的是一个字符串【返回 JSON 或 XML 形式数据】而不是一个页面名称,这也就是 REST 风格的控制器
          • 单独使用 @Controller 不加 @ResponseBody的话一般是用在要返回一个视图的情况
          • @Controller +@ResponseBody 返回 JSON 或 XML 形式数据。当然,返回什么样的数据格式,根据客户端的 ACCEPT 请求头来决定。
          • 用于前后端传值的注解:
            在这里插入图片描述
            • @PathVariable用于获取路径参数
              • @PathVariable和@RequestParam两个注解都 用于方法参数,获取参数值的方式不同,@RequestParam 注解的参数从请求携带的参数中获取,而 @PathVariable 注解从请求的 URI 中获取
            • @RequestParam用于获取查询参数
            • @RequestBody:用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会 自动将数据绑定到 Java 对象上去系统会使用HttpMessageConverter或者自定义的HttpMessageConverter将请求的 body 中的 json 字符串转换为 java 对象
              在这里插入图片描述
              • 当我们需要返回JSON字符串时,可以使用 @ResponseBody 注解,或者使用包含 @ResponseBody 注解的 @RestController 注解。当然,还是需要配合相应的支持 JSON 格式化的 HttpMessageConverter 实现类。例如,Spring MVC 默认使用 MappingJackson2HttpMessageConverter
              • 一个请求方法只可以有一个@RequestBody,但是可以有多个@RequestParam和@PathVariable。 如果你的方法必须要用两个 @RequestBody来接受数据的话,大概率是你的数据库设计或者系统设计出问题了

二当家IOC说,终于把眼前的困难

  • 关于顾客的,把bean放进篮子里从篮子里拿出来给顾客,这个困难算是告一段落了。
  • 关于自己手底下人的,搞了个自动化手臂,也算是告一段落了。
    但是,毕竟xml配置文件这个东西不是咱java自己亲生的呀(咱们学基础时只会写java类呀方法呀属性呀等等),哪见过什么配置文件啥的。所以为了未来考虑,IOC决定留一手(事实证明确实留对了,为springboot做了很好的铺垫)—使用java(写类写方法)的方式配置Spring
    在这里插入图片描述
    具体的未来设计蓝图说明书如下:
    在这里插入图片描述
    在这里插入图片描述
    除此之外呢,为了集团市场的良好发展以及稳定性,二当家IOC决定制定一些政策:
  • 规定篮子里面的bean们不能无限制的放到篮子里,每个bean必须有自己的存放时长
    • Spring 中的 bean 的作用域有哪些?:(Spring bean 支持 5 种 scope,仅当用户使用支持 Web 的 ApplicationContext 时,最后三个才可用
      在这里插入图片描述
      • singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的
        • 如果一个xxXxx是一个单例bean,那么通过相同的名字去getBean时拿到的就是同一个对象
          • 每个 Bean 在 Spring 容器中都有一个唯一的名字(beanName)和 0 个或多个别名(aliases)。我们从 Spring 容器中获取 Bean 的时候,可以根据 beanName,也可以通过别名。beanFactory.getBean(“beanName or alias”);
            • 比如,<bean id="messageService" name="m1, m2, m3" class="com.xxx.example.MessageServiceImpl">表示配置的结果就是:beanName 为 messageService,别名有 3 个,分别为 m1、m2、m3
            • 比如,<bean name="m1, m2, m3" class="com.xxx.example.MessageServiceImpl" />表示配置的结果就是:beanName 为 m1,别名有 2 个,分别为 m2、m3
            • 比如,<bean class="com.xxx.example.MessageServiceImpl">表示beanName 为:com.xxx.example.MessageServiceImpl#0,别名 1 个,为: com.javadoop.example.MessageServiceImpl
            • 比如,<bean id="messageService" class="com.javadoop.example.MessageServiceImpl">表示配置的结果就是:beanName 为 messageService,没有别名
        • 实现原理和单例模式没啥关系,搞一个Map就行,键值对中的键是bean的名字,值就是这个bean对象就行了。这个Map其实就是单例池
        • 单例 Bean 的线程安全问题:单例 Bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。【不过,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。】。常见的有两种解决办法:
          • 在 Bean 中尽量避免定义可变的成员变量。
          • 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)
      • prototype : 每次请求都会创建一个新的 bean 实例
        • 如果一个xxXxx是一个prototype原型bean,那么通过相同的名字去getBean时拿到的就是不同的对象
        • 常见的 HTTP 请求类型
          • GET :请求从服务器获取特定资源。举个例子:GET /users(获取所有学生)
            • @GetMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.GET)
            • @GetMappinghe和@RequestMapping的区别:
              • @RequestMapping:可注解在类和方法上;@GetMapping 仅可注册在方法上
              • @RequestMapping:可进行 GET、POST、PUT、DELETE 等请求方法;@GetMapping 是 @RequestMapping 的 GET 请求方法的特例,目的是为了提高清晰度
          • POST :在服务器上创建一个新的资源。举个例子:POST /users(创建学生)
            • @PostMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.POST)
          • PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:PUT /users/12(更新编号为 12 的学生)
            • @PutMapping("/users/{userId}") 等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.PUT)
          • DELETE :从服务器删除特定的资源。举个例子:DELETE /users/12(删除编号为 12 的学生)
            • @DeleteMapping(“/users/{userId}”)等价于@RequestMapping(value=“/users/{userId}”,method=RequestMethod.DELETE)
          • PATCH :更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少。我们都是 PUT 不够用了之后才用 PATCH 请求去更新数据。
      • request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效
      • session : :在一个HTTP Session中,一个Bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效【每一次来自新 session 的 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。】
      • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean
      • application/global-session (仅 Web 应用可用): 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。 每个 Web 应用在启动时创建一个 Bean(应用 Bean),,该 bean 仅在当前应用启动时间内有效。【Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话】
        • Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。在 global session 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范围内。如果你在 web 中使用 global session 作用域来标识 bean,那么 web 会自动当成 session 类型来使用
          在这里插入图片描述
        • Spring5.x 版本中 Web 模块的 Portlet 组件已经被废弃掉,同时增加了用于异步响应式处理的 WebFlux 组件
    • Spring中作用域的配置方式:
      • 基于xml方式:<bean id="..." class="..." scope="singleton"></bean>
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
      • 基于注解方式:@Scope(“singleton”),声明 Spring Bean 的作用域
        在这里插入图片描述
        在这里插入图片描述
  • Spring 中的 bean 生命周期(和bean scope不一样):Bean的生命周期是由容器来管理的。主要在创建销毁两个时期
    在这里插入图片描述
    • Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:
      • Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法
      • Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法
      • 容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
        • BeanPostProcessor接口包括2个方法postProcessAfterInitialization和postProcessBeforeInitialization,这两个方法的第一个参数都是要处理的Bean对象,第二个参数都是Bean的name。返回值也都是要处理的Bean对象
        • InstantiationAwareBeanPostProcessor 接口本质是BeanPostProcessor的子接口,一般我们继承Spring为其提供的适配器类InstantiationAwareBeanPostProcessor Adapter来使用它
      • 工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。
        在这里插入图片描述
    • 第一步:首先创建spring容器然后开启spring容器
      在这里插入图片描述
    • 第二步就是,Spring 的Bean容器找到配置文件中关于Bean的定义信息,也就是根据那四个标识这个类是属于哪一层的注解,来利用 Java Reflection API 创建一个 Bean 的实例。大部分时候实例化出来这个bean对象和最后依赖注入完成被别人调用得到的bean对象是一个bean对象。但有一种特殊情况下【就是通过AOP增强了,但是其实AOP底层也是调用那个BeanPostProcess接口实现的增强】创建出来的bean对象和拿出来的bean对象不是一个bean对象。
      在这里插入图片描述
      在这里插入图片描述
      • 这种特殊情况就是:当中间过程用了AOP之后,那么最后被调用的bean对象就不是spring实例化出来的那个bean对象,而是代理对象:
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        就是得判断一下你这个类是不是已经被AOP过了,如果你本身就是切面、切点、通知等,那说明你就不需要进行AOP了。
        在这里插入图片描述
        在这里插入图片描述
        如果你这个类有切点在你这个类中的某个方法上,那说明你这个类需要进行AOP,那我spring在第二步是不是实例化时就需要先进行AOP
        在这里插入图片描述
        那具体过程就是,我得先找出spring中哪些bean是切面,找出这些切面bean之后缓存起来,后期直接从这个缓存中拿切面bean就行。
        然后找出哪些类的方法上有pointcut切点,不管是以注解形式还是配置文件形式,给找出来,就说明这些类是需要进行AOP的。然后才是通过AOP生成代理对象,这个具体过程也就是缓存切面类以及寻找切点pointcut这两件事。
        在这里插入图片描述
    • 第三步:接着判断一下你这个类或者对象中有没有属性需要进行属性填充或者说依赖注入,需要的话就利用 set()方法设置Bean的属性【或者说用依赖注入的三种方法中的某一种都行】,属性填充或者叫依赖注入
      在这里插入图片描述
      • 第三步之一:也就是在这个populateBean中真正处理@Autowired、@Resource这些依赖注入功能的
        在这里插入图片描述
        在这里插入图片描述
      • 第三步之二:如果通过Aware接口声明了依赖关系,根据 其实现的Aware接口(主要是BeanFactoryAware接口,BeanFactoryAware,ApplicationContextAware)设置依赖信息。【如果通过Aware接口声明了依赖关系,则会注入Bean对容器基础设施层面的依赖Aware接口是为了感知到自身的一些属性。容器管理的Bean一般不需要知道容器的状态和直接使用容器。但是在某些情况下是需要在Bean中对IOC容器进行操作的。这时候需要在bean中设置对容器的感知。SpringIOC容器也提供了该功能,它是通过特定的Aware接口来完成的。】
        在这里插入图片描述
        在这里插入图片描述
        • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字
          • 【说白了,BeanNameAware就是spring给咱们提供的用来设置bean的名字的接口,人家spring也很人性化,不能说对你们程序员来说完全就是黑箱,这也不太好,至少这种修改bean的名字的操作我还是可以做的,虽然你是个框架】
            • BeanNameAware接口,可以知道自己在容器中的名字。 如果这个Bean已经实现了BeanFactoryAware接口,可以用这个方式来获取其它Bean
              在这里插入图片描述
            • Aware回调
              在这里插入图片描述
              在这里插入图片描述
        • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
        • 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
        • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法
      • 第三步之三:如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,调用执行BeanPostProcessor 对象的前置初始化方法postProcessBeforeInitialization() 方法完成“在Spring完成实例化之后,初始化之前,对Spring容器实例化的Bean添加自定义的处理逻辑。”的增强,操作具体如下:
        • 就是在初始化之前我先调用BeanPostProcess接口中的前置初始化方法在初始化之前像AOP那样做一些增强之类的:这些方法的调用有点类似AOP。【除了上面那个设置bean的名字的接口,人家spring也提供了这个InitializingBean,实现对bean的一些初始化逻辑的执行
          在这里插入图片描述
          • 咱们调用BeanPostProcess的postProcessBeforeInitialization方法,完成initial前的自定义逻辑;【你有上面那两个接口,也就是spring很友好,提供了两个接口,说你可以修改bean的名字,以及对bean进行一些初始化的操作那我程序元如果想你spring实例化出来这个bean对象之后我程序员想做点啥操作,那你spring能不能再友好一点,给我再提供一个什么接口呀,有,就是这个BeanPostProcess接口】【调用BeanPostProcess的前置初始化方法postProcessBeforeInitialization,主要作用是在Spring完成实例化之后,初始化之前,对Spring容器实例化的Bean添加自定义的处理逻辑。有点类似于AOP】
            在这里插入图片描述
            在这里插入图片描述在这里插入图片描述
            在这里插入图片描述
        • InitializingBean和BeanPostProcess有一个共同的实现类就是BeanValidationPostProcessor
          在这里插入图片描述
          在这里插入图片描述
      • 第三步之四:如果实现了BeanFactoryPostProcessor接口的afterPropertiesSet方法做一些属性被设定后的自定义的事情
        • 这一步也可以通过InitializingBean接口的afterPropertiesSet方法来检验一下之前的属性填充或者说依赖注入有没有什么错误。
          在这里插入图片描述
    • 第四步:调用Bean自身定义的init方法,去做一些初始化相关的工作【属性填充或者叫依赖注入完了之后spring调用InitializingBean的afterPropertiesSet方法进行初始化,那肯定spring人家得先看看你这个类有没有提前实现InitializingBean这个接口,实现了我spring就调用InitializingBean的afterPropertiesSet方法进行初始化】:
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      • 初始化 Bean 的回调有四种方法:
        在这里插入图片描述
      • 先调用Bean自身定义的init方法,去做一些初始化相关的工作;
        • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
          • 这个init-method是Bean自身的方法:Bean自身的方法包括了Bean本身调用的方法和 通过配置文件中<bean>的init-method和destroy-method指定的方法
      • 然后,如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象的 后置初始化方法,执行postProcessAfterInitialization() 方法去做一些 bean初始化之后的自定义工作
    • 完成以上创建之后,也就是第一步到第四步之后,就可以在应用里使用这个Bean了
    • 第五步:销毁过程:当Bean不再用到,便要销毁 (如果实现了DisposableBean的destroy方法,则调用destroy方法,如果实现了自定义的销毁方法,比如若配置了destry-method属性,则会调用其配置的销毁方法)
      • 销毁 Bean 的回调:
        在这里插入图片描述
      • 若实现了DisposableBean接口,则会调用destroy方法【当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
      • 若配置了destry-method属性,则会调用其配置的销毁方法【当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。】
  • 同时,为了对顾客的生命安全进行保障,放bean的这个篮子的制作材料必须进行一定的规定,(springIOC容器)
    //web.xml文件是用来初始化配置信息,加载spring以及spring-mvc的配置文件,进行初始化,只有加载了歇着配置信息的配置文件才能使用框架或者中间件的配置呀
    web.xml里面配置:
    <context-param>
    	<param-name>contextClass</param-name>
    	<param-value>org.springframework.web.context.support.XmlWebApplicationContext
    	</param-value>
    </context-param>
    <context-param>
    	<param-name>contextConfigLocation</param-name>
    	<param-value>classpath*:/applicationContext*.xml</param-value>
    </context-param>
    
    • web.xml 配置文件:web.xml主要用来配置Filter,Listener,Servlet等。但是web.xml并不是必须的,一个web工程可以没有web.xml文件。web容器的加载顺序ServletContext -> context-param -> listener -> filter -> servlet

ps:补充一些小知识:什么是 spring 的内部bean?

  • 只有将 bean 用作另一个 bean 的属性时,才能将 bean 声明为内部 bean(内部 bean 总是匿名的,它们总是作为原型)
    • 为了定义bean,Spring的基于XML的配置元数据在< property>或< constructor-arg> 中提供了< bean> 元素的使用
      在这里插入图片描述
  • Spring中出现同名bean怎么办?
    • 同一个配置文件内同名的Bean,以最上面定义的为准
    • 不同配置文件中存在同名Bean,后解析的配置文件会覆盖先解析的配置文件
    • 同文件中ComponentScan和@Bean出现同名Bean。同文件下@Bean的会生效,@ComponentScan扫描进来不会生效。通过@ComponentScan扫描进来的优先级是最低的,原因就是它扫描进来的Bean定义是最先被注册的(后解析的配置文件会覆盖先解析的配置文件)
      • 默认情况下,allowBeanDefinitionOverriding 属性为 null。如果在同一配置文件中 Bean id 或 name 重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。可是有些时候我们希望在系统启动的过程中就严格杜绝发生 Bean 覆盖,因为万一出现这种情况,会增加我们排查问题的成本
  • Spring 怎么解决循环依赖问题(循环依赖就是说两个对象相互依赖,形成了一个环形的调用链路【如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,循环依赖只有在单例情况下才会产生】)?
    在这里插入图片描述
    • Spring容器循环依赖包括构造器循环依赖和setter循环依赖
    • spring对循环依赖的处理或者说解决有三种情况【spring解决循环依赖有两个前提条件:1.不全是构造器方式的循环依赖(否则无法分离初始化和实例化的操作)、2.必须是单例(否则无法保证是同一对象)】
      • 构造器的循环依赖:这种通过构造器注入构成的循环依赖spring是处理不了的,直接抛出BeanCurrentlylnCreationException异常
        • Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符在创建过程中将一直保持在这个池中,因此如果在创建bean过程中发现自己已经在“当前创建bean池”里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从“当前创建bean池”中清除
      • 单例模式下的setter循环依赖:spring通过“三级缓存”处理循环依赖核心逻辑就是把实例化和初始化的步骤分开【对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter属性注入)的bean来完成的,而且只能解决单例作用域的bean循环依赖,通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean】,然后放入缓存中,供另一个对象调用
        在这里插入图片描述
        换句话说,Spring处理循环依赖的解决办法就是在B中创建依赖A时通过ObjectFactory提供的实例化方法来中断A中的属性填充,使B中持有的A仅仅是刚刚初始化并没有填充任何属性的A,而真正初始化A的步骤还是在最开始创建A的时候进行的,但是因为A与B中的A所表示的属性地址是一样的,所以在A中创建好的属性填充自然可以通过B中的A获取,这样就解决了循环依赖的问题
        在这里插入图片描述
        • 第一级缓存:用来保存实例化、初始化都完成的对象
        • 第二级缓存:用来保存实例化完成,但是未初始化完成的对象
        • 第三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象
        • 为什么要使用三级缓存,二级缓存不能解决吗?
          • 可以,三级缓存的功能是只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象。如果使用二级缓存解决循环依赖,意味着所有 Bean 在实例化后就要完成 AOP 代理,这样违背了 Spring 设计的原则,Spring 在设计之初就是在 Bean 生命周期的最后一步来完成 AOP 代理,而不是在实例化后就立马进行 AOP 代理
          • Spring的单例对象的初始化主要分为三步:(从单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步,也就是构造器循环依赖和field循环依赖。)
            在这里插入图片描述
            • createBeanInstance:此方法的目的就是实例化我们指定的类,其实也就是调用对象的构造方法实例化对象(构造器循环依赖)
            • populateBean:该方法负责进行属性设值,处理依赖。这一步主要是多bean的依赖属性进行填充(field循环依赖)
            • initializeBean:调用spring xml中的init 方法,属性注入完成后,这一步其实就是处理各种回调了在这里插入图片描述
      • 非单例循环依赖:无法处理。比如对于“prototype”作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存“prototype”作用域的bean,因此无法提前暴露一个创建中的bean
  • Spring 中的单例 bean 的线程安全问题?
    • 多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程 会并发执行 该请求对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对单例状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题线程安全问题都是由全局变量及静态变量引起的,因为线程安全问题都是共享两个字害的,而全局变量和静态变量正好沾上了共享两个字的边
      • 若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的
      • 若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全.
    • 有状态bean:是非线程安全的。有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态
      • 有状态的Bean在多线程环境下不安全,适合用Prototype原型模式
      • Spring使用ThreadLocal解决线程安全问题。如果你的Bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全 。
      • 例子:
        在这里插入图片描述
        • 当咱们实际开发时:
          在这里插入图片描述
        • 这一个点非常容易忽略。开发基类的人将基类设计为有状态的,但并不知道子类是怎么使用基类的;而开发子类的人,没多想就直接标记了 @Service,让类成为了 Bean,通过 @Autowired 注解来注入这个服务但这样设置后,有状态的基类就可能产生内存泄露或线程安全问题
          在这里插入图片描述
        • 正确的方式是,在为类标记上 @Service 注解把类型交由容器管理前,首先评估一下类是否有状态,然后为 Bean 设置合适的 Scope也就是为 SayHello 和 SayBye 两个类都标记了 @Scope 注解,设置了 PROTOTYPE 的生命周期,也就是多例@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        • 但,上线后还是出现了内存泄漏,证明修改是无效的。从日志可以看到,第一次调用和第二次调用的时候,SayBye 对象都是 4c0bfe9e,SayHello 也是一样的问题。从日志第 7 到 10 行还可以看到,第二次调用后 List 的元素个数变为了 2,说明父类 SayService 维护的 List 在不断增长,不断调用必然出现 OOM:这就引出了单例的 Bean 如何注入 Prototype 的 Bean 这个问题
        • Controller 标记了 @RestController 注解,而 @RestController 注解 =@Controller 注解 +@ResponseBody 注解,又因为 @Controller 标记了 @Component 元注解,所以 @RestController 注解其实也是一个 Spring Bean。Bean 默认是单例的,所以单例的 Controller 注入的 Service 也是一次性创建的,即使 Service 本身标识了 prototype 的范围也没用。
          在这里插入图片描述
        • 修复方式有两种
          • 让 Service 以代理方式注入。这样虽然 Controller 本身是单例的,但每次都能从代理获取 Service。这样一来,prototype 范围的配置才能真正生效@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)。通过日志可以确认这种修复方式有效:
          • 如果不希望走代理的话还有一种方式是,每次直接从 ApplicationContext 中获取 Bean
            在这里插入图片描述
    • 无状态bean:无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的
      • 在spring中无状态的Bean适合用不变模式,就是单例模式,这样可以共享实例提高性能
  • spring 中有哪些核心模块?
    在这里插入图片描述
    在这里插入图片描述
    • Core Container
      • Spring Core:Spring核心工具类,SpringCore是Spring框架最基础的部分,向外或者说向框架的使用者提供IOC和依赖注入DI特性
      • spring-beans :提供对 bean 的创建、配置和管理等功能的支持。
      • spring-expression :提供对表达式语言(Spring Expression Language) SpEL 的支持,只依赖于 core 模块,不依赖于其他模块,可以单独使用
      • Context(Spring Context):Spring上下文容器,Spring Context是BeanFactory功能加强的一个子接口。
    • Web (Spring Web):Spring Web提供Web应用开发的支持
      • spring-web :对 Web 功能的实现提供一些最基础的支持。
      • spring-webmvc : 提供对 Spring-MVC 的实现。
      • spring-websocket : 提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。
      • spring-webflux :提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步。
        #Messaging
    • Spring-MVC:Spring-MVC针对Web应用中MVC思想的实现
    • Spring Dao:提供对JDBC抽象层,简化了JDBC编码,同时使得编码更具有健壮性
    • Data Access/Integration
      • (Spring ORM):它支持用于流行的ORM框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO的整合等
      • spring-jdbc :提供了对数据库访问的抽象 JDBC。不同的数据库都有自己独立的 API 用于操作数据库,而 Java 程序只需要和 JDBC API 交互,这样就屏蔽了数据库的影响。
      • spring-tx :提供对事务的支持。
      • spring-oxm :提供一个抽象层支撑 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。
      • spring-jms : 消息服务。自 Spring Framework 4.1 以后,它还提供了对 spring-messaging 模块的继承。
    • AOP (Spring AOP):即面向切面编程,它提供了与AOP联盟兼容的编程实现
      • spring-aspects :该模块为与 AspectJ 的集成提供支持。
      • spring-aop :提供了面向切面的编程实现。
      • spring-instrument :提供了为 JVM 添加代理(agent)的功能。 具体来讲,它为 Tomcat 提供了一个织入代理,能够为 Tomcat 传递类文 件,就像这些文件是被类加载器加载的一样,这个模块的使用场景非常有限
    • Spring Test
      • Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。
      • Spring 的测试模块对 JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final, static, private 方法)等等常用的测试框架支持的都比较好

巨人的肩膀:
https://www.bilibili.com/video/BV1PE411i7CV?p=60
https://www.javalearn.cn/
https://javaguide.cn/
https://javadoop.com/post/
Spring源码深度解析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值