简单的aop实现日志打印(切入点表达式)

Spring中可以使用注解或XML文件配置的方式实现AOP。
1、导入jar包

  • com.springsource.net.sf.cglib -2.2.0.jar
  • com.springsource.org.aopalliance-1.0.0 .jar
  • com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar
  • commons-logging-1.1.3. jar
  • spring-aop-4.0.0.RELEASE.jar
  • spring-aspects-4.0.0.RELEASE.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

aspectaop相关jar包 ---> 资源目录--->jar包资源--->AOP日志打印相关jar包(切面类) 

Spring相关jar包 ---> 资源目录--->jar包资源--->Spring相关jar包 

2、开启基于注解的AOP功能

在Spring的配置文件中加入

<context:component-scan base-package="com.bwlu.aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

3、声明一个切面类,并把这个切面类加入到IOC容器中

 1 @Component//加入IOC容器
 2 @Aspect//表示这是一个切面类
 3 public class LogAspect{
 4     //value中为切入点表达式
 5     @Before(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//前置通知
 6     public void showBeginLog(){
 7         System.out.println("AOP日志开始");
 8     }
 9     @After(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//后置通知
10     public void showReturnLog(){
11         System.out.println("AOP方法结束");
12     }
13     @AfterThrowing(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//异常通知
14     public void showExceptionLog(){
15         System.out.println("AOP方法异常");
16     }
17     @AfterReturning(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//返回通知
18     public void showAfterLog(){
19         System.out.println("AOP方法最终返回");
20     }
21 }

4、被代理的对象也需要加入IOC容器

 1 @Component
 2 public class MathCalculator {
 3     public void add(int i, int j) {
 4         int result=i+j;
 5         System.out.println("目标方法add(int)执行了");
 6     }
 7     public void sub(int i, int j) {
 8         int result=i-j;
 9         System.out.println("目标方法sub执行了");
10     }
11     public void mult(int i, int j) {
12         int result=i*j;
13         System.out.println("目标方法mult执行了");
14     }
15     public void div(int i, int j) {
16         int result=i/j;
17         System.out.println("目标方法div执行了");
18     }
19 }
MathCalculator

5、新建Junit测试类进行测试

 1 ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
 2 @Test
 3 public void test() {
 4     //需要进行强转,如果该类实现了一个接口(并且切入点表达式指向这个类),那么获取到的将不是该类的对象,需要强转
 5     //MathCalculatorImpl实现了MathCalculator接口,则
 6     //MathCalculator bean = (MathCalculator)ioc.getBean("mathCalculatorImpl");
 7     MathCalculator bean = (MathCalculator)ioc.getBean("mathCalculator");
 8     bean.add(10, 5);
 9     System.out.println();
10     bean.add(10.0, 5);
11     System.out.println();
12     bean.sub(10, 5);
13     System.out.println();
14     bean.mult(10, 5);
15     System.out.println();
16     bean.div(10, 0);
17 }

6、切入点表达式

  • 切入点表达式的语法格式

execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

  • 切入点表达式支持通配符
  • 两种切入表达式
    • 最详细的切入点表达式:

 

execution(public void com.bwlu.aop.MathCalculator.add(int, int))

 

    • 最模糊的切入点表达式:
execution (* *.*(..))

execution(public void com.bwlu.aop.MathCalculator.add(int, int)):只有add方法加入了4个通知,

execution(public void com.bwlu.aop.MathCalculator.*(int, int)):任意方法,参数为int,int

execution(public void com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意参数列表

execution(public * com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意参数列表,任意返回值

execution( * com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意参数列表,任意返回值,任意访问修饰符

execution (* *.*(..)):任意匹配

需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值!

7、优化

用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可!

 1 @Component
 2 @Aspect
 3 public class LogAspect{
 4     @Pointcut(value="execution(* *.*(..))")
 5     public void showLog(){}
 6     @Before(value="showLog()")
 7     public void showBeginLog(){
 8         System.out.println("AOP日志开始");
 9     }
10     @After(value="showLog()")
11     public void showReturnLog(){
12         System.out.println("AOP方法结束");
13     }
14     @AfterThrowing(value="showLog()")
15     public void showExceptionLog(){
16         System.out.println("AOP方法异常");
17     }
18     @AfterReturning(value="showLog()")
19     public void showAfterLog(){
20         System.out.println("AOP方法最终返回");
21     }
22 }
LogAspect

8、通知方法的细节

(1)在通知中获取目标方法的方法名和参数列表

  • 在通知方法中声明一个JoinPoint类型的形参
  • 调用JoinPoint对象的getSignature()方法获取目标方法的签名
  • 调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表

(2)在返回通知中获取方法的返回值

  • 在@AfterReturning注解中添加returning属性:@AfterReturning (value="showLog()", returning= "result")
  • 在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致:showReturnLog(JoinPoint joinPoint, Object result)

(3)在异常通知中获取异常对象

  • 在@ AfterThrowing注解中添加throwing属性:@AfterThrowing (value="showLog()",throwing= "throwable" )
  • 在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致:showExceptinLog(JoinPoint joinPoint, Throwable throwable)
 1 @Component
 2 @Aspect
 3 public class LogAspect{
 4     @Pointcut(value="execution(* *.*(..))")
 5     public void showLog(){}
 6     @Before(value="showLog()")
 7     public void showBeginLog(JoinPoint jPoint){
 8         Object[] args = jPoint.getArgs();
 9         List<Object> asList = Arrays.asList(args);
10         Signature signature = jPoint.getSignature();
11         String name = signature.getName();
12         System.out.println("AOP日志开始");
13         System.out.println("目标方法名:"+name+",参数列表:"+asList);
14     }
15     @After(value="showLog()")
16     public void showReturnLog(){
17         System.out.println("AOP方法结束");
18     }
19     @AfterThrowing(value="showLog()",throwing="ex")
20     public void showExceptionLog(Exception ex){
21         System.out.println("AOP方法异常");
22         System.out.println("异常信息:"+ex.getMessage());
23     }
24     @AfterReturning(value="showLog()",returning="result")
25     public void showAfterLog(Object result){
26         System.out.println("方法返回值:"+result);
27         System.out.println("AOP方法最终返回");
28     }
29 }
LogAspect

9、环绕通知@Around

环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数:

public void around(ProceedingJoinPoint joinPoint){}

环绕通知能够替代其他4个通知。
注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理!

 1 @Component
 2 @Aspect
 3 public class LogAspect{
 4     @Around(value="execution(* *.*(..))")
 5     public Object showLog(ProceedingJoinPoint point){
 6         Object[] args = point.getArgs();
 7         List<Object> asList = Arrays.asList(args);
 8         Signature signature = point.getSignature();
 9         String name = signature.getName();
10         Object result = null;
11         try {
12             try {
13                 //目标方法之前要执行的操作,相当于@before
14                 System.out.println("[环绕日志]"+name+"开始了,参数为:"+asList);
15                 //调用目标方法
16                 result = point.proceed(args);
17             } finally {
18                 //方法最终结束时执行的操作,相当于@after
19                 System.out.println("[环绕日志]"+name+"结束了!");
20             }
21             //目标方法正常执行之后的操作,相当于@AfterReturning
22             System.out.println("[环绕日志]"+name+"返回了,返回值为:"+result);
23         } catch (Throwable e) {
24             //目标方法抛出异常信息之后的操作,相当于@AfterThrowing
25             System.out.println("[环绕日志]"+name+"出异常了,异常对象为:"+e);
26             throw new RuntimeException(e.getMessage());
27         }
28         return result;
29     }
30 }
LogAspect

 10、切面的优先级

对于同一个代理对象,可以同时有多个切面共同对它进行代理。
可以在切面类上通过@Order (value=50)注解来进行设置,值越小优先级越高!

@Aspect

@Component

@Order(value=40)

public class LogAspect {}

 

posted @ 2017-08-29 13:40  bwlu---ed  阅读(7025)  评论(0编辑  收藏  举报