Spring 使用介绍(五)—— AOP(一)
一、简单使用:Hello World实例
1、定义目标类
public interface Hello { void sayHello(); }
public class HelloImpl implements Hello { @Override public void sayHello() { System.out.println("hello matt!"); } }
2、定义切面支持类
public class HelloAspect { public void beforeAdvice() { System.out.println("****beforeAdvice"); } public void afterFinnallyAdvice() { System.out.println("****afterFinnallyAdvice"); } }
3、配置切面
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <!-- 目标类 --> <bean id="hello" class="cn.matt.aop.HelloImpl"></bean> <!-- 切面支持类 --> <bean id="helloAspect" class="cn.matt.aop.HelloAspect"></bean> <aop:config> <!-- 切点 --> <aop:pointcut id="pointcut" expression="execution(* cn.matt.aop..*.*(..))"/> <!-- 切面 --> <aop:aspect ref="helloAspect"> <aop:before pointcut-ref="pointcut" method="beforeAdvice"/> <aop:after pointcut="execution(* cn.matt.aop..*.*(..))" method="afterFinnallyAdvice"/> </aop:aspect> </aop:config> </beans>
4、测试
@Test public void testSayHello() { ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); Hello hello = context.getBean(Hello.class); hello.sayHello(); }
输出:
****beforeAdvice hello matt! ****afterFinnallyAdvice
二、AOP XML配置
AOP定义必须放在<aop:config>标签下,该标签下可以有<aop:pointcut>、<aop:advisor>、<aop:aspect>标签,配置顺序不可变
AOP配置步骤:
1)声明切面支持bean(通过<bean>标签实例化支持类)
2)声明切面,引用切面支持bean(切面由<aop:aspect>标签指定,ref属性用来引用切面支持Bean)
3)声明切入点,有两种方式(注意:切入点也是bean)
i)使用<aop:pointcut>声明一个切入点Bean,该切入点可被多个切面共享
<aop:config> <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/> <aop:aspect ref="aspectSupportBean"> <aop:before pointcut-ref="pointcut" method="before"/> </aop:aspect> </aop:config>
ii)匿名切入点Bean,通过pointcut属性指定
<aop:config> <aop:aspect ref="aspectSupportBean"> <aop:after pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/> </aop:aspect> </aop:config>
4)声明通知,有五种:
i)前置通知 方法调用前调用
ii)后置返回通知 方法调用后且正常返回时调用
iii)后置异常通知 方法调用后且抛出异常时调用
iv)后置最终通知 方法调用后始终调用
v)环绕通知 可控制方法的执行过程,如决定方法是否执行,什么时候执行,执行时替换方法参数,执行后替换返回值等
注意:当method属性需要指定支持类的某个重载方法时,需要指定参数列表
具体实例如下:
目标接口及实现
public interface Hello { void sayBefore(String str); String sayAfterReturning(String str); void sayAfterThrowing(); void sayAfterFinnally(); void sayAround(String str); }
public class HelloImpl implements Hello { @Override public void sayBefore(String str) { System.out.println("sayBefore " + str); } @Override public String sayAfterReturning(String str) { System.out.println("sayAfterReturning " + str); return "returning"; } @Override public void sayAfterThrowing() { System.out.println("sayAfterThrowing"); throw new RuntimeException("test exception!"); } @Override public void sayAfterFinnally() { System.out.println("sayAfterFinnally"); } @Override public void sayAround(String str) { System.out.println("sayAround " + str); } }
AOP支持类
public class HelloAspect { public void beforeAdvice(String param) { System.out.println("****beforeAdvice " + param); } public void afterReturningAdvice(String retVal) { System.out.println("****afterFinnallyAdvice " + retVal); } public void afterThrowingAdvice(Exception ex) { System.out.println("****afterThrowingAdvice " + ex.getMessage()); } public void afterFinnallyAdvice() { System.out.println("****afterFinnallyAdvice "); } public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable { System.out.println("===========around before advice"); Object retVal = pjp.proceed(new Object[] {"replace"}); System.out.println("===========around after advice"); return retVal; } }
AOP配置
<!-- 目标类实例化 --> <bean id="aopHello" class="cn.matt.aop.HelloImpl"></bean> <!-- aop支持类实例化 --> <bean id="helloAspect" class="cn.matt.aop.HelloAspect"></bean> <!-- aop配置 --> <aop:config> <aop:aspect ref="helloAspect"> <!-- 前置通知(简单) --> <aop:before pointcut="execution(* cn.matt.aop..*.sayHello(..))" method="beforeAdvice"/> <!-- 前置通知(复杂) --> <aop:before pointcut="execution(* cn.matt.aop..*.sayBefore(..)) and args(param)" method="beforeAdvice(java.lang.String)" arg-names="param"/> <!-- 后置返回通知 --> <aop:after-returning pointcut="execution(* cn.matt.aop..*.sayAfterReturning(..))" method="afterReturningAdvice" arg-names="retVal" returning="retVal"/> <!-- 后置异常通知 --> <aop:after-throwing pointcut="execution(* cn.matt.aop..*.sayAfterThrowing(..))" method="afterThrowingAdvice" arg-names="exception" throwing="exception"/> <!-- 后置最终通知 --> <aop:after pointcut="execution(* cn.matt.aop..*.sayAfterFinnally(..))" method="afterFinnallyAdvice"/> <!-- 环绕通知 --> <aop:around pointcut="execution(* cn.matt.aop..*.sayAround(..))" method="aroundAdvice"/> </aop:aspect> </aop:config>
测试
public class HelloTest { private static Hello hello; static { ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); hello = context.getBean(Hello.class); } @Test public void testSayBefore() { hello.sayBefore("opjuy"); } @Test public void testSayAfterReturning() { hello.sayAfterReturning("opjuy"); } @Test(expected = RuntimeException.class) public void testSayAfterThrowing() { hello.sayAfterThrowing(); } @Test public void testSayAfterFinnally() { hello.sayAfterFinnally(); } @Test public void testSayAround() { hello.sayAround("around"); } }
三、AOP注解配置
使用@AspectJ风格的注解配置,首先须添加如下配置:
<aop:aspectj-autoproxy />
具体使用步骤如下:
1)使用@Aspect将切面支持类声明为切面,并实例化bean
@Aspect() public class Aspect{ …… }
<bean id="aspect" class="……Aspect"/>
2)使用@Pointcut+方法(方法必须是返回void类型)声明切入点
@Pointcut(value="切入点表达式", argNames = "参数名列表") public void pointcutName(……) {}
3)声明通知
@Before(value = "切入点表达式或命名切入点", argNames = "参数列表参数名") @AfterReturning(value="切入点表达式或命名切入点", pointcut="切入点表达式或命名切入点", argNames="参数列表参数名", returning="返回值对应参数名") @AfterThrowing (value="切入点表达式或命名切入点", pointcut="切入点表达式或命名切入点", argNames="参数列表参数名", throwing="异常对应参数名") @After (value="切入点表达式或命名切入点", argNames="参数列表参数名") @Around (value="切入点表达式或命名切入点", argNames="参数列表参数名")
注意:
i)value和pointcut均可指定切入点表达式或命名切入点,如两者同时指定,则pointcut覆盖value
ii)使用命名切入点时,须指定切入点名及其参数,若无参数,括号也不可省略,如:
错误:@Before(value = "pointcut1")
正确:@Before(value = "pointcut1()")
正确:@Before(value = "pointcut2(param)", argNames = "param")
具体实例如下:
目标接口及实现,同xml配置实例
AOP支持类
@Aspect public class HelloAspect { // 切点1 @Pointcut(value = "execution(* cn.matt.aop..*.sayHello(..))") public void pointcut1() {} // 切点2 @Pointcut(value = "execution(* cn.matt.aop..*.sayBefore(..)) && args(param)", argNames = "param") public void pointcut2(String param) {} // 前置通知(简单) @Before(value = "pointcut1()") public void beforeAdvice() { System.out.println("****beforeAdvice"); } // 前置通知(复杂) @Before(value = "pointcut2(param)", argNames = "param") public void beforeAdvice(String param) { System.out.println("****beforeAdvice " + param); } // 后置返回通知 @AfterReturning(value = "execution(* cn.matt.aop..*.sayAfterReturning(..))", argNames = "retVal", returning = "retVal") public void afterReturningAdvice(String retVal) { System.out.println("****afterFinnallyAdvice " + retVal); } // 后置异常通知 @AfterThrowing(value = "execution(* cn.matt.aop..*.sayAfterThrowing(..))", argNames = "ex", throwing = "ex") public void afterThrowingAdvice(Exception ex) { System.out.println("****afterThrowingAdvice " + ex.getMessage()); } // 后置最终通知 @After(value = "execution(* cn.matt.aop..*.sayAfterFinnally(..))") public void afterFinnallyAdvice() { System.out.println("****afterFinnallyAdvice "); } // 环绕通知 @Around(value = "execution(* cn.matt.aop..*.sayAround(..))") public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable { System.out.println("===========around before advice"); Object retVal = pjp.proceed(new Object[] {"replace"}); System.out.println("===========around after advice"); return retVal; } }
AOP配置
<!-- 目标类实例化 --> <bean id="aopHello" class="cn.matt.aop.HelloImpl"></bean> <!-- aop支持类实例化 --> <bean id="helloAspect" class="cn.matt.aop.HelloAspect"></bean> <!-- 开启aop注解支持 --> <aop:aspectj-autoproxy/>
测试,同xml配置实例
参考:
第六章 AOP 之 6.1 AOP基础 ——跟我学spring3
第六章 AOP 之 6.2 AOP的HelloWorld ——跟我学spring3