这篇文章简单通过一个例子,介绍几种增强的基本配置,以方便spring框架初学者对aop的代码结构有个清楚的了解认识。首先,spring支持aop编程,支持aspectJ的语法格式来表示切入点,切面,增强等,可以两种方式进行配置,一种是基于xml,一种是基于注解,本文介绍基于注解的集中增强的配置和使用。首先,在项目中引入spring的jar包和其他必备的常用jar包,还需引入一个aspectj的支持jar包,maven引用格式为:
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.4</version> </dependency>
版本号自己决定,这样做,可以在JAVA代码中使用如@Aspect注解等,另外,需要在资源配置xml文件中,增加说明:
<!-- 自动注册service --> <context:component-scan base-package="com.minlz.aop.anno" /> <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
这样子,就可以自动扫描被注解的aop切面,切入点等。
其他没有介绍的配置选项,参照普通spring项目的环境配置搭建即可,不再啰嗦,下面展示简单的栗子,来说明该如何通过代码和注解配置AOP的组件。
首先是一个主业务逻辑,我们有一个接口和一个实现类,实现类中的方法将传入的参数的绝对值返回,但是如果参数为0,则抛出异常信息。代码如下:
接口:
package com.minlz.aop.anno; /** * @author Bruce.Min * @date 2016年7月20日 */ public interface IAnnoService { /** * 主业务 * @param param * @return */ int mainService(int param); }
实现类为:
package com.minlz.aop.anno; import org.springframework.stereotype.Component; /** * @author Bruce.Min * @date 2016年7月20日 */ @Component public class AnnoService implements IAnnoService { public int mainService(int param) { if(param == 0) { throw new RuntimeException("参数为0"); } System.out.println("主业务,参数:" + param); return Math.abs(param); } }
然后就是我们的切面实体了:
package com.minlz.aop.anno; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * @author Bruce.Min * @date 2016年7月20日 */ @Aspect @Component public class AnnoAspectBean { /** * 切入点声明 * args(param)表明参数,也可以通过增强方法中的JoinPoint获取 */ private static final String ASPECT = "execution(* com.minlz.aop.anno.*.*(..)) and args(param)"; @Before(ASPECT) public void before(JoinPoint point, int param) { System.out.println("前置通知,参数为:" + param); } @After(ASPECT) public void after(JoinPoint point, int param) { System.out.println("后置通知,参数为:" + param); } /** * returning 表示主业务的返回值,名称和增强方法中的参数名匹配 */ @AfterReturning(value = ASPECT, returning = "retValue") public void afterReturning(JoinPoint point, int param, int retValue) { System.out.println("返回后通知,参数为: " + param + " ,返回值为:" + retValue); } @AfterThrowing(value = ASPECT, throwing = "ex") public void afterThrowing(JoinPoint point, int param, Throwable ex) { System.out.println("抛出异常通知,参数为: " + param + ", 异常信息: " + ex.toString()); } @Around(ASPECT) public Object around(ProceedingJoinPoint proPoint, int param) throws Throwable { System.out.println("环绕通知开始,参数为:" + param); Object retValue = proPoint.proceed(); System.out.println("环绕通知结束,参数为:" + param); return retValue; } }
这里,关于切面的实体,有部分需要说明的地方:
1、@Aspect和@Component注解,两个都需要有,前者告之spring这是一个切面,后者告之spring该类被作为组件管理,如果不在资源配置xml中声明该bean且不使用@Component注解,则AOP不会生效。
2、所有的增强方法,都可以传入一个类型为JoinPoint或者ProceddingJoinPoint类型的切入点参数,通过该参数可以获取目标方法和目标方法参数等信息,也可以通过上面代码示例获取方法参数,方法抛出异常和返回值
现在万事俱备,我们写个测试类来测试上面代码的运行结果:
package com.minlz.test.aop; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.minlz.aop.anno.IAnnoService; /** * @author Bruce.Min * @date 2016年7月20日 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-aop-anno.xml") public class AopAnnoTest { @Autowired private IAnnoService iAnnoService; @Test public void testAnnoAop() { iAnnoService.mainService(-1); //iAnnoService.mainService(0);
} }
如果参数为-1,则运行结果为:
通过结果可以知道,环绕通知发生在前置通知之前,在后置通知之前的,返回值增强顺序为最后。
如果,把参数改成0,则会抛出异常,那么会有哪些增强还能够继续运行的?结果如下:
可以看到,进入主业务之前,环绕通知前部分和前置通知正常运行,然后主业务抛出异常信息,主业务“完成”,后置通知启动,再接着异常增强运行,但是环绕通知后部分和返回值增强都没有运行!
总结:
本文只是简单介绍了集中增强的配置和使用,实际使用过程中,可能面临复杂的逻辑,但是原理不变,了解了每种增强的运行时机和结果,综合使用便不是问题。
PS:如有错误,还请各位赐教,不胜感激!