Spring管理bean的作用域,为什么不会被GC处理?
当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。
Spring支持如下5种作用域:
- singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
- prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
- request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
- session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
- globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效
说明:其中比较常用的是singleton和prototype两种作用域。
- 对于singleton作用域的Bean:每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;
- 对于prototype作用域的Bean:程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。
Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。
prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。如果不指定Bean的作用域,Spring默认使用singleton作用域。spring底层使用map来存放bean实体,而map的键值是强引用,所以不会被GC,可以重复使用。
Spring对bean是如何解析
所谓bean的解析就是将我们的xml文件中的bean解析出来,上面的入口看到使用的是ClassPathXmlApplicationContext来获取ApplicationContext,所以,分析的入口也就从ClassPathXmlApplicationContext类中相应的构造函数开始。
- getBean方法开始创建过程,getBean()有一系列的重载方法,最终都是调用doGetBean() 方法
- getSingleton 方法尝试从缓存中获取单例 bean
当前 bean 是单例且缓存不存在则通过 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法创建单例对象
主要包含下下面三个主要方法:
- createBeanInstance 方法用于创建 Bean 实例
- populateBean 方法主要给 Bean 进行属性注入
- initializeBean 方法主要处理各种回调
Spring 核心特性?
Spring的核心特性就是IOC和AOP,IOC(Inversion of Control),即“控制反转”;AOP(Aspect-OrientedProgramming),即“面向切面编程”。
IOC:IOC,另外一种说法叫DI(Dependency Injection),即依赖注入。它并不是一种技术实现,而是一种设计思想。在任何一个有实际开发意义的程序项目中,我们会使用很多类来描述它们特有的功能,并且通过类与类之间的相互协作来完成特定的业务逻辑。这个时候,每个类都需要负责管理与自己有交互的类的引用和依赖,代码将会变的异常难以维护和极度的高耦合。而IOC的出现正是用来解决这个问题,我们通过IOC将这些相互依赖对象的创建、协调工作交给Spring容器去处理,每个对象只需要关注其自身的业务逻辑关系就可以了。在这样的角度上来看,获得依赖的对象的方式,进行了反转,变成了由spring容器控制对象如何获取外部资源(包括其他对象和文件资料等等)。
AOP:面向切面编程,往往被定义为促使软件系统实现关注点的分离的技术。系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。
讲讲Spring的IOC(DI)和AOP动态代理
传统的程序开发(不用IOC):举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有
长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其
所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。
IOC:
IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个美眉,我们只需要去和她谈恋爱、结婚就行了。
总结控制反转:所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
AOP的各种实现
AOP就是面向切面编程,我们可以从以下几个层面来实现AOP
- 在编译期修改源代码
- 在运行期字节码加载前修改字节码
- 在运行期字节码加载后动态创建代理类的字节码
AOP各种实现机制的比较
以下是各种实现机制的比较:
类别 | 机制 | 原理 | 优点 | 缺点 |
---|---|---|---|---|
静态AOP | 静态织入 | 在编译期,切面直接以字节码的形式编译到目标字节码文件中 | 对系统无性能影响 | 灵活性不够 |
动态AOP | 动态代理 | 在运行期,目标类加载后,为接口动态生成代理类,将切面织入到代理类中 | 相对于静态AOP更加灵活 | 切入的关注点需要实现接口。 对系统有一点性能影响 |
动态字节码生成 | CGLIB | 在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中 | 没有接口也可以织入 | 扩展类的实例方法为final时,则无法进行织入 |
自定义类加载器 | 在运行期,目标加载前,将切面逻辑加到目标字节码里 | 可以对绝大部分类进行织入 | 代码中如果使用了其他类加载器,则这些类将不会被织入 | |
字节码转换 | 在运行期,所有类加载器加载字节码前进行拦截 | 可以对所有类进行织入 |
AOP里的公民
- Joinpoint:拦截点,如某个业务方法
- Pointcut:Joinpoint的表达式,表示拦截哪些方法。一个Pointcut对应多个Joinpoint
- Advice:要切入的逻辑
- Before Advice:在方法前切入
- After Advice:在方法后切入,抛出异常则不会切入
- After Returning Advice:在方法返回后切入,抛出异常则不会切入
- After Throwing Advice:在方法抛出异常时切入
- Around Advice:在方法执行前后切入,可以中断或忽略原有流程的执行
Spring框架AOP执行原理简单说下?
什么是动态代理:AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
主要有两种方式:JDK动态代理和CGLIB动态代理。
- JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
- CGLIB(Code GenerationLibrary),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
AOP在事务管理方面,Spring使用AOP来完成声明式的事务管理有annotation和xml两种形式。开发中,方便代码编写,很多时候都是在spring配置文件中配置事务管理器并开启事务控制注解。在业务类或业务类方法中添加@Transactional实现事务控制。
Spring 分布式事务如何处理的?
第一种方案:可靠消息最终一致性,需要业务系统结合MQ消息中间件实现,在实现过程中需要保证消息的成功发送及成功消费。即需要通过业务系统控制MQ的消息状态
第二种方案:TCC补偿性,分为三个阶段TRYING-CONFIRMING-CANCELING。每个阶段做不同的处理。
- TRYING阶段主要是对业务系统进行检测及资源预留
- CONFIRMING阶段是做业务提交,通过TRYING阶段执行成功后,再执行该阶段。默认如果TRYING阶段执行成功,CONFIRMING就一定能成功。
- CANCELING阶段是回对业务做回滚,在TRYING阶段中,如果存在分支事务TRYING失败,则需要调用CANCELING将已预留的资源进行释放。