首先,需要加载依赖的Spring相关类库,如下所示,这里创建的是普通java工程,web工程的配置,这里不再多说:
其次,配置 .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"
<!-- 配置aop命名空间 -->
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- 基于注解的aop动态代理配置 -->
<aop:aspectj-autoproxy/>
<bean id="duke" class = "com.springaction.springidol.Juggler">
<constructor-arg value="15">
</constructor-arg>
</bean>
<!-- 切面 -->
<bean id="audience" class="com.springaction.aop.domain.Audience" />
<aop:config>
<aop:aspect ref="audience">
<!--定义切点,只在audienct切面中使用,如果想被多个切面使用,可以放到<aop:config>元素中 -->
<aop:pointcut id="performance" expression="execution(* com.springaction.springidol.Performer.perform(..))"/>
<!--应用切点,前置通知-->
<aop:before pointcut-ref="performance"
method="takeSeats" />
<aop:before pointcut-ref="performance"
method="turnOffCellPhones" />
<!-- 后置通知,方法成功执行之后调用通知 -->
<aop:after-returning pointcut-ref="performance"
method="applaud" />
<!--后置通知, 方法抛出异常之后调用通知 -->
<aop:after-throwing pointcut-ref="performance"
method="demandRefund" />
<!-- 声明环绕通知 -->
<aop:around pointcut-ref="performance"
method="watchPerformance" />
</aop:aspect>
</aop:config>
</beans>
上面的配置中,定义了切面、切点以及各种通知方式,其中比较特殊的是环绕通知。
然后,看看切面:
public class Audience
{
//表演之前:前置通知
public void takeSeats()
{
System.out.println("The audience is taking their seats.");
}
//表演之前:前置通知
public void turnOffCellPhones()
{
System.out.println("The audience is turning off their cellPhones");
}
//表演之后:后置通知,成功返回之后执行
public void applaud()
{
System.out.println("CLAP CLAP CLAP CLAP");
}
//表演失败之后:后置通知,抛异常之后执行
public void demandRefund()
{
System.out.println("Boo! We want out money back!");
}
/**
* 环绕通知:
* Around advice可以通过一个在joinpoint执行前后做一些事情的机会,
* 可以决定什么时候,怎么样去执行joinpoint,甚至可以决定是否真的执行joinpoint的方法调用。
* Around advice通常是用在下面这样的情况:
* 在多线程环境下,在joinpoint方法调用前后的处理中需要共享一些数据。
* 如果使用Before advice和After advice也可以达到目的,
* 但是就需要在aspect里面创建一个存储共享信息的field,但这种做法并不是线程安全的。
* &&
* ProceedingJoinPoint作为入参,可以让我们在通知里调用被通知方法。
* @param joinpoint
*/
public void watchPerformance(ProceedingJoinPoint joinpoint)
{
try
{
//执行前的操作
System.out.println("Around-before:The audience is taking their seats.");
System.out.println("Around-before:The audience is turning off their cellPhones");
long start = System.currentTimeMillis();
//执行被通知的方法
//如果没有执行这个方法,则通知会阻止被通知方法的调用。
joinpoint.proceed();
//执行后的操作
long end = System.currentTimeMillis();
System.out.println("Around-after:CLAP CLAP CLAP CLAP CLAP");
System.out.println("Around-after:The performance took " + (end - start)
+ " milliseconds.");
}
catch (Throwable e)
{
System.out.println("Boo!We want our money back!");
}
}
}
上面代码对该切面中关键点都做了注释,其余的不用多说,其中环绕通知必须包含入参org.aspectj.lang.ProceedingJoinPoint
通过它的方法proceed()执行被通知的方法。另外,任何通知方法可以将第一个参数定义为 org.aspectj.lang.JoinPoint
类型。JoinPoint
接口提供了一系列有用的方法, 比如 getArgs()
(返回方法参数)、getThis()
(返回代理对象)、getTarget()
(返回目标)、getSignature()
(返回正在被通知的方法相关信息)和 toString()
(打印出正在被通知的方法的有用信息)。
然后,目标对象:
public class Juggler implements Performer
{
private int beanBags = 3;
public Juggler()
{
}
public Juggler(int beanBags)
{
this.beanBags = beanBags;
}
public void perform() throws Exception
{
//抛异常则触发after-throwing通知
//throw new Exception("");
//执行成功则触发after-returning通知
System.out.println("JUGGLING " + beanBags + " BeanBags");
//无论成功与否,则触发after通知
}
}
最后是测试代码:
public class Test
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("file:../../src/spring.xml");
Performer performer = (Performer) ctx.getBean("duke");
try
{
performer.perform();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试代码中,使用应用上下文ClassPathXmlApplicationContext加载类路径下的配置文件,关于加载配置文件的方式,后面需要具体具体分析下。
执行结果如下:
The audience is taking their seats.
The audience is turning off their cellPhones
Around-before:The audience is taking their seats.
Around-before:The audience is turning off their cellPhones
JUGGLING 15 BeanBags
Around-after:CLAP CLAP CLAP CLAP CLAP
Around-after:The performance took 2 milliseconds.
CLAP CLAP CLAP CLAP
上面的这个Demo是基于XML配置切面的应用,基于注解的应用以及动态代理等后面会给出具体解释和Demo。
在这个例子调试中,也遇到了一些问题,主要包括如下几个:
>.1.spring 的依赖注入是面向接口编程的,在测试代码中Performer performer = (Performer) ctx.getBean("duke");必须转换为接口,而不是具体实现,否则报错:
Exception in thread "main" java.lang.ClassCastException: $Proxy2 cannot be cast to com.springaction.springidol.Juggler
at com.springaction.springidol.Test.main(Test.java:14)
ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
JDWP exit error AGENT_ERROR_NO_JNI_ENV(183): [../../../src/share/back/util.c:820]
>.2.主要是缺少一些jar包,比如commons-logging.jar, spring-expression.jar等,这个根据具体报错信息添加具体jar包就可以,关键是理解这些jar包的具体作用。