170324、Spring 处理器和Resource

1、Spring 框架允许开发者使用两种后处理器扩展 IoC 容器,这两种后处理器扩展 IoC 容器,这两种后处理器可以后处理 IoC 容器本身,或对容器中所有的 Bean 进行后处理。IoC 容器还提供了 AOP 功能,极好地丰富了 Spring 容器的功能。

2、Spring AOP 是 Spring 框架另一个吸引人的地方,AOP 本身是一种非常前沿的编程思想,它从动态角度考虑程序运行过程,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,能更好地抽离出各模块的交叉关注点。

3、Spring 的声明式事务管理正是通过AOP 来实现的。当然,如果我们仅仅想使用 Spring 的声明式事务管理,其实完全无须掌握 AOP,但如果我们希望开发出使用结构更优雅的应用,例如集中处理应用的权限控制、系统日志等需求,则应该使用 AOP 来处理。

两种后处理器

4、Spring 框架提供了很好的扩展性,除了可以与各种第三方框架良好整合外,其 IoC 容器也允许开发者进行扩展,这种扩展甚至无须实现 BeanFactory 或 ApplicationContext 接口,而是允许通过两个后处理器对 IoC 容器进行扩展。

Spring 提供了两种常用的后处理器:

  • Bean 后处理器:这种后处理器会对容器中的 Bean 进行后处理,对 Bean 功能进行额外加强。

  • 容器后处理器:这种后处理器对 IoC 容器进行后处理,对 Bean 功能进行额外加强。

5、实现 BeanPostProcessor接口的 Bean 后处理器必须实现postProcessBeforeInitialization、postProcessAfterInitialization这两个方法,这两个方法会对容器的 Bean 进行后处理,会在目标 Bean 初始化之前、初始化之后分别被回调,这两个方法用于对容器中的 Bean 实例进行增强处理。

6、Bean 后处理器是对 IoC 容器一种极好的扩展,Bean后处理器可以对容器中 Bean 进行后处理,而到底要对Bean 进行怎样的后处理则完全取决于开发者。Spring 容器负责把各 Bean 创建出来、Bean 后处理器(由开发者提供)可以依次对每个 Bean 进行某种修改、增强,从而可以对容器中 Bean 集中增加某种功能。

7、采用 BeanFactory 作为 Spring 容器时,必须手动注册BeanPostProcessor;如果采用 ApplicationContext 作为 Spring 容器,则无须手动注册 Bean 后处理器。ApplicationContext 可自动检测到容器中的 Bean 后处理器,自动注册。因此如果需要使用 Bean 后处理器,Spring 容器建议使用 ApplicationContext,而不是 BeanFactory。

8、如果需要对容器汇总某一批 Bean进行通用的增强处理,则可以考虑使用 Bean后处理器。

9、容器后处理器必须实现BeanFactoryPostProcessor 接口。实现该接口必须实现 postProcessBeanFactory 方法。由于postProcessBeanFactory 方法只是对 Spring 容器进行后处理,它并不需要完全替换 Spring 容器,因此该方法无需任何返回值。

10、类似于BeanPostProcessor,ApplicationContext 可自动检测到容器中的容器后处理器,并且自动注册容器后处理器。但若使用 BeanFactory 作为 Spring 容器,则必须手动调用该容器后处理器来处理 BeanFactory 容器。

11、Spring 没有提供ApplicationContextPostProcessor。也就是说,对于 ApplicationContext容器,一样使用BeanFactoryPostProcessor作为容器后处理器。

12、Spring 已提供如下几个常用的容器后处理器:

  • PropertyPlaceholderConfigurer:属性占位符配置器。

  • PropertyOverrideConfigurer:重写占位符配置器。

  • CustomAutowireConfigurer:自定义自动装配的配置器。

  • CustomScopeConfigurer:自定义作用域的配置器。

13、容器后处理器的作用域范围是容器级,它只是对容器本身进行处理,而不是对容器中的 Bean 进行处理;如果需要对容器中的 Bean 实例进行后处理,则应该考虑使用 Bean 后处理器,而不是使用容器后处理器。

14、Spring 提供了PropertyPlaceholderConfigurer 后处理器,可以将 Spring 配置文件中的部分元数据放在属性文件中设置,这种配置方式当然有优势:可以将部分相似的配置(比如说数据库的 URL、用户名和密码)放在特定的属性文件中,如果只需要修改这部分配置,则无须修改 Spring 配置文件,修改属性文件即可。

15、PropertyOverrideConfigurer是 Spring 提供的另一个容器后处理器,这个后处理器的作用比上面那个容器后处理器的功能更加强大:PropertyOverrideConfigurer 的属性文件指定的信息可以直接覆盖Spring 配置文件中的元数据。

16、在曾经的岁月里,Java 和 XML 是如此“恩爱”,许多人认为Java 是跨平台的语言,而 XML 是跨平台的数据交换格式,所以 Java 和 XML 应该是天作之和。在这种潮流下,以前的 Java框架不约而同地选择了 XML 作为配置文件。时至今日,也许是受Rails框架的启发,现在的 Java框架又都开始对 XML 配置方式“弃置不顾”了,几乎所有的主流 Java 框架都打算支持“零配置”特性了。

17、Rails 框架的处理比较简单,它采用一种所谓的“约定优于配置”的方式,它要求将不同组件放在不同路径下,而 Rails 框架中是加载固定路径下的所有组件。

18、Spring 没有采用“约定优于配置”的策略,Spring 依然要求程序员显示指定搜索哪些路径下的Java类,Spring 将会把合适的Java类全部注册成Spring Bean。

19、我们还可以通过为<component-scan.../>元素添加<include-filter.../>或<exclude-filter.../>子元素来指定 Spring类,只要位于指定路径下的Java 类满足这种规则,即使这些Java类没有使用任何Annotation标注,Spring 一样会将它们当成Bean类来处理

20、<include-filter.../>元素用于指定满足该规则的Java类会被当成Bean类处理,<exclude-filter.../>指定满足该规则的Java类不会被当成Bean 类处理。使用这两个元素都要求指定如下两个属性。

  • type:指定过滤器类型。

  • expression:指定过滤器所需要的表达式。

Spring 内建支持如下 4 种过滤器。

  • annotation:Annotation过滤器,该过滤器需要指定一个 Annotation 名,如 lee.AnnotationTest。

  • assignable:类名过滤器,该过滤器直接指定一个Java类。

  • regex:正则表达式过滤器,该过滤器指定一个正则表达式,匹配该正则表达式的Java类将满足该过滤规则,如 org.example.Default.*。

  • aspectj:AspectJ过滤器,如 org.example..*Service+。

21、当使用@Resource 修饰 setter 方法时,如果省略 name属性,则 name 属性默认是该 setter 方法去掉前面的 set 子串、首字母小写后得到的子串。例如,使用 @Resource 标注 setName()方法,则 Spring 默认会注入容器中名为 name 的组件。

22、当使用 @Resource 修饰 Field 时,如果省略 name 属性,则 name 属性默认与该 Field 同名。例如使用@Resource 标注 name Field,则 Spring 默认会注入容器中名为 name 的组件。

23、Spring 提供了@Autowired Annotation 来指定自动装配,使用 @Autowired 可以标注 setter 方法、普通方法、Field 和构造器等。

24、@Autowired 总是采用 byType 的自动装配策略,在这种策略下,符合自动装配类型的候选 Bean 实例常常有多个,这个时候就可能引起异常了(对于数组类型参数、集合类型参数则不会)。

25、为了实现精确的自动装配,Spring 提供了@QualifierAnnotation,通过使用@Qualifier,允许根据 Bean 标识来指定自动装配。

26、创建 Spring 容器时通常需要访问 XML配置文件,除此之外,我们可能有大量地方需要访问各种类型的文件、二进制流等——Spring 把这些文件、二进制流等统称为资源。

27、Spring 改进了 Java 资源访问的策略,Spring为资源访问提供了一个 Resource 接口,该接口提供了一个更强的资源访问能力,Spring框架大量使用了Resource来访问底层资源。

28、Resource本身是一个接口,是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。Resource 接口主要提供了如下几个方法。

  • getInputStream():定位并打开资源,返回资源对应的输入流。每次调用都返回新的输入流。调用者必须负责关闭输入流。

  • exists():返回 Resource 所指向的资源是否存在。

  • isOpen():返回资源文件是否打开,如果资源文件不能多次读取,每次读取结束时应该显示关闭,以防止资源泄露。

  • getDescription():返回资源的描述信息,用于资源处理出错时输出该信息,通常是全限定文件名或实际 URL。

  • getFile():返回资源对应的 File 对象。

  • getURL():返回资源对应的 URL 对象。

最后两个方法通常无须使用,仅在通过简单方式访问无法实现时,Resource 才提供传统的资源访问功能。Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑。

29、Resource 接口是 Spring 资源访问的接口,具体的资源访问由该接口的实现类完成。Spring 提供了Resource接口的大量实现类。

  • UrlResource:访问网络资源的实现类。

  • ClassPathResource:访问类加载路径里资源的实现类。

  • FileSystemResource:访问文件系统里资源的实现类。

  • ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类。

  • InputStreamResource:访问输入流资源的实现类。

  • ByteArrayResource:访问字节数组资源的实现类。

这些Resource实现类,针对不同的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。

30、ClassPathResource实例可使用ClassPathResource构造器显示地创建,但更多的时候它都是隐式创建的。当执行Spring的某个方法时,该方法接受一个代表资源路径的字符串参数,当Spring识别该字符串中包含classpath:前缀后,系统将会自动创建ClassPathResource对象。

31、FileSystemResource 实例可使用FileSystemResource构造器显示地创建,但更多的时候它都是隐式创建的。执行Spring的某个方法时,该方法接受一个代表资源路径的字符串参数,当Spring识别该字符串参数包含file:前缀后,系统将会自动创建FileSystemResource对象。

32、在默认情况下,JSP不能直接访问WEB-INF路径下的任何资源,而通过使用ServletContextResource就可让JSP页面直接访问WEB-INF下的资源了。

33、Spring提供了InputStreamResource来访问二进制输入流资源,InputStreamResource是针对输入流的Resource实现,只有当没有合适的Resource实现时,才考虑使用该InputStreamResource。通常情况下,优先考虑使用ByteArrayResource,或者基于文件的Resource实现。

34、与其他Resource实现不同的是,InputStreamResource是一个总是被打开的Resource,所以isOpen() 方法总是返回true。因此如果需要多次读取某个流,就不要使用InputStreamResource,InputStreamResource虽然是适应性很广的Resource实现,但效率并不好。

35、Spring 提供的ByteArrayResource用于直接访问字节数组资源,字节数组是一种常见的信息传输方式:网络Socket之间的信息交互,或者线程之间的信息交换等,字节数组都被作为信息载体。ByteArrayResource可将字节数组包装成Resource使用。

36、对于需要采用InputStreamResource访问的资源,可先从InputStream流中读出字节数组,然后从字节数组来创建ByteArrayResource。这样,InputStreamResource也可被转换成ByteArrayResource,从而方便多次读取。

37、Spring提供如下两个标志性接口。

  • ResourceLoader:该接口实现类的实例可以获得一个Resource实例。

  • ResourceLoaderAware:该接口实现类的实例将获得一个ResourceLoader的引用。

在ResourceLoader接口里有如下方法。

  • Resource getResouce(String location):该接口仅包含这个方法,该方法用于返回一个Resouce实例。ApplicationContext的实现类都实现ResourceLoader接口,因此ApplicationContext可用于直接获取Resource实例。

38、当Spring应用需要进行资源访问时,实际上并不需要直接使用Resource实现类,而是调用ResourceLoader实例的getResource() 方法来获得资源。ResourceLoader将会负责选择Resource 的实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来,这就是典型的策略模式。

39、以下是常见的前缀及对应的访问策略。

  • classpath:——以 ClassPathResource 实例来访问类加载路径下的资源。

  • file:——以 UrlResource 实例来访问本地文件系统的资源。

  • http:——以 UrlResource 例来访问基于HTTP协议的网络资源。

  • 无前缀——由ApplicationContext的实现类来决定访问策略。

40、ResourceLoaderAware接口则用于指定该接口的实现类必须持有一个ResourceLoader实例。类似于 Spring提供的BeanFactoryAware、BeanNameAware接口,ResourceLoaderAware接口也提供了一个setResourceLoader() 方法,该方法将由Spring容器负责调用,Spring容器会将一个ResourceLoader对象作为该方法的参数传入。

41、classpath*: 前缀仅对ApplicationContext有效。实际情况是:创建ApplicationContext时,分别访问多个配置文件(通过ClassLoader的getResources方法实现)。因此,classpath*: 前缀不可用于Resource,使用 classpath*: 前缀一次性访问多个资源是行不通的。

41、还有一种可以一次性装载多份配置文件的方式:指定配置文件时指定使用通配符。除此之外,Spring 甚至允许将classpath*: 前缀和通配符结合使用。

42、当FileSystemXmlApplicationContext 作为ResourceLoader使用时,FileSystemXmlApplicationContext会简单让所有绑定的FileSystemResource实例把绝对路径都当成相对路径处理,而不管是否以斜杠开头。如果程序中需要访问绝对路径,则不要直接使用 FileSystemResource或FileSystemXMLApplicationContext来指定绝对路径。建议强制使用 file: 前缀来区分相对路径和绝对路径。

原文地址:https://www.cnblogs.com/zrbfree/p/6708991.html