在使用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=?