SpringAOP学习

author:luojie

  1. 1.  AOP中的基本概念

AOP的通用术语,并非spring java所特有。很遗憾AOP的术语不是特别的直观。但如果让Spring java来定义自己的专用名词,可能会更加教人糊涂。 



方面(Aspect):对横向分布在多个对象中的关注点所做的模块化。在企业应用中,事务管理就是一个典型的横切关注点。Spring
java将方面实现为Advisor或拦截器(interceptor)。(按:Advisor是通知和切入点的组合,拦截器实际就是指通知,注意在本文档中,一般会把环绕通知称为拦截器,而将其它类型的通知称为通知,这是因为环绕通知实现的是AopAlliance.Intercept.IMethodInterceptor接口,而其它通知类型实现的都是Spring.Aop命名空间下的通知接口。) 



连接点(Joinpoint):程序执行过程中的一个点,例如对某个方法的调用或者某个特定异常的抛出都可以称为连接点。 



通知(Advice):AOP框架在某个连接点所采取的行为。通知有多种类型,包括“环绕”通知,“前置”通知和“异常”通知等,后文将对通知类型进行讨论。包括Spring.NET在内的很多AOP框架都把通知建模为拦截器(interceptor),并且会维护一个"包围"在连接点周围的拦截器链。 



切入点(Pointcut):指通知的应用条件,用于确定某个通知要被应用到哪些连接点上。AOP框架应允许让开发人员指定切入点,例如,可以使用正则表达式来指定一个切入点。 



引入(Introduction):向目标对象添加方法或字段的行为。Spring.NET允许为任何目标对象引入新的接口。例如,可以利用引入让任何对象在运行期实现IAuditable接口,以简化对象状态变化的跟踪过程。(按:也称为mixin,混入) 



目标对象(Target object):指包含连接点的对象。也称为被通知或被代理对象。(按:“被通知对象”实际是“被应用了通知的对象”,在译文中,将advised object或proxied object统称为目标对象,这样更为统一) 



AOP代理(AOP
proxy):由AOP框架在将通知应用于目标对象后创建的对象。在Spring.NET中,AOP代理是使用IL代码在运行时创建的动态代理。 



织入(Weaving):将方面进行组装,以创建一个目标对象。织入可以在编译期完成(例如使用Gripper_Loom.NET编译器),也可以在运行时完成。Spring.NET在运行时执行织入。 







各种通知类型包括: 



环绕通知(Around Advise):包围(按:即在连接点执行的前、后执行)某个连接点(如方法调用)的通知。这是功能最强大的一种通知。环绕通知允许在方法调用的前后执行自定义行为。它可以决定是让连接点继续执行,还是用自己的返回值或异常来将连接点“短路”。 



前置通知(Before Advise):在某个连接点执行之前执行,但是不具备阻止连接点继续执行的能力(除非它抛出异常)。 



异常通知(Throws Advise):当方法(连接点)抛出异常时执行。Spring.NET的异常通知是强类型的(按:Spring.NET用标识接口来定义异常通知,异常通知的处理方法仅需遵循一定的命名规则,可以用具体的异常类型声明其参数,参见12.3.2.3节),所以,可以在代码中直接捕捉某个类型的异常(及其子类异常),不必从Exception转型。 



后置通知(After returning Advise):在连接点正常执行完成后执行,例如,如果方法正常返回,没有抛出异常时,后置通知就会被执行。 



Spring java内置了以上所有类型的通知。在应用时,应尽量使用功能最少(只要对要实现的行为来说是足够的)的通知类型,这样可简化编程模型并减少出错的可能。例如,如果只需使用某个方法的返回值来更新缓存,那么用后置通知就比环绕通知合适。因为,尽管环绕通知可以完成同样的功能,但在后置通知中不用象环绕通知那样必须调用IMethodInvocation接口的Proceed()方法来允许连接点继续执行,所以连接点总是能正常执行。(按:换句话说,因为环绕通知可以控制连接点的继续执行,所以如果没有调用IMethodInvocation接口的Proceed()方法,连接点就会被“短路”;而使用后置通知就不存在这个问题)。 



切入点是AOP的关键概念,使AOP从根本上区别于旧的拦截技术。切入点使通知可以独立于OO的继承层次之外。例如,一个声明式事务处理的环绕通知可以应用于不同对象的方法。切入点是AOP的结构性要素。

 

2.  jar

Spring框架的包,和aop相关的jar。

3.  学习例子

如在给controller做切面,记录访问controller时的日志

LogAspect.java

//@Component//扫描,等价<bean>

//@Aspect//等价于<aop:aspect
ref="">

public class LogAspect {

private static Logger log = LoggerFactory.getLogger(getClass());

//@Around("within(org.tedu.cloudnot.controller..*)")

//等价于<aop:around method="logController" pointcut
= "">

public Object aroundController(ProceedingJoinPoint
joinPoint) throws
Throwable{

String targetName = joinPoint.getTarget().getClass().getName();

String methodName = joinPoint.getSignature().getName();

String description = "";

Class targetClass = targetClass.getMethods();

Method[] methods = targetClass.getMethods();

for(Method method :
methods){

if(method.getName().equals(methodName)){

Class[] clazzs = method.getParameterTypes();

for(Class<?>
clas : clazzs){

String parameterName = clas.getName();

description = description = "" + parameterName;

}

}

}

log.info("enter into
Controller---------------------------------");

log.info("targetName:" + targetName + ";");

log.info("methodName:" + methodName + ";");

log.info("parameterTypes:" + description + ";");

Object[] args = joinPoint.getArgs();

log.info("parameterValues" + Arrays.toString(args));

Object object = null;

try{

object = joinPoint.proceed();

}catch(Exception ex){

log.error("=============================controller方法执行异常==============");

BaseResult rr = new BaseResult();

rr.setErrorCode();

rr.setErrorMsg);

return rr;

}

Object[] objs = joinPoint.getArgs();

log.info("returnResult:" + object);

return object;

}

public void afterThrowingController(JoinPoint joinPoint, Throwable e){

String params = "";

if(joinPoint.getArgs() != null &&
joinPoint.getArgs().length > 0){

for(int i = 0; i<joinPoint.getArgs().length; i++){

params += joinPoint.getArgs()[i] + ";";

}

}

try{

System.out.println("------异常通知开始-------------");

System.out.println("异常代码");

System.out.println("异常信息");

System.out.println("异常方法" + (joinPoint.getTarget().getClass.getName + "."

+
joinPoint.getSignature().getName()+"()"));

System.out.println("请求参数"+ params);

}catch(Exception ex){

log.error("==异常通知异常=====");

log.error("异常信息:{}", ex.getMessage());

}

/*===========记录本地异常日志============*/

log.error("异常方法:{}异常代码:{}异常参数:{}参数{}", new
Object[]{1,2,3,4});

}

4.  spring.xml配置

<aop:config>

<aop:pointcut expression="(within(com.me..*)or

within(coom.me..*))"

id="controllerPointcut"/>

<aop:aspect id = "logAspect"
ref="loggerBean">

<aop:arount method="arountController"
pointcut-ref="contrllerPointcut"/>

<aop:after-throwing method="afterThrowingController" pointcut-ref="controllerPointcut" throwing="e"/>

</aop:aspect>

</aop:config>

*需要添加aop的schema配置

*可能需要添加aop的自定义扫描注解(这里不需要,已测试,使用注解则需要)

*可能如果aop的切面程序无法起作用,注意可能是配置要写在spring-servlet.xml(即springmvc的配置文件中)

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

<aop:aspectj-autoproxy />

<!-- 通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller -->

<aop:aspectj-autoproxy proxy-target-class="true" />

4.  SpringAOP的表达式

任意公共方法的执行: 

execution(public *
*(..)) 

任何一个以“set”开始的方法的执行: 

execution(*
set*(..)) 

AccountService
接口的任意方法的执行: 

execution(*
com.xyz.service.AccountService.*(..)) 

定义在service包里的任意方法的执行: 

execution(*
com.xyz.service.*.*(..)) 

定义在service包或者子包里的任意方法的执行: 

execution(*
com.xyz.service..*.*(..)) 

在service包里的任意连接点(在Spring AOP中只是方法执行) : 

within(com.xyz.service.*) 

在service包或者子包里的任意连接点(在Spring AOP中只是方法执行) : 

within(com.xyz.service..*) 

实现了 AccountService 接口的代理对象的任意连接点(在Spring
AOP中只是方法执行) : 

this(com.xyz.service.AccountService) 

'this'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得代理对象可以在通知体内访问到的部分。 

实现了 AccountService 接口的目标对象的任意连接点(在Spring
AOP中只是方法执行) : 

target(com.xyz.service.AccountService) 

'target'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得目标对象可以在通知体内访问到的部分。 

任何一个只接受一个参数,且在运行时传入的参数实现了
Serializable 接口的连接点 (在Spring AOP中只是方法执行) 

args(java.io.Serializable) 

'args'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得方法参数可以在通知体内访问到的部分。 请注意在例子中给出的切入点不同于
execution(* *(java.io.Serializable)): args只有在动态运行时候传入参数是可序列化的(Serializable)才匹配,而execution 在传入参数的签名声明的类型实现了
Serializable 接口时候匹配。 

有一个 @Transactional 注解的目标对象中的任意连接点(在Spring
AOP中只是方法执行) 

@target(org.springframework.transaction.annotation.Transactional) 

'@target' 也可以在binding form中使用:请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。 

任何一个目标对象声明的类型有一个
@Transactional 注解的连接点(在Spring
AOP中只是方法执行) 

@within(org.springframework.transaction.annotation.Transactional) 

'@within'也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。 

任何一个执行的方法有一个 @Transactional
annotation的连接点(在Spring
AOP中只是方法执行) 

@annotation(org.springframework.transaction.annotation.Transactional) 

'@annotation'
也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。 

任何一个接受一个参数,并且传入的参数在运行时的类型实现了
@Classified annotation的连接点(在Spring
AOP中只是方法执行) 

@args(com.xyz.security.Classified)

spring-aop学习的更多相关文章

  1. Spring AOP 学习例子

    http://outofmemory.cn/code-snippet/3762/Spring-AOP-learn-example     工作忙,时间紧,不过事情再多,学习是必须的.记得以前的部门老大 ...

  2. spring aop学习记录

    许多AOP框架,比较常用的是Spring AOP 与AspectJ.这里主要学习的Spring AOP. 关于AOP 日志.事务.安全验证这些通用的.散步在系统各处的需要在实现业务逻辑时关注的事情称为 ...

  3. 在Intellij上面导入项目 & AOP示例项目 & AspectJ学习 & Spring AoP学习

    为了学习这篇文章里面下载的代码:http://www.cnblogs.com/charlesblc/p/6083687.html 需要用Intellij导入一个已有工程.源文件原始内容也可见:link ...

  4. AOP和spring AOP学习记录

    AOP基本概念的理解 面向切面AOP主要是在编译期或运行时,对程序进行织入,实现代理, 对原代码毫无侵入性,不破坏主要业务逻辑,减少程序的耦合度. 主要应用范围: 日志记录,性能统计,安全控制,事务处 ...

  5. Spring AOP学习笔记01:AOP概述

    1. AOP概述 软件开发一直在寻求更加高效.更易维护甚至更易扩展的方式.为了提高开发效率,我们对开发使用的语言进行抽象,走过了从汇编时代到现在各种高级语言繁盛之时期:为了便于维护和扩展,我们对某些相 ...

  6. Spring AOP学习笔记02:如何开启AOP

    上文简要总结了一些AOP的基本概念,并在此基础上叙述了Spring AOP的基本原理,并且辅以一个简单例子帮助理解.从本文开始,我们要开始深入到源码层面来一探Spring AOP魔法的原理了. 要使用 ...

  7. Spring AOP学习笔记03:AOP的核心实现之获取增强器

    上文讲了spring是如何开启AOP的,简单点说就是将AnnotationAwareAspectJAutoProxyCreator这个类注册到容器中,因为这个类最终实现了BeanPostProcess ...

  8. Spring AOP学习笔记05:AOP失效的罪因

    前面的文章中我们介绍了Spring AOP的简单使用,并从源码的角度学习了其底层的实现原理,有了这些基础之后,本文来讨论一下Spring AOP失效的问题,这个问题可能我们在平时工作中或多或少也会碰到 ...

  9. spring aop 学习1

    1.配置自动扫描的包 <context:component-scan base-package="com.ddf.spring.aop.impl"/> 2.使用spri ...

  10. Spring AOP学习笔记

      Spring提供了一站式解决方案:          1) Spring Core  spring的核心功能: IOC容器, 解决对象创建及依赖关系          2) Spring Web ...

随机推荐

  1. js原生方法传参的细节(面试必问)

    废话不说,直接上题. slice(),接收两个参数,第一个为开始index(从0开始),第二个为结束的index(也是从0开始,但是不包括index本身,只到index-1).返回值是截取的数组,原数 ...

  2. bootstrap--小李子demo

    最近忙啊...看到各位冬鞋都在认真写博客,认真敲代码,认真工作,总觉得自己时间太少,总觉得时间不够,老了...... 进正题: 上次不知从哪里(忘了)下载了bootstrap的一些使用小demo,以后 ...

  3. 【转】关于LinQ的动态Or查询

    /// <summary> /// 构造函数使用True时:单个AND有效,多个AND有效:单个OR无效,多个OR无效:混合时写在AND后的OR有效 /// 构造函数使用False时:单个 ...

  4. android_demo之生成颜色布局

    前面学习了动态生成表格,不单单是要动态生成控件,也同时生成一个事件. 接下来用个小小栗子去了解这个知识点. <LinearLayout xmlns:android="http://sc ...

  5. 对dijkstra算法的自我理解,c#例子

    dijkstra该算法主要应用在求解最短路径,从最近点开始,广度搜索. 假设有向图中有10个顶点,求其中某个顶点a到其它顶点的最短路径..满足贪心算法的2个标准.时间复杂度为O(N2) 此问题可以进行 ...

  6. linux whoami命令

    whoami显示的是当前"操作用户"的用户名.

  7. LVS_DR模式构建配置

    一.环境准备 lvs负载均衡器 系统:centos6.8 ip:192.168.2.203 vip:192.168.2.17 web服务器RS1 系统:centos6.8 ip:192.168.2.2 ...

  8. MySQL binlog基本操作

    常用操作: 1. 设置启用binlog log-bin = binlog 2. 设置全备和增量备份 #crontab -e * 0 * * 7 /usr/bin/mysqldump mybinlog ...

  9. [ubuntu] adb devices出现no permissions

    简书排版 http://www.jianshu.com/p/46e8848c6646 今天把一款测试的华为手机带回家,发现无法联机调试 笔者操作系统是 ubuntu 14.04 如果是windows找 ...

  10. 关于KB905474正版验证补丁破解办法 KB905474是个微软操作系统正版/盗版监测间谍软件。更新安装后,右下角有个提示说“系统监测到你的操作系统是盗版”。 如果没有安装的: 在系统提示更新的时候注意看一下,如果包含有“更新KB905474”就去掉“更新KB905474”方框前的勾,点击关闭(注意如果没有去掉那个勾得话,会找不到“关闭”,而是“确定”),在不在提示我该消息前打勾。 如果已经安装

    关于KB905474正版验证补丁破解办法 KB905474是个微软操作系统正版/盗版监测间谍软件.更新安装后,右下角有个提示说“系统监测到你的操作系统是盗版”. 如果没有安装的: 在系统提示更新的时候 ...