spring核心:Ioc和Aop

1.Spring简介

Spring是一个分层的javaEE轻量级开源框架,Spring的核心是IOC和AOP。

Spring主要优点:

  • 方便解耦,简化开发,通过Spring提供的Ioc容器。我们可以将对象之间的依赖关系由Spring进行控制,避免硬编码造成的程序耦合度过高
  • AOP编程的支持,通过Spring提供的AOP功能,方便进行面向切面编程
  • 声明式事务的支持,在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式事务灵活的进行事务管理,提高开发的效率和质量
  • 方便程序的测试,可以用非容器依赖的编程方式进行几乎所有的测试工作
  • 方便集成各种优秀的框架。Spring提供了对各种优秀框架的直接支持

2.Spring的五个模块

2.1 核心模块(Core Container)

Spring的核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述。由IoC容器负责类的创建,管理,获取等。BeanFactory接口是Spring框架的核心接口,实现了容器很多核心的功能。

Context模块构建于核心模块之上,扩展了BeanFactory的功能,包括国际化,资源加载,邮件服务,任务调度等多项功能。ApplicationContext是Context模块的核心接口。

表达式语言(Expression Language)是统一表达式语言(EL)的一个扩展,支持设置和获取对象属性,调用对象方法,操作数组、集合等。使用它可以很方便的通过表达式和Spring IoC容器进行交互。

2.2 AOP模块

Spring AOP模块提供了满足AOP Alliance规范的实现,还整合了AspectJ这种AOP语言级的框架。通过AOP能降低耦合。

2.3 数据访问集成模块(Data Access/Integration )

该模块包括了JDBC、ORM、OXM、JMS和事务管理:

  • 事务模块:该模块用于Spring管理事务,只要是Spring管理对象都能得到Spring管理事务的好处,无需在代码中进行事务控制了,而且支持编程和声明性的事务管理。
  • JDBC模块:提供了一个JBDC的样例模板,使用这些模板能消除传统冗长的JDBC编码还有必须的事务控制,而且能享受到Spring管理事务的好处。
  • ORM模块:提供与流行的“对象-关系”映射框架的无缝集成,包括hibernate、JPA、MyBatis等。而且可以使用Spring事务管理,无需额外控制事务。
  • OXM模块:提供了一个对Object/XML映射实现,将Java对象映射成XML数据,或者将XML数据映射成java对象,Object/XML映射实现包括JAXB、Castor、XMLBeans和XStream。
  • JMS模块:用于JMS(Java Messaging Service),提供一套“消息生产者、消息消费者”模板用于更加简单的使用JMS,JMS用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

2.4 Web模块

该模块建立在ApplicationContext模块之上,提供了Web应用的功能,如文件上传、FreeMarker等。Spring可以整合Struts2等MVC框架。此外,Spring自己提供了MVC框架Spring MVC。

2.5 测试模块

Spring可以用非容器依赖的编程方式进行几乎所有的测试工作,支持JUnit和TestNG等测试框架。

3.核心之一:IOC

所谓的IOC就是控制反转,那么什么是控制反转,将控制反转拆分开理解,一半是控制,一半是反转:

  • 所谓的控制,控制的是创建对象过程
  • 所谓的反转,反转的是创建对象的主体,由程序员转化为容器

在spring当中,容器相当于一个单例工厂,里面的每一个对象称作为bean,我们只需在容器外部定义bean的创建过程,真正创建bean的过程是由容器负责的,在spring工程被启动,容器就会被初始化,所有的单例bean就会被实例化,这么去理解,我们将一个bean的构建由运行期提前至启动时期,这样如果一些bean的创建过程,或者bean和bean之间的依赖存在一些问题,那么容器启动的时候就会抛出异常。这也是fast-fail理论的最佳实践,我们可以提早发现问题解决问题,而不是在运行期才发现问题,采取停机维护,这样大大提高了项目的安全性。

大家都知道,我们的spring容器管理的是所有单例bean(这里我们仅仅考虑作用范围是单例的bean,毕竟spring中大部分的bean都是单例的),比如我们的controller、service,有了这个单例工厂,我们就可以轻松实现容器级别的单例,而不需要我们程序员在需要对象的时候再去手动创建bean,**如果将创建对象的过程交给程序员,每次使用对象就去new一个对象,会造成大量的对象被创建,特别是在高并发的情况下,此时,jvm中因为内存的暴增,会导致频繁的GC,甚至OOM。**有了Spring之后,所有对象的创建过程和耗时过程都在启动时期被创建,同时内存当中保存对象都是单例的,每次使用直接从容器中拿即可,这样内存的抖动不会过于严重。从这个角度上讲,Spring可以保证我们程序的可靠性和稳定性。

有了容器,可以帮助我们更好的实现解耦,在面向对象软件设计原则中,有一条设计原则叫做依赖倒置原则,即高层模块不能直接依赖于低层模块,而是依赖其抽象,低层模块需要实现其抽象(也就是我们三层架构中service层和dao层都要有接口),这样可以彻底的将高层模块和低层模块进行解耦,具体来说,在我们日常开发当中,Controller层一般不直接依赖service层,service层不直接依赖dao层,而是依赖各自的接口,而各层需要实现接口。在容器里面,我们需要按照接口的类型,将具体的实现类注入到容器里面,剩下的是交给容器来帮我们进行依赖注入,

依赖注入(DI):

如果容器里面bean和bean存在依赖关系,那么IOC容器需要自动去实现其依赖注入。

三种方式描述bean和bean的依赖关系:接口注入、setter注入和构造器注入

为了灵活的提供依赖注入,spring支持了更加灵活的注解注入:@Resource(基于bean的name进行注入;@Autowired(基于bean的type注入)。

4.核心之二:AOP

Spring AOP(Aspect-Oriented Programming)是Spring框架提供的一种面向切面编程的技术。它通过将横切关注点(例如日志记录、事务管理、安全性检查等)从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。

在Spring AOP中,切面(Aspect)是一个模块化的关注点,它可以跨越多个对象,例如日志记录、事务管理等。切面通过定义切点(Pointcut)和增强(Advice)来介入目标对象的方法执行过程。

切点是一个表达式,用于匹配目标对象的一组方法,在这些方法执行时切面会被触发。增强则定义了切面在目标对象方法执行前、执行后或抛出异常时所要执行的逻辑。

Spring AOP提供了以下几种类型的增强:

  1. 前置增强(Before Advice):在目标方法执行之前执行的逻辑。
  2. 后置增强(After Advice):在目标方法执行之后执行的逻辑,不管目标方法是否抛出异常。
  3. 返回增强(After Returning Advice):在目标方法正常返回时执行的逻辑。
  4. 异常增强(After Throwing Advice):在目标方法抛出异常时执行的逻辑。
  5. 环绕增强(Around Advice):在目标方法执行前后都可以执行的逻辑,它可以完全控制目标方法的执行。

Spring AOP通过使用动态代理技术,在目标对象方法执行时将切面的逻辑织入到目标对象的方法中。这样,我们可以在不修改原始业务代码的情况下,实现横切关注点的统一处理。

总而言之,Spring AOP是一种通过切面将横切关注点模块化的技术,它提供了一种简洁的方式来管理和重用跨越多个对象的关注点逻辑。

4.1 aop优点

使用Spring AOP的主要原因是它可以帮助我们更好地管理各种横切关注点,例如日志记录、事务管理、安全性检查等。以下是一些使用Spring AOP的优点:

  1. 模块化:Spring AOP将横切关注点从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。这样,我们可以更容易地维护代码,并且可以将同一个关注点的逻辑应用到多个方法或类中。
  2. 非侵入式:使用Spring AOP时,我们不需要修改原始业务逻辑代码,只需要在切点和增强中定义我们所需要的逻辑即可。这样,我们可以保持原始代码的简洁性和可读性。
  3. 可重用性:我们可以将同一个切面应用于多个目标对象进行横切处理。这样,我们可以提高代码的重用性,并且可以更加方便地维护和更新切面逻辑。
  4. 松耦合:AOP可以减少各个业务模块之间的耦合度,这是因为我们可以将某些通用的逻辑作为切面来实现,而不是直接在各个业务模块中实现。这样可以使得各个业务模块之间更加独立,从而提高代码的可维护性。

总之,使用Spring AOP可以帮助我们更好地管理和重用横切关注点逻辑,使得代码更易于维护和理解,并且可以提高代码的可重用性和灵活性。

4.2 核心概念

Spring AOP 的核心概念包括切面、连接点、通知、切点以及织入。下面我将对这些概念做一些简要的解释:

  1. 切面(Aspect):切面是一个模块化的横切关注点实现,它包括了连接点和通知。可以通过配置文件、注解等方式定义切面。
  2. 连接点(Joinpoint):程序中能够被切面插入的点,典型的连接点包括方法调用、方法执行过程中的某个时点等等。
  3. 通知(Advice):在连接点处执行的代码。通知分为各种类型,如前置通知、后置通知、环绕通知等。
  4. 切点(Pointcut):用于定义哪些连接点上应该应用通知。切点通过表达式进行定义,如匹配所有 public 方法或匹配某个包下的所有方法等。
  5. 织入(Weaving):指将切面应用到目标对象并创建新的代理对象的过程。织入可以在运行时完成,也可以在编译时完成。 Spring AOP 提供了两种织入方式:编译期织入和运行期织入。

除此之外,Spring AOP 还有其他常用的概念,如目标对象(Target)、代理对象(Proxy)等。目标对象是含有连接点的对象,而代理对象是 Spring AOP 创建的一个包含切面代码的对象。

以上就是 Spring AOP 的核心概念,它们共同构成了切面编程的基础。

4.3 spring aop JDK动态代理和cglib动态代理的区别

Spring AOP 支持两种类型的动态代理:JDK 动态代理和 CGLIB 动态代理。它们之间有以下区别:

  1. 基于类型:JDK 动态代理是基于接口的代理,而 CGLIB 动态代理是基于类的代理。
  2. 代理对象创建:JDK 动态代理通过 Java 自带的 java.lang.reflect.Proxy 创建代理对象,该对象必须实现一个或多个接口。CGLIB 动态代理通过字节码生成技术创建代理对象,无需目标类实现接口,直接继承目标类。
  3. 性能:JDK 动态代理在运行时需要使用反射,导致较低的性能。CGLIB 动态代理通过生成字节码,避免了反射,因此通常比 JDK 动态代理速度更快。
  4. 对象类型:JDK 动态代理只能代理具有接口的目标对象,不适用于没有接口的类。CGLIB 动态代理可以代理任何类,包括没有实现接口的类。
  5. 继承:JDK 动态代理只能代理目标对象的接口方法,不能代理其父类中的方法。CGLIB 动态代理可以代理目标类及其父类中的方法。

综上所述,选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体的需求和场景。如果目标对象实现了接口并且对性能要求较高,可以选择 JDK 动态代理;如果目标对象没有实现接口或者对性能要求不那么苛刻,可以选择 CGLIB 动态代理。默认情况下,Spring AOP 使用 JDK 动态代理,但在某些情况下会自动切换到 CGLIB 动态代理。