首页 > 技术文章 > 简单版的AOP实现日志打印功能

xuesheng 2017-08-30 21:31 原文

需要的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

一、创建一个接口

  

public interface MyMath {
public int add(int i,int j);
public void sub(int i,int j);
public void mul(int i,int j);
public void div(int i,int j);
}

二、创建一个类实现上面的接口

  这里使用了@component注解的方式将MymathImpl对象注入到IOC容器中

@Component
public class MyMathImpl implements MyMath{

    @Override
    public int add(int i, int j) {
        int result=i+j;
        System.out.println("目标方法add执行了, "+result);
        return result;
        
    }

    @Override
    public void sub(int i, int j) {
        // TODO Auto-generated method stub
        int result=i-j;
        System.out.println("目标方法sub执行了, "+result);
    }

    @Override
    public void mul(int i, int j) {
        // TODO Auto-generated method stub
        int result=i*j;
        System.out.println("目标方法mul执行了, "+result);
    }

    @Override
    public void div(int i, int j) {
        // TODO Auto-generated method stub
        int result=i/j;
        System.out.println("目标方法div执行了, "+result);
    }
}

 

因为使用注解,所以在applicationcontext.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" xmlns:context="http://www.springframework.org/schema/context"
    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.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
    <1--扫描包-->
     <context:component-scan base-package="com.neuedu"></context:component-scan>
</beans>

三、创建一个切面类  

  1.开启基于注解的AOP功能
  < aop:aspectj-autoproxy />

  需要在applicationcontext.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"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
     
     <!--扫描包  -->
     <context:component-scan base-package="com.neuedu.bean"></context:component-scan>
 <!-- 开启AOP注解 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

  2.声明一个切面类,并把这个切面类加入到IOC容器中
  @Aspect//表示这是一个切面类
  @Component//加入IOC容器

@Component
@Aspect
public class MyMathAspect {
    @Pointcut(value="execution(public * com.neuedu.bean.MyMathImpl.*(..))")
    public void showLog(){
    }  
@Before(value
= "showLog()") public void showBeforLog(JoinPoint point ){ Object[] args = point.getArgs(); List<Object> asList = Arrays.asList(args); Signature signature = point.getSignature(); String name = signature.getName(); System.out.println("日志,前置通知,参数为:"+name+"方法为:"+asList); } @After(value = "showLog()") public void showAfterLog(){ System.out.println("日志,后置通知"); } @AfterThrowing(value = "showLog()",throwing="ex") public void showAfterThrowingLog(Exception ex){ System.out.println("日志,异常通知,异常信息为 "+ex); } @AfterReturning(value = "showLog()",returning="result") public void showAfterReturningLog(Object result){ System.out.println("日志,返回置通知,结果为: "+result); } }

3.说明:1)   [1]前置通知:@Before
            [2]返回通知:@AfterReturning
       [3]异常通知:@AfterThrowing
       [4]后置通知:@After

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

      如果execution写成excution在applicationcontext.xml 中扫描包会出现问题。

      value="execution(public * com.neuedu.bean.MyMathImpl.*(..))"

      支持通配符 第一个*代表了任意的返回类型,第二*代表了该类下任意的方法,..代表任意的参数类型和个数。

      最详细的切入点表达式:
        execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
      最模糊的切入点表达式:
        execution (* *.*(..))

  

    用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可!
    需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值!

    3)通知方法的细节,参数

        a).通知方法中有一个JoinPoint类型的参数

           可以获取目标方法的签名,进而获取方法名 

               Signature signature = point.getSignature();
                  String name = signature.getName();
         可以获取方法的参数,不过需要转换:          
             Object[] args = point.getArgs();
                List<Object> asList = Arrays.asList(args);
         b) 在返回通知中获取方法的返回值     

            [1]在@AfterReturning注解中添加returning属性
            @AfterReturning (value="myPointCut()", returning= "result")
            [2]在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致
            showReturnLog(JoinPoint joinPoint, Object result)
            [3]在异常通知中获取异常对象
              [1]在@ AfterThrowing注解中添加throwing属性
              @AfterThrowing (value="myPointCut()",throwing= "throwable" )
              [2]在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致
              showExceptinLog(JoinPoint joinPoint, Throwable throwable)

 四、创建一个测试类:
    
public class IOCTest {
    
    private ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");

    @Test
    public void test() {
        /*Object bean = ioc.getBean("myMathImpl");
        System.out.println(bean);*/
        MyMath bean2 = ioc.getBean(MyMath.class);
        bean2.add(10, 2);
    }

}

执行结果:

日志,前置通知,参数为:add方法为:[10, 2]
目标方法add执行了, 12
日志,后置通知
日志,返回置通知,结果为: 12

当出现异常:除法中一个数除以0 会出现异常

  

public class IOCTest {
    
    private ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");

    @Test
    public void test() {
        /*Object bean = ioc.getBean("myMathImpl");
        System.out.println(bean);*/
        MyMath bean2 = ioc.getBean(MyMath.class);
        bean2.div(10, 0);
    }
}

执行结果为:

日志,前置通知,参数为:div方法为:[10, 0]

java.lang.ArithmeticException: / by zero

日志,后置通知

 

推荐阅读