深入使用Spring(二)

一、资源访问

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

         在Sun所提供的标准API里,资源访问通常由java.net.URL和文件IO来完成,尤其是当我们需要访问来自网络的资源时,通常会选择URL类。

         Spring改进了Java资源访问的策略,Spring为资源访问提供了一个Resource接口,该接口提供了更强的资源访问能力,它是具体资源访问策略的抽象,也是所有资源访问类所实现的接口(Resource不仅可在Spring的项目中使用,也可直接作为资源访问的工具类使用,即即使不使用Spring框架,也可以使用Resource作为工具类用来代替URL)


深入使用Spring(二)
         1、UrlResource:访问网络资源,是java.net.URL类的包装,主要用于访问之前通过URL类访问的资源对象。URL资源通常应该提供标准的协议前缀。file:用于访问文件系统;http:用于通过HTTP协议访问资源;ftp:用于通过FTP协议访问资源等。

         2、ClassPathResource:访问类加载路径下的资源,相对于其他的Resource实现类,其主要优势是方便访问类加载路径里的资源,尤其对于Web应用,ClassPathResource可自动搜索位于WEB-INF/classes下的资源文件,无须使用绝对路径访问。

        3、FielSystemResource:用于访问文件系统资源

        4、ServletContextResource:用于访问Web Context下相对路径下的资源。使用ServletContextResource访问的资源,也可通过文件IO访问或URL访问。通过java.io.File访问要求资源被解压缩,而且在本地文件系统中;但使用ServletContextResource进行访问则无须关心资源是否被解压缩出来或者直接存放在JAR文件中,总可通过Servlet容器访问。

        5、InputStreamResource(访问输入流):用于访问二进制输入流资源,由于InputStreamResource是一个总是被打开的Resource,所以isOpen()返回总是true。如果需要多次读取某个流,就不要使用InputStreamResource(虽然它适用性很广,但效率并不好。尽量使用ByteArrayResource或FileSystemResource代替它)

        6、ByteArrayResource:用于访问字节数组资源。对于需要采用InputStreamResource访问的资源,可先从InputStream流中读出字节数组,然后以字节数组来创建ByteArrayResource。

      ②获取Resource实现类

          Spring提供两个标志性接口:

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

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

       在ResourceLoader接口中有如下方法:Resource getResource(String location):该接口仅包含这个方法,该方法用于返回一个Resource实例。ApplicationContext的实现类都实现ResourceLoader接口,因此ApplicationContext可用于直接获取Resource实例(默认采用与ApplicationContext相同的资源访问策略,即如果ApplicationContext是FileSystemXmlApplicationContext,获取的Resource实例就是FileSystemResource;如果ApplicationContext是ClassPathXmlApplicationContext,获取的Resource实例就是ClassPathResource;如果ApplicationContext是XmlWebApplicationContext,获取的Resource实例就是ServletContextResource实例)从上面的介绍看出,当Spring应用需要进行资源访问时,实际上并不需要直接使用Resource实现类,而是调用ResourceLoader实例的getResource方法获得资源,由ResourceLoader负责选择具体的Resource实现类,这就是典型的策略模式。另外,如果想强制使用指定的实现类来读取资源,可通过不同的前缀来指定:

      classpath:-----以ClassPathResource实例来访问类加载路径下的资源      file:------以UrlResource实例访问本地文件系统资源

      http:------以UrlResource实例访问基于HTTP协议的网络资源

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

          ResourceLoaderAware接口用于指定该接口的实现类必须持有一个ResourceLoader实例,该接口也提供一个setResourceLoader方法,该方法由Spring容器负责调用,Spring将把自身作为ResourceLoader使用

       ③Bean实例需要访问资源有两种解决方案:代码中获取Resource实例;(这种方法意味着Resource所在的位置要被耦合到代码中,如果资源位置发生变法则必须改写程序)使用依赖注入(在XML中的property中配置,推荐)

       ④ApplicationContext确定资源访问策略通常有两个方法:

           1、ApplicationContext实现类指定访问策略:通常使用ClassPathXmlApplicationContext(对应ClassPathResource)、FileSystemApplicationContext(对应FileSystemResource)和XmlWebApplicationContext(对应ServletContextResource)

           2、前缀指定访问策略:即使new的是别的ApplicationContext,只要指定前缀:classpath或者http或者ftp,都会强制按照前缀对应的方式访问资源

                 classpath*:提供装载多个XML配置文件的能力,当使用classpath*:前缀来指定XML配置文件时,系统将搜索类加载路径,找出所有与文件名匹配的文件,分别装载文件中的配置定义,最后合并成一个ApplicationContext(此前缀只对ApplicationContext有效)另外还有一种匹配多个文件的通配符,比如bean*.xml会将bean.xml和bean1.xml都装配进来。

                 file*:前缀用来区分相对路径和绝对路径

二、Spring的AOP

       AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。

       ①AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能,其他很多AOP框架都借鉴或采纳其中的一些思想。Spring3的AOP与AspectJ进行了很好的集成。(具体请参看P645关于AspectJ的使用介绍)

       ②AOP的基本概念
深入使用Spring(二)
深入使用Spring(二)
 AOP代理其实是由AOP框架动态生成的一个对象,该对象可作为目标对象使用。AOP代理包含了目标对象的全部方法,但AOP代理中的方法与目标对象的方法存在差异:AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。

        ③Spring中的AOP代理由Spring的IoC容器负责生成、管理,其依赖关系也由IoC容器负责管理。因此,AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可由IoC容器的依赖注入提供。Spring默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。

           Spring AOP使用纯Java实现。它不需要专门的编译过程。目前仅支持将方法调用作为连接点(Joinpoint),如果需要将对Field的访问和更新也作为增强处理的连接点,则可以考虑使用AspectJ。

           纵观AOP编程,其中需要程序员参与的只有三个部分:定义普通业务组件;定义切入点,一个切入点可能横切多个业务组件;定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作。Spring依然提供两种方式来定义切入点和增强处理:基于Annotation的零配置方式和基于XML配置管理的方式

           ④基于Annotation的零配置方式:为了启用Spring对@Aspect切面配置的支持,并保证Spring容器中的目标Bean被一个或多个切面自动增强,必须在Spring配置文件中配置如下片段
深入使用Spring(二)

如果不打算使用Spring的XML Schema配置方式,则应该再Spring配置文件中增加如下片段来启动@AspectJ支持

         <!--启动@AspectJ支持---->

         <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>

        上面的配置文件中的AnnotationAwareAspectJAutoProxyCreator是一个Bean后处理器,该Bean后处理器将会为容器中Bean生成AOP代理,为了在Spring应用中启动@AspectJ支持,还需要在应用的类加载路径中增加两个库:aspectjweaver.jar和aspectjrt.jar

         @AspectJ:定义切面Bean,Spring将不会把定义为切面的Bean当成组件Bean处理,这些类不会被增强处理

          @Before:使用Before增强处理只能在目标方法执行前织入增强,使用Before增强处理无须理会目标方法的执行,所以Before处理无法阻止目标方法的执行。

          @AfterReturning:只有在目标方法正常执行完成后才能织入增强,可以获取到目标方法的返回值,但不能改变目标方法的返回值

          @AfterThrowing:对目标方法的异常进行处理,这种处理与catch捕捉不同,catch意味着可以正常结束,而AfterThrowing不能完全处理该异常,该异常依然会传播到上一级调用者

          @After:与AfterReturning的区别是无论目标方法如何结束(正常或异常)它都会被织入(类似finally)

          @Around:既可在执行目标方法之前织入增强动作,也可在执行目标方法后织入增强动作,可以决定目标方法什么时候执行,如何执行,甚至完全阻止执行

          以上织入顺序由优先级决定:
深入使用Spring(二)
 如果想指定执行顺序,可以让切面类实现org.springframework.core.Ordered接口或者直接使用@Order

         如果想在增强处理中获取目标方法的参数,最简单的做法就是定义增强处理方法时将第一个参数定义为JoinPoint类型,该参数即代表织入增强处理的连接点
深入使用Spring(二)
 如果多个切面类中增强处理的切入点是同一个,则可以考虑单独定义切入点,在使用时用定义好的切入点,切入点的定义包含两部分:一个切入点表达式;一个包含名字和任意参数的方法签名

           完整的AspectJ支持大量的切入点指示符:call,get,set,cflow等等,Spring仅支持execution、with、this、target、args以及自己特有的bean,多个切入点表达式可以用&&和||和!进行组合

         ⑤基于XML配置文件的管理模式(见P666)

相关推荐