Spring IOC/AOP

本文深入讲解Spring框架的核心概念,包括IOC、AOP的基本原理,以及BeanFactory和ApplicationContext的使用。探讨了Spring AOP的实现机制,包括JDK动态代理和CGLib动态代理的生成方式。同时,对比了静态代理和动态代理的区别,介绍了Spring AOP的织入方式。最后,分析了Spring IOC中的单例模式和原型模式,以及Spring设计模式的应用。

  Spring是一个开源框架,框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件。Spring的核心是控制反转(IOC)和面向切面(AOP)。
IOC:控制反转。比方说有一个类,想要调用类里面的方法(不是静态方法),就要创建类的对象,使用对象调用方法实现。对于Spring来说,Spring创建对象的过程,不是在代码里面实现的,而是交给Spring来进行配置实现的。
AOP:面向切面编程。

IOC

IOC底层原理使用技术:
(1)xml配置文件
(2)dom4j解析xml文件
(3)工厂设计模式
(4)反射

那么这几种技术是怎么用到IOC里面的呢?
第一步:创建xml配置文件,配置要创建对象类
第二步:创建工厂类,使用dom4j解析配置文件+反射

如果类的路径或类名发生了变化,只需更改配置文件即可,降低了类之间的耦合度。不适用IOC最大不便是:如果有接口的属性对象,使用者必须知道该对象的具体实现类,不符合解耦思想。

  IOC是一种可以帮助解耦各业务对象间依赖关系的对象绑定方式,Spring 提供了两种容器类型来提供支持 IOC方式。这两种类型是:

  • BeanFactory: 基础类型的IOC容器,提供完整的IOC服务支持
  • ApplicationContext: ApplicationContext是在 BeanFactory的基础之上构建的,是相对高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext提供了其他高级特性

BeanFactory

  BeanFactory 是基础类型IOC容器,提供完整的IOC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入工作。
  BeanFactory就是生产 Java Bean 的工厂,作为Spring 提供的基本的IoC容器,BeanFactory 帮助完成业务对象的注册和对象间依赖关系的绑定。
  实际上,BeanFactory只是一个接口,它负责定义如何访问容器内管理的Bean的方法,各个BeanFactory的具体实现类负责具体Bean的注册以及管理工作。BeanFactory有三个直接子类:

  • ListableBeanFactory: 通过继承该接口可以列出所有的Bean,也可以只列出与预期类型相对应的bean
  • HierarchicalBeanFactory: 支持分层bean的管理,使BeanFactory支持双亲IOC容器的管理功能
  • AutowireCapableBeanFactory: 可以填充不受Spring 控制的 Bean

ApplicationContext

  ApplicationContext是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了拥有 BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如:统一资源加载策略、国际化信息支持、容器内部事件发布机制。
  在ApplicationContext 容器启动之后,默认全部初始化并绑定完成,所以,对于BeanFactory来说,ApplicationContext 往往要求更多的系统资源

对象注入方式

  IOC容器一般有两种对象注入方式:基于XML配置文件基于注解驱动的方式
  基于XML和基于注解注入Bean 的方式是不一样的,基于XML的应用上下文ClassPathXmlApplicationContext需要设置配置路径,基于注解的应用上下文AnnotationConfigApplicationContext需要注册一个配置Bean,但它们相同的一步就是必须要调用refresh()方法,该方法可以看做是IOC容器的启动方法,它会做很多操作,比如完成配置的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等,不调用它,容器就无法启动。

AOP

  Spring AOP(面向切面编程),是面向对象编程的一种补充,用于处理系统中分布的各个模块的横切关注点,比如说事务管理、日志、缓存等。AOP利用“横切”技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”。所谓“方面”就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度。

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码
Target(目标对象):织入 Advice 的目标对象.
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

Spring AOP代理对象的生成

  Spring AOP使用了两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用CGLib来生成代理。
  JDK动态代理只能为接口创建动态代理实例,而不能对类创建动态代理。需要获得被目标类的接口信息(应用Java的反射技术),生成一个实现了代理接口的动态代理类,再通过反射机制获得动态代理类的构造函数,利用构造函数生成动态代理类的实例对象,再调用具体方法前调用invokeHandler方法来处理。
  CGLib动态代理需要依赖asm包,把被代理对象类的class文件加载进来,修改其字节码生成子类。

基于JDK的动态代理

  JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例。InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,在并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。缺点:jdk动态代理,必须是面向接口,目标业务类必须实现接口。实现方式:

  1. 通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
  2. 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
  3. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
  4. 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

  InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。接下来看下invoke()方法实现下Spring AOP的织入切面的具体过程:

主流程可以简述为:
获取可以应用到此方法上的通知链(Interceptor Chain),
如果有,则应用通知,并执行joinpoint; 
如果没有,则直接反射执行joinpoint。
而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。

  首先,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,这个方法的实现:实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存:从提供的配置实例config中获取advisor列表,遍历处理这些advisor。如果是IntroductionAdvisor,则判断此Advisor能否应用到目标类targetClass上。如果是PointcutAdvisor,则判断此Advisor能否应用到目标方法method上。将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回。这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor。
  再看下得到的拦截器链是怎么起作用的:如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行。

基于CGLIB的动态代理

  使用JDK创建代理有一个限制,即JDK动态代理只能为接口创建代理。如果对于没有通过接口定义业务方法的类,如何动态创建代理实例呢?CGLib作为基于JDK的动态代理。CGLib采用字节码技术,可以为一个类创建子类并在子类中采用方法拦截的技术拦截所有父类方法的调用,并在拦截方法相应地方织入横切逻辑。

1、 CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口
2、 用CGlib生成代理类是目标类的子类
3、 用CGlib生成代理类不需要接口
4、 用CGLib生成的代理类重写了父类的各个方法
5、 拦截器中的intercept方法内容正好就是代理类中的方法体

总结
  Spring AOP在底层就是利用JDK动态代理或CGLib动态代理技术为目标Bean织入横切逻辑。Spring AOP通过Pointcut(切点)指定在哪些类的哪些方法上施加横切逻辑,通过Advice(增强)描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等),此外,Spring还通过Advisor(切面)组合Pointcut和Advice。有了Advisor的信息,Spring就可以利用JDK或CGLib的动态代理技术为目标Bean创建织入切面的代理对象了。
  JDK动态代理所创建的代理对象,在JDK 1.3下,性能强差人意。而CGLib在创建代理对象时性能却比JDK动态代理慢很多,所以对于singleton的代理对象或者具有实例池的代理,因为不需要频繁创建代理对象,所以比较适合用CGLib动态代理技术,反之适合用JDK动态代理技术。此外,由于CGLib采用生成子类的技术创建代理对象,所以不能对目标类中的final方法进行代理。

CGLIB动态代理与JDK动态区别
  java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。 因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。

Spring中:
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

附:

静态代理和动态代理的区别

  静态代理在程序运行前,代理类的.class文件就已经存在了。只能针对对应的类进行代理,如果类很多就需要很多的代理。 动态代理就是弥补了静态代理的这个缺陷。动态代理在程序运行时运用反射机制动态创建而成,与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

Spring AOP织入方式——编译期织入、装载期织入、运行时织入

  AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
 静态代理有:编译时织入(特殊编译器实现)、类加载时织入(特殊的类加载器实现)。
 动态代理有:jdk动态代理(基于接口来实现)、CGlib(基于类实现)。

Spring的两种动态代理:JDK和cglib

  java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法

动态代理和字节码的关系:字节增强

Spring IOC中的单例模式和原型模式

Spring定义了多种作用域,可以基于这些作用域创建bean,包括:

单例( Singleton):在整个应用中,只创建bean的一个实例。
原型( Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。

  在默认情况下, Spring应用上下文中所有bean都是作为以单例的形式创建的,即不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例。在大多数情况下,单例bean是很理想的方案。初始化和垃圾回收对象实例所带来的成本只留给一些小规模任务,在这些任务中,让对象保持无状态并且在应用中反复重用这些对象可能并不合理。
  有时候,可能会发现,所使用的类是易变的( mutable),它们会保持一些状态,因此重用是不安全的。在这种情况下,将class声明为单例的bean就不是什么好主意了,因为对象会被污染,稍后重用的时候会出现意想不到的问题。原型作用域,每次注入或从Spring应用上下文中检索该bean的时候,都会创建新的实例。

Spring中的设计模式

Spring中的设计模式

spring xml配置文件方式和注解方式的优缺点

xml配置文件方式优点:
1、降低耦合,使容易扩展。
2、对象之间的关系一目了然。
3、xml配置文件比注解功能齐全。

xml配置文件方式缺点:
1、配置文件配置工作量相对注解要打。

注解方式优点:
1、在class文件中,可以降低维护成本
2、提高开发效率。

注解方式缺点:
1、如果对annotation进行修改,需要重新编译整个工程。
2、业务类之间的关系不如XML配置那样一目了然。
3、程序中过多的annotation,对于代码的简洁度有一定影响。
4、annotation貌似功能没有xml配置文件齐全

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值