Spring4.0学习笔记008——AOP的配置(基于注解)
Posted yfyzwr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring4.0学习笔记008——AOP的配置(基于注解)相关的知识,希望对你有一定的参考价值。
AOP开发环境的准备
新建名为
lib
的文件夹,并往其中导入所需的jar包。- commons-logging-1.2.jar
- spring-beans-4.0.0.RELEASE.jar
- spring-context-4.0.0.RELEASE.jar
- spring-core-4.0.0.RELEASE.jar
- spring-expression-4.0.0.RELEASE.jar
- spring-aop-4.0.0.RELEASE.jar
- spring-aspects-4.0.0.RELEASE.jar
解压
springsource-tool-suite-3.7.2.RELEASE-e4.5.1-updatesite.zip
文件,再导入一些jar包。- org.aopalliance_1.0.0.jar(关键字为
aopalliance
的包) - org.aspectj.weaver_1.8.7.jar(关键字为
aspectj.weaver
的包) - org.aspectj.runtime_1.8.7.jar(关键字为
aspectj.runtime
的包)
- org.aopalliance_1.0.0.jar(关键字为
将之前加入的所有jar包,都加入到build路径。
配置组件的自动扫描环境
在前一篇博文的基础上新建名为
ApplicationContext
的xml配置文件,并且另外引入context命名空间。xmlns:context="http://www.springframework.org/schema/context"
在xml配置文件中声明基于注解的组件扫面范围。
<!-- 配置基于注解的组件自动扫面范围 --> <context:component-scan base-package="com.yfyzwr.spring.aop"></context:component-scan>
为ArithmeticCalculatorImpl类添加自动扫描的注解。
@Component public class ArithmeticCalculatorImpl implements ArithmeticCalculator
修改main方法的实现。
public class Main public static void main(String[] args) ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); ArithmeticCalculatorImpl target = (ArithmeticCalculatorImpl)context.getBean("arithmeticCalculatorImpl"); int result = target.add(1, 2); System.out.println("result : " + result);
运行程序,查看输出结果。
result : 3
从输出结果可以得知,Spring容器已经加载并实例化ArithmeticCalculatorImpl类型的对象,基于注解的自动扫描环境配置成功。
前置通知
为xml配置文件再引入aop命名空间,并且还要使得@Aspect注解生效。
xmlns:aop="http://www.springframework.org/schema/aop" <!-- 使@Aspect注解生效:自动为匹配的类生产代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
新建名为
LoggingAspect
的java类。@Aspect @Component public class LoggingAspect @Before("execution(* com.yfyzwr.spring.aop.*.*(..))") public void beforeMerhod(JoinPoint joinPoint) String MethodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("The Method " + MethodName + "begin with" + args);
需要注意的是:
- 用作切面的类,同样需要添加@Component注解,这样Spring容器才能检测到并实例化它的对象。
- 用作切面的类,需要添加@Aspect注解,这样才能标识出它是用作切面。
- 前置通知使用@Before注解,并将切入点表达式的值作为注解值。
- 第一个
*
号表示:任意修饰符及任意返回值(如果是public *
则表示只对公有方法起作用) - 第二个
*
号表示:任意类 - 第三个
*
号表示:任意方法 - 括号里的
..
号表示:匹配任意数量的参数(如果是double, ..
则表示只对第一个参数是double类型的方法起作用) - 方法的实现也可以不带JoinPoint类型的参数,但是如果带上该JoinPoint 类属于
org.aspectj.lang.JoinPoint;
包。
修改main方法的实现
public static void main(String[] args) ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); //注意这一行的变化,同上一篇博文讲的那样,我们需要的是接口类型来匹配生产的Proxy对象 ArithmeticCalculator target = (ArithmeticCalculator)context.getBean(ArithmeticCalculator.class); int result = target.add(1, 2); System.out.println("result : " + result);
需要注意的是:
与之前的genBean()方法的使用有所不同,这里传入的参数是接口类型的Class对象。这个要求与上一篇博文在直接使用动态代理时一样,返回的代理对象是接口类型。运行程序,查看输出结果。
The Method addbegin with[1, 2]
result : 3从输出结果可以得知,前置通知方法被成功调用。
后置通知
在
LoggingAspect
类添加后置通知的方法。@After("execution(* com.yfyzwr.spring.aop.*.*(..))") public void afterMerhod(JoinPoint joinPoint) String MethodName = joinPoint.getSignature().getName(); System.out.println("The Method " + MethodName + " end");
运行程序,查看输出结果。
The Method add begin with[1, 2]
The Method add end
result : 3需要注意的是:
- 后置通知不论目标方法的执行是否出现异常,都会被执行。
- 后置通知里面还不能访问目标方法的返回值。
修改main方法的实现。
public static void main(String[] args) ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); //注意这一行的变化,同上一篇博文讲的那样,我们需要的是接口类型来匹配生产的Proxy对象 ArithmeticCalculator target = (ArithmeticCalculator)context.getBean(ArithmeticCalculator.class); int result = target.add(1, 2); System.out.println("result : " + result); result = target.dev(12, 0); System.out.println("result : " + result);
再运行程序,查看输入结果。
The Method add begin with[1, 2]
The Method add end
result : 3
The Method dev begin with[12, 0]
The Method dev end
Exception in thread “main” java.lang.ArithmeticException: / by zero
返回通知
在 LoggingAspect
类添加返回通知的方法。
@AfterReturning(value="execution(* com.yfyzwr.spring.aop.*.*(..))", returning="result")
public void returnMerhod(JoinPoint joinPoint, Object result)
String MethodName = joinPoint.getSignature().getName();
System.out.println("The Method " + MethodName + " end with " + result);
异常通知
在 LoggingAspect
类添加异常通知的方法。
@AfterThrowing(value="execution(* com.yfyzwr.spring.aop.*.*(..))", throwing="ex")
public void throwingMerhod(JoinPoint joinPoint, Exception ex)
String MethodName = joinPoint.getSignature().getName();
System.out.println("The Method " + MethodName + " occur exception " + ex);
需要注意的是:
可以为方法的形参指定具体类型的异常(如ArithmeticException、NullPointerException等),这样就会只在发生指定异常时,调用异常通知方法。
切面的优先级
上述的LoggingAspect类是切面类,如果我们的项目中有很多的切面类,并且都针对同一目标方法设置了各自的通知方法。在这种情况下,这些不同切面类的通知方法的执行顺序是不确定的,所有我们需要为不同的切面类设置各自的优先级。
切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定。
@Order(0) //指定切面的执行优先级
@Aspect
@Component
public class LoggingAspect
重用切入点表达式
上述的各种通知方法,它们都有着相同的切点表达式,所以我们应该实现对该切点表达式的重用。
需要修改 LoggingAspect
类的实现。
@Order(0) //指定切面的执行优先级
@Aspect
@Component
public class LoggingAspect
//定义一个方法,用于声明切入点表达式。通常该方法的方法体是空的,因为将切入点定义与应用程序逻辑混在一起是不合理的
@Pointcut("execution(* com.yfyzwr.spring.aop.*.*(..))")
public void declareJoinPointExpression()
@Before("declareJoinPointExpression()")
public void beforeMerhod(JoinPoint joinPoint)
String MethodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The Method " + MethodName + " begin with" + args);
@After("declareJoinPointExpression()")
public void afterMerhod(JoinPoint joinPoint)
String MethodName = joinPoint.getSignature().getName();
System.out.println("The Method " + MethodName + " end");
@AfterReturning(value="declareJoinPointExpression()", returning="result")
public void returnMerhod(JoinPoint joinPoint, Object result)
String MethodName = joinPoint.getSignature().getName();
System.out.println("The Method " + MethodName + " end with " + result);
@AfterThrowing(value="declareJoinPointExpression()", throwing="ex")
public void throwingMerhod(JoinPoint joinPoint, NullPointerException ex)
String MethodName = joinPoint.getSignature().getName();
System.out.println("The Method " + MethodName + " occur exception " + ex);
其他文件、其他包都可以使用这个切入点的表达式,只需要附上该表达式的包、文件信息即可。
以上是关于Spring4.0学习笔记008——AOP的配置(基于注解)的主要内容,如果未能解决你的问题,请参考以下文章
Spring4.0学习笔记009——AOP的配置(基于XML文件)
Spring4.0学习笔记009——AOP的配置(基于XML文件)
Spring4.0学习笔记007——AOP基础:动态代理概念解析
Spring4.0学习笔记007——AOP基础:动态代理概念解析