(转)spring aop
工作忙,时间紧,不过事情再多,学习是必须的。记得以前的部门老大说过:“开发人员不可能一天到晚只有工作,肯定是需要自我学习。第一:为了更充实自己,保持进步状态。第二:为了提升技术,提高开发能力。第三:保持程序员对技术和学习的热情,工作的激情。程序员还是需要把基础打扎实,修炼自己的内功。” 所以赶紧把学习的东西总结一下,加深印象。之前有说了下AOP的原理 (http://www.cnblogs.com/yanbincn/archive/2012/06/01/2530377.html) 。基于代理模式,了解了jdk动态代理和cglib的用法。但是在真正的使用AOP的时候,不可能写这么厚重的方法。
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/2636961.html
(转)spring aop的更多相关文章
- 学习AOP之深入一点Spring Aop
上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...
- 学习AOP之认识一下Spring AOP
心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...
- spring aop
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...
- spring aop注解方式与xml方式配置
注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...
- 基于Spring AOP的JDK动态代理和CGLIB代理
一.AOP的概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...
- Spring AOP详解
一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...
- Spring AOP实例——异常处理和记录程序执行时间
实例简介: 这个实例主要用于在一个系统的所有方法执行过程中出线异常时,把异常信息都记录下来,另外记录每个方法的执行时间. 用两个业务逻辑来说明上述功能,这两个业务逻辑首先使用Spring AOP的自动 ...
- 从零开始学 Java - Spring AOP 实现用户权限验证
每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...
- 从零开始学 Java - Spring AOP 实现主从读写分离
深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...
- 从零开始学 Java - Spring AOP 拦截器的基本实现
一个程序猿在梦中解决的 Bug 没有人是不做梦的,在所有梦的排行中,白日梦最令人伤感.不知道身为程序猿的大家,有没有睡了一觉,然后在梦中把睡之前代码中怎么也搞不定的 Bug 给解决的经历?反正我是有过 ...
随机推荐
- jquery实现抽奖小游戏
在很多网站或游戏活动中我们都会看到有关抽奖的活动或界面: 下面我将给大家介绍下如何通过javascript来实现这样的一个抽奖功能,主要从下面三个步骤入手(文中着重介绍第三部分有关功能的实现): 1. ...
- HTTP请求过程(http是一种无状态协议,即不建立持久的连接)
一.一个完整的HTTP请求,通常有7个步骤: 1.建立TCP连接: 2.web浏览器向web服务器发送请求命令: 3.浏览器发送请求头信息: 4.服务器应答: 5.服务器发送应答头信息: 6.服务器向 ...
- 文本域、bootstrap-table显示以及MySQL三者间的换行符问题
首先,今天在做项目的时候遇到的一个问题,如何实现文本输入换行以及在前台Bootstrap-table中显示也能够换行. 也许你马上就会想到说,用富文本编辑器,然而我们需要实现的只是文本输入以及换行功能 ...
- 休息,归类一下CSS初级的东西
css基础的东西集中体现在了"磊盒子"这一个枯燥无味的东西上面,灵活的运用盒子的内外边距,浮动,定位以及一些基础的属性,将一个静态的页面变得磊出来,这是CSS基础的练习. 在css ...
- [转载] Hadoop和Hive单机环境搭建
转载自http://blog.csdn.net/yfkiss/article/details/7715476和http://blog.csdn.net/yfkiss/article/details/7 ...
- 完美解决--用VS中的Git做代码管理器,与他人共享代码
1.创建代码仓库,这里说一下为什么要创建仓库,Git不能够作为源代码管理器,vs中自带的也只能够在本地进行管理,要和他们共享的话必须要有服务器端去存储代码,类似于SVN,它就有客户端和服务器端,这里推 ...
- 移动应用开发者最应该知道的8款SDK
2017年双11全球狂欢节结束后,据大数据公司统计显示,2017年双11全网销售额达2539.7亿,移动端销售占比91.2%.不难看出,智能手机因随身携带.时刻在线等特点,已取代PC,成为网络生活新的 ...
- 2)C语言的基本知识(C自考学习)
字符集 在C语言程序中允许出现的所有基本字符的组合称为C语言的字符集.C语言的字符集就是ASCII字符集.主要包含一下几类: 1)大小写英文字母A~Z,a~z(52个) 2)数字0-9(10个) 3) ...
- 负载均衡手段之DNS轮询
大多数域名注册商都支持对统一主机添加多条A记录,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,随机分配到不同的IP上,这样就完成了简单的负载均衡.下图的例子是:有3台联通服务器.3台电信服 ...
- JavaScript中typeof,instanceof,hasOwnProperty,in的用法和区别
一. typeof操作符 typeof操作符用于返回正在使用值的类型. // 使用原始值 let mNull = null; let mUndefined = undefined; let mStri ...