200 likes | 526 Views
J avaEE 程序设计 第16讲 Spring的AOP. 课程结构 教学任务 培养目标 教学时间与方式. 课程结构 :. 教学任务 :. 培养目标 : 掌握AOP的基本工作原理,学会Spirng的AOP框架,并能够应用Spring的框架进行程序开发。 教学时间与方式 : 讲授,2学时. 16.1 AOP入门 AOP是Asppect Oriented Programming的缩写,可翻译为面向方面编程。AOP并不是一个新的名词,但近年来许多技术支持AOP的实现,才造成AOP这个名词的风行。 16.1.1 AOP概述
E N D
课程结构 教学任务 培养目标 教学时间与方式
培养目标: 掌握AOP的基本工作原理,学会Spirng的AOP框架,并能够应用Spring的框架进行程序开发。 教学时间与方式: 讲授,2学时
16.1 AOP入门 AOP是Asppect Oriented Programming的缩写,可翻译为面向方面编程。AOP并不是一个新的名词,但近年来许多技术支持AOP的实现,才造成AOP这个名词的风行。 16.1.1 AOP概述 (1)AOP是在一个服务的流程中,插入与该服务的业务逻辑无关的系统服务逻辑,如日志,安全等。这样的逻辑称为横切关心(Cross Cutting Concern),将横切关心独立出来设计为一个对象,这样的特殊对象称为方面(Aspect)。这样的服务逻辑可应用那个到应用程序的很多地方。在AOP中,它们只需要编写一次,就可以吧它们自动在整个目标应用程序中实施。AOP着重在方面的设计及它们在目标应用程序中的织入(Weaving)。 (2)目前有两种主流的AOP实现:静态AOP和动态AOP。 静态AOP,比如AspectJ(www.apsectj.org),提供了编译器的方法来构建基于AOP的逻辑,并把它加入到应用程序中。 动态AOP,比如SpringAOP,允许在运行时把服务逻辑应用到任意一段代码中。两种不同的AOP方法都有其适用面,实际上,AOP提供了与AspectJ整合的功能。 (3)AOP与OOP(Object Oriented Programming)并不相互抵触,它们是可以相辅相成的两个设计模式。Spring AOP是实现AOP的一种技术,而且Spring AOP也是Spring中一些框架或子功能所依赖的核心。与Spring IOC一样,Spring AOP也是Spring框架的核心技术。
16.1 AOP入门 16.1.2 AOP的主要术语 AOP的许多术语都过于抽象,难以理解。但是这些术语构成了AOP的基础,下面我们对AOP的主要术语进行介绍。 (1)横切关心(Cross-cutting concern):像安全检查、事务等系统层面的服务,常被安插到一些程序中各个对象的处理流程中,这样的服务逻辑在AOP中称为横切关心。如果把横切关心直接编写在负责某业务的对象流程中,会使得维护程序的成本增高。假如某天你要从该对象中修改或移除该服务逻辑时,你需要修改所有与该服务相关的程序代码,然后重新编译;另一方面,把横切关心混杂于业务逻辑中,会使得业务对象本身的逻辑或程序的编写更为复杂。AOP通过把横切关心织入(Weave)到业务逻辑中,比较成功地解决了以上问题。 (2)方面(Aspect):将横切关心设计为独立可重用的对象,这些对象称为方面(Aspect)。 (3)连接点(Joinpoint):方面在应用程序过程时加入目标对象的业务流程中的特定点,称为连接点(Joinpoint)。连接点是AOP的核心概念之一。它用来定义你在目标程序的哪里通过AOP加入新的逻辑。 (4)通知(Advice):方面在某个具体连接点采取的行为或动作,称为通知(Advice)。实际上,通知是方面的具体实现。它是在某一特定的连接点处织入目标业务程序中的服务对象。它又分为在连接点之前执行前置通知(Before Advice)和在连接点之后执行的后置通知(Advice)。
16.1 AOP入门 16.1.2 AOP的主要术语 AOP的许多术语都过于抽象,难以理解。但是这些术语构成了AOP的基础,下面我们对AOP的主要术语进行介绍。 (5)切入点(Pointcut):切入点指定某个通知在哪些连接点被织入到应用程序之中。切入点是通知要被织入到应用程序中的所有连接点的集合。我们可以在一个文件中,例如XML文件中,定义一个通知的切入点,即它的所有连接点。 (6)织入(Weaving):将通知加入应用程序的过程,称为织入(Weaving)。对于静态AOP而言,织入是在编译时完成的,通常在编译过程中增加一个步骤。而动态AOP是在程序运行时动态织入的。 (7)目标(Target):通知被应用的对象,称为目标(Target)。 引入(Introduction):通过引入,我们可以在一个对象中加入新的方法和属性,而不用修改它的程序。具体说,可以为某个已编写、编译完成的类,在执行时期动态加入一些方法或行为,而不用修改或新增任何一个程序代码。 (8)代理(Proxy):代理是由AOP框架生成的一个对象,用来执行方面(Aspect)的内容。
16.1 AOP入门 • 16.1.3 AOP入门实例 • (1)定义一个MessageWriter类可以输出“World”,其代码如下: • Package test; • Public class MessageWriter • { • Public void writeMessage() • { • System.out.print(“World”); • } • }
16.1 AOP入门 16.1.3 AOP入门实例 (2)在不修改Message Writer的代码的前提下,要求程序在“World”的前面插入“Hello”,后面添加“!”,输出“Hello World!”。这时,我们需要应用AOP技术来解决这个问题。我们把Message Writer称作目标对象。接着,我们需要编写另一个类,名为MessageDecorator,也就是一个通知。 Package test; Import org.aopalliance.intercept.MethodInterceptor; //AOP联盟定义的标准接口,用来实现方法调用连接点的包围通知 Import org.aopalliance.intercept.MethodInvocation; //代表当前被通知的方法调用。使用该类来控制具体什么时候方法调用 Public class MessageDecorator implements MethodInterceptor { Public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(“”); System.out.println(“Hello”);//打印”Hello” System.out.println(“”); object obj=invocation.proceed();//调用目标对象的方法 System.out.println(“!”);//打印”!” Return obj; } }
16.1 AOP入门 • 16.1.3 AOP入门实例 • (3)编写一个测试类,名为AOPDemo,利用AOP的代理机制,把MessageDecorator所提供的服务逻辑织入到目标对象MessageWriter中。 • Package test; • Import org.springframework.aop.framework.ProxyFactory; • Public class AOPDemo{ • Public static void main (String args[]){ • MessageWriter target=new MessageWriter(); • ProxyFactory pf=new ProxyFactory();//创建代理对象 • Pf.addAdvice(new MessageDecorator()); • Pf.setTarget(target); • MessageWriter proxy=(MessageWriter)pf.getProxy(); • Target.writeMessage();//输出”World” • System.out.println(“”); • Proxy.writeMessage();//输出”Hello World!” • } • } • 运行AOPDemo,就会输出”Hello World!”。这样,我们就利用AOP技术,在不修改MessageWriter类的代码的前提下,成功地在”World”的前面插入“Hello”,后面添加“!”,然后输出”Hello World!”
16.2 AOP框架 Spring AOP框架是一个AOP实现框架。不同的AOP框架会有不同的AOP实现方式,主要的差别在于如何实现切入点、通知,以及它们如何被织入到应用程序中。 16.2.1 通知器(Advisor) 为了把切点和通知拉到一起,需要包含以下两者的对象:即包含应该被加入的行为(通知)及行为应该被应用的地方(切点)。 (1)Spring引入了通知器(Advisor)的概念:包含通知和切点的对象,指定应该在哪里应用通知。与通知和切点不同,通知器不是一个AOP概念,而只是一个Spring特定的术语。 (2)通知器的目的是为了让通知和切点都可以单独使用。 (3)如果使用通知而没有切点,将创建一个匹配所有方法调用的切点。在这种情况下,Spring使用标准的实例Pointcut。因此,如果想要通知所有已代理的方法,使用通知而不是通知器通常是可能的。
16.2 AOP框架 16.2.2 代理(Proxy) 如果我们要为目标对象提供通知,则必须为它们建立代理(Proxy)对象。在Spring中,为了创建代理,需要使用代理工厂(Proxy Factory)。 (1)常用的代理工厂是org.springframework.aop.framework.ProxyFactoryBean。ProxyFactoryBean是Spring提供的一个类,它是在Spring IoC环境中创建代理的最底层和最灵活的方法。它有2个最重要的属性:proxyInterfaces和interceptorName。proxyInterfaces属性是指被代理的接口;而interceptorName属性是指共同建立拦截器链的通知或通知器的名字列表。 (2)ProxyFactoryBean需要在Spring的Bean定义文件(beans-config.xml)或应用程序上下文文件(applicationContext.xml)中设定. (3)代理应用Bean的定义文件 在上面的文件中,我们声明了一个名为”helloProxy”的代理,它对应的类是org.pringframework.aop.framework.ProxyFactoryBean,并设定了它的proxyInterfaces属性和interceptorNames属性的值。 在应用程序规模比较大时,如果要提供通知的目标对象很多,则一一为它们建立代理对象是一件很麻烦的事,为此Spring提供了自动代理(Autoproxing)。这样,我们不必为每一个目标对象手工定义代理对象了。
16.2 AOP框架 16.2.3 方法拦截器(MethodInterceptor)和拦截器链(Interceptor Chain) (1)Spring的AOP框架是基于AOP联盟的API的。这是一个很小的接口集合,用于指定实现方法拦截器代码所需的签名,而这些代码可以在多个AOP框架中运行,其中最重要的是org.aopalliance.intercept.MethodInterceptor的接口。AOP联盟是在2003初,由Rod Johnson、Jon Tirsen(Nanning方面创建者及基于代理的AOP倡导者)和Bob Lee(作者及JAdvice和DynAOP AOP框架的创建者)建立的。 (2)AOP联盟的方法拦截器接口的定义如下: Public interface org.aopalliance.intercept.MethodInterceptor extends Interceptor { object invoke(MethodInvocation invocation)throws Trowable; } Invoke()方法的MethodInvocation参数暴露了被激活的方法、目标连接点、AOP代理和该方法的参数。Invoke()方法应该返回调用的结果。
16.2 AOP框架 • 16.2.3 方法拦截器(MethodInterceptor)和拦截器链(Interceptor Chain) • (3)简单的MethodInterceptor实现 • Public class DebugInterceptor implements MethodInterceptor • { • Public Object invoke(MethodInvocation invocation)throws Throwable • { • System.out.println(“Before:invocation=[“+invocation+”]”); • object rval=invocation.proceed(); • System.out.println (“invocation returned”); • return rval; • } • }. • MethodInterceptor接口上的proceed()方法被用于调用链中的下一个拦截器。如果调用proceed()的拦截器是链中最后一个,通过调用proceed()将激活目标对象上的目标方法,并且控制沿着链向后倒退。
16.3 Spring的AOP框架实例 • 16.3.1 定义业务组件 • 设计系统的核心业务组件。基于针对接口编程的原则,一个好习惯是先使用接口来定义业务组件的功能,下面使用Component来代表业务组件接口。 • Component.java代码如下: • (1)定义一个业务接口 • package springroad.demo.chap5.exampleB; • public interface Component • { • void business1();//商业逻辑方法1 • void business2();//商业逻辑方法2 • void business3();//商业逻辑方法3 • } • (2)写一个Component的实现ComponentImpl类,ComponentImpl.java代码如下: • package springroad.demo.chap5.exampleB; • public class ComponentImpl implements Component { • public void business1() { • System.out.println("执行业务处理方法1"); } • public void business2() { • System.out.println("执行业务处理方法2");} • public void business3() { • System.out.println(“执行业务处理方法3”);}}
16.3 Spring的AOP框架实例 16.3.1 定义业务组件 (3)写一个Spring的配置文件,配置业务Bean。aspect-spring.xml的内容如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="component" class="springroad.demo.chap5.exampleB.ComponentImpl"> </bean> </beans>
16.3 Spring的AOP框架实例 • 16.3.1 定义业务组件 • (4)然后写一个用于在客户端使用业务Bean的ComponentClient类,代码如下: • package springroad.demo.chap5.exampleB; • import org.springframework.context.ApplicationContext; • public class ComponentClient { • public static void main(String[] args) { • ApplicationContext context=new org.springframework.context.support.ClassPathXmlApplicationContext("springroad/demo/chap5/exampleB/aspect-spring.xml"); • Component component=(Component)context.getBean("component"); • component.business1(); • System.out.println("-----------"); • component.business2(); • } • } • 运行程序,我们可以看到结果输出为: • 执行业务处理方法1 • ----------- • 执行业务处理方法2
16.3 Spring的AOP框架实例 16.3.2 Spring AOP的实现 这个业务Bean只是简单的执行业务方法中代码,现在由于企业级应用的需要,我们需要把业务Bean中的所有business打头所有方法中的业务逻辑前,都要作一次用户检测、启动事务操作,另外在业务逻辑执行完后需要执行结束事务、写入日志的操作。直接修改每一个方法中的代码,添加上面的逻辑,前面已经说过存在不可维护等诸多问题,是不可取的。由于安全检测、事务处理、日志记录等功能需要穿插分散在各个方法中,具有横切关注点的特性,因此我们想到使用Spring的AOP来实现。
16.3 Spring的AOP框架实例 16.3.2 Spring AOP的实现 (1)写一个用来具体处理连接点的通知Bean,这个Bean实现了MethodBeforeAdvice及AfterReturningAdvice接口。 同前面解释的一样,作为演示,我们把切面模块处理的代码直放在了AdviceBean中,实际应用中将会调用具体的处理模块来实现。 (2)接下就是在Spring配置文件中进行配置,分别配置一个代表直接业务组件的targetBean,一个用来代表通知(Advice)具体实现的adviceBean,一个代表切入点描述的pointcutBean,一个代表切面模块的aspectBean,最后是使用代理Bean来定义的业务组件。 (3)运行客户端程序,确实在调用业务组件的业务方法之前及之后都确保调用了安全检查、事务处理、日志记录等模块。细心的读取会发现,此时客户端程序中用到的component这个Bean被定义为一个ProxyFactoryBean类型,其实,也正是这个代理工厂Bean的作用,才使得客户端用到的业务组件集成了处理横切问题的功能。