Spring设计理念:面向Bean编程!类似于Java面向对象编程。
spring源码解析:https://segmentfault.com/u/h2pl/articles?page=7(蚂蚁大佬的文章)
https://segmentfault.com/a/1190000011503081(Spring4.0)
spring的核心组件(骨骼架构)—— 共同创建了Bean关系网络
spring的特性组件(特性功能)
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
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 个步骤:
可以看到,依赖注入实际上,只需要 “低级容器” 就可以实现。
IOC(控制反转)就是依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。
这么做的目的是把程序业务代码与事物(组件、POJO类)代码进行分离,程序有关事物的创建、属性和依赖对象的注入、以及生命周期交由容器进行加载和管理。
业务代码只需从容器中获取组件或POJO实例对象即可,无需再考虑组件之间、组件与POJO之间的依赖关系以及属性的注入。
https://www.cnblogs.com/yssjun/p/11102162.html
https://www.cnblogs.com/baizhanshi/p/8318087.html
还设计到单例模式,适配器模式。
附:一篇讲解IOC很好的博客:https://www.jianshu.com/p/17b66e6390fd
https://www.jianshu.com/p/e71818303971
https://www.cnblogs.com/hthuang/p/7807844.html
bean的实例化过程:
https://www.cnblogs.com/vingLiu/p/12052096.html
一篇讲解很好的博客:https://www.jianshu.com/p/e18fd44964eb
可以将 AOP 分成 2 个部分来扯,哦,不,来分析。。。 第一:代理的创建; 第二:代理的调用。
注意:我们尽量少贴代码,尽量用文字叙述,因为面试的时候,也是文字叙述,不可能让你把代码翻出来的。。。所以,这里需要保持一定的简洁。
代码位置:com.interface21.aop 包下。
开始分析(扯):
Spring AOP实现原理:默认是应用JDK动态代理实现AOP功能,当代理对象为接口时使用JDK动态代理,否则使用ObjenesisCglibAopProxy(CGLIB)实现。
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象实现了接口,可以强制使用CGLIB实现AOP
如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
2、JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。 因为是继承,所以该类或方法不要声明成final ,final可以阻止继承和多态。
3、CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适
代理模式:https://www.cnblogs.com/daniels/p/8242592.html(动态代理和cglib代理,搞懂这个很重要)
责任链模式:https://www.cnblogs.com/fengyumeng/p/10839570.html
https://blog.csdn.net/u014191220/article/details/81387596
https://www.jianshu.com/p/8a20c547e245
https://blog.csdn.net/floating_dreaming/article/details/89089214
segment大神的文章:https://segmentfault.com/u/h2pl/articles?page=5
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三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决
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
用于实现Spring事务的Advisor,Pointcut以及Advice都已经找到了。关于这三个类的具体作用,我们这里进行整体的上的讲解,深入其内部讲解其是如何进行bean的过滤以及事务逻辑的织入的。
深入理解 Spring 之 SpringBoot 事务原理:https://www.jianshu.com/p/8ff9201ed7d6
当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播.
例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行. 事务的传播行为可以由传播属性指定
Spring的事务传播行为定义在Propagation这个枚举类中,一共有七种,分别为:
REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务,是默认的事务传播行为。
NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。
REQUIRES_NEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。
MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。
SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。
NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。
原理:https://www.jianshu.com/p/a65f50451ed3
区别:https://www.cnblogs.com/jichi/p/10073404.html
https://blog.csdn.net/wangbiao007/article/details/53183764
后续补充网上的问题总结:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。