前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring框架知识梳理

Spring框架知识梳理

原创
作者头像
hhss
修改2021-02-14 15:22:02
1.5K0
修改2021-02-14 15:22:02
举报

Spring设计理念:面向Bean编程!类似于Java面向对象编程。

spring源码解析:https://segmentfault.com/u/h2pl/articles?page=7(蚂蚁大佬的文章)

一:Spring的组成模块

https://segmentfault.com/a/1190000011503081(Spring4.0)

clipboard.png
clipboard.png

spring的核心组件(骨骼架构)—— 共同创建了Bean关系网络

  • Core:主要定义了资源的访问方式
  • Context:给spring提供一个运行时的环境
  • Bean:Bean的定义,创建以及解析

spring的特性组件(特性功能)

  • Web:web模块提供对常见框架的支持以及管理
  • AOP:提供了拦截器机制
  • DAO:对JDBC进行封装,spring允许JDBC使用spring资源,并统一管理JDBC事务
  • Instrumentation: Spring的Instrumentation模块提供了为JVM添加代理(agent)的功能。具体来讲,它为Tomcat提供了一个织入代理,能够为Tomcat传递类文件,就像这些文件是被类加载器加载的一样。如果这听起来有点难以理解,不必对此过于担心。
  • test: Spring提供了测试模块以致力于Spring应用的测试。 通过该模块,你会发现Spring为使用JNDI、Servlet和Portlet编写单元测试提供了一系列的mock对象实现。对于集成测试,该模块为加载Spring应用上下文中的bean集合以及与Spring上下文中的bean进行交互提供了支持。

二:IOC

2.1 三大组件:

Beans:包装Object;其中Bean Factory是容器核心,本质是“工厂设计模式”的实现,而且无需编程实现“单例设计模式”,单例完全由容器控制,而且提倡面向接口编程,而非面向实现编程;所有应用程序对象及对象间关系由框架管理,从而真正把你从程序逻辑中把维护对象之间的依赖关系提取出来,所有这些依赖关系都由BeanFactory(顶层接口来维护。

Core:建立Bean之间关系的一系列工具。

Context:Bean关系的集合,也就是IOC容器;以Core和Beans为基础,集成Beans模块功能并添加资源绑定、数据验证、国际化、Java EE支持、容器生命周期、事件传播等;核心接口是ApplicationContext

问题:BeanFactory和ApplicationContext的区别

https://blog.csdn.net/pythias_/article/details/82752881

https://blog.csdn.net/pseudonym_/article/details/72826059

2.2 IOC容器的技术剖析

IOC中最基本的技术就是“反射(Reflection)”编程,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才被决定到底是哪一种对象。只是在Spring中要生产的对象都在配置文件中给出定义,目的就是提高灵活性和可维护性。

我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

IOC容器的加载过程:

1.资源定位(ResourceLoader其中的getResource()方法,可以将外部的资源,读取为Resource类)

2.解析(DefaultBeanDefinitionDocumentReader完成主要解析,最终解析的结果都封装为BeanDefinitionHolder)

3.注册 (BeanFactory里完成,beanDefinitionMap是一个ConcurrentHashMap。key:beanName;value:BeanDefinition)

4.实例化 (在BeanFactory的getBean()方法之中,会完成初始化)-->实例化具体过程详见下面

问题:依赖为什么不是在加载的时候,就直接注入呢?

因为加载的顺序不同,很可能 Bean_A 依赖的 Bean_B 还没有加载好,也就无法从容器中获取,你不能要求用户把 Bean 的加载顺序排列好,这是不人道的。

所以,Spring 将其分为了依赖注入 2 个步骤:

  1. 加载所有的 Bean 配置成 BeanDefinition 到容器中,如果 Bean 有依赖关系,则使用占位符暂时代替。(把上面的123放在一起了);
  2. 然后,在调用 getBean 的时候,进行真正的依赖注入,即如果碰到了属性是 ref 的(占位符),那么就从容器里获取这个 Bean,然后注入到实例中(因此产生了递归) —— 称之为依赖注入。

可以看到,依赖注入实际上,只需要 “低级容器” 就可以实现。

2.3 IOC的好处:

IOC(控制反转)就是依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。

这么做的目的是把程序业务代码与事物(组件、POJO类)代码进行分离,程序有关事物的创建、属性和依赖对象的注入、以及生命周期交由容器进行加载和管理。

业务代码只需从容器中获取组件或POJO实例对象即可,无需再考虑组件之间、组件与POJO之间的依赖关系以及属性的注入。

2.4 IOC使用到的工厂模式:

https://www.cnblogs.com/yssjun/p/11102162.html

https://www.cnblogs.com/baizhanshi/p/8318087.html

还设计到单例模式,适配器模式。

附:一篇讲解IOC很好的博客:https://www.jianshu.com/p/17b66e6390fd

三:bean的生命周期、作用域

https://www.jianshu.com/p/e71818303971

https://www.cnblogs.com/hthuang/p/7807844.html

bean的实例化过程:

四:AOP

https://www.cnblogs.com/vingLiu/p/12052096.html

一篇讲解很好的博客:https://www.jianshu.com/p/e18fd44964eb

4.1 面向切面编程:

可以将 AOP 分成 2 个部分来扯,哦,不,来分析。。。 第一:代理的创建; 第二:代理的调用。

注意:我们尽量少贴代码,尽量用文字叙述,因为面试的时候,也是文字叙述,不可能让你把代码翻出来的。。。所以,这里需要保持一定的简洁。

代码位置:com.interface21.aop 包下。

开始分析(扯):

1.代理的创建(按步骤):

  • 首先,需要创建代理工厂,代理工厂需要 3 个重要的信息:拦截器数组,目标对象接口数组,目标对象。
  • 创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。
  • 当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。
  • 注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。

2.代理的调用

  • 当对代理对象进行调用时,就会触发外层拦截器。
  • 外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。
  • 当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回。

Spring AOP实现原理:默认是应用JDK动态代理实现AOP功能,当代理对象为接口时使用JDK动态代理,否则使用ObjenesisCglibAopProxy(CGLIB)实现。

4.2 CGLIB动态代理与JDK动态代理区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

4.3 Spring中分别在什么情况下使用这两个代理?

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

如果目标对象实现了接口,可以强制使用CGLIB实现AOP

如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

2、JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。 因为是继承,所以该类或方法不要声明成final ,final可以阻止继承和多态。

3、CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适

4.4 Spring AOP使用了哪些设计模式:

代理模式:https://www.cnblogs.com/daniels/p/8242592.html(动态代理和cglib代理,搞懂这个很重要)

责任链模式:https://www.cnblogs.com/fengyumeng/p/10839570.html

五:SpringMVC的工作流程

https://blog.csdn.net/u014191220/article/details/81387596

https://www.jianshu.com/p/8a20c547e245

https://blog.csdn.net/floating_dreaming/article/details/89089214

  1. 用户发起请求到前端控制器(DispatcherServlet),该控制器会过滤出哪些请求可以访问Servlet、哪些不能访问。就是url-pattern的作用,并且会加载springmvc.xml配置文件。
  2. 前端控制器会找到处理器映射器(HandlerMapping),通过HandlerMapping完成url到controller映射的组件,简单来说,就是将在springmvc.xml中配置的或者注解的url与对应的处理类找到并进行存储,用map<url,handler>这样的方式来存储。
  3. HandlerMapping有了映射关系,并且找到url对应的处理器,HandlerMapping就会将其处理器(Handler)返回,在返回前,会加上很多拦截器。
  4. DispatcherServlet拿到Handler后,找到HandlerAdapter(处理器适配器),通过它来访问处理器,并执行处理器。
  5. 执行处理器
  6. 处理器会返回一个ModelAndView对象给HandlerAdapter
  7. 通过HandlerAdapter将ModelAndView对象返回给前端控制器(DispatcherServlet)
  8. 前端控制器请求视图解析器(ViewResolver)去进行视图解析,根据逻辑视图名解析成真正的视图(jsp),其实就是将ModelAndView对象中存放视图的名称进行查找,找到对应的页面形成视图对象
  9. 返回视图对象到前端控制器。
  10. 视图渲染,就是将ModelAndView对象中的数据放到request域中,用来让页面加载数据的。
  11. 通过第8步,通过名称找到了对应的页面,通过第10步,request域中有了所需要的数据,那么就能够进行视图渲染了。最后将其返回即可。

segment大神的文章:https://segmentfault.com/u/h2pl/articles?page=5

补充1:如何解决循环依赖;

https://blog.csdn.net/qq_36381855/article/details/79752689

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }

这里就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。

这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。

知道了这个原理时候,肯定就知道为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

补充2:spring声明式事务的实现原理;

Spring的事务机制包括声明式事务(@Transactional)和编程式事务(TransactionTemplate):

编程式事务:Spring推荐使用TransactionTemplate,实际开发中使用声明式事务较多。

声明式事务管理:将我们从复杂的事务处理中解脱出来,获取连接,关闭连接、事务提交、回滚、异常处理等这些操作都不用我们处理了,Spring都会帮我们处理。声明式事务管理使用了AOP面向切面编程实现的,本质就是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务。

https://blog.csdn.net/jie_liang/article/details/77600742

声明式事务原理(AOP) https://my.oschina.net/zhangxufeng/blog/1935556#h3_3

Transaction
Transaction

用于实现Spring事务的Advisor,Pointcut以及Advice都已经找到了。关于这三个类的具体作用,我们这里进行整体的上的讲解,深入其内部讲解其是如何进行bean的过滤以及事务逻辑的织入的。

  • BeanFactoryTransactionAttributeSourceAdvisor:封装了实现事务所需的所有属性,包括Pointcut,Advice,TransactionManager以及一些其他的在Transactional注解中声明的属性;
  • TransactionAttributeSourcePointcut:用于判断哪些bean需要织入当前的事务逻辑。这里可想而知,其判断的基本逻辑就是判断其方法或类声明上有没有使用@Transactional注解,如果使用了就是需要织入事务逻辑的bean;
  • TransactionInterceptor:这个bean本质上是一个Advice,其封装了当前需要织入目标bean的切面逻辑,也就是Spring事务是借助于数据库事务来实现对目标方法的环绕的。

深入理解 Spring 之 SpringBoot 事务原理:https://www.jianshu.com/p/8ff9201ed7d6

事务没生效的情况:https://blog.csdn.net/allen170257702/article/details/106325488?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

补充3:Spring 的不同事务传播行为有哪些,干什么用的?

当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播.

例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行. 事务的传播行为可以由传播属性指定

Spring的事务传播行为定义在Propagation这个枚举类中,一共有七种,分别为:

REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务,是默认的事务传播行为。

NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。

REQUIRES_NEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。

MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。

SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。

NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。

NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

补充4:@Autowired实现原理;@Autowired与@Resource的区别

原理:https://www.jianshu.com/p/a65f50451ed3

区别:https://www.cnblogs.com/jichi/p/10073404.html

补充5:BeanFactory和FactoryBean的区别;

https://blog.csdn.net/wangbiao007/article/details/53183764

后续补充网上的问题总结:

https://blog.csdn.net/a745233700/article/details/80959716

https://segmentfault.com/a/1190000013882720

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:Spring的组成模块
  • 二:IOC
    • 2.1 三大组件:
      • 2.2 IOC容器的技术剖析
        • 2.3 IOC的好处:
          • 2.4 IOC使用到的工厂模式:
          • 三:bean的生命周期、作用域
          • 四:AOP
            • 4.1 面向切面编程:
              • 1.代理的创建(按步骤):
              • 2.代理的调用
            • 4.2 CGLIB动态代理与JDK动态代理区别
              • 4.3 Spring中分别在什么情况下使用这两个代理?
                • 4.4 Spring AOP使用了哪些设计模式:
                • 五:SpringMVC的工作流程
                • 补充1:如何解决循环依赖;
                • 补充2:spring声明式事务的实现原理;
                • 补充3:Spring 的不同事务传播行为有哪些,干什么用的?
                • 补充4:@Autowired实现原理;@Autowired与@Resource的区别
                • 补充5:BeanFactory和FactoryBean的区别;
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档