文档章节

Spring学习笔记(14)-----------aop学习转折点(1)

Simon丶Ma
 Simon丶Ma
发布于 2016/04/14 14:56
字数 2300
阅读 0
收藏 0
点赞 1
评论 0
Spring有两大核心,IOC和AOP。IOC在java web项目中无时无刻不在使用。然而AOP用的比较少,的确也是一般的项目用的场所不多。事务控制基本都用,但却是Spring封装的不需要我们再去实现,但Spring的AOP远不止这些,不能因为项目中没有使用,而不去学习及理解。我觉得这是作为一个java web软件开发人员必须具备的技能。业内很多将AOP应用在日志记录上,可惜我们项目没这么做,后面需要学习下。在这先把Spring AOP的基本用法,在脑子里理一边,做一次积累。

1、概念术语  

  在开始之前,需要理解Spring aop 的一些基本的概念术语(总结的个人理解,并非Spring官方定义):

  切面(aspect):用来切插业务方法的类。

  连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。

  通知(advice):在切面类中,声明对业务方法做额外处理的方法。

  切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。

  目标对象(target object):被代理对象。

  AOP代理(aop proxy):代理对象。

  通知:

  前置通知(before advice):在切入点之前执行。

  后置通知(after returning advice):在切入点执行完成后,执行通知。

  环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。

  异常通知(after throwing advice):在切入点抛出异常后,执行通知。

2、Spring AOP环境

  要在项目中使用Spring AOP 则需要在项目中导入除了spring jar包之外,还有aspectjweaver.jar,aopalliance.jar ,asm.jar 和cglib.jar 。

好了,前提工作准备完成,Spring 提供了很多的实现AOP的方式,在学习过程中,循序渐进。进行Spring 接口方式,schema配置方式和注解的三种方式进行学习。好了废话不多说了,开始spring aop学习之旅:

3、方式一:AOP接口

  利用Spring AOP接口实现AOP,主要是为了指定自定义通知来供spring AOP机制识别。主要接口:前置通知 MethodBeforeAdvice ,后置通知:AfterReturningAdvice,环绕通知:MethodInterceptor,异常通知:ThrowsAdvice 。见例子代码:

a、业务接口:

/**
 * 代理类接口,也是业务类接口<br>
 * 
 * 利用接口的方式,spring aop 将默认通过jdk 动态代理来实现代理类<br>
 * 不利用接口,则spring aop 将通过cglib 来实现代理类
 * 
 * @author yanbin
 * 
 */
public interface IBaseBusiness {

    /**
     * 用作代理的切入点方法
     * 
     * @param obj
     * @return
     */
    public String delete(String obj);

    /**
     * 这方法不被切面切
     * 
     * @param obj
     * @return
     */
    public String add(String obj);

    /**
     * 这方法切不切呢?可以设置
     * 
     * @param obj
     * @return
     */
    public String modify(String obj);

}

b、业务类:

/**
 * 业务类,也是目标对象
 * 
 * @author yanbin
 * 
 */
public class BaseBusiness implements IBaseBusiness {

    /**
     * 切入点
     */
    public String delete(String obj) {
        System.out.println("==========调用切入点:" + obj + "说:你敢删除我!===========\n");
        return obj + ":瞄~";
    }

    public String add(String obj) {
        System.out.println("================这个方法不能被切。。。============== \n");
        return obj + ":瞄~ 嘿嘿!";
    }

    public String modify(String obj) {
        System.out.println("=================这个也设置加入切吧====================\n");
        return obj + ":瞄改瞄啊!";
    }

}

c、通知类:

前置通知:

/**
 * 前置通知。
 * 
 * @author yanbin
 * 
 */
public class BaseBeforeAdvice implements MethodBeforeAdvice {

    /**
     * method : 切入的方法 <br>
     * args :切入方法的参数 <br>
     * target :目标对象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("===========进入beforeAdvice()============ \n");

        System.out.print("准备在" + target + "对象上用");
        System.out.print(method + "方法进行对 '");
        System.out.print(args[0] + "'进行删除!\n\n");

        System.out.println("要进入切入点方法了 \n");
    }

}

后置通知:

/**
 * 后置通知
 * 
 * @author yanbin
 * 
 */
public class BaseAfterReturnAdvice implements AfterReturningAdvice {

    /**
     * returnValue :切入点执行完方法的返回值,但不能修改 <br>
     * method :切入点方法 <br>
     * args :切入点方法的参数数组 <br>
     * target :目标对象
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==========进入afterReturning()=========== \n");
        System.out.println("切入点方法执行完了 \n");

        System.out.print(args[0] + "在");
        System.out.print(target + "对象上被");
        System.out.print(method + "方法删除了");
        System.out.print("只留下:" + returnValue + "\n\n");
    }

}

环绕通知:

/**
 * 环绕通知
 * 
 * @author yanbin
 * 
 */
public class BaseAroundAdvice implements MethodInterceptor {

    /**
     * invocation :连接点
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("===========进入around环绕方法!=========== \n");

        // 调用目标方法之前执行的动作
        System.out.println("调用方法之前: 执行!\n");

        // 调用方法的参数
        Object[] args = invocation.getArguments();
        // 调用的方法
        Method method = invocation.getMethod();
        // 获取目标对象
        Object target = invocation.getThis();
        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
        Object returnValue = invocation.proceed();

        System.out.println("===========结束进入around环绕方法!=========== \n");
        
        System.out.println("输出:" + args[0] + ";" + method + ";" + target + ";" + returnValue + "\n");

        System.out.println("调用方法结束:之后执行!\n");

        return returnValue;
    }

}

异常通知:

/**
 * 异常通知,接口没有包含任何方法。通知方法自定义
 * 
 * @author yanbin
 * 
 */
public class BaseAfterThrowsAdvice implements ThrowsAdvice {

    /**
     * 通知方法,需要按照这种格式书写
     * 
     * @param method
     *            可选:切入的方法
     * @param args
     *            可选:切入的方法的参数
     * @param target
     *            可选:目标对象
     * @param throwable
     *            必填 : 异常子类,出现这个异常类的子类,则会进入这个通知。
     */
    public void afterThrowing(Method method, Object[] args, Object target, Throwable throwable) {
        System.out.println("删除出错啦");
    }

}

d、定义指定切点:

/**
 * 定义一个切点,指定对应方法匹配。来供切面来针对方法进行处理<br>
 * 
 * 继承NameMatchMethodPointcut类,来用方法名匹配
 * 
 * @author yanbin
 * 
 */
public class Pointcut extends NameMatchMethodPointcut {

    private static final long serialVersionUID = 3990456017285944475L;

    @SuppressWarnings("rawtypes")
    @Override
    public boolean matches(Method method, Class targetClass) {
        // 设置单个方法匹配
        this.setMappedName("delete");
        // 设置多个方法匹配
        String[] methods = { "delete", "modify" };
        
        //也可以用“ * ” 来做匹配符号
        // this.setMappedName("get*");
        
        this.setMappedNames(methods);

        return super.matches(method, targetClass);
    }

}

e、配置: 

<?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:p="http://www.springframework.org/schema/p"
    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.0.xsd     
          http://www.springframework.org/schema/context     
          http://www.springframework.org/schema/context/spring-context-3.0.xsd 
          http://www.springframework.org/schema/aop     
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
    default-autowire="byName">

    <!-- ==============================利用spring自己的aop配置================================ -->
    <!-- 声明一个业务类 -->
    <bean id="baseBusiness" class="aop.base.BaseBusiness" />
    
    <!-- 声明通知类 -->
    <bean id="baseBefore" class="aop.base.advice.BaseBeforeAdvice" />
    <bean id="baseAfterReturn" class="aop.base.advice.BaseAfterReturnAdvice" />
    <bean id="baseAfterThrows" class="aop.base.advice.BaseAfterThrowsAdvice" />
    <bean id="baseAround" class="aop.base.advice.BaseAroundAdvice" />

    <!-- 指定切点匹配类 -->
    <bean id="pointcut" class="aop.base.pointcut.Pointcut" />

    <!-- 包装通知,指定切点 -->
    <bean id="matchBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="pointcut">
            <ref bean="pointcut" />
        </property>
        <property name="advice">
            <ref bean="baseBefore" />
        </property>
    </bean>

    <!-- 使用ProxyFactoryBean 产生代理对象 -->
    <bean id="businessProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 代理对象所实现的接口 ,如果有接口可以这样设置 -->
        <property name="proxyInterfaces">
            <value>aop.base.IBaseBusiness</value>
        </property>

        <!-- 设置目标对象 -->
        <property name="target">
            <ref local="baseBusiness" />
        </property>
        <!-- 代理对象所使用的拦截器 -->
        <property name="interceptorNames">
            <list>
                <value>matchBeforeAdvisor</value>
                <value>baseAfterReturn</value>
                <value>baseAround</value>
            </list>
        </property>
    </bean>
</beans>

f、测试类:

public class Debug {

public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("aop/schema_aop.xml");
        IBaseBusiness business = (IBaseBusiness ) context.getBean("businessProxy");
        business.delete("猫");
    }
}

g、测试结果:运行下测试类,清晰明了。由于结果呈现太长就不贴了。

  具体的代码实现可以从代码注释中很容易理解 接口方式的实现。结果也可想而知,前置方法会在切入点方法之前执行,后置会在切入点方法执行之后执行,环绕则会在切入点方法执行前执行同事方法结束也会执行对应的部分。主要是调用proceed()方法来执行切入点方法。来作为环绕通知前后方法的分水岭。然后在实现的过程中,有几点却是可以细揣摩一下的。

  可以看出在xml 配置 businessProxy这个bean的时候,ProxyFactoryBean类中指定了,proxyInterfaces参数。这里我把他配置了IBaseBusiness接口。因为在项目开发过程中,往往业务类都会有对应的接口,以方便利用IOC解耦。但Spring AOP却也能支持没有接口的代理。这就是为什么需要导入cglib.jar的包了。看过spring的源码,知道在目标切入对象如果有实现接口,spring会默认走jdk动态代理来实现代理类。如果没有接口,则会通过cglib来实现代理类。

  这个业务类现在有 前置通知,后置通知,环绕三个通知同时作用,可能以及更多的通知进行作用。那么这些通知的执行顺序是怎么样的?就这个例子而言,同时实现了三个通知。在例子xml中,则显示执行before通知,然后执行around的前处理,执行切点方法,再执行return处理。最后执行around的后处理。经过测试,知道spring 处理顺序是按照xml配置顺序依次处理通知,以队列的方式存放前通知,以压栈的方式存放后通知。所以是前通知依次执行,后通知到切入点执行完之后,从栈里在后进先出的形式把后通知执行。

  在实现过程中发现通知执行对应目标对象的整个类中的方法,如何精确到某个方法,则需要定义一个切点匹配的方式:spring提供了方法名匹配或正则方式来匹配。然后通过DefaultPointcutAdvisor来包装通知,指定切点。

 

本文借鉴:http://www.cnblogs.com/yanbincn/archive/2012/08/13/2635413.html

 

© 著作权归作者所有

共有 人打赏支持
Simon丶Ma
粉丝 4
博文 134
码字总数 299850
作品 0
深圳
程序员
Spring.NET学习笔记——目录(原)

目录 前言 Spring.NET学习笔记——前言 第一阶段:控制反转与依赖注入IoC&DI Spring.NET学习笔记1——控制反转(基础篇) Level 200 Spring.NET学习笔记2——环境搭建(基础篇) Level 200 Sprin...

长平狐 ⋅ 2012/06/11 ⋅ 1

springmvc+mybatis学习笔记(汇总)

springmvc+mybatis学习笔记(汇总) 标签 : springmvc mybaits [TOC] 笔记分为两大部分:mybatis和springmvc mybatis springmvc 笔记内容主要是mybatis和springmvc的一些基本概念和使用方法,...

brianway ⋅ 2016/03/30 ⋅ 2

Spring学习——Spring MVC 中定时器实现

在一些工作需要使用到定时器,Spring很好的集成了定时器的功能! 在Spring 中使用Quartz,本文介绍Spring3.0以后自主开发的定时任务工具,spring task,可以将它比作一个轻量级的Quartz,而且...

IT达仁 ⋅ 05/10 ⋅ 0

springmvc学习笔记(6)-springmvc整合mybatis(IDEA中通过maven构建

springmvc学习笔记(6)-springmvc整合mybatis(IDEA中通过maven构建) 标签: springmvc mybatis [TOC] 本文主要展示如何在intellij IDEA中通过maven构建springmvc+mybatis框架的开发环境。 需求...

brianway ⋅ 2016/03/10 ⋅ 0

让Spring Security 来保护你的Spring Boot项目吧

参考资料: 书籍:Spring实战(第4版) 第9章和第14章 Spring Security 参考手册 初识 Spring Security 程序猿DD的Spring Security学习笔记 Spring Security 简介 Spring Security是一个能够为...

潇潇漓燃 ⋅ 05/19 ⋅ 0

后台开发常问面试题集锦(问题搬运工,附链接)

Java基础问题 String的’+’的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水线 抽象 & abstract关键字 Java final 修饰符知识点总结(必看篇) Java中的...

大黄有故事 ⋅ 2017/11/18 ⋅ 0

Aspectj与Spring AOP比较

1、简介 今天有多个可用的 AOP 库, 它们需要能够回答许多问题: 是否与用户现有的或新的应用程序兼容? 在哪里可以实现 AOP? 与自己的应用程序集成多快? 性能开销是多少? 在本文中, 我们将...

沈渊 ⋅ 04/18 ⋅ 0

Java Web学习计划

--- 本月为入门阶段,从零开始,一步一步的做出一个实用的网站。 深入学习Java语言,初步掌握前端技术,使用JSP和MySQL完成一个简单的网站 第1周 Java高级编程学习目标:
1.深入了解JDK环境...

SVD ⋅ 2016/12/01 ⋅ 0

Mini 容器学习目录

Mini 容器学习笔记1——环境搭建(基础篇) Mini 容器学习笔记2——组件元数据(基础篇) Mini 容器学习笔记3——组件的注册(基础篇) Mini 容器学习笔记4——组件的生命周期(应用篇) Mini 容器学...

netcasewqs ⋅ 2011/08/26 ⋅ 0

Spring 中常用的两种事务配置方式以及事务的传播性、隔离级别

一、注解式事务 1、注解式事务在平时的开发中使用的挺多,工作的两个公司中看到很多项目使用了这种方式,下面看看具体的配置demo。 2、事务配置实例 (1)、spring+mybatis 事务配置 (2)、...

哲别0 ⋅ 04/20 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Windows下安装运行phpMyAdmin

首先确保安装了phpMyAdmin,其次要求服务器是打开的。 如果是在Windows下,建议下载安装WampServer,这是一个集成软件,集成了Apache+MySQL+PHP的开发环境,而且也自带了phpMyAdmin这个软件。...

临江仙卜算子 ⋅ 3分钟前 ⋅ 0

jdk1.8 安装及环境变量配置

1.根据jdk 的软件安装包,首先安装,jdk,

kuchawyz ⋅ 4分钟前 ⋅ 0

给Java字节码加上”翅膀“的JIT编译器

给Java字节码加上”翅膀“的JIT编译器 上面文章在介绍Java的内存模型的时候,提到过由于编译器的优化会导致重排序的问题,其中一个比较重要的点地方就是关于JIT编译器的功能。JIT的英文单词是...

九劫散仙 ⋅ 5分钟前 ⋅ 0

PCI简介(二)

1.x86处理器系统地址空间简介 1.1 CPU地址空间 CPU地址空间是指CPU所能寻址的空间大小,比如对于32位CPU来说,其所能寻址的空间大小为0~4G。这是由CPU自身的地址总线数目决定的。这段空间也被...

深山野老 ⋅ 6分钟前 ⋅ 0

spring中的InitializingBean接口

好久没更博了,真有点怀念,前段时间刚和上家公司say bye,这次进的是电商公司,今天刚开始看代码,逻辑很复杂。 今天看的注册功能,里面见到一个知识点,现在记录一下,今天看项目时见到里面...

千元机爱好者 ⋅ 7分钟前 ⋅ 0

机器学习:数据预处理之独热编码(One-Hot)

前言 ———————————————————————————————————————— 在机器学习算法中,我们经常会遇到分类特征,例如:人的性别有男女,祖国有中国,美国,法国等。 ...

NateHuang ⋅ 16分钟前 ⋅ 0

MyBatis之输入与输出(resultType、resultMap)映射

在MyBatis中,我们通过parameterType完成输入映射(指将值映射到sql语句的占位符中,值的类型与dao层响应方法的参数类型一致),通过resultType完成输出映射(从数据库中输出,通过dao层的方法查...

瑟青豆 ⋅ 16分钟前 ⋅ 0

屏蔽运营商广告劫持

在今天早上我查找知乎时再次遇到了恶心的运营商广告劫持,右下角硕大的广告直接让知乎挂掉了,我刷了五次知乎才好,之前休息的时候逛知乎也是多次加载错误,估计也是这劫持的锅,相信各位也遇...

gcudwork ⋅ 20分钟前 ⋅ 0

java web 进度条实现原理

资料路径 https://blog.csdn.net/fengsheng5210/article/details/79305026

zaolonglei ⋅ 21分钟前 ⋅ 0

命令行输出java版本与环境变量配置的不一样问题解决

问题:java10刚出来,本着好奇的心,急切的装了体验一下,然后实际项目需求还是java8,所以体验完了就把环境变量改回来了,但是出现了一个问题,命令行输出java版本与环境变量配置的不一样,...

消散了的诗意 ⋅ 23分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部