Spring4 学习系列之——AOP的初解

在学习AOP以前,咱们先简单理解下IOCAOP的原理。java

IOC:控制反转编程

简单明白什么是控制反转app

传统的面向对象编程是在一个类里须要哪一个对象就new一个,new多了,就会形成各个类彼此之间的依赖性很高,甚至会形成一个对象出了个问题,致使其余类对象都不能运行的状况。因此这就是所谓的耦合度很高。ide

那么确定须要一个方法来解耦啦,那就是ioc,ioc它是一个容器,在容器里存放的是bean对象,那么一个类要成为ioc内置的bean对象,须要在java类里面加入相关的注解声明,有了这个声明,ioc容器加载时就会全权管理这个类对象的一切,包括生命周期,也能够理解ioc是beanFactory的升级版,咱们知道,beanFactory一般是在须要的类里直接静态或者继承关系来得到beanFactory里面的对象或者方法的,这种方法的依赖性仍是有的,那么做为它的升级版ioc,那就不同了,它能够说彻底不依赖任何java文件,而是哪一个java类须要某个对象,直接注入提供给他,极大的下降了耦合度。学习

DI:依赖注入。其实和IOC是一个原理,它只是基于IOC思想的一个实现测试

依赖注入有三种方法spa

  1. set方法
  2. 构造器
  3. 接口(不推荐)

依赖注入很简单,不作描述,通常都是配置自动扫描,而后自动注入@autowrite代理

 

AOP:面向切面日志

说实话也是ioc思想的衍生,面向对象完善,是一个很值得研究深透的一个思想。具体东西不说,直接简单点解释。在开发中,好比要作一个日志监控功能,功能要求每一个方法先后都要后台打印执行的开始,结束时间,咱们能够写一个接口或者继承来实现,但总管这样,效率依旧很低。咱们能够这样子想:既然这些方法都要一个日志功能,传统的面向接口不太好,那么咱们就用aop面向切面吧! 把日志功能当成一个切面,而这个切面是由许多切点构成的,想一下:不少个点,而后就成了一个面。。可是这些点的业务需求是同样的,只是实现不同,不然有其余的点混入进来,这个“面”就不完美了,不完美那就不提倡了。那么什么是点呢?点就是链接点,一个方法里先后加入日志功能,那么它就有两个点,一前一后,这两个点咱们就能够在抽出来的那个所谓的 "面 "的类中插入日志功能,咱们能够经过注解或者xml来插入,有5种插入顺序,下面的列子会看到,其内部经过java的动态代理来实现,好,上个经典的例子。。component

 

配置文件

        <!-- 自动扫描 -->
        <context:component-scan base-package="aop"></context:component-scan>
        <!-- 自动代理 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

有两个类:一个接口:ArithmeticCalculator     一个实现:ArithmeticCalculatorImpl

接口定义了简单的加减乘除

public interface ArithmeticCalculator {

    int add(int i, int j);
    int sub(int i, int j);
    
    int mul(int i, int j);
    int div(int i, int j);
    
}

接口的实现,返回结果

@Component
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        return result;
    }

}
 

接下来  我要在每一个方法里先后,或者出现异常的时候都打印日志,由于这些功能都是相同的,并且每一个方法里都有接入点,因此就组成一个切面类:AspectjTest.class

@Component // 加入到ioc容器中,让其自动扫描
@Aspect // 声明为一个切面
public class AspectjTest {
    // 前置通知,在目标方法开始前执行
    @Before(value = "execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
    public void beforeMthod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        Object[] args = joinpoint.getArgs();
        List<Object> largs = (List) Arrays.asList(args);
        System.out.println("the methood " + methodName + " begins with " + largs);
    }
    
    //后置通知,在目标方法执行完成后,不管结果有没有异常,都会执行的一个通知
    //在后置通知里还不能访问目标方法的结果
    @After(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
    public void afterMethod(JoinPoint joinpoint){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("the methood " + methodName + " ends");
    }
    
    //运行结束通知,在目标方法正常执行完成后的一个通知
    //能够访问到返回结果
    @AfterReturning(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))",returning="result")
    public void afterReturnRunMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("the methood " + methodName + " ends with: "+result);
    }
    
    //异常通知,在目标方法执行时发生异常返回的一个通知
    //能够访问到异常结果
    @AfterThrowing(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))",throwing="expection")
    public void afterThrowingMethod(JoinPoint joinPoint,Object expection){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("the methood "+methodName+" throw: "+expection    );
    }
    
    //环绕通知:需携带ProceedingJoinPoint类型的参数
    //环绕通知相似动态代理的全过程,ProceedingJoinPoint 类型的参数能够决定是否执行目标方法
    //环绕通知必须有返回值,返回值为目标方法的返回值
    
    @Around(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
    public Object aroundMethod(ProceedingJoinPoint pjp){
        String methodName = pjp.getSignature().getName();
        Object result = null;
        try {
            //前置通知
            System.out.println("the methood " + methodName + " begins with " +Arrays.asList(pjp.getArgs()));
            //执行目标方法
            result = pjp.proceed();
            //运行结束通知(正常结束)
            System.out.println("the methood " + methodName + " ends with: "+result);
        } catch (Throwable e) {
            //异常通知
            System.out.println("the methood "+methodName+" throw: "+e);
        }
        
        //后置通知(无论有没有异常都会返回)
        System.out.println("the methood " + methodName + " ends");
        return result;
    }
}
 

客户端测试下:

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        ArithmeticCalculator ar = (ArithmeticCalculator) ctx.getBean("arithmeticCalculatorImpl");
        int result = ar.add(1, 2);
        System.out.println("result:"+result);
        
        int result2 = ar.div(3, 1);
        System.out.println("result2:"+result2);
    }

}
 

观察控制台,结合上述,便能理解AOP的美妙之处

相关文章
相关标签/搜索