spring aop 必须的包 及里面用到的东西_Spring 原理初探——IoC、AOP

c4bfa93c-2728-4f69-9155-edc1eff31da0

前言

众所周知, 现在的 Spring 框架已经成为构建企业级 Java 应用事实上的标准了,众多的企业项目都构建在 Spring 项目及其子项目之上,特别是 Java Web 项目。

Spring 的两个核心概念是 IoC(控制反转)和 AOP(面向切面编程)。想了解 Spring 的工作原理,毫无疑问,首先要从这两个概念的 Spring 实现入手。但是 Spring 源码浩如烟海,里面掺杂了太多的实现细节,入门可谓极其困难。当我正苦于难以入门时,好友介绍了 tiny-spring 这个开源项目,这个项目用了不到千行的代码,就将 Spring 的 IoC、AOP 的核心流程实现完毕,真是居家旅行、吹逼面试之必备呀!

废话少说,我们开始吧!

目录结构

在 github 上 clone 下项目来之后,我们关注 src 文件夹,其余的是一些爱好者提的注释 PR,恰巧被作者 merge 了,不必理会。目录结构是这样的:

0263c6d7b2c74039a6ebfafba09a7d2b

1.aop包,顾名思义,实现了 Spring 的 AOP 功能,可以通过 bean 的自动 AOP 切入,文件稍多,暂时先不展开。 2.bean.factory包,通过BeanFactory、AbstractBeanFactory、AutowireCapableBeanFactory三个类,实现了BeanFactory的核心功能,详情稍后讲解。 3.bean.io包定义了资源加载相关的抽象概念,这里的资源包括 xml 配置文件等。 4.bean.xml包中只包含一个类:XmlBeanDefinitionReader,主要负责在 xml 配置文件中读取 bean 定义。 5.bean包其他类,定义了 BeanDefinition 等核心概念,详情后讲。 6.context包定义了ApplicationContext的核心概念。 7.BeanReference指的是引用类型的 Bean,而不是实体类。

IOC–浮沙筑台之根基

IOC(控制翻转)是一种编程范式,可以在一定程度上解决复杂系统对象耦合度太高的问题,并不是 Spring 的专利。IOC 最常见的方式是 DI(依赖注入),可以通过一个容器,将 Bean 维护起来,方便在其他地方直接使用,而不是重新 new。可以说,IOC 是 Spring 最基本的概念,没有 IOC 就没有 Spring。

为什么 DI 可以起到解耦的作用?

一个软件系统包含了大量的对象,每个对象之间都有依赖关系,在普通的软件编写过程中,对象的定义分布在各个文件之中,对象之间的依赖,只能通过类的构造器传参,方法传参的形式来完成。当工程变大之后,复杂的逻辑会让对象之间的依赖梳理变得异常困难。

在 Spring IOC 中,一般情况,我们可以在 XML 文件之中,统一的编写 bean 的定义,bean 与 bean 之间的依赖关系,从而增加逻辑的清晰度。而且,bean 的创建是由 Spring 来完成的,不需要编程人员关心,编程人员只需要将精力放到业务的逻辑上面,减轻了思维的负担。

在tiny-spring里面,整个beans和context包都是用来实现IOC的。

beans包实现的核心关注点是BeanFactory,BeanFactory也叫作 Bean 容器,顾名思义,是用来盛放、管理 bean 的。

context包实现的核心关注是ApplicationContext,ApplicationContext也是用来获取 Bean 的,但是它更高层,它的面向用户是 Spring 的使用者,而 BeanFactory 面向的用户更多是 Spring 开发者。BeanFactory 定义了 Bean 初始化的流程,ApplicationContext 定义了从 XML 读取,到 Bean 初始化,再到使用的过程。

Bean 在哪定义?

刚才有说到,Spring 通常通过 xml 文件,来统一的描述 bean,bean 与 bean 的依赖关系。所以说,bean 的定义表述,发生在 xml 配置文件之中。这个 XML 文件就是我们需要读取的资源文件。

因此,首要任务就是研究与读取 XML 资源文件相关的类。

bean.io中存放的是读取资源文件的抽象概念。其中包含了三个类或者接口:

1.Resource接口,这个接口只有一个方法,InputStream getInputStream() throws IOException;。实现这个接口的类就是一个抽象的资源,可以获取这个资源的输入流,从而获取其中的内容。 2.UrlResource类,这个类实现了Resource接口,通过构造器传入一个 url 地址,代表的是这个 url 所对应的文件。 3.ResourceLoader类,只有一个方法,public Resource getResource(String location)。输入 url 的文件地址(并不是真正的 URL 格式地址),来获取 Resource。

通过分析上面三个类、接口,我们知道,这个包完成了一个任务:通过ResourceLoader这个类,获取某一个地址的Resource,从而获取这个文件的输入流。因为使用了 Resource 概念,可以使用网络文件或者本地文件。

Bean 如何定义?

1.BeanDefinition是 Bean 定义的核心概念,BeanDefinition包含了:bean 的对象、bean 的类类型、bean 的名字,bean 的所有属性。这个类对 bean 的基本信息做好一个包装。 2.BeanDefinitionReader接口,只有一个方法:void loadBeanDefinitions(String location) throws Exception;,实现这个接口的类,具有将某一个文件中的所有 bean 定义载入的功能。所以BeanDefinitionReader定义了,在哪载入 bean 定义,至于载入到哪里、如何载入,稍后看具体实现。 3.AbstractBeanDefinitionReader抽象类,上面刚说了实现了BeanDefinitionReader接口的类,具有将某一个文件中描述的 bean 定义载入的功能,AbstractBeanDefinitionReader就实现了这样一个抽象功能。它的作用就是定义,载入到哪和如何载入的问题。在这个类里面,有两个属性:Map registry;和ResourceLoader resourceLoader;。registry是一个注册表,他保存的就是所有的 Bean 定义,Map 结构,key 是 bean 的名字,value 就是 BeanDefinition。resourceLoader描述了如何载入。 4.XmlBeanDefinitionReader这是beans.xml包里面的唯一一个方法,也是最重要的方法之一。它继承了AbstractBeanDefinitionReader,实现了所有方法,解决了 bean 定义中:在哪载入、如何载入、载入到哪的三个大问题。这个类面向用户的方法有两个,一个是loadBeanDefinitions,毫无疑问,这个是必须的。另一个是getRegistry用来获取 bean 注册表,得到所有 bean 的信息,registry 是 bean 们在内存中实际的家。但是这个getRegistry方法并不是面向用户的,而是面向 ApplicationContext 的。 5.PropertyValue和PropertyValue代表一种抽象概念,在 xml 中,bean 的属性包括属性名和属性对象,PropertyValue就是这么一个实体。 6.BeanReference代表的是 Bean 的属性不是真实对象,而是另一个 bean 的引用。

Bean 的组装全过程

上面两部分是铺垫,而 BeanFactory 才是重点对象。beans.factory包中有三个类用来定义 BeanFactory 相关的概念。

1.BeanFactory接口,只有一个方法:Object getBean(String name) throws Exception;,实现这个接口的类,就具有了得到一个 bean 的能力。 2.AbstractBeanFactory类,较为复杂。详情后讲。 3.AutowireCapableBeanFactory继承了AbstractBeanFactory,实现了 applyPropertyValues 方法,通过反射,将 bean 的所有属性,通过 set 方法注入进去。

AbstractBeanFactory有三大属性:

_beanDefinitionMap,类似于 registry,但是他是 BeanFactory 里面私有的,代表的是这个 BeanFactory 里面暂时有哪些 bean 定义。

_beanDefinitionNames代表里面,这个 BeanFactory 里面有哪些 bean(名字)。 *beanPostProcessors,代理处理器,AOP 会用到,详情后讲。

AbstractBeanFactory实现了几大功能:

_getBean,这是主要功能,可以获取一个 Bean 对象。

_registerBeanDefinition,面向 ApplicationContext,用来将 XML 配置文件里面的 bean 定义注册到 BeanFactory 里面。

_preInstantiateSingletons,面向 ApplicationContext,用来在开始的时候,将所有的 bean 都初始化完毕,避免懒加载。

_addBeanPostProcessor添加代理处理器。 *getBeansForType,在 BeanFactory 里面,获取某一个类型的所有 bean。

经过上面的分析,我们可以知道 BeanFactory 完成了 Bean 初始化的整个流程。BeanFactory 的工作流程如下:

  1. getBean, 在 beanDefinitionMap 里面得到 bean,如果没有的话,先初始化。(为什么会没有,因为 ApplicationContext 读取 xml 文件时候,只是给 BeanDefinition 服了类类型,并没有赋值对象,这个对象还是需要 BeanFactory 通过反射生成的)。
  2. createBeanInstance,通过反射,根据 BeanDefinition 的类对象新建实体对象 -> 将得到的 bean 对象赋值给 beandefinition,然后将 BeanDefinition 里面的属性都注入到 Bean 里面,这就完成了 doCreateBean。
  3. initializeBean 就是调用 BeanPostProcessor 的 postProcessBeforeInitilizztion 方法和 postProcessAfterIntilizatin 方法,获取新的 bean,这里会在 aop 中用到。

好了,到这 BeanFactory 就讲完了,下面是更重要的 ApplicationContext。

ApplicationContext - 用户与 BeanFactory 之间的桥梁

beans.context包有三个类、接口,完成了 ApplicationContext 的基本功能。

  1. ApplicationContext 接口,没有任何方法,只是继承了 BeanFactory 接口,暗示 ApplicationContext 与 BeanFactory 都是获取 Bean 的地方。 2.AbstractApplicationContext抽象类,首先,它的构造函数接收入参 BeanFactory,所以说 ApplicationContext 内部具有一个 BeanFactory。类似于一种装饰器模式,但不是装饰器模式,类似于代理模式,但也不是代理模式。fresh 方法分为三个步骤:1.loadBeanDefinitions,这个是一个模板方法,需要子类实现,它的作用就是从某一个地方读取 BeanDefinition,然后写入到 ApplicationContext 自己的 BeanFactory 里面,这就是 ApplicationContext 与 BeanFactory 之间的联系,也就是 ApplicationContext 还负责了读取定义。2. registerBeanPostProcessors,这个就是在 BeanFactory 里面找到 BeanPostProcessor,然后将他们放到 BeanFactory 的 beanPostProcessors 容器里面,方便 BeanFactory 初始化使用。3. onRefresh 初始化一遍所有的 bean。 3.ClassPathXmlApplicationContext实现了 loadBeanDefinitions 的方法,将 xml 文件和 BeanFactory 结合在一起。

总结 - ApplicationContext 初始化流程

2091e630cd9945af98a4c168da88effc

ApplicationContext 初始化流程

总结 - ApplicationContext 获取 bean 流程

f2010c50452f463ca1c473e985d25eef

AOP–移花接木之魔法

上一节,讲完了 Spring IOC 的整个流程,也就是 bean 从定义获取,到得到 bean 之间的整个流程。本节,我们接触一下 Spring 另一个重要概念,AOP。AOP 用途十分广泛,其中 Spring 内部的声明式事务和拦截器都是利用了 AOP 的强大威力,才得以优雅的实现。

AOP 是什么呢,简单来说,它可以让编程人员在不修改对象代码的情况下,为这个对象添加额外的功能或者限制。

很熟悉吧,这就是代理模式!

Java 中存在两种代理模式:

一种叫静态代理,就是通过接口继承复用的方式来完成的, 代理类与被代理对象实现相同的接口,然后代理类里面会拥有一个被代理对象,代理类与被代理对象相同的方法,活调用被代理对象的方法,不过中间会加以限制,您翻开任何一本设计模式相关的书,翻到代理模式这一节,讲的就是它了。

另一种叫做动态代理,动态代理就是允许我们在程序运行过程中,为动态生成的对象,动态的生成代理。显然,这比静态代理灵活太多了。

Java 默认提供了动态代理的实现方式,但是有限制,它要求被代理对象必须实现某一个接口。为了突破这一限制,为普通类也可以提供代理,CGLib 这个库横空出世。

因为 AOP 涉及的知识较为复杂,所以我先将背景知识介绍一下。

  1. Java 动态代理,就是 Java 本身提供的代理实现,要求对象必须实现某一个接口。
  2. CGLib 库,为 Java 提供了,为普通类提供代理的功能。
  3. aopalliance,aop 联盟包,统一类 aop 编程的一些概念,这个包里没有具体的类实现,而是定义了几个重要的概念接口,具体的 aop 实现,要遵从这些接口编程,才能达到一定的通用性。
  4. aspectj 包,实现了,通过一种固定的编程语言,通过这种简单的编程语言,我们可以定位到被代理的类,自动完成代理。

在 aopallicance 里面,定义了几个核心概念:

  • Advice,增强,实现这个接口,说明这个类负责某一种形式的增强。
  • Joinpoint,连接点,表示切点与目标方法连接处的信息。
  • MethodInterceptor,继承了 Interceptor 接口,而 Interceptor 继承了 Advice 接口,是一种 Advice,但是有一个方法 invoke。这个方法需要一个参数 MethodInvocation。
  • MethodInvocation,表示的是连接点的信息以及连接点函数的调用。

结合上面的信息,我们发现,其实 MethodInterceptor 的 invoke 方法,调用的就是 MethodInvocation 的 proceed 方法,而这个 proceed 方法呢,应该调用的肯定是 Method.invoke 方法。所以,这是一种变相调用 method.invoke 的方式。为什么这样做呢,猜一猜的话,肯定是为了代码的复用,哈哈哈,这是废话。

在 Spring 中,还定义了几个核心概念:

  • Pointcut,切点,可以定位类以及方法。
  • Advisor,可以获取一个增强。
  • PointcutAdvisor,定义了哪些方法,具有什么类型的增强。
  • MethodMatcher,表示某一个方法是否符合条件。
  • ClassFilter,定义了某个类是否符合条件。
  • TargetSource,被代理的对象,包括对象本身,类类型、所有接口。
  • AdvisedSupport,代理相关的元数据,包括被代理的对象,增强等。
  • ProxyFactory,代理工厂,可以获得一个代理对象,同时具有 AdvisedSupport 的所有特性。
  • Cglib2AopProxy,使用 cglib 实现的动态代理类,继承了 AbstractAopProxy 抽象类,这个类的主要方法就是 getProxy,通过什么呢,通过 AdvisorSupport。
  • ReflectiveMethodInvocation,可以获取连接点的信息,代理的对象等。
  • JdkDynamicAopProxy,和 Cglib2AopProxy 类一个作用,通过 AdvisorSupport 来 getProxy,不过是使用 Java 自带的动态代理实现的。

其中,ProxyFactory 是获取一个代理对象的直接工厂,而这个代理对象,可以通过 Cglib2AopProxy 产生,也可以通过 JdkDynamicAopProxy 产生。

Spring AOP 之所以能够为动态生成的 Bean 提供代理,得益于 PostProcessor 接口。我们会议 IOC 初始化流程中,最后一部,就是得到 BeanFactory 之中所有继承了 PostProcessor 接口的 bean,调用它们的 postProcessBeforeInitilization、postProcessAfterInitilization 方法,来代理 bean,生成新的 bean。

基于这个突破口,我们只需要在 xml 配置文件中,放入 PostProcessor 对象,Spring 就会自动的用这写对象,来代理真正的对象。

8c2fbfe0735b485e94f5e11ef011a406

在这里,我们的对象是 AspectJAwareAdvisorAutoProxyCreator。

在这个对象的方法中,逻辑是这样的,找到 xml 里面所有切面 bean,然后在这些 bean 里面,找到符合被代理类的切面 bean,找到切面 bean 之后,就可以获得增强,切点等,于是可有构造一个 AdvisorSupport,知道了 AdvisorSupport,我们就能够通过 proxyFactory 来获取代理了。

至于如何这个类切面是用来切入代理类的,这个就要交给 PointCut 来实现了,pointcut 有很多实现方式,这里我们用的是 aspectj。具体这个类我就不细讲了。

到目前位置,我自己已经将整个 AOP 的流程搞清楚了,下面通过流程图的形式展示出来:

1636a892c08447cb8cfe21934d51a979

作者: WindMt

来源: WindMt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/543272.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android平台和java平台 DES加密解密互通程序及其不能互通的原因

为什么80%的码农都做不了架构师?>>> 网上的demo一搜一大堆,但是,基本上都是一知半解(包括我)。为什么呢?我在尝试分别在两个平台加密的时候,竟然发现Android DES 加密和Java DES加密…

PDM系统服务器管理,基于PDM的异地协同设计系统

基于PDM的异地协同设计系统随着经济全球化的不断发展,产品设计分工越来越细,产品协同设计团队越来越分散。由于产品设计的需要,分布在不同地方的设计人员和其他相关人员都要参与产品的开发过程,各自承担相应的设计任务&#xff0c…

flex 下对齐_Flex 布局示例

感谢阮一峰老师的教程http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html本示例将教程上所有的布局都简单的实现了一遍,并保存GitHub上面https://github.com/JailBreakC/flex-box-demo​github.com容器的属性1、flex-direction属性flex-direction属性决定主…

python读csv最快方法_使用Python读写csv文件的三种方法

Python读写csv文件觉得有用的话,欢迎一起讨论相互学习~Follow Me前言逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件…

url过滤怎么解除_Shiro-实战(四)---过滤器机制

1 简介Shiro使用了与Servlet一样的Filter接口进行扩展 1.1 NameableFilterNameableFilter给Filter起个名字,如果没有设置默认就是FilterName 当我们组装拦截器链时会根据这个名字找到相应的拦截器实例1.2 OncePerRequestFilter用于防止多次执行Filter,也就是说一次请…

django使用LDAP验证

1.安装Python-LDAP(python_ldap-2.4.25-cp27-none-win_amd64.whl)pip install python_ldap-2.4.25-cp27-none-win_amd64.whl 2.安装django-auth-ldap(django-auth-ldap-1.2.8.tar.gz)(下载:https://pypi.py…

风变的python课程怎么样_风的解释|风的意思|汉典“风”字的基本解释

【戌集下】【風】 風康熙筆画:9 部外筆画:0〔古文〕 𠙊 𠙈凬飌檒【唐韻】方戎切【集韻】方馮切【正韻】方中切, 𠀤音楓。風以動萬物也。【莊子齊物論】大塊噫氣,其名爲風。【河圖】風者&#x…

getprivateprofilestring读不到数据_Tomcat NIO(11)请求数据读取

在上一篇文章里我们主要介绍了 tomcat io 线程中涉及到的主要核心类,包括 AbstractProcessorLight,Http11Processor,CoyoteAdapter,这里主要介绍对于请求数据的读取。对于 tomcat 请求数据的读取来说,可以分为请求行的…

企业办公自动化系统_OA系统的核心功能有哪些?分析当下OA系统的缺陷以及相关解决方案...

什么是OA系统,OA是英语Office Automation的简称,字面意思是办公自动化,即将计算机、通信等现代化技术运用到传统办公中,进而形成的一种新型办公方式。它利用现代化设备和信息化技术,代替了办公人员传统的部分手动或重复…

python whl_python whl是什么文件

在安装whl文件之前,我们需要先搞清楚什么是whl文件,whl格式本质上是一个压缩包,里面包含了py文件,以及经过编译的pyd文件。使得可以在不具备编译环境的情况下,选择合适自己的python环境进行安装。在Win7下安装whl文件&…

android activity生命周期_Android岗高频面试题合一集,看你能答出几题?

前言面试季黄金时期来袭,想必许多朋友在这时有找工作、换工作、跳槽涨薪等想法,但你们都有一个相同的过程那就是面试。大家肯定想知道面试时都问哪些面试相关问题,所以就去网上查找题去刷题,为了省去大家找题的时间,小…

java web判断服务器是否是本机

1,如何获取浏览器的ip Java代码 /*** * 获取客户端ip地址(可以穿透代理) * param request * return */ public static String getClientIpAddr(HttpServletRequest request) { String ip request.getHeader("X-Forwarded-For"…

数据字典简单例子_Python学习100天-Day14(数据分析篇-pandas02)

Day14-读取文件接着上一天的内容,我在学习的过程中发现,DF数据还可以利用几个Series数据组成,具体看一个例子:两种形式,第一种是直接对Series创建DF对象,得出的结果。第二种是通过字典的形式创建DF对象&…

把百度网站设为首页_网站百度推广效果好不好?怎样才能把网站推广到百度首页?...

在移动互联网营销的过程中,有着各式各样的营销手段,也有着丰富多彩的营销平台,而百度平台是大家非常熟悉的,它拥有着亿万用户,几乎小伙伴们每天都需要通过百度查找各种资讯,来满足自己的需求,丰…

ZZULI 1876: 蛤玮的项链 Hash + 二分

Time Limit: 6 Sec Memory Limit: 128 MBSubmit: 153 Solved: 11SubmitStatusWeb BoardDescription 蛤玮向心仪的妹子送了一条项链,这条项链是由小写字母构成的首尾相接的字符串,妹子看了看项链对蛤玮说,"我希望它是对称的",蛤玮想了想之后决定,从项链上截取出一段…

python调用菜单响应事件_Python处理菜单消息操作示例【基于win32ui模块】

本文实例讲述了Python处理菜单消息操作。分享给大家供大家参考,具体如下: 一、代码 # -*- coding:utf-8 -*- #! python3 import win32ui import win32api from win32con import * from pywin.mfc import window class MyWnd(window.Wnd): def __init__ (…

全连接条件随机场_条件随机场CRF简介

作者:Ravish Chawla编译:ronghuaiyang导读一个简单明了的对条件随机场的说明,给大家一个非常直观的印象,CRF到底是个什么东西,能干什么用。条件随机场是一种用于序列预测的判别模型。它们使用来自以前标签的上下文信息…

PHP文件下载过滤类

Java代码 <?php /** $filename index.php; $download new Download(php,exe,html, false); if (!$download->downloadfile($filename)) { echo $download->getErrorMsgs(); } */ class Download { private $debug false; private $errorMsg ; …

解决outlook2013设置错误无法启动

今天在新的机器上安装office 2013&#xff0c;在进行outlook2013账户设置时&#xff0c;账户设置错误导致无法正常启动outlook。 查询相关资料发现可以通过&#xff0c;控制面板用户帐户和家庭安全—邮件&#xff0c;重新设置账户即可。 相关操作如下图&#xff1a;

论文公式编号右对齐_论文不会写?最详细的论文排版技巧

说明本文根据严思奇在2017年在"重庆邮电大学硕士学位论文"撰写期间的粗陋经验总结而作。使用的word版本为word2016。在使用时&#xff0c;首先应该根据第2章&#xff0c;按照顺序设置好所有需要全局设定的内容。然后再论文写作中&#xff0c;会依次碰到参考文献、公式…