所谓Spring静态切入点,相对于动态切入点来说,具有良好的性能,因为静态切入点只在代理创建时候执行一次,而不是在运行期间,每次目标方法执行前都进行执行,下面,以实例说明如何定义静态切入点
看我我前一篇blog的朋友都知道,如果不定义切入点,通知方法是会对整个目标类的所有方法均进行切入的 但实际需求中,我们可能对其中的几个方法执行A通知,对其他的方法执行B通知,这时候,就需要通过定义不同的切入点来进行区分
目标接口:
package
StaticAdvisorTest; public interface Shopping ...
{ public String buySomething(String type); public String buyAnything(String type); public String sellSomething(String type); public String sellAnything(String type); }
javabean:
package
StaticAdvisorTest; public class Customer ...
{ private String name; private String age; public Customer()... { } public Customer(String name,String age)... { this.name= name; this.age= age; } public String getAge() ... { return age; } public void setAge(String age) ... { this.age = age; } public String getName() ... { return name; } public void setName(String name) ... { this.name = name; } }
业务目标实现类:
package
StaticAdvisorTest; public class ShoppingImpl implements Shopping ...
{ private Customer customer; public Customer getCustomer() ... { return customer; } public void setCustomer(Customer customer) ... { this.customer = customer; } public String buySomething(String type) ... { System.out.println(this.getCustomer().getName()+" bye "+type+" success" ); return null ; } public String buyAnything(String type) ... { System.out.println(this.getCustomer().getName()+" bye "+type+" success" ); return null ; } public String sellAnything(String type) ... { System.out.println(this.getCustomer().getName()+" sell "+type+" success" ); return null ; } public String sellSomething(String type) ... { System.out.println(this.getCustomer().getName()+" sell "+type+" success" ); return null ; } }
通知(切面)方法:
package
StaticAdvisorTest; import
java.lang.reflect.Method; import
org.springframework.aop.MethodBeforeAdvice; //前置通知
public class WelcomeAdvice implements MethodBeforeAdvice ...
{ public void before(Method method, Object[] args, Object obj) throws Throwable ... { String type=(String)args[0 ]; System.out.println("Hello welcome to buy "+ type); } }
下面是重点,我们想对所有的buy方法进行通知处理,也就是在所有的buy方法上定义切面
spring为我们创建了静态切入点的父类 StaticMethodMatcherPointCut ,如果我们想实现自制的静态切入点,只要继承这个类就可以了,不过一般情况下,我们使用spring提供的静态切入点NameMatchMethodPointCut就足够了
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
<bean id="customer" class="StaticAdvisorTest.Customer">
<constructor-arg index="0">
<value>gaoxiang</value>
</constructor-arg>
<constructor-arg index="1">
<value>26</value>
</constructor-arg>
</bean>
<bean id="shoppingImpl" class="StaticAdvisorTest.ShoppingImpl">
<property name="customer">
<ref local="customer"/>
</property>
</bean>
<!-- 定义通知 -->
<bean id="shoppingAdvise" class="StaticAdvisorTest.WelcomeAdvice"></bean>
<!-- 定义切入点 -->
<bean id="shoppingPointCutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedName">
<value>sell*</value>
</property>
<property name="advice">
<ref bean="shoppingAdvise"/>
</property>
</bean>
<!-- 定义代理 -->
<bean id="StaticAdvisorTest" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>StaticAdvisorTest.Shopping</value>
</property>
<property name="interceptorNames">
<list>
<value>shoppingPointCutAdvisor</value>
</list>
</property>
<property name="target">
<ref bean="shoppingImpl"/>
</property>
</bean>
</beans>
<!-- 如果不使用通配符,则用以下表达 <property name="mappedNames"> <list> <value>sellSomething</value> <value>sellAnything</value> </list> </property> -->
测试程序:
package
StaticAdvisorTest; import
java.io.File; import
org.springframework.beans.factory.BeanFactory; import
org.springframework.beans.factory.xml.XmlBeanFactory; import
org.springframework.core.io.FileSystemResource; public class TestAdvisor ...
{ public static void main(String[] args) ... { String filePath=System.getProperty("user.dir")+File.separator+"StaticAdvisorTest"+File.separator+"hello.xml" ; BeanFactory factory=new XmlBeanFactory(new FileSystemResource(filePath)); Shopping shopping=null ; shopping=(Shopping)factory.getBean("StaticAdvisorTest" ); shopping.buySomething("something" ); shopping.buyAnything("anything" ); shopping.sellAnything("anything" ); shopping.sellSomething("something" );
()
Spring的切入点(二 静态Pointcut的例子)
2009-01-05 19:26
本文地址:http://www.blogjava.net/cmzy/archive/2008/08/10/220917.html)
下面以JdkRegexpMethodPointcut为例,通过一个完整的范例展示如何使用静态切入点(完整工程代码见例程4.3)。在工程中我们定义一个People类和一个切面,并将他们在Spring xml配置文件中联系起来。当People对象执行我们切入点中定义的方法时,前置装备LogerPeople将会给出相应的提示信息。
新建一个工程AOP_Test4.3,添加Spring开发库后,新建aop.test包。 创建目标类People,该类有speak、Running、Loving、died四个成员方法。代码如下:
package aop.test; public class People{ public void speak() { System.out.println("Hello,我是People!" ); } public void Running() { System.out.println("我在跑……跑…………逃……" ); } public void Loving() { System.out.println("我在和MM恋爱……别来打搅我!" ); } public void died() { System.out.println("完了,我死了" ); } }
/** * */ package aop.test; /** * @author zhangyong * */ public class People{ // 讲话 public void speak() { System.out.println("Hello,我是People!"); } // 跑步 public void Running() { System.out.println("我在跑……跑…………逃……"); } // 恋爱 public void Loving() { System.out.println("我在和MM恋爱……别来打搅我!"); } // 死亡 public void died() { System.out.println("完了,我死了"); } } 创建一个类名为LogerPeople的前置装备,它实现MethodBeforeAdvice接口,在before方法中利用Log4J输出相关信息,代码如下:
package aop.test; import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.MethodBeforeAdvice; public class LogerPeople implements MethodBeforeAdvice { private static final Log log = LogFactory.getLog(LogerPeople.class ); public void before(Method method, Object[] args, Object target) throws Throwable { log.info(target.getClass().getSimpleName() + "正在" + method.getName()+ "!" ); } }
/** * */ package aop.test; import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.MethodBeforeAdvice; /** * @author zhangyong * */ public class LogerPeople implements MethodBeforeAdvice { private static final Log log = LogFactory.getLog(LogerPeople.class); public void before(Method method, Object[] args, Object target) throws Throwable { // 这里什么也不做,只是利用Log4J输出日志信息 log.info(target.getClass().getSimpleName() + "正在" + method.getName()+ "!"); } } > 再编写Spring配置文件,完成后如下:
<? 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.5.xsd"> < bean id ="Computer" class ="aop.test.People" > </ bean > < bean id ="LogerComputer" class ="aop.test.LogerPeople" /> < bean id ="ProxyFactoryBean" class ="org.springframework.aop.framework.ProxyFactoryBean" > < property name ="target" > < ref bean ="Computer" /> </ property > < property name ="interceptorNames" > < list > < value > DefaultAdvisor</ value > </ list > </ property > </ bean > < bean id ="DefaultAdvisor" class ="org.springframework.aop.support.DefaultPointcutAdvisor" > < property name ="pointcut" ref ="JdkRegexpPointcut" /> < property name ="advice" ref ="LogerComputer" /> </ bean > < bean id ="JdkRegexpPointcut" class ="org.springframework.aop.support.JdkRegexpMethodPointcut" > < property name ="patterns" > < list > < value > .*spea.*</ value > < value > .*ing</ value > < value > .*di.*</ value > </ list > </ property > < property name ="excludedPattern" value =".*Run.*" /> </ bean > </ beans >
<?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.5.xsd"> <bean id="Computer" class="aop.test.People"></bean> <bean id="LogerComputer" class="aop.test.LogerPeople" /> <bean id="ProxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref bean="Computer" /> </property> <property name="interceptorNames"> <list> <value>DefaultAdvisor</value> </list> </property> </bean> <bean id="DefaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="JdkRegexpPointcut" /> <property name="advice" ref="LogerComputer" /> </bean> <bean id="JdkRegexpPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"> <property name="patterns"> <list> <value>.*spea.*</value> <value>.*ing</value> <value>.*di.*</value> </list> </property> <property name="excludedPattern" value=".*Run.*" /> </bean> </beans> > 为了让ProxyFactoryBean使用我们定义的JdkRegexpMethodPointcut而不是默认的Pointcut,我们需要配置一个切入点配置器PointcutAdvisor,其advice属性指定装备,Pointcut属性指定切入点。然后再将该切入点配置器注入给ProxyFactoryBean。各个Bean的依赖关系和说明如下:
图4.4 ProxyFactoryBean代理生成
在JdkRegexpMethodPointcut中,我们使用了它两个属性patterns和excludedPattern:patterns利用正则表达式指定了我们要监视的方法这里是包含了所有的方法;excludedPattern指定了我们要排除的方法,这里指定了以Run开头的方法。
注意:
1)“.*spea.*”表示所有名字以spea开头的方法,例程中是指speak方法;
2)“.*ing”表示所有名字以ing结束的方法,例程中是指Running和Loving方法;
3)“.*di.*”表示所有名字以di开头的方法,例程中是指died方法;
4)“.*Run.*”表示所有名字以Run开头的方法,例程中是指Running方法;
创建含主方法的测试类TestMain,在一种我们从ProxyFactoryBean中获得People实例对象,并一次调用该对象的方法,代码如下:
package aop.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestMain { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext( "applicationContext.xml" ); People c = (People) ac.getBean("ProxyFactoryBean" ); c.speak(); c.Running(); c.Loving(); c.died(); } }
/** * */ package aop.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author zhangyong * */ public class TestMain { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext( "applicationContext.xml"); //通过ProxyFactoryBean获取IComputer接口实现类的实例 People c = (People) ac.getBean("ProxyFactoryBean"); c.speak(); c.Running(); c.Loving(); c.died(); } } 该类运行结果如下:
图4.5 例程4.3运行结果
可以看到People类中的speak、Loving、died方法已经被拦截。但是Running方法却没有拦截,这是因为我们在JdkRegexpMethodPointcut中指定其excludedPattern属性把它排除在切入点之外的缘故。
()
2009-06-29 10:43
摘 要 面向方面编程(AOP)是面向对象编程(OOP)的一种扩展技术,能够很好的解决横切关注点问题以及相关的设计难题来实现松散耦合。Spring AOP 是 AOP 技术的一种实现。本文介绍了AOP 概念,然后详细讨论了如何利用Spring AOP 开发AOP 程序,最后展望了Spring AOP 的前景。 关键词 AOP Spring AOP Java 1 引言 AOP(Aspected Oriented Programming)是一种新兴的编程技术。它可以解决OOP和过程化方法不能够很好解决的横切(crosscut)问题,如:事务、安全、日志等横切关注。当未来系统变得越来越复杂,横切关注点就成为一个打问题的时候,AOP就可以很轻松的解决横切关注点这个问题,使得AOP编程成为。Spring 是基于J2EE的轻量级开源开发框架,其中Spring AOP组件实现了面向方面编程。 2 AOP 概述 面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。 面向对象将应用程序分解成各个层次的对象,而AOP将程序分解成各个方面或者说关注点 。这使得可以模块化诸如事务管理等这些横切多个对象的关注点。 2.1 AOP 基本概念 方面(Aspect): 一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调 用或特定的异常被抛出。 通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类 型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架 包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。 切入点(Pointcut): 指定一个通知将被引发的一系列连接点 的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。 目标对象(Target Object): 包含连接点的对象。也被称作 被通知或被代理对象。 AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时 完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。 3 Spring AOP 介绍 Spring的一个关键组件就是AOP框架。 Spring IoC容器(BeanFactory 和ApplicationContext)并不依赖于AOP, 这意味着如果你不需要使用,AOP可以不用,AOP完善了Spring IoC,使之成为一个有效的中间件解决方案。 Spring AOP 是Spring 框架的重要组成部分,它实现了AOP联盟约定的接口。Spring AOP 是由纯Java开发完成的。Spring AOP 只实现了方法级别的连接点,在J2EE应用中,AOP拦截到方法级的操作已经足够。OOP倡导的是基于setter/getter 的方法访问,而非直接访问域,而Spring 有足够理由仅仅提供方法级的连接点。为了使控制反转(IoC)很方便的使用到非常健壮、灵活的企业服务,则需要Spring AOP 的实现。Spring AOP 在运行时才创建Advice 对象。Spring AOP的优点如下: 允许开发者使用声明式企业服务,比如事务服务、安全性服务。 开发者可以开发满足业务需求的自定义方面。 开发Spring AOP Advice 很方便,可以借助代理类快速搭建Spring AOP 应用。 4 使用Spring AOP松散耦合 4.1 创建通知 为实现AOP,开发者需要开发AOP 通知(Advice)。AOP 通知(Advice) 包含了方面(Aspect)的逻辑。当创建一个Advice 对象的时候,你就编写了实现横切(cross-cutting)功能。 Spring 的连接点是用方法拦截器实现的,这就意味着你编写的Spring AOP 通知将在方法调用的不同点组入进程序中。由于在调用一个方法时有几个不同的时间点,Spring 可以在不同的时间点组入进程序。Spring AOP中,提供了四种通知的接口: MethodBeforeAdvice 用于在目标方法调用前触发;AfterReturningAdvice 用于在目标方法调用后触发; ThrowsAdvice 用于在目标方法抛出异常时触发;MethodInterceptor 用于实现 Around 通知(Advice),在目方法执行的前后触发。
如果要实现相应功能,则需要实现上述对应的接口。例如:实现Before 通知(Advice)需要实现方法 void before(Method method, Object[] args, Object target) ,实现 After 通知(Advice) 需要实现方法 void afterReturning (Method method, Object[] args, Object target)。 4.2 在Spring 中定义切入点 在不能明确调用方法的时候,通知就很不实用。切入点则可以决定特定的类,特定的方法是否匹配特定标准。如果某匹配,则通知将应用到此方法上。Spring 切入点允许用很灵活的方式将通知组织进我们的类中。Spring 中的切入点框架的核心是Pointcut接口,此接口允许我们定义组入通知中的类和方法。许多方面是通过一系列的通知和切入点组合来定义。 在Spring中,一个advisor就是一个方面的完整的模块化表示。Spring提供了PointcutAdvisor接口把通知和切入点组合成一个对象。Spring中很多内建的切入点都有对应的PointcutAdvisor,这使得你可以很方便在一个地方管理切入点和通知。Spring中的切入点分为两类:静态和动态。因为静态切入点的性能要优于动态切入点,所以优先考虑使用。Spring 为我们提供创建静态切入点很实用的类StaticMethodMatherPointcut。在这个类中,我们只需要关心setMappedName和setMappedNams方法。你可以使用具体的类名,也可以使用通配符。如:设置mappedName属性为set* 则匹配所有的set方法。Spring还提供了另通过正则表达式来创建静态切入点的实用类RegexpMethodPointcut。通过使用Perl样式的正则表达式来定义你感兴趣的方法。当切入点需要运行时参数值来执行通知时,这时就需要使用动态切入点。Spring提供了一个内建的动态切入点:ControlFlowPointcut,此切入点匹配基于当前线程的调用堆栈。我们可以在只有在当前线程执行的执行时找到特定的类和特定的方法才返回true。使用动态切入点有很大的性能损耗。大多数的切入点可以静态确定,我们很少有机会创建动态切入点。为了增加可切入点的可重用性,Spring 提供了切入点上的集合操作——交集和合并。 4.3 用ProxyFactoryBean创建AOP代理 ProxyFactoryBean,和其他Spring的 FactoryBean实现一样,引入一个间接的层次。如果你定义一个名字为myfactory的ProxyFactoryBean, 引用myfactory的对象所看到的不是ProxyFactoryBean 实例本身,而是由实现ProxyFactoryBean的类的 getObject()方法所创建的对象。这个方法将创建一个包装了目标对象 的AOP代理。使用ProxyFactoryBean或者其他IoC可知的类来创建AOP代理的最重要的优点之一是IoC可以管理通知和切入点。这是一个非常的强大的功能,能够实现其他AOP框架很难实现的特定的方法。例如,一个通知本身可以引用应用对象(除了目标对象, 它在任何AOP框架中都可以引用应用对象),这完全得益于依赖注入所提供的可插入性。通常,我们不需要ProxyFactoryBean的全部功能,因为我们常常只对一个方面感兴趣: 例如,事务管理。当我们仅仅对一个特定的方面感兴趣时,我们可以使用许多便利的工厂来创建AOP代理,如:TransactionProxyFactoryBean。 4.4 自动代理 在应用较小时,只有很少类需要被通知的时,ProxyFactoryBean 可以很好的工作。当有许多类需要通知的时,显示的创建每个代理就显得很繁琐。幸运的是Spring提供了是使用自动通过容器来创建代理。这时,就只需要配置一个Bean来做繁琐的工作。Spring提供了两个类实现自动代理:BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator。 BeanNameAutoProxyCreator为匹配名字的Bean产生代理,它可以使用在将一个或者多个方面应用在命名相似的Bean中。自动代理框架假设代理将要暴露出什么接口。如果目标Bean没有实现任何接口,这时就会动态产生一个子类。而更强大的自动代理是DefaultAdvisorAutoProxyCreator,你所需要做的是在BeanFactory中包含它的配置。这个类的奇妙之处在于他使用实现了BeanPostProcessor接口。在Bean定义被加载倒Spring容器中后,DefaultAdvisorAutoProxyCreator将搜索上下文中的Advisor,最后它将Advisor应用到匹配Advisor切入点的Bean中。这个代理只对Advisor起作用,它需要通过Advisor来得到需要通知的Bean。元数据自动代理(MetaData AutoProxy)。元数据自动代理配置依赖于源代码属性而不是外部XML配置文件。这可以非常方便的使源代码和AOP元数据组织在同一个地方。元数据自动代理最常用的地方是用来声明事务。Spring提供了很强的框架来通过AOP框架来声明事务。这提供了在EJB使用声明式事务的相同功能。 5 结论 AOP 是面向对象编程的有力补充。通过方面就可以聚合在应用中行为形成可重用模块。 通过程序可以实现怎样和在什么地方来调用这些行为。这可以减少代码重复,并使你更加关注业务逻辑。Spring 提供了AOP框架来实现调用方法时加入方面。在AOP框架中可以很方便的使用预定义的静态切入点来定义被调用的类和方法。我们需要通过Spring提供的代理类来产生代理对象,可以使用ProxyFactoryBean也可以使用自动代理。Spring AOP 的编程方式模块化了横向关注点的实现,提供了一个更好更快的软件开发方式。在软件结构日益扩大,结构日益复杂的今天,Spring AOP 将会发挥越来越重要的作用。