tiny-spring analysis

tiny-spring analysis

Foreword

Spring encountered in reading the source code (part of dependency injection and aspect-oriented programming part) a lot of confusion, a large class file structure, the complicated method calls polymorphic achieve lumpy, and let yourself get stuck, confused. 
Later, I noticed  code4craft  of  tiny-spring  project, implemented a miniature of Spring, provide basic support for IoC and AOP, the small but perfectly formed, cognitive Spring's clear a lot. This miniature frame structure including file names, method names are to achieve the reference Spring, Spring for the first reading of the learner, as an aid to study Spring should be able to benefit. 
In the study of  tiny-spring  time, learned a lot, some analysis of this micro-framework written down, the wording may be a bit disorder.

The paper is organized

  1. Achieve a first portion IoC container corresponding to the  tiny-spring  to  1 step-  to  step-5  section, five step realization of the basic IoC container support singleton type of the bean, including initialization, attributes injection, and dependency Bean injection, can be read configuration from XML, XML is no specific way to read in depth.
  2. AOP to achieve the second portion of the container corresponding to the  tiny-spring  of  step-6  into the  step-9  portion. step-10  does not support the analysis of cglib. This four step can be written using the syntax of AspectJ AOP, support interface agent. Taking into account only AspectJ syntax for implementing the  execution("***") Resolution section, not the main content, you can use Java regular expression roughly completed, so there is no attention to these details.

Bibliography 
"Spring combat" "Spring Inside"


 

 

Why not support GitHub TOC

 

Achieve IoC containers

 

File Structure

 

Resource

To  Resource interface to the core emanating several classes, it is used to solve the problem content IoC container came from, that is, where the configuration file read, how to read the configuration file.

The class name Explanation
Resource Interface that identifies an external resource. By  getInputStream() acquiring resource method input stream.
UrlResource Implement  Resource resource interface, access to resources through URL.
ResourceLoader Resource loading class. By  getResource(String) acquiring a method  Resouce object is to get  Resouce the main pathway.

Note:  There are some problems in the design, ResourceLoader direct return a  UrlResourcebetter approach is to declare an  ResourceLoader interface and then implement a  UrlResourceLoader class for loading  UrlResource.

 

BeanDefinition

To  BeanDefinition class as the core emanating several classes, are used to solve  Bean specific definition of the problem, including  Bean what the name is, what its type is that it gives the property which reference or by value, that is, how in the IoC container definition of a  Bean, so that the problem may be generated IoC container according to an example of this definition.

The class name Explanation
BeanDefinition This class holds the  Bean definition. Including  Bean name  String beanClassName, type Class beanClass, properties  PropertyValues propertyValues. Instance of a class may be generated depending on its type, may then be injected into the property. propertyValues Which contains one  PropertyValue entry, each entry in key-value pairs  String -  Objectrespectively corresponding to the names and types of instance attribute to be generated. In the Spring of the XML  property , the key is  key , the value is  value or  ref. For  value as long as the property is injected directly into the line, but  ref must first be resolved. Object If  BeanReference the type, then it is a reference, which stores the name referenced need to be parsed, converted to the corresponding actual  Object.
BeanDefinitionReader Parsing  BeanDefinition interface. By  loadBeanDefinitions(String) loading a class definition from address.
AbstractBeanDefinitionReader Implement  BeanDefinitionReader the abstract class interface (not embodied  loadBeanDefinitions, but the specification of the  BeanDefinitionReader basic structure). A built-in  HashMap rigistry, to save  String -  beanDefinition the pairs. A built  ResourceLoader resourceLoaderfor holding class loader. The intention is that, when used, its only  loadBeanDefinitions() pass a resource address, you can automatically call its class loader, and the resolve to  BeanDefinition save to  registry go.
XmlBeanDefinitionReader A specific implementation  loadBeanDefinitions() method, the class definition read from the XML file.
 

BeanFactory

To  BeanFactory interface the core emanating several classes, are for solving the IoC container has been acquired in  Bean the case definition, how to assemble the acquired  Bean instance problem.

The class name Explanation
BeanFactory Interfaces, identifying a IoC container. By  getBean(String) acquiring an object method
AbstractBeanFactory BeanFactory An abstract class that implements the basic structure specification IoC container, but to generate the  Bean specific implementation is left subclass implementation. IoC container structure: AbstractBeanFactory maintaining a  beanDefinitionMap hash table is used to define the class information storage ( BeanDefinition). Acquisition  Bean, if  Bean already present in the container is returned, otherwise the call to  doCreateBean a method for assembling a  Bean. (Called present in the container, the container means can  beanDefinitionMap obtain  BeanDefinition further through its  getBean() acquisition method  Bean.)
AutowireCapableBeanFactory Automatic assembly can be achieved  BeanFactory. In this plant, the realization of a  doCreateBean method of three steps: 1, by  BeanDefinition saving the information in the instance of an object class; 2, the object is stored in  BeanDefinition the order to prepare for the next acquisition; 3, its assembly properties. When assembling property, by BeanDefinition the maintenance of the  PropertyValues collection classes, the  String -  Value key injected into  Bean the properties to. If  Value the type  BeanReference is illustrated which is a reference (corresponding to the XML  ref), by  getBean obtaining them, and then injected into the property.
 

ApplicationContext

To  ApplicationContext interface the core emanating several classes, mainly on the front  Resouce ,  BeanFactory, BeanDefinition is encapsulated function, problem solving and IoC container obtained according to the address used.

The class name Explanation
ApplicationContext Marker interface, inherited  BeanFactory. Typically, a IoC container is to be achieved, it is necessary to pass  ResourceLoader a acquisition  Resource, including the configuration of the container, Bean the definition information. Next,  BeanDefinitionReader the reading  Resource of the  BeanDefinitioninformation. Finally,  BeanDefinition kept in  BeanFactory the container has been configured for use. Note that  BeanFactory only implements  Bean the assembly, acquisition, did not specify  Bean the source of that it is  BeanDefinition how loaded. The interface to  BeanFactory and  BeanDefinitionReader bonded together.
AbstractApplicationContext ApplicationContext Abstract implementation, the interior contains a  BeanFactory class. There are major methods  getBean() and  refresh() methods. getBean() Directly call a built-  BeanFactory in  getBean() method refresh() is used to achieve  BeanFactory refresh, which tells  BeanFactory of the use which resources ( Resource) to load the class definition ( BeanDefinition) information, which left sub-class implementation, to achieve loaded from different sources of different types of resources effect class definition.
ClassPathXmlApplicationContext 从类路径加载资源的具体实现类。内部通过 XmlBeanDefinitionReader 解析 UrlResourceLoader 读取到的 Resource,获取 BeanDefinition 信息,然后将其保存到内置的 BeanFactory 中。

注 1:在 Spring 的实现中,对 ApplicatinoContext 的分层更为细致。AbstractApplicationContext 中为了实现 不同来源 的 不同类型 的资源加载类定义,把这两步分层实现。以“从类路径读取 XML 定义”为例,首先使用 AbstractXmlApplicationContext 来实现 不同类型 的资源解析,接着,通过 ClassPathXmlApplicationContext 来实现 不同来源 的资源解析。 
注 2:在 tiny-spring 的实现中,先用 BeanDefinitionReader 读取 BeanDefiniton 后,保存在内置的 registry (键值对为 String - BeanDefinition 的哈希表,通过 getRigistry() 获取)中,然后由 ApplicationContext 把 BeanDefinitionReader 中 registry 的键值对一个个赋值给 BeanFactory 中保存的 beanDefinitionMap。而在 Spring 的实现中,BeanDefinitionReader 直接操作 BeanDefinition ,它的 getRegistry() 获取的不是内置的 registry,而是 BeanFactory 的实例。如何实现呢?以 DefaultListableBeanFactory 为例,它实现了一个 BeanDefinitonRigistry接口,该接口把 BeanDefinition 的 注册 、获取 等方法都暴露了出来,这样,BeanDefinitionReader 可以直接通过这些方法把 BeanDefiniton 直接加载到 BeanFactory 中去。

 

设计模式

注:此处的设计模式分析不限于 tiny-spring,也包括 Spring 本身的内容

 

模板方法模式

该模式大量使用,例如在 BeanFactory 中,把 getBean() 交给子类实现,不同的子类 **BeanFactory 对其可以采取不同的实现。

 

代理模式

在 tiny-spring 中(Spring 中也有类似但不完全相同的实现方式),ApplicationContext 继承了 BeanFactory 接口,具备了 getBean() 功能,但是又内置了一个 BeanFactory 实例,getBean() 直接调用 BeanFactory 的 getBean() 。但是ApplicationContext 加强了 BeanFactory,它把类定义的加载也包含进去了。

 

AOP 的实现

 

重新分析 IoC 容器

注:以下所说的 BeanFactory 和 ApplicationContext 不是指的那几个最基本的接口类(例如 BeanFactory 接口,它除了 getBean 空方法之外,什么都没有,无法用来分析。),而是指这一类对象总体的表现,比如 ClasspathXmlApplicationContextFileSystemXmlApplicationContext 都算是 ApplicationContext

 

BeanFactory 的构造与执行

BeanFactory 的核心方法是 getBean(String) 方法,用于从工厂中取出所需要的 Bean 。AbstractBeanFactory 规定了基本的构造和执行流程。

getBean 的流程:包括实例化和初始化,也就是生成 Bean,再执行一些初始化操作。

  1. doCreateBean :实例化 Bean。 
    a. createInstance :生成一个新的实例。 
    b. applyProperties :注入属性,包括依赖注入的过程。在依赖注入的过程中,如果 Bean 实现了 BeanFactoryAware 接口,则将容器的引用传入到 Bean 中去,这样,Bean 将获取对容器操作的权限,也就允许了 编写扩展 IoC 容器的功能的 Bean
  2. initializeBean(bean) : 初始化 Bean。 
    a. 从 BeanPostProcessor 列表中,依次取出 BeanPostProcessor 执行 bean = postProcessBeforeInitialization(bean,beanName) 。(为什么调用 BeanPostProceesor 中提供方法时,不是直接 post...(bean,beanName) 而是 bean = post...(bean,beanName) 呢?见分析1 。另外,BeanPostProcessor列表的获取有问题,见分析2。) 
    b. 初始化方法(tiny-spring 未实现对初始化方法的支持)。 
    c. 从 BeanPostProcessor 列表中, 依次取出 BeanPostProcessor 执行其 bean = postProcessAfterInitialization(bean,beanName)
 

ApplicationContext 的构造和执行

ApplicationContext 的核心方法是 refresh() 方法,用于从资源文件加载类定义、扩展容器的功能。

refresh 的流程:

  1. loadBeanDefinitions(BeanFactory) :加载类定义,并注入到内置的 BeanFactory 中,这里的可扩展性在于,未对加载方法进行要求,也就是可以从不同来源的不同类型的资源进行加载。
  2. registerBeanPostProcessors(BeanFactory) :获取所有的 BeanPostProcessor,并注册到 BeanFactory 维护的 BeanPostProcessor 列表去。
  3. onRefresh : 
    a. preInstantiateSingletons :以单例的方式,初始化所有 Bean。tiny-spring 只支持 singleton 模式。
 

IoC 实现的一些思考与分析

 
分析 1:AOP 可以在何处被嵌入到 IoC 容器中去?

在 Bean 的初始化过程中,会调用 BeanPostProcessor 对其进行一些处理。在它的 postProcess...Initialization 方法中返回了一个 Bean,这个返回的 Bean 可能已经不是原来传入的 Bean 了,这为实现 AOP 的代理提供了可能!以 JDK 提供的动态代理为例,假设方法要求传入的对象实现了 IObj 接口,实际传入的对象是 Obj,那么在方法中,通过动态代理,可以 生成一个实现了 IObj 接口并把 Obj 作为内置对象的代理类 Proxy 返回,此时 Bean 已经被偷偷换成了它的代理类。

 
分析 2:BeanFactory 和 ApplicationContext 设计上的耦合

BeanFactory 中的 BeanPostProcessor 的列表是哪里生成的呢?是在 ApplicationContext 中的 refresh 方法的第二步,这里设计上应该有些问题,按理说 ApplicationContext 是基于 BeanFactory 的,BeanFactory 的属性的获取,怎么能依赖于 ApplicationContext 的调用呢?

 
分析 3:tiny-spring 总体流程的分析

总体来说,tiny-spring 的 ApplicaitonContext 使用流程是这样的: 
1. ApplicationContext 完成了类定义的读取和加载,并注册到 BeanFactory 中去。 
2. ApplicationContext 从 BeanFactory 中寻找 BeanPostProcessor,注册到 BeanFactory 
维护的 BeanPostProcessor 列表中去。 
3. ApplicationContext 以单例的模式,通过主动调用 getBean 实例化、注入属性、然后初始化 BeanFactory 中所有的 Bean。由于所有的 BeanPostProcessor 都已经在第 2 步中完成实例化了,因此接下来实例化的是普通 Bean,因此普通 Bean 的初始化过程可以正常执行。 
4. 调用 getBean 时,委托给 BeanFactory,此时只是简单的返回每个 Bean 单例,因为所有的 Bean 实例在第三步都已经生成了。

 

JDK 对动态代理的支持

JDK 中几个关键的类:

类名 说明
Proxy 来自 JDK API。提供生成对象的动态代理的功能,通过Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法返回一个代理对象。
InvocationHandler 来自 JDK API。通过 Object invoke(Object proxy, Method method,Object[] args) 方法实现代理对象中方法的调用和其他处理。

假设以下的情况:

  • 对象 obj 实现了 IObj 接口,接口中有一个方法 func(Object[] args)
  • 对象 handler 是 InvocationHandler 的实例。

那么,通过 Proxy 的 newProxyInstance(obj.getClassLoader(), obj.getClass().getInterfaces(), handler,可以返回 obj 的代理对象 proxy

当调用 proxy.func(args) 时,对象内部将委托给 handler.invoke(proxy, func, args) 函数实现。

因此,在 handler 的 invoke 中,可以完成对方法拦截的处理。可以先判断是不是要拦截的方法,如果是,进行拦截(比如先做一些操作,再调用原来的方法,对应了 Spring 中的前置通知);如果不是,则直接调用原来的方法。

 

AOP 的植入与实现细节

 

在 Bean 初始化过程中完成 AOP 的植入

解决 AOP 的植入问题,首先要解决 在 IoC 容器的何处植入 AOP 的问题,其次要解决 为哪些对象提供 AOP 的植入 的问题。 
tiny-spring 中 AspectJAwareAdvisorAutoProxyCreator 类(以下简称 AutoProxyCreator)是实现 AOP 植入的关键类,它实现了两个接口:

  1. BeanPostProcessor :在 postProcessorAfterInitialization 方法中,使用动态代理的方式,返回一个对象的代理对象。解决了 在 IoC 容器的何处植入 AOP 的问题。
  2. BeanFactoryAware :这个接口提供了对 BeanFactory 的感知,这样,尽管它是容器中的一个 Bean,却可以获取容器的引用,进而获取容器中所有的切点对象,决定对哪些对象的哪些方法进行代理。解决了 为哪些对象提供 AOP 的植入 的问题。
 

AOP 中动态代理的实现步骤

 
动态代理的内容

首先,要知道动态代理的内容(拦截哪个对象、在哪个方法拦截、拦截具体内容),下面是几个关键的类:

类名 说明
PointcutAdvisor 切点通知器,用于提供 对哪个对象的哪个方法进行什么样的拦截 的具体内容。通过它可以获取一个切点对象 Pointcut 和一个通知器对象 Advisor
Pointcut 切点对象可以获取一个 ClassFilter 对象和一个 MethodMatcher 对象。前者用于判断是否对某个对象进行拦截(用于 筛选要代理的目标对象),后者用于判断是否对某个方法进行拦截(用于 在代理对象中对不同的方法进行不同的操作)。
Advisor 通知器对象可以获取一个通知对象 Advice 。就是用于实现 具体的方法拦截,需要使用者编写,也就对应了 Spring 中的前置通知、后置通知、环切通知等。
 
动态代理的步骤

接着要知道动态代理的步骤:

  1. AutoProxyCreator(实现了 BeanPostProcessor 接口)在实例化所有的 Bean 前,最先被实例化。
  2. 其他普通 Bean 被实例化、初始化,在初始化的过程中,AutoProxyCreator 加载 BeanFactory 中所有的 PointcutAdvisor(这也保证了 PointcutAdvisor 的实例化顺序优于普通 Bean。),然后依次使用 PointcutAdvisor 内置的 ClassFilter,判断当前对象是不是要拦截的类。
  3. 如果是,则生成一个 TargetSource(要拦截的对象和其类型),并取出 AutoProxyCreator 的 MethodMatcher(对哪些方法进行拦截)、Advice(拦截的具体操作),再,交给 AopProxy 去生成代理对象。
  4. AopProxy 生成一个 InvocationHandler,在它的 invoke 函数中,首先使用 MethodMatcher 判断是不是要拦截的方法,如果是则交给 Advice 来执行(Advice 由用户来编写,其中也要手动/自动调用原始对象的方法),如果不是,则直接交给 TargetSource 的原始对象来执行。
 

设计模式

 

代理模式

通过动态代理实现,见分析1中的内容,不再赘述。

 

策略模式

生成代理对象时,可以使用 JDK 的动态代理和 Cglib 的动态代理,对于不同的需求可以委托给不同的类实现。

 

为 tiny-spring 添加拦截器链

tiny-spring 不支持拦截器链,可以模仿 Spring 中拦截器链的实现,实现对多拦截器的支持。 
tiny-spring 中的 proceed() 方法是调用原始对象的方法 method.invoke(object,args)。(参见 ReflectiveMethodInvocation 类) 
为了支持多拦截器,做出以下修改:

    • 将 proceed() 方法修改为调用代理对象的方法 method.invoke(proxy,args)
    • 在代理对象的 InvocationHandler 的 invoke 函数中,查看拦截器列表,如果有拦截器,则调用第一个拦截器并返回,否则调用原始对象的方法。

Guess you like

Origin www.cnblogs.com/endv/p/11257621.html
Recommended