Spring学习笔记

本文详细介绍了Spring框架的核心概念,包括Spring的起源、发展、优势和核心结构。深入探讨了IoC和AOP的概念,以及如何手动实现这两个特性。通过源码分析,揭示了Spring的IoC容器初始化流程、BeanFactory的创建过程和循环依赖问题。此外,还涵盖了Spring的声明式事务处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章内容输出来源:拉勾教育Java高薪训练营;

一、Spring概述

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

Spring 简介

Spring 是分层的 full-stack(全栈) 轻量级开源框架,以 IoC 和 AOP 为内核,提供了展现层 Spring MVC 和业务层事务管理等众多的企业级应⽤技术,还能整合开源世界众多著名的第三⽅框架和类库,已经成为使⽤最多的 Java EE 企业应⽤开源框架。

Spring 官⽅⽹址:http://spring.io/

我们经常说的 Spring 其实指的是Spring Framework(spring 框架)。

Spring 发展历程

  • 1997年 IBM 提出了EJB的思想; 1998年,SUN 制定开发标准规范EJB1.0; 1999年,EJB 1.1发布; 2001年,EJB 2.0发布; 2003年,EJB 2.1发布; 2006年,EJB 3.0发布;
  • Rod Johnson(spring之⽗)
    • Expert One-to-One J2EE Design and Development(2002) 阐述了J2EE使⽤EJB开发设计的优点及解决⽅案
    • Expert One-to-One J2EE Development without EJB(2004) 阐述了J2EE开发不使⽤EJB的解决⽅式(Spring雏形)

2017 年 9 ⽉份发布了 Spring 的最新版本 Spring 5.0 通⽤版(GA)

Spring 的优势

  • ⽅便解耦,简化开发
    通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进⾏控制,避免硬编码所造成的过度程序耦合。⽤户也不必再为单例模式类、属性⽂件解析等这些很底层的需求编写代码,可以更专注于上层的应⽤。
  • AOP编程的⽀持
    通过Spring的AOP功能,⽅便进⾏⾯向切⾯的编程,许多不容易⽤传统OOP实现的功能可以通过
    AOP轻松应付。
  • 声明式事务的⽀持
    @Transactional
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式⽅式灵活的进⾏事务的管理,提⾼开发效率和质量。
  • ⽅便程序的测试
    可以⽤⾮容器依赖的编程⽅式进⾏⼏乎所有的测试⼯作,测试不再是昂贵的操作,⽽是随⼿可做的事情。
  • ⽅便集成各种优秀框架
    Spring可以降低各种框架的使⽤难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接⽀持。
  • 降低JavaEE API的使⽤难度
    Spring对JavaEE API(如JDBC、JavaMail、远程调⽤等)进⾏了薄薄的封装层,使这些API的使⽤
    难度⼤为降低。
  • 源码是经典的 Java 学习范例
    Spring的源代码设计精妙、结构清晰、匠⼼独⽤,处处体现着⼤师对Java设计模式灵活运⽤以及对Java技术的⾼深造诣。它的源代码⽆意是Java技术的最佳实践的范例。

Spring 的核心结构

在这里插入图片描述

  • Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。
  • ⾯向切⾯编程(AOP)/Aspects Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。
  • 数据访问与集成(Data Access/Integration)
    Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。
  • Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。
  • Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。

二、核心思想

IoC

IoC Inversion of Control (控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现描述的事情:Java开发领域对象的创建,管理的问题
传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象

IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可我们丧失了⼀个权利(创建、管理对象的权利),得到了⼀个福利(不⽤考虑对象的创建、管理等⼀系列事情)

为什么叫做控制反转?
控制:指的是对象创建(实例化、管理)的权利
反转:控制权交给外部环境了(spring框架、IoC容器)

IOC和DI描述的是同⼀件事情,只不过角度不⼀样罢了
IOC是站在对象的角度,对象实例化及其管理的权力交给了容器。
DI是站在容器的角度,容器来处理对象之间的依赖关系,使用注入将对象进行完善。

AOP

AOP是面向切面编程,它是OOP的延续。

我们将多个方法中编写同样代码的地方叫做横切逻辑代码。

AOP就是将重复的横切逻辑代码抽离出来,减少代码耦合度。

为什么叫面向切面编程?

  • 切:横切逻辑。
  • 面:横切逻辑代码往往是影响多个方法的,每个方法都有相同的点,多个点构成面。

三、手写实现 IoC 和 AOP

这里我简单记录下手写的思路
首先我们要实现IOC那么就需要实现容器

第一步:实现容器

容器的主要作用是存放和提取bean。
那么我们很容易就能想到Map这个数据结构,因为它的查找的时间复杂度是O(1)的。

第二步:实现xml读取并注册

这一步我们可以基于xml文件进行bean的编写,通过dom4j来解析xml中的bean存入map中,bean要有beanId作为容器的key,bean本身作为value。

第三步:实现依赖注入

bean如果有需要依赖的属性,需要使用额外的标签进行指定,比如使用property标签,通过ref属性指定所依赖的bean的名称。在代码中先进行注册,注入操作在所有bean注册完成之后再进行处理。这样比较容易实现。

第四部:实现事务的AOP

如何进行事务控制呢?

我们知道在sql中使用begin和commit进行事务控制。
那么这就意味着我们需要保证事务中的执行逻辑要在一个session中进行。

那么我们需要在执行事务方法前获取一个数据库的连接,并将连接中的事务自动提交关闭,然后该事务方法使用这个连接,方法调用完毕后需要进行commit的操作,如果方法中间出了异常就需要进行rollback。

这么这里很容易看出横切逻辑代码是开启事务(获取连接,关闭自动提交)、提交事务和回滚。

那么这里就需要使用到动态代理了,那么你可以选择JDK动态代理也可以选择CGlib动态代理。当然也可以视情况动态选择。

数据库连接的获取可以自定义一个工具类,获取到connection存入ThreadLocal里面这样一个线程就可容易获取到自己的连接了。

四、Spring IOC源码深度剖析

Spring IoC容器初始化主体流程

@Override
public void refresh() throws BeansException, IllegalStateException {
 synchronized (this.startupShutdownMonitor) {
 // 第⼀步:刷新前的预处理
 prepareRefresh();
 /*
 第⼆步:
 获取BeanFactory;默认实现是DefaultListableBeanFactory
 加载BeanDefition 并注册到 BeanDefitionRegistry
 */
 ConfigurableListableBeanFactory beanFactory =
obtainFreshBeanFactory();
 // 第三步:BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加
载器等)
 prepareBeanFactory(beanFactory);
 try {
 // 第四步:BeanFactory准备⼯作完成后进⾏的后置处理⼯作
 postProcessBeanFactory(beanFactory);
 // 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
 invokeBeanFactoryPostProcessors(beanFactory);
 // 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执
⾏
 registerBeanPostProcessors(beanFactory);
 // 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
 initMessageSource();
 // 第⼋步:初始化事件派发器
 initApplicationEventMulticaster();
 // 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
 onRefresh();
 // 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器
bean
 registerListeners();
 /*
 第⼗⼀步:
 初始化所有剩下的⾮懒加载的单例bean
 初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
 填充属性
 初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
 调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
 */
 finishBeanFactoryInitialization(beanFactory);
 /*
 第⼗⼆步:
 完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事
件 (ContextRefreshedEvent)
 */
 finishRefresh();
 }

BeanFactory创建流程

BeanFactory获取子流程

在这里插入图片描述

BeanDefinition加载、解析及注册子流程

在这里插入图片描述

Bean创建流程

AbstractApplicationContext#refresh()->
finishBeanFactoryInitialization()->
// 实例化所有立即加载的单例bean
DefaultListableBeanFactory#preInstantiateSingletions()->
getBean(beanName)->
AbstractBeanFactory#doGetBean->
AbstractAutowireCapableBeanFactory#doCreateBean()->

  • createBeanInstance() //创建Bean实例,尚未设置属性
  • populateBean() //Bean属性填充
  • initializeBean() //调用初始化方法,应用BeanPostProcessor后置处理器

lazy-init 延迟加载机制原理

  • lazy-init 延迟加载机制分析:普通 Bean 的初始化是在容器启动初始化阶段执⾏的,⽽被lazy-init=true修饰的 bean 则是在从容器⾥第⼀次进⾏context.getBean() 时进⾏触发。Spring 启动的时候会把所有bean信息(包括XML和注解)解
    析转化成Spring能够识别的BeanDefinition并存到Hashmap⾥供下⾯的初始化时⽤,然后对每个BeanDefinition 进⾏处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进⾏初始化并依赖注⼊。
public void preInstantiateSingletons() throws BeansException {
 // 所有beanDefinition集合
 List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
 // 触发所有⾮懒加载单例bean的初始化
 for (String beanName : beanNames) {
 // 获取bean 定义
 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
 // 判断是否是懒加载单例bean,如果是单例的并且不是懒加载的则在容器创建时初始化
 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
 // 判断是否是 FactoryBean
 if (isFactoryBean(beanName)) {
 final FactoryBean<?> factory = (FactoryBean<?>)
getBean(FACTORY_BEAN_PREFIX + beanName);
 boolean isEagerInit;
 if (System.getSecurityManager() != null && factory instanceof
SmartFactoryBean) {
 isEagerInit = AccessController.doPrivileged(new
PrivilegedAction<Boolean>() {
 @Override
 public Boolean run() {
 return ((SmartFactoryBean<?>) factory).isEagerInit();
 }
 }, getAccessControlContext());
 }
 }else {
 /*
 如果是普通bean则进⾏初始化并依赖注⼊,此 getBean(beanName)接下来触发的逻辑
和
 懒加载时 context.getBean("beanName") 所触发的逻辑是⼀样的
 */
 getBean(beanName);
 }
 }
 }
}
  • 总结
    • 对于被修饰为lazy-init的bean Spring 容器初
      ⼀次进行getBean时候才进行初始化并依赖注入
    • 对于非懒加载的bean,getBean的时候会从缓存里头获取,因为容器初始化阶段 Bean 已经初始化完成并缓存了起来

Spring IoC循环依赖问题

构造器注入是无法解决循环依赖问题的

始化阶段不会进行init 并且依赖注入,当第
假设两个类A和B是相互依赖的,A先装载

  • 在容器装载类A的时候,会执行doGetBean方法也就是图中的步骤1;

  • 接下来会从缓存中获取对象A也就步骤2:getSingleton方法,该方法会依次查找一、二、三级缓存;

  • 此时缓存中没有A,依次执行步骤5、6、7,在第七步addSingletonFactory,会将实例化且未设置属性的A实例放入三级缓存;

  • 接着进行解析属性B,经过步骤8、9、10最后到达步骤11其实也就是步骤1;

  • 那么B在缓存中也是没有的,一直到步骤7,将为设置属性的B实例也加入三级缓存;

  • 然后解析B的属性A,那么继续到步骤2在三级缓存中找到了实例A,将实例A从三级缓存转移至二级缓存,将实例A返回给B也就是步骤4;

  • 接下来从步骤12、13、14、15、16,在addSingleton方法中将完整的实例B加入一级缓存,并清理其它缓存

  • 接着B实例返回到A类的属性填充也就是步骤13,一直到步骤16,将完整的实例A对象也加入一级缓存,并清理其它缓存。

以上就是循环依赖在三级缓存中的处理过程

简单来讲就是没有进行属性设置、没有被注入到其它对象中的对象会存放在三级缓存中。

没有进行属性设置、被注入到其它对象中的对象会存放在二级缓存中。

进行属性设置的对象会被放在一级缓存中。

就是先实例化再处理属性之间的依赖关系。实例化和属性设置时分离的。因此通过构造注入的方法是不能解决循环依赖问题的

五、Spring AOP源码深度剖析

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#i
nitializeBean
调⽤
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#a
pplyBeanPostProcessorsAfterInitialization
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProce
ssAfterInitialization(后置处理器AbstractAutoProxyCreator完成bean代理对象创建)
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNec
essary
调⽤
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createPro
xy (在这⼀步把委托对象的aop增强和通⽤拦截进⾏合并,最终给代理对象)
调⽤
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
调⽤
org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)

六、Spring声明式事务分析

@EnableTransactionManagement 注解
1)通过@import引⼊了TransactionManagementConfigurationSelector类
 它的selectImports⽅法导⼊了另外两个类:AutoProxyRegistrar和

ProxyTransactionManagementConfiguration
2)AutoProxyRegistrar类分析
 ⽅法registerBeanDefinitions中,引⼊了其他类,通过
 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)引⼊

InfrastructureAdvisorAutoProxyCreator,
 它继承了AbstractAutoProxyCreator,是⼀个
后置处理器类
3)ProxyTransactionManagementConfiguration 是⼀个添加了@Configuration注解的配置类
(注册bean)
 注册事务增强器(注⼊属性解析器、事务拦截器)
 属性解析器:AnnotationTransactionAttributeSource,内部持有了⼀个解析器集合
 Set<TransactionAnnotationParser> annotationParsers;
 具体使⽤的是SpringTransactionAnnotationParser解析器,⽤来解析
@Transactional的事务属性
 事务拦截器:TransactionInterceptor实现了MethodInterceptor接⼝,该通⽤拦截
会在产⽣代理对象之前和aop增强合并,最终⼀起影响到代理对象
 TransactionInterceptor的invoke⽅法中invokeWithinTransaction会触发原有业
务逻辑调⽤(增强事务)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值