Spring核心系列之AOP(二)

栏目: Java · 发布时间: 8年前

内容简介:Spring核心系列之AOP(二)

Hello,大家好,在 Spring核心系列之AOP(一) 中给大家讲了Spring AOP的最强用的注解方式实现AOP(没看的小伙伴最好先看一看,本文后面的例子大多都使用注解开发),这一篇就给大家分享一下Spring如何基于XML来做AOP,文章结构:

  1. Spring AOP - XML配置
  2. Aspect的优先级
  3. Spring AOP实际运用场景
  4. Spring AOP 底层实现选择

1. Spring AOP - XML配置

这里直接以一个案例的形式对xml的开发形式进行简要分析,定义一个切面类MyAspectXML:

public class MyAspectXML {
    
    public void before(){
        System.out.println("MyAspectXML====前置通知");
    }

    public void afterReturn(Object returnVal){
        System.out.println("后置通知-->返回值:"+returnVal);
    }

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("MyAspectXML=====环绕通知前");
        Object object= joinPoint.proceed();
        System.out.println("MyAspectXML=====环绕通知后");
        return object;
    }

    public void afterThrowing(Throwable throwable){
        System.out.println("MyAspectXML======异常通知:"+ throwable.getMessage());
    }

    public void after(){
        System.out.println("MyAspectXML=====最终通知..来了");
    }
}

注意这个类没有加任何注解. 然后看下我们的XML文件:

<!-- 把切面引入到Spring容器中 -->
    <bean name="myAspectXML" class="com.zdy.MyAspectXML" />
    <!-- 配置AOP 切面 -->
    <aop:config>
        <!-- 定义切点 -->
        <aop:pointcut id="pointcut" expression="execution(...)" />

        <!-- 定义其他切点函数 -->
        <aop:pointcut id="otherPointcut" expression="execution(...)" />

        <!-- 定义通知 order 定义优先级,值越小优先级越大-->
        <aop:aspect ref="myAspectXML" order="0">
            <!-- 定义通知
            method 指定通知方法名,必须与MyAspectXML中的相同
            pointcut 指定切点函数
            -->
            <aop:before method="before" pointcut-ref="pointcut" />

            <!-- 后置通知  returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
            <aop:after-returning method="afterReturn" pointcut-ref="pointcut"  returning="returnVal" />

            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pointcut"  />

            <!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>

            <!--
                 method : 通知的方法(最终通知)
                 pointcut-ref : 通知应用到的切点方法
                -->
            <aop:after method="after" pointcut-ref="otherPointcut"/>
        </aop:aspect>
    </aop:config>

大家可以看到,<aop:aspect>这个标签其实对应的就是MyAspectXML类,然后在类中组装各种增强(建言)。其实如果在上一篇里对注解的那种形式比较了解的话,可以看到XML注解无非就是把各种元素放到了XML里去组装。效果是一样一样的。xml配置在工作中用的比较少,一般都是注解。所以大家看了了解下就OK了 。也是比较简单的。然后给XMl配置的传送门,参考了看下吧:

2. Aspect的优先级

首先说一下, 设计到优先级问题的前提是,增强的Pointcut有交集。 Aspect优先级分两种,一种是一个Aspect中定义多个增强。另一种是不同的Aspect中的多个增强.我先说下结论,然后针对不同情况搞俩例子。

  • 在目标方法前面的增强@Before,优先级越高,越先执行。 在目标方法后面的增强@After,@AfterReturning,优先级越高,越后执行。
  • 一个Aspect中定义多个增强。和定义的先后顺序有关,越先出现,优先级越高。
  • 不同的Aspect中的多个增强。根据Aspect实现Ordered接口,方法getOrder返回值有关,返回值越低,优先级越高。

2.1 一个Aspect中定义多个增强

@Aspect
public class AspectOne {

    /**
     * Pointcut定义切点函数
     */
    @Pointcut("execution(...)")
    private void myPointcut(){}

    @Before("myPointcut()")
    public void beforeOne(){
        System.out.println("前置通知....执行顺序1");
    }

    @Before("myPointcut()")
    public void beforeTwo(){
        System.out.println("前置通知....执行顺序2");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningThree(){
        System.out.println("后置通知....执行顺序3");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningFour(){
        System.out.println("后置通知....执行顺序4");
    }
}

打印结果:

前置通知....执行顺序1
前置通知....执行顺序2
后置通知....执行顺序4
后置通知....执行顺序3

2.2 不同的Aspect中的多个增强

@Aspect
public class AspectOne implements Ordered {

    /**
     * Pointcut定义切点函数
     */
    @Pointcut("execution(...)")
    private void myPointcut(){}

    @Before("myPointcut()")
    public void beforeOne(){
        System.out.println("前置通知..AspectOne..执行顺序1");
    }

    @Before("myPointcut()")
    public void beforeTwo(){
        System.out.println("前置通知..AspectOne..执行顺序2");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningThree(){
        System.out.println("后置通知..AspectOne..执行顺序3");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningFour(){
        System.out.println("后置通知..AspectOne..执行顺序4");
    }

    /**
     * 定义优先级,值越低,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}


//切面类 AspectTwo.java
@Aspect
public class AspectTwo implements Ordered {

    /**
     * Pointcut定义切点函数
     */
    @Pointcut("execution(...)")
    private void myPointcut(){}

    @Before("myPointcut()")
    public void beforeOne(){
        System.out.println("前置通知....执行顺序1--AspectTwo");
    }

    @Before("myPointcut()")
    public void beforeTwo(){
        System.out.println("前置通知....执行顺序2--AspectTwo");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningThree(){
        System.out.println("后置通知....执行顺序3--AspectTwo");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningFour(){
        System.out.println("后置通知....执行顺序4--AspectTwo");
    }

    /**
     * 定义优先级,值越低,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 1;
    }
}

输出结果:

前置通知..AspectOne..执行顺序1
前置通知..AspectOne..执行顺序2
前置通知....执行顺序1--AspectTwo
前置通知....执行顺序2--AspectTwo
后置通知....执行顺序4--AspectTwo
后置通知....执行顺序3--AspectTwo
后置通知..AspectOne..执行顺序4
后置通知..AspectOne..执行顺序3

好了,关于AspectJ的优先级分享完了,大家参照着前面总结的结论,再看下两个例子,应该是可以搞懂的。不过说实在的,用的比较少。(^_^)

3. Spring AOP实际运用场景

Spring AOP的实际运用场景其实还是比较多的,别的不说,Spring自己的事务管理其实就运用了AOP,这里我来一个相对而言靠近开发者的案例, 性能监控 。其实说是性能监控,没那么高大上,无非就是计算一下一个Web接口调用的时间。然后打日志或者写入到其他监控平台(granafa等).废话少说,直接撸代码:

先定义一个实体类,用于存监控的一些信息:

public class MonitorData {
    //类名
    private String className;
    //方法名
    private String methodName;
    //消耗时间
    private String consumeTime;
    //记录时间
    private Date logTime;
    
    //gettter setter toString什么的省略
    ....
}
@Aspect
@Component
public class MonitorAspectJ {

    /**
     * 定义切点函数,过滤controller包下的名称以Controller结尾的类所有方法
     */
    @Pointcut("execution(* com..*Controller.*(..))")
    void timer() {
    }

    @Around("timer()")
    public Object logTimer(ProceedingJoinPoint thisJoinPoint) throws Throwable {

        MonitorData monitorData=new MonitorData();
        //获取目标类名称
        String clazzName = thisJoinPoint.getTarget().getClass().getName();
        //获取目标类方法名称
        String methodName = thisJoinPoint.getSignature().getName();
        
        
        //记录类名称
        monitorData.setClassName(clazzName);
        //记录对应方法名称
        monitorData.setMethodName(methodName);
        //记录时间
        monitorData.setLogTime(new Date());

        // 计时并调用目标函数
        long start = System.currentTimeMillis();
        Object result = thisJoinPoint.proceed();
        Long time = System.currentTimeMillis() - start;

        //设置消耗时间
        monitorData.setConsumeTime(time.toString());
        //把monitorTime记录的信息上传给监控系统,并没有实现,需要自行实现即可
        //MonitoruUtils.report(monitorTime)
        System.out.println(monitorData.toString());
        return result;
    }
}

其实还是比较好理解的,无非就是在把所有以Controller结尾的类的所有方法上加上环绕,记录时间。然后存储下来(我这是打印了一下).

AOP的应用远不止这两种,诸如缓存,权限验证、内容处理、事务控制等都可以使用AOP实现,其中事务控制Spring中提供了专门的处理方式,限于篇幅就先聊到这。

4. Spring AOP 底层实现选择

Spring AOP的底层实现有两种可选,一种是JDK动态代理,一种是CGLib动态代理。先说下结论,如果要代理的target有接口,则默认采用JDK动态代理。如果没有,则采用CGLib动态代理。当然也可以强制指定使用CGLib动态代理。方法:

  • XML配置AOP: <aop:config proxy-target-class="true">
  • 注解配置AOP: <aop:aspectj-autoproxy proxy-target-class="true"/>

如果要代理的对象没有实现任何接口,则必须使用CGLIb代理,如果有,则可以使用JDK代理和CGLib代理,至于为什么,其实有点一言难尽,这里不准备展开。后期有机会专门写动态代理了再展开说。对于我们在Web项目中常用的单例类,尽量使用CGLib动态代理来实现Spring AOP.

CGlib特点如下: 字节码技术,启动慢,为每一个方法做索引,效率高。 而JDK动态代理特点则是: 启动快,每个方法通过反射调用,效率低,方法没有索引。

结语

好了,Spring AOP和大家分享完了,有点遗憾的是关于它底层的JDK动态代理和CGLib动态代理没有展开来讲。不过没关系,后期有时间一定专门出一篇动态代理的文章,通过Spring AOP 的两篇文章,希望大家至少能够在使用层面掌握好AOP这种思想,并能运用到工作当中去。Spring,以及Spring boot其实很多地方都运用到了Spring AOP,后面的文章如果涉及到会给大家点出来。 Over ,Have a good day .


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Introduction to Programming in Java

Introduction to Programming in Java

Robert Sedgewick、Kevin Wayne / Addison-Wesley / 2007-7-27 / USD 89.00

By emphasizing the application of computer programming not only in success stories in the software industry but also in familiar scenarios in physical and biological science, engineering, and appli......一起来看看 《Introduction to Programming in Java》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具