前言
本人复习的总结,文字图片因为懒所以是复制粘贴的,侵权删。
IoC
IoC是什么:IoC - Inversion of Control, 控制反转
控制反转就是把bean创建与管理交由第三方管理。这个第三方在spring中就是IoC容器。
Bean是什么:在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化、组装和管理的对象。可以当成被包装了的对象。
DI是什么:dependency injection 依赖注入。我们在创建一个类时可能会引用其他的类或数据,这对于这个类来说属于外部资源,依赖注入就是将需要的资源注入到这个类中。
DI和IoC:IoC是一种编程思想,而DI则是spring为了实现IoC而使用的一个具体方法。即IoC是逻辑层面,DI是应用使用层面。
IoC 容器
spring是如何来管理IoC容器的?
使用 ApplicationContext,它是 BeanFactory 的子类,更好的补充并实现了 BeanFactory 。
字面解释
控制反转:
- 控制:控制指的是对象的创建和管理;bean的创建和管理,bean的整个生命周期。
- 反转:一开始是我们自己控制对象的创建,即我们通过new方法来创建对象实例。反转就是我们将创建的权利交给第三方,不由我们来控制了。在soring中就是交给IoC容器来管理,由容器来交给我们bean。
控制反转就像是我们吃饭要自己做菜到出去下馆子一样。菜就是对象,菜馆就是容器,我们把做菜的权力交给了菜馆,我们只管吃就可以了。
依赖注入:
- 依赖:程序运行需要依赖外部的资源,提供程序内对象的所需要的数据、资源。
- 注入:配置文件把资源从外部注入到内部,容器加载了外部的文件、对象、数据,然后把这些资源注入给程序内的对象,维护了程序内外对象之间的依赖关系。
仔细想想更能发现IoC和DI是一种东西的不同表达。
为什么使用IoC
为了解耦。
在项目中,底层的实现都是由很多个对象组成的,对象之间彼此合作实现项目的业务逻辑。但是,很多很多对象紧密结合在一起,一旦有一方出问题了,必然会对其他对象有所影响,所以才有了解藕的这种设计思想。 看下图,本来 ABCD 是互相关联在一起的,当加入第三方容器的管理之后,每个对象都和第三方法的 IoC 容器关联,彼此之间不再直接联系在一起了,没有了耦合关系,全部对象都交由容器来控制,降低了这些对象的亲密度,就叫“解藕”。
比如说有一个myserviceImpl类实现了Myservice接口,被多个类所调用总共有30多处被使用new创建调用,一旦要替换成其他的实现Myservice接口的类就要手动地替换这些地方而且可能会出现问题。而使用IoC的方式则可以通过注解或xml文件创建我们想使用的类的bean,在具体使用的地方注入进来就可以了,这样我们就不需要一个个地去修改。有点提供了什么就用什么的感觉。
AoP
AoP是什么
AOP,也就是 Aspect-oriented Programming,译为面向切面编程,是计算机科学中的一个设计思想,旨在通过切面技术为业务主体增加额外的通知(Advice),从而对声明为“切点”(Pointcut)的代码块进行统一管理和装饰。
AOP 是对面向对象编程(Object-oriented Programming,俗称 OOP)的一种补充,OOP 的核心单元是类(class),而 AOP 的核心单元是切面(Aspect)。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而降低耦合度,提高程序的可重用性,同时也提高了开发效率。
面向对象编程时是将程序抽象成多个层次的对象,每个对象负责不同的模块,这样的话各个对象分工明确,各司其职,也不互相藕合,确实有力地促进了工程开发与分工协作。但不同的模块可能会有一些公共行为,这些行为难以使用接口来继承实现,使用工具类也是不利于维护。AOP的引入就是为了解决这类问题而生的,它要达到的效果是保证开发者在不修改源代码的前提下,为系统中不同的业务组件添加某些通用功能。
AoP名词解释
连接点(Joinpoint)
程序执行过程中的某一行为。可以当成程序执行时的一个个时间点,比如方法执行的时候、异常抛出的时候等。在连接点上切面的编码是可以注入的。(Aspect可以join(加入)的点)
切点(Pointcut)
一个项目中有很多的类,一个类有很多个连接点,我们一般也不会在所有的连接点上进行织入,所以我们就要对需要织入的点进行删选确认,切点就是干这个的。切点会通过一组规则来定位到需要的连接点。
切面(Aspect)
官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。
增强/通知(Advice)
指定连接点被织入时的具体时机和逻辑,是切面代码真正被执行的地方。就是“切面”对于某个“连接点”所产生的动作。主要有五个织入时机
- Before Advice: 在 JoinPoints 执行前织入
- After Advice: 在 JoinPoints 执行后织入(不管是否抛出异常都会织入)
- After returning advice: 在 JoinPoints 执行正常退出后织入(抛出异常则不会被织入)
- After throwing advice: 方法执行过程中抛出异常后织入
- Around Advice: 这是所有 Advice 中最强大的,它在 JoinPoints 前后都可织入切面代码,也可以选择是否执行原有正常的逻辑,如果不执行原有流程,它甚至可以用自己的返回值代替原有的返回值,甚至抛出异常。在这些 advice 里我们就可以写入切面代码了 综上所述,切面(Aspect)我们可以认为就是 pointcut 和 advice,pointcut 指定了哪些 joinpoint 可以被织入,而 advice 则指定了在这些 joinpoint 上的代码织入时机与逻辑。
目标对象(Target)
我们需要对它进行增强/通知的业务类
AOP代理(AOP proxy)
一个类被AOP织入后生成出了一个结果类,它是融合了原类和增强逻辑的代理类。
织入(Weaving)
切面作用于委托类对象以创建 adviced object 的过程(即代理)
AoP的实现原理
AoP是基于动态代理实现的,spring是使用CGlib来实现的动态代理。
什么是代理
代理就像是中介,我要租房子就去找中介,我只管住进房子(调用接口),代理在完成任务的时候会收取中介费(增强公告/进行通知)。
代理分类
代理可分为静态代理和动态代理。
代理的UML图如下:
为什么这么分
首先 Java 源代码经过编译生成字节码,然后再由 JVM 经过类加载,连接,初始化成 Java 类型,可以看到字节码是关键,静态和动态的区别就在于字节码生成的时机
静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在编译时已经将接口,被代理类(委托类),代理类等确定下来,在程序运行前代理类的.class文件就已经存在了
动态代理:在程序运行后通过反射创建生成字节码再由 JVM 加载而成
静态代理的缺点
如上所说,静态代理锁代理的类在类的编写阶段就已经定好了,是无法改变的,所以延伸出来的缺点如下:
- 代理类只代理一个委托类(其实可以代理多个,但不符合单一职责原则),也就意味着如果要代理多个委托类,就要写多个代理(别忘了静态代理在编译前必须确定)
- 第一点还不是致命的,再考虑这样一种场景:如果每个委托类的每个方法都要被织入同样的逻辑,比如说我要计算前文提到的每个委托类每个方法的耗时,就要在方法开始前,开始后分别织入计算时间的代码,那就算用代理类,它的方法也有无数这种重复的计算时间的代码
动态代理
动态代理的代理类是在运行期间动态分配的,所以就没有静态代理那样写死引发的问题。
动态代理分为jdk动态代理和CGlib动态代理。
jkd动态代理
JDK 动态代理使用 Proxy 来创建代理类,增强逻辑写在 InvocationHandler.invoke() 里。
jkd动态代理中有一个重要的代码:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
其中第二个参数是委托类的接口,是必传的,JDK 动态代理是通过与委托类实现同样的接口,然后在实现的接口方法里进行增强来实现的,这就意味着如果要用 JDK 代理,委托类必须实现接口。
CGlib动态代理
CGlib 动态代理也提供了与JDK动态代理invoke 类似的 Enhance 类,增强逻辑写在了MethodInterceptor.intercept() 中。CGlib是基于继承实现的动态代理,所以只能继承非final修饰的类。本质是重写了代理类的方法加上了增强/通知功能。