Spring AOP:使用NameMatchMethodPointcutAdvisor实现切面编程

 
 

在使用MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice的基础上(可以参考文章 在Spring的IOC容器中装配AOP代理 ),Spring定义了Advisor,其中PiointcutAdvisor比较重要,它由两部分构成:一个Advice + 一个Pointcut。

PiointcutAdvisor可以指定切面所作用的方法。

PiointcutAdvisor具有两个属性:String mappdeName和String[] mappedNames。下面主要从这两个属性入手,学习使用。

第一部分 使用 String mappdeName

关于PiointcutAdvisor的使用,在这里,基于文章 Spring中编程式事务处理(使用TransactionTemplate) 中的一些准备工作,即,通过编程式事务处理,Spring集成Hibernate,获取到一个DAO,具体装配bean的XML配置如下所示:

<bean id="threeDataSource"
   class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName"
    value="com.microsoft.jdbc.sqlserver.SQLServerDriver">
   </property>
   <property name="url"
    value="jdbc:microsoft:sqlserver://localhost:1433;databasename=hibernate">
   </property>
   <property name="username" value="sa"></property>
   <property name="password" value="111111"></property>
</bean>

<bean id="threeSessionFactory"
   class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
   <property name="dataSource">
    <ref bean="threeDataSource" />
   </property>
   <property name="mappingResources">
    <list>
     <value>org/shirdrn/entity/Person.hbm.xml</value>
    </list>
   </property>
   <property name="hibernateProperties">
    <props>
     <prop key="hibernate.dialect">
      org.hibernate.dialect.SQLServerDialect
     </prop>
     <prop key="hibernate.show_sql">
                    true
                </prop>
    </props>
   </property>
</bean>

<bean id="hTransactionManager"
   class="org.springframework.orm.hibernate3.HibernateTransactionManager"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="sessionFactory">
    <ref bean="threeSessionFactory" />
   </property>
</bean>

<bean id="threePersonDao" class="org.shirdrn.no.three.dao.PersonDao"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="transactionManager">
    <ref bean="hTransactionManager"/>
   </property>
   <property name="sessionFactory">
    <ref bean="threeSessionFactory" />
   </property>
</bean>

为了测试使用PointcutAdvisor,新建了一个PersonServiceImpl类,假设PersonServiceImpl实现的服务就是PersonDao中提供的增删改查操作。PersonServiceImpl类实现了IPersonDao接口(实际上DAO的操作都封装在PersonDao中了,关于PersonDao的实现可以参考文章 Spring中编程式事务处理(使用TransactionTemplate) 中的实现)。PersonServiceImpl类实现如下所示:

package org.shirdrn.service.person;

import java.util.List;

import org.shirdrn.entity.Person;
import org.shirdrn.no.three.dao.PersonDao;
import org.shirdrn.no.three.interf.IPersonDao;

public class PersonServiceImpl implements IPersonDao {
private PersonDao personDao;
public void setPersonDao(PersonDao personDao){
   this.personDao = personDao;
}
public void createPerson(Person person) {
   personDao.createPerson(person);  
}
public void deletePerson(Person person) {
   personDao.deletePerson(person);
  
}
public Person queryOnePerson(String hql) {
   return null;
}
public List queryPerson(String hql) {
   return null;
}
public void updatePerson(Person person) {
   personDao.updatePerson(person);  
}
}

首先,要把一个PersonDao注入到PersonServiceImpl,Spring的XML文件配置如下所示:

<bean id="personServiceImpl"
   class="org.shirdrn.service.person.PersonServiceImpl"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="personDao">
    <ref bean="threePersonDao" />
   </property>
</bean>

其次,实现我们的分离出来的切面,假设就把调用一个createPerson()方法作为一个切面,在调用该方法之前,打印出调用该方法的日志信息。

该切面为CreatePersonBeforeMethodAdvice,实现了MethodBeforeAdvice接口,如下所示:

package org.shirdrn.aop.one;

import java.lang.reflect.Method;
import java.util.Date;

import org.springframework.aop.MethodBeforeAdvice;

public class CreatePersonBeforeMethodAdvice implements MethodBeforeAdvice {

public void before(Method arg0, Object[] arg1, Object arg2)
    throws Throwable {
   Date date = new Date();
   System.out.println("信息:["+date.toLocaleString()+"] 调用了createPerson()方法。");
}
}

再次,将这个切面装配到Spring的IOC容器中,XML配置文件如下所示:

<bean id="createPersonBeforeMethodAdvice"
   class="org.shirdrn.aop.one.CreatePersonBeforeMethodAdvice"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
</bean>

前面讲过,一个PointcutAdvisor实际上由两部分构成:一个Advice + 一个Pointcut。现在,已经准备好了一个Advice,即:CreatePersonBeforeMethodAdvice。至于Pointcut可以直接使用Spring提供的,这里使用了NameMatchMethodPointcut。

到此为止,一个PointcutAdvisor可以进行装配了。

接着,指定了NameMatchMethodPointcut的mappedName属性,即:我们想要增强的方法,这里是createPerson()方法;同时将一个Advice注入到Advisor中,如下所示:

<bean id="createPersonBeforeMethodAdvisor"
   class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="mappedName">
    <value>createPerson</value>
   </property>
   <property name="advice">
    <ref bean="createPersonBeforeMethodAdvice" />
   </property>
</bean>

然后,就是获取一个ProxyFactoryBean代理,在这里将CreatePersonBeforeMethodAdvisor注入进去。获取到一个代理后,就可以调用切面的方法了,XML配置文件如下所示:

<bean id="personService"
   class="org.springframework.aop.framework.ProxyFactoryBean"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="target">
    <ref bean="personServiceImpl" />
   </property>
   <property name="interceptorNames">
    <list>
     <value>createPersonBeforeMethodAdvisor</value>
    </list>
   </property>
</bean>

最后,编写测试主函数。我们的目标是:在调用createPerson()方法之前,会在切面CreatePersonBeforeMethodAdvisor中对调用的该方法输出日志跟踪信息。

主函数测试代码如下所示:

package org.shirdrn.aop.one.test;

import org.shirdrn.entity.Person;
import org.shirdrn.no.three.interf.IPersonDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

public static void main(String[] args) {
   ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
   IPersonDao personDao = (IPersonDao)ctx.getBean("personService");    // 获取代理
   Person p = new Person();
   p.setId("200804160022");
   p.setName("异域王者");
   p.setGender("男");
   p.setAddr("Beijing");
   p.setAge(new Integer(20));
   personDao.createPerson(p);
}
}

运行结果如下所示:

信息:[2008-4-16 21:38:06] 调用了createPerson()方法。
Hibernate: insert into hibernate.dbo.person (name, gender, age, addr, id) values (?, ?, ?, ?, ?)

可见,实现了在调用createPerson()方法之前,输出日志跟踪信息。

上面的实现只是针对一个方法实现AOP,当然可以使用通配符实现对具有相同前缀或者后缀的方法进行AOP。看看使用通配符如何设置。

在上面的基础上修改,新建一个XPersonBeforeMethodAdvice,代码如下所示:

package org.shirdrn.aop.one;

import java.lang.reflect.Method;
import java.util.Date;

import org.springframework.aop.MethodBeforeAdvice;

public class XPersonBeforeMethodAdvice implements MethodBeforeAdvice {
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
   Date date = new Date();
   System.out.println("信息:["+date.toLocaleString()+"] 调用了与Person实体有关的业务方法(形如*Person()方法)。");
}
}

装配到Spring的IoC容器中,如下所示:

<bean id="xPersonBeforeMethodAdvice"
   class="org.shirdrn.aop.one.XPersonBeforeMethodAdvice"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
</bean>

<bean id="xPersonBeforeMethodAdvisor"
   class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="mappedName">
   <value>*Person</value>
   </property>
   <property name="advice">
    <ref bean="xPersonBeforeMethodAdvice" />
   </property>
</bean>

<bean id="personService"
   class="org.springframework.aop.framework.ProxyFactoryBean"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="target">
    <ref bean="personServiceImpl" />
   </property>
   <property name="interceptorNames">
    <list>
     <value>xPersonBeforeMethodAdvisor</value>
    </list>
   </property>
</bean>

通配符指定了<value>*Person</value>,调用以Person为后缀的方法时实现拦截。

在测试主函数中,我们调用三个方法:createPerson()、updatePerson()、deletePerson(),都具有相同的后缀“Person”。

主函数如下所示:

package org.shirdrn.aop.one.test;

import org.shirdrn.entity.Person;
import org.shirdrn.no.three.interf.IPersonDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

public static void main(String[] args) {
   ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
   IPersonDao personDao = (IPersonDao)ctx.getBean("personService");
   Person p = new Person();
   p.setId("200804160024");
   p.setName("异域王者");
   p.setGender("男");
   p.setAddr("Beijing");
   p.setAge(new Integer(20));
   personDao.createPerson(p);
  
   p.setId("200804160022");
   p.setAddr("长春");
   personDao.updatePerson(p);
  
   p.setId("200804160021");
   personDao.deletePerson(p);
}
}

输出结果输出如下所示:

信息:[2008-4-16 22:18:07] 调用了与Person实体有关的业务方法(形如*Person()方法)。
Hibernate: insert into hibernate.dbo.person (name, gender, age, addr, id) values (?, ?, ?, ?, ?)
信息:[2008-4-16 22:18:08] 调用了与Person实体有关的业务方法(形如*Person()方法)。
Hibernate: update hibernate.dbo.person set name=?, gender=?, age=?, addr=? where id=?
信息:[2008-4-16 22:18:08] 调用了与Person实体有关的业务方法(形如*Person()方法)。
Hibernate: delete from hibernate.dbo.person where id=?

第二部分 使用 String[] mappdeNames

只需要在mappedNames属性配置的地方使用子元素list,在list中列出拦截的方法,XML配置片段,如下所示:

<bean id="nPersonBeforeMethodAdvice"
   class="org.shirdrn.aop.one.NPersonBeforeMethodAdvice"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
</bean>
<bean id="nPersonBeforeMethodAdvisor"
   class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="mappedNames">
    <list>
     <value>createPerson</value>
     <value>updatePerson</value>
     <value>deletePerson</value>
    </list>
   </property>
   <property name="advice">
    <ref bean="nPersonBeforeMethodAdvice" />
   </property>
</bean>

<bean id="personService"
   class="org.springframework.aop.framework.ProxyFactoryBean"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="target">
    <ref bean="personServiceImpl" />
   </property>
   <property name="interceptorNames">
    <list>
     <value>nPersonBeforeMethodAdvisor</value>
    </list>
   </property>
</bean>

其中,NPersonBeforeMethodAdvice 的实现与上面是一样的:

package org.shirdrn.aop.one;

import java.lang.reflect.Method;
import java.util.Date;

import org.springframework.aop.MethodBeforeAdvice;

public class NPersonBeforeMethodAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] arg1, Object arg2)
throws Throwable {
   Date date = new Date();
   System.out.println("信息:[NPersonBeforeMethodAdvice] ["+date.toLocaleString()+"] 调用了业务方法 "+method.getName()+" .");
}
}

在测试主函数中调用任何一个在上面配置list中出现的方法,都会在调用方法之前,登录跟踪日志:

package org.shirdrn.aop.one.test;

import org.shirdrn.entity.Person;
import org.shirdrn.no.three.interf.IPersonDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

public static void main(String[] args) {
   ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
   IPersonDao personDao = (IPersonDao)ctx.getBean("personService");
   Person p = new Person();
   p.setId("200804170001");
   p.setName("异域王者");
   p.setGender("男");
   p.setAddr("Beijing");
   p.setGender("female");
   p.setAge(new Integer(20));
   personDao.createPerson(p);
  
   p.setId("200804160022");
   p.setName("风平浪静");
   p.setAddr("长春");
   p.setGender("female");
   p.setAge(new Integer(22));
   personDao.updatePerson(p);
  
   p.setId("200804160020");
   personDao.deletePerson(p);
}
}

测试结果如下所示:

信息:[NPersonBeforeMethodAdvice] [2008-4-17 9:46:28] 调用了业务方法 createPerson .
Hibernate: insert into hibernate.dbo.person (name, gender, age, addr, id) values (?, ?, ?, ?, ?)
信息:[NPersonBeforeMethodAdvice] [2008-4-17 9:46:29] 调用了业务方法 updatePerson .
Hibernate: update hibernate.dbo.person set name=?, gender=?, age=?, addr=? where id=?
信息:[NPersonBeforeMethodAdvice] [2008-4-17 9:46:29] 调用了业务方法 deletePerson .
Hibernate: delete from hibernate.dbo.person where id=?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值