Spring -- AOP入门基础

动态代理

我们在日常开发过程中是否会遇到下图中的这种状况

红框中的是我们要输出的日志,你是否发现,日志中大部分信息都是相同的,并且如果我们要修改一个地方,所有的地方都需要改,而且代码看起来还比较冗余

下面我们就可以通过动态代理的方式解决这个问题

看下代码

public interface Calculation {

    public int add(int x, int y);

    public int sub(int x, int y);

    public int mul(int x, int y);

    public int dev(int x, int y);
}

定义接口,加减乘除方法。

public class CalculationImpl implements Calculation {

    @Override
    public int add(int x, int y) {
        int result = x + y;
        return result;
    }

    @Override
    public int sub(int x, int y) {
        int result = x - y;
        return result;
    }

    @Override
    public int mul(int x, int y) {
        int result = x * y;
        return result;
    }

    @Override
    public int dev(int x, int y) {
        int result = x / y;
        return result;
    }

}

具体实现类,这里我们看到,没有植入日志。

public class CalculationProxy {

    private Calculation calculation = null;

    CalculationProxy(Calculation calculation) {
        this.calculation = calculation;
    }

    public Calculation getCalculationLog() {
        Calculation proxy = null;

        ClassLoader loader = calculation.getClass().getClassLoader();
        Class[] interfaces = new Class[] { Calculation.class };

        InvocationHandler h = new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                System.out
                        .println("GP-->invoke begin , execute method is "
                                + method.getName() + ", args is "
                                + Arrays.asList(args));
                Object obj = method.invoke(calculation, args);

                return obj;
            }
        };

        proxy = (Calculation) Proxy.newProxyInstance(loader, interfaces, h);

        return proxy;

    }
}

动态代理类的实现,在实现invoke方法的时候,我们输出日志在调用目标方法之前。

测试

public class Main {
    public static void main(String[] args) {
        Calculation calculation = new CalculationImpl();
        CalculationProxy calculationProxy = new CalculationProxy(calculation);
        Calculation cal = calculationProxy.getCalculationLog();

        System.out.println(cal.add(1, 3));
        System.out.println(cal.mul(1, 5));
    }
}

输出结果

GP–>invoke begin , execute method is add, args is [1, 3]

4

GP–>invoke begin , execute method is mul, args is [1, 5]

5

看起来是不是要清晰多了,我们将日志单独提取到动态代理的方法中,对核心的业务方法没有侵入,而且还便于我们的维护。

AOP简介

AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.

AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.

在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.

AOP 的好处:

  • 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
  • 业务模块更简洁, 只包含核心业务代码.

AOP术语

切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知(Advice): 切面必须要完成的工作

目标(Target): 被通知的对象

代理(Proxy): 向目标对象应用通知之后创建的对象

连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置

切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

基于Aspect的AOP – 前置通知

更多内容,参考:http://jinnianshilongnian.iteye.com/blog/1420689

涉及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.1.jar

spring-aop-4.1.7.RELEASE.jar

spring-aspects-4.1.7.RELEASE.jar

spring-beans-4.1.7.RELEASE.jar

spring-context-4.1.7.RELEASE.jar

spring-core-4.1.7.RELEASE.jar

spring-expression-4.1.7.RELEASE.jar

看下代码

public interface Calculation {

    public int add(int x, int y);

    public int sub(int x, int y);

    public int mul(int x, int y);

    public int dev(int x, int y);
}
@Component
public class CalculationImpl implements Calculation {

    @Override
    public int add(int x, int y) {
        int result = x + y;
        return result;
    }

    @Override
    public int sub(int x, int y) {
        int result = x - y;
        return result;
    }

    @Override
    public int mul(int x, int y) {
        int result = x * y;
        return result;
    }

    @Override
    public int dev(int x, int y) {
        int result = x / y;
        return result;
    }

}

实现接口,并且使用@Component标记为IOC容器中的组件。

    <context:component-scan base-package="com.gp.spring.aop.impl"></context:component-scan>

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

加入扫描注解的包路径。

Spring默认不支持@AspectJ风格的切面声明,增加aspectj-autoproxy,这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象。

@Aspect
@Component
public class CalculationAspect {

    @Before("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
    public void beforeMethod(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println("Method begin ... ,method=" + name + ", args = "
                + list);
    }

}
  • CalculationAspect 标记为Component组件,注册到IOC容器中
  • @Aspect标记,spring对其进行AOP相关的配置,生成相应的代理类
  • @Before,为前置通知,方法执行前的通知
  • “execution(public int com.gp.spring.aop.impl.Calculation.add(int,

    int))”表达式,表示此通知要执行的包路径、方法的相关信息。此表达式可进行模糊匹配,比如”execution(public int

    com.gp.spring.aop.impl.Calculation.*(*))”,表示Calculation类下的所有方法。

  • 方法中的参数JoinPoint joinPoint,表示连接点,通过此参数可以获取到执行方法的相关信息,比如方法名、方法参数。

执行测试方法

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Calculation calculation = (Calculation)context.getBean("calculationImpl");
        int result = calculation.add(3, 4);
        System.out.println(result);
    }

输出结果

Method begin … ,method=add, args = [3, 4]

7

基于Aspect的AOP – 后置通知

后置通知与前置通知用法类似,区别就是在执行方法之后执行后置通知方法。

代码如下

    @After("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
    public void afterMethod(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println("Method end ... ,method=" + name + ", args = "
                + list);
    }

这里用的@After注解,其他都没变化。

测试输出结果如下

Method begin … ,method=add, args = [3, 4]

executeing …

Method end … ,method=add, args = [3, 4]

7

我在对add方法增加了一个输出,便于区分前置通知、后置通知

    public int add(int x, int y) {
        int result = x + y;
        System.out.println("executeing ...");
        return result;
    }

基于Aspect的AOP – 返回通知

方法执行成功后,调用返回通知,如果方法在运行过程中抛出异常,则不会调用。

代码

    @AfterReturning(value = "execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))", returning = "ret")
    public void afterReturnMethod(JoinPoint joinPoint, Object ret) {
        String name = joinPoint.getSignature().getName();
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println("@AfterReturning ... ,method=" + name + ", args = "
                + list + ", return = " + ret);
    }

与后置通知不同的是,后置通知在方法抛出异常,仍会被调用,这里我们可以使用try … catch … 进行类比。

输出结果

@Before … ,method=add, args = [3, 4]

executeing …

@After … ,method=add, args = [3, 4]

@AfterReturning … ,method=add, args = [1, 3], return = 4

7

基于Aspect的AOP – 异常通知

方法在运行过程中,如果抛出异常,则调用异常通知

代码

    @AfterThrowing(value = "execution(public int com.gp.spring.aop.impl.Calculation.*(int, int))", throwing = "ex")
    public void afterThrowMethod(JoinPoint joinPoint, Exception ex) {
        System.out.println("@AfterThrowing ... ,ex = " + ex);
    }

此处与之前的用法有些却别,在注解的参数中增加了throwing,然后在方法中增加了要捕获的异常,此处类似于try…catch…的catch中代码块

输出结果

Exception in thread “main” @AfterThrowing … ,ex = java.lang.ArithmeticException: / by zero

java.lang.ArithmeticException: / by zero

at com.gp.spring.aop.impl.CalculationImpl.dev(CalculationImpl.java:29)

基于Aspect的AOP – 环绕通知

环绕通知,类似与我们最开始讲解的动态代理,在此通知中你可以去调用目标方法,并在目标方法的上下做各种通知(前置、返回、后置、异常等处理)

代码:

    @Around("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
    public Object aroundMethod(ProceedingJoinPoint pjd) {
        String name = pjd.getSignature().getName();
        List<Object> list = Arrays.asList(pjd.getArgs());
        Object obj = null;

        System.out.println("前置通知 ... ,method=" + name + ", args = "
                + list);
        try {
            obj = pjd.proceed();
            System.out.println("返回通知 ... ,method=" + name + ", args = "
                    + list);
        } catch (Throwable e) {
            System.out.println("异常通知 ... , exception = " + e);
            e.printStackTrace();
        }
        System.out.println("后置通知 ... ,method=" + name + ", args = "
                + list);
        return obj;
    } 

返回结果:

前置通知 … ,method=add, args = [1, 3]

executeing …

返回通知 … ,method=add, args = [1, 3]

后置通知 … ,method=add, args = [1, 3]

4

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-10 08:07:46

Spring -- AOP入门基础的相关文章

Spring AOP入门基础-继承、装饰者,代理的选择

关于Spring AOP,底层是基于动态代理实现的,下面简单的学习下为什么选择动态代理,而不选择继承实现,装饰者模式实现,下面参考如下业务场景下理解. 业务场景 业务层如果有业务需求,需要在注册用户,升级用户,和删除用户方法前都进行一次权限验证,最原始的方法就是在业务层每个方法前都添加代码验证.这是最原始的方式,在实际业务中有很多的方法,那都需要重写修改,很显然这是不合理的,因此衍生如下几个解决方案: (1)使用继承类,在继承类中对继承的方法进行修改,参考DogDemo01 (2)使用装饰者模式

Spring AOP入门——概念及注意点

AOP是什么? AOP从功能上来说就是在执行某些业务逻辑的前后,可以允许你动态地添加一些操作(比如记录日志.或者是判断是否有权限等),这些操作的添加,完全不耦合于原来的业务逻辑,从而对原有业务逻辑完全是透明. 也就是说,这段操作和业务逻辑是完全分开的,它可能在项目中需要横切多个模块,且其自身也是一个独立的模块,贯穿了整个项目.我们完全可以根据需要启用或者停用这个功能. AOP的典型应用就是事务管理和日志. AOP中的概念 下面这些术语并不是Spring定义的.由于AOP中的术语不是那么形象,所以

Spring AOP 入门

引言 AOP是软件开发思想发展到一定阶段的产物,AOP的出现并不是为了代替OOP,仅作为OOP的有益补充,在下面的例子中这个概念将会得到印证.AOP的应用场合是受限制的,一般适用于那些具有横切逻辑的应用场合,例如性能监测,访问控制,事务管理,日志记录.在平常的应用开发中AOP很难被使用到,但是AOP是Spring的亮点之一,有必要一看. 一 AOP以及术语 AOP是Aspect Oriented Programing的简称,被译为面向切面编程.AOP希望将散落在业务逻辑函数中的相同代码抽取到一个

spring aop入门

一.何为AOP? spring 的两大核心思想无非是 IOC和AOP.那么Spring 的 aop 是神马意思呢?AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现. 二.有何用途? 主要用于将日志记录,性能统计,安全控制

Spring batch 入门基础

Spring Batch是一个轻量级的,完全面向Spring的批处理框架,可以应用于企业级大量的数据处理系统.Spring Batch以POJO和大家熟知的Spring框架为基础,使开发者更容易的访问和利用企业级服务.Spring Batch可以提供大量的,可重复的数据处理功能,包括日志记录/跟踪,事务管理,作业处理统计工作重新启动.跳过,和资源管理等重要功能. 业务方案: 批处理定期提交. 并行批处理:并行处理工作. 企业消息驱动处理 大规模的并行处理 手动或是有计划的重启 局部处理:跳过记录

[Spring框架]Spring AOP基础入门总结一.

前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务

Spring MVC 编程基础

p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-bottom: .0001pt; text-align: justify; font-size: 12.0pt; font-family: Consolas } h1 { margin-top: 17.0pt; margin-right: 0cm; margin-bottom: 16.5pt; margin-left: 21.25pt; text-align: justify

[Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.

前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习. 一, AspectJ的概述: AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. Spring为了简化自身的AOP的开发,将AspectJ拿过来作为Spring自身一个AOP的开发.

Spring:(一)入门基础学习

前述 因为前几日刚刚学完Spring的基础,因此写一篇博客整理一下相关的基础知识. 什么是Spring? Spring 是一个轻量级的 DI / IoC 和 AOP 容器的开源框架,帮助分离项目组件之间的依赖关系.,来源于 Rod Johnson 在其著作<Expert one on one J2EE design and development>中阐述的部分理念和原型衍生而来. Spring:SE/EE开发的一站式框架. 一站式框架:有EE开发的每一层解决方案. WEB层