深刻理解SpringIOC与AOP

简单来说Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!

IOC(inversion of control IoC)

IOC是一种设计思想,而不是一种具体的技术实现。Ioc的思想就是将原本在程序中手动创建对象的控制权交给由Spring框架来管理。Ioc并非Spring特有,在其他语言中也有应用。

 

     简单的来说是把创建对象的过程交给Spring管理,不需要用new创建对象。依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。首先,IoC(Inverse of Control,控制反转)在其他语言中也有应用,并非 Spring 特有,它是一种设计思想,基本概念就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。IoC 具体的实现方式是依赖注入。

     之前我们在代码中创建一个对象是通过 new 关键字,而使用了 Spring 之后,我们不在需要自己去 new 一个对象了,而是直接通过容器里面去取出来,再将其自动注入到我们需要的对象之中,也就说创建对象的控制权不在我们程序员手上了,全部交由 Spring 进行管理。

     交给 Spring 管理的也称为 Bean,所有的 Bean 都被存储在一个 Map 集合中,这个 Map 集合也称为 IoC 容器。

     将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。

     比如说,在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能每次都要搞清这个 Service 所有底层类的构造函数,这显然过于繁琐。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

      采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

IOC创建对象的方式

  1. 使用无参构造创建对象,默认!获取spring上下文的时候,所有的对象在容器中就已经被创建了
  2. 假设我们要使用有参构造创建对象。
    1. 下标赋值
    2. 类型赋值
    3. 直接通过参数名赋值
  • 总结:在配置文件加载的时候,容器(< bean>)中管理的对象就已经初始化了

    举个例子:

    bean容器就像是个婚介所,里边所有对象都是一开始就初始化完成的,我们只需要直接挑选就好了

    简单来讲就是:婚介所内部的可供挑选的对象并不会在你挑选以后才进行初始化,而是在婚介所准备工作完成并开张的时候(配置文件加载的时候)就初始化完成了。

AOP

先来看一个简单的小例子,体会一下 AOP 能解决什么问题:

假设有这么三个类 A B C,它们都拥有各自实现的 do 方法:

 

如果我们要在 A,B,C 三个类的 do 方法中的最后执行一个名为 log 方法来打印日志,最简单的,我们可以这样写:

 

这样做可以解决问题,但是总感觉有些别扭,每个类的 do 方法中都调用了打印日志的方法,但是,打印日志其实并不是我们的核心业务,我们却要去花费大力气去处理它

随着系统越来越完善,类似这样的非核心业务也会越来越多,比如权限,异常处理,性能监控等

这样的功能出现在很多类的很多方法中干扰了我们的核心业务代码,怎么解决呢?

AOP 就是为此而生:

看看 AOP 是如何解决的?

 

从上图可以看出日志记录,性能监控,异常处理这样的非核心功能被单独抽取出来了,与业务代码分离,横切在核心业务代码之上

这就是我们通常所说的面向切面编程(AOP),通过一个例子看看他是如何实现的

创建一个 UserDao 类:

 

这就是 Spring AOP 的强大之处,在运行时通过动态代理技术对 UserDao 的 addUser 方法进行了增强,添加了打印日志的功能。

动态代理其实就是在运行时动态的生成目标对象的代理对象在代理对象中对目标对象的方法进行增强,小伙伴们可以直接公众号里搜一下看看,下面来解释下 AOP 中几个重要的概念。

AOP 就是在运行时通过动态代理技术对目标方法进行增强,可以在目标方法的调用前后或者调用过程中执行其他额外的逻辑。

动态代理它其实是代理模式的一种,所谓代理模式就是,使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。代理模式有三大角色:

  • Real Subject:真实类,也就是被代理类、委托类。用来真正完成业务服务功能
  • Proxy:代理类。将自身的请求用 Real Subject 对应的功能来实现,代理类对象并不真正的去实现其业务功能
  • Subject:定义 RealSubject 和 Proxy 角色都应该实现的接口

代理模式分为静态代理和动态代理。

先来说静态代理:

静态代理就是,对于你想要增强的委托类,我们需要新建一个代理类,这两个类实现一个同样的接口,然后将委托类注入进代理类中,在代理类的方法中调用委托类中的对应方法。这样,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

从 JVM 层面来说, 静态代理就是在编译时就将接口、委托类、代理类这些都变成了一个个实际的 .class 文件。

静态代理的弊端很明显,一个委托类对应一个代理类,多个委托类就需要新建多个代理类,我们能不能将代理类做成一个通用的呢?

为此,动态代理应用而生。

动态代理的实现方式有很多种,Spring 中使用了 JDK 动态代理和 CGLIB 动态代理

先来说 JDK 动态代理:

同样的,JDK 动态代理需要委托类实现一个接口,不过代理类就不需要也实现同样的接口了,但是,JDK 动态代理机制中添加了一个新的角色,那就是处理类。具体来说,我们需要新建一个处理类,然后将委托类注入处理类,另外,这个处理类需要实现 InvocationHandler 接口,并重写其 invoke 方法,在 invoke 方法中可以利用反射机制调用委托类的方法,并可以在其前后添加一些额外的处理逻辑。最后,我们定义一个创建代理对象的工厂类(代理类),通过 Proxy.newProxyInstance() 创建委托类对象的代理对象

JDK 动态代理有一个最致命的问题是它只能代理实现了某个接口的实现类,并且代理类也只能代理接口中实现的方法,要是实现类中有自己私有的方法,而接口中没有的话,该方法就不能进行代理调用。

为了解决这个问题,我们可以用 CGLIB 动态代理机制,CGLIB(Code Generation Library)其实就是一个基于 ASM 的 Java 字节码生成框架。

解释一下什么是字节码生成框架:

一个 Class 类对应一个 .class 字节码文件,对吧,也就是说字节码文件中存储了一个类的全部信息。字节码其实是二进制文件,内容是只有 JVM 能够识别的机器码。

JVM 解析字节码文件也就是加载类的过程是这样的:JVM 读取 .class 字节码文件,取出二进制数据,加载到内存中,解析字节码文件内的信息,然后生成对应的 Class类对象。

显然,这个过程是在编译期就发生的。

那如果我们在运行期遵循 Java 编译系统组织 .class 字节码文件的格式和结构,生成相应的二进制数据(这就是字节码工具做的事情),然后再把这个二进制数据加载转换成对应的类。这样,我们不就完成了在运行时动态的创建一个类吗。这个思想其实也就是动态代理的思想。

简单来说,动态代就是通过字节码技术生成一个子类,并在子类中拦截父类方法的调用(这也就是为什么说 CGLIB 是基于继承的了),织入额外的业务逻辑。关键词就是拦截,CGLIB 引入一个新的角色方法拦截器,让其实现接口 MethodInterceptor,并重写 intercept 方法,这里的 intercept 用于拦截并增强委托类的方法(和 JDK 动态代理 InvocationHandler 中的 invoke 方法类似),最后,通过 Enhancer.create() 创建委托类对象的代理对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值