目录
IOC
一、IOC原理
依赖注入的方式
依赖倒置原则、IOC、DI和Spring IOC容器的关系
二、Spring IOC的应用
spring IOC支持的功能
spring IOC容器的核心接口
getBean方法的代码逻辑
Spring Bean的作用域
Spring Bean的生命周期
AOP
一、AOP的介绍和使用
AOP的三种织入方式
AOP的主要名词概念
Advice的种类
二、AOP的原理
AOP的实现:JdkProxy和Cglib
代理模式:接口+真实实现类+代理类
IOC(Inversion of Control):控制反转,是Spring Core最核心部分。
需要先了解依赖注入DI(Dependency Inversion):把底层类作为参数传递给上层类,实现上层对下层的“控制”。
IOC的另外一种实现方式:依赖查找DL(Dependency Lookup),Apache Avalon和EJB就是利用DL实现的IOC。DL相对DI而言是更为主动的方法,在需要的时候通过调用框架提供的方法来获取对象。获取时需要通过提供相关的配置文件路径、key等信息来确定获取对象的状态。DL已经被抛弃,因为它需要用户自己去使用API查找资源和组装对象,有侵入性。DI是当今IOC的主流实现方式。
Set注入:实现特定属性的public setter()方法来让IOC容器调用注入所依赖类型的对象
接口注入:实现特定的接口来让IOC容器注入所依赖类型的对象
构造函数注入:实现特定参数的构造函数,在创建对象时让IOC注入所依赖类型的对象
注解注入:通过Java注解机制,让IOC容器注入所依赖类型的对象。例如Spring框架里的Autowire标签都是能够实现注解功能的
依赖倒置原则是一种思想,大致含义是高层模块不应该依赖低层模块,两者都应该依赖其抽象。
有了依赖倒置原则的指导才有了IOC的思路,实现IOC思路离不开DI依赖注入的支撑。spring框架基于IOC提出了容器的概念,对于IOC来说,最重要的就是容器,容器管理着Bean的生命周期,控制着Bean的依赖注入。
因为采用依赖注入,在初始化过程中会写大量new,容器解决这个问题。容器自动对代码进行初始化,只需要维护一个Configuration(可以是xml,也可以是一段代码)而不用每次初始化时都要亲手去写一大段初始化的代码。IoC Container的第二个好处是我们在创建实例的时候不需要了解其中的细节。从最上层开始往下找依赖关系,到达最底层之后再往上一步一步new,类似深度遍历。
参考:Spring IoC和AOP的实现原理解析
spring启动时读取应用程序提供的bean配置信息,并在Spring容器中生成一份相应的bean配置表,根据注册表去实例化bean,装配好bean之间的依赖关系,为上层提供准备就绪的运行环境。spring提供配置文件描述bean以及bean之间的依赖关系,利用Java语言的反射功能实例化bean并建立bean之间的依赖关系。
依赖注入、依赖检查、自动装配、支持集合、指定初始化方法和销毁方法、支持回调方法(需要实现Spring接口,这种实现方式带有侵入性)
BeanFactory、ApplicationContext
BeanDefinition主要用来描述Bean的定义,Spring容器在启动的时候会将xml或者注解里bean的定义解析成spring内部的BeanDefinition。
BeanDefinitionRegistry接口提供向容器手工注册BeanDefinition对象的方法。
BeanFactory是Spring框架最核心的接口,提供IOC的配置机制;包含Bean的各种定义,便于实例化Bean;建立Bean之间的依赖关系;Bean生命周期的控制。
BeanFactory的体系结构:
BeanFactory与ApplicationContext的比较
ApplicationContext是BeanFactory的子接口之一。BeanFactory是spring框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者
ApplicationContext继承至多个接口:
1)BeanFactory:能够管理装配Bean
2)ResourcePatternResolver:能够加载资源文件
3)MessageSource:管理Message,能够实现国际化等功能
4)ApplicationEventPublisher:能够注册监听器,实现监听机制
Bean如何装载到IOC中,如何从IOC获取Bean实例并使用,IOC容器的创建配置,Bean如何获取的底层原理
1)转换beanName
2)从缓存中加载实例
3)实例化Bean
4)检测parentBeanFactory
5)初始化依赖的Bean
6)创建Bean
singleton:Spring的默认作用域,容器里拥有唯一的Bean实例。适合无状态的Bean
prototype:针对每个getBean请求,容器都会创建一个Bean实例。适合有状态的Bean(把Bean设置成prototype作用域需谨慎,频繁创建和销毁Bean是有开销的)
web容器支持另外三种作用域
request:会为每个Http请求创建一个Bean实例
session:会为每个session创建一个Bean实例
globalsession:会为每个全局Http Session创建一个Bean实例,该作用域仅对Portlet有效
创建过程
1)实例化Bean对象以及设置Bean属性
2)如果通过各种Aware接口声明依赖关系,则会注入Bean对容器基础设施层面的依赖。Aware接口是为了感知到自身的一些属性。容器管理的Bean一般不需要了解容器的状态和直接使用容器。在某些情况下,就需要在Bean中设定对容器的感知。Spring IOC容器也提供了该功能,它是通过特定的Aware接口来完成的。例如BeanNameAware可以在Bean中得到它在IOC容器中的Bean的实例的名字。
3)调用BeanPostProcessor的前置初始化方法postProcessorBeforeInitialization,主要作用是在Spring完成实例化之后对Spring容器实例化的Bean添加自定义的处理逻辑。
4)如果实现InitializingBean接口,则会调用afterPropertiesSet方法,做一些属性被设置后的自定义的事情。
5)调用Bean自身定义的init方法,做一些初始化相关的工作。
6)调用BeanPostProcessor的后置初始化方法postProcessAfterInitialization做Bean实例初始化化后的自定义工作。
7)Bean初始化完毕
销毁过程
若实现了DisposableBean接口,则会调用destory方法
若配置了destroy-method属性,则会调用其配置的销毁方法
面向切面编程。软件工程有一个基本原则关注点分离,不同的问题交给不同的部分去解决,每部分专注解决自己的问题。面向切面编程AOP正是此种技术的体现。代码主要实现某种特定的业务逻辑,但往往不能专注于业务逻辑,写业务逻辑代码的同时还要写事务管理、缓存、日志等等通用块的功能。每个通用的功能都要和这些业务功能混在一起。为了将业务功能的关注点和通用化功能的关注点分离开来,就出现了AOP的技术。通用化功能代码的实现,对应的就是所谓的切面。业务功能代码和切面代码分开后,架构变得高内聚低耦合。确保功能的完整性,切面最终需要被合并到业务中(Weave)
编译时织入,需要特殊的Java编译器,如AspectJ
类加载时织入,需要特殊的Java编译器,如AspectJ和AspectWerkz
运行时织入,Spring采用的方式,通过动态代理的方式,实现简单
Aspect通用功能的代码实现
Target被织入Aspect的对象
Join Point可以作为切入点的机会,所有方法都可以作为切入点。
Pointcut Aspect实际被应用在Join Point,支持正则。
Advice类里面的方法以及这个方法如何织入到目标方法的方式
Weaving AOP的实现过程。AOP实现的过程中将切面应用到实际对象,从而创建一个新的代理对象的过程。
前置通知(Before)
后置通知(AfterReturning)
异常通知(AfterThrowing)
最终通知(After)
环绕通知(Around)
由AopProxyFactory根据AdvisedSupport对象的配置来决定,默认策略如果目标类是接口,则用JDKProxy来实现,否则用后者。
JDKProxy通过反射接收被代理的类,并且要求被代理的类必须实现一个接口,JDK动态代理的核心是InvocationHandle接口和Proxy类。Cglib以继承的方式动态生成目标类的代理。目标类没有实现接口,SpringAOP就会使用Cglib来动态代理目标类,Cglib全称code generation lib,代码生成的类库,在运行时动态生成某个类的子类,通过修改字节码来实现代理。Cglib通过继承的方式实现动态代理,某个类被标记为final它是无法使用Cglib来做动态代理的。Cglib底层借助ASM实现,ASM是一种能够操作字节码的框架。反射机制在生成类的过程中比较高效;ASM在生成类之后的执行过程中比较高效。
真实实现类和代理类要实现接口,实例化时使用代理类。Spring AOP需要做的是生成一个代理类,替换掉真实实现类来对外提供服务。
Spring里代理模式的实现:真实实现类的逻辑包含在了getBean方法里,getBean方法返回的实际上是Proxy类的实例。Proxy类实例是Spring采用JDK Proxy或者CGLib动态生成的。getBean方法用于查找或者实例化容器中的Bean,这也是为什么Spring AOP只能作用于Spring容器中Bean的原因。对于不是使用IOC管理的对象,Spring AOP无能为力。