AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。 AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

我们现在做的一些非业务,如:日志、事务、安全等都会写在业务代码中(也即是说,这些非业务类横切于业务类),但这些代码往往是重复,复制——粘贴式的代码会给程序的维护带来不便,AOP就实现了把这些业务需求与系统需求分开来做。这种解决的方式也称代理机制。

先来了解一下AOP的相关概念,《Spring参考手册》中定义了以下几个AOP的重要概念,结合以上代码分析如下:

  • 切面(Aspect):官 方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例 如,AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext 中<aop:aspect>来配置。
  • 连接点(Joinpoint) :程序执行过程中的某一行为,例如,UserService.get的调用或者UserService.delete抛出异常等行为。
  • 通知(Advice) :“切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记录的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如ServiceAspect。
  • 切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(* com.spring.service.*.*(..))来决定。
  • 目标对象(Target Object) :被一个或者多个切面所通知的对象。例如,AServcieImpl和BServiceImpl,当然在实际运行时,Spring AOP采用代理实现,实际AOP操作的是TargetObject的代理对象。
  • AOP代理(AOP Proxy) : 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理,例 如,AServiceImpl;反之,采用CGLIB代理,例如,BServiceImpl。强制使用CGLIB代理需要将 <aop:config>的 proxy-target-class属性设为true。

通知(Advice)类型:

  • 前置通知(Before advice):在 某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中 在<aop:aspect>里面使用<aop:before>元素进行声明。例如,TestAspect中的doBefore方 法。
  • 后置通知(After advice):当 某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使 用<aop:after>元素进行声明。例如,ServiceAspect中的returnAfter方法,所以Teser中调用 UserService.delete抛出异常时,returnAfter方法仍然执行。
  • 返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
  • 环绕通知(Around advice):包 围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执 行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。例 如,ServiceAspect中的around方法。
  • 抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。例如,ServiceAspect中的returnThrow方法。

注:可以将多个通知应用到一个目标对象上,即可以将多个切面织入到同一目标对象。

使用Spring AOP可以基于两种方式,一种是比较方便和强大的注解方式,另一种则是中规中矩的xml配置方式。

先说注解,使用注解配置Spring AOP总体分为两步,

第一步是在xml文件中声明激活自动扫描组件功能,同时激活自动代理功能:

<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- 激活组件扫描功能,在包cn.ysh.studio.spring.aop及其子包下面自动扫描通过注解配置的组件 -->
<context:component-scan base-package="cn.ysh.studio.spring.aop"/>
<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 用户服务对象 -->
<bean id="userService" class="cn.ysh.studio.spring.aop.service.UserService" /> </beans>
 
第二步是为Aspect切面类添加注解:
/**
* 系统服务组件Aspect切面Bean
* @author Shenghany
* @date 2013-5-28
*/
//声明这是一个组件
@Component
//声明这是一个切面Bean
@Aspect
public class ServiceAspect { private final static Log log = LogFactory.getLog(ServiceAspect.class); //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
@Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
public void aspect(){ } /*
* 配置前置通知,使用在方法aspect()上注册的切入点
* 同时接受JoinPoint切入点对象,可以没有该参数
*/
@Before("aspect()")
public void before(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("before " + joinPoint);
}
} //配置后置通知,使用在方法aspect()上注册的切入点
@After("aspect()")
public void after(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("after " + joinPoint);
}
} //配置环绕通知,使用在方法aspect()上注册的切入点
@Around("aspect()")
public void around(JoinPoint joinPoint){
long start = System.currentTimeMillis();
try {
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
if(log.isInfoEnabled()){
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
}
} catch (Throwable e) {
long end = System.currentTimeMillis();
if(log.isInfoEnabled()){
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
} //配置后置返回通知,使用在方法aspect()上注册的切入点
@AfterReturning("aspect()")
public void afterReturn(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("afterReturn " + joinPoint);
}
} //配置抛出异常后通知,使用在方法aspect()上注册的切入点
@AfterThrowing(pointcut="aspect()", throwing="ex")
public void afterThrow(JoinPoint joinPoint, Exception ex){
if(log.isInfoEnabled()){
log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
}
} }
测试代码:
/**
* Spring AOP测试
* @author Shenghany
* @date 2013-5-28
*/
public class Tester { private final static Log log = LogFactory.getLog(Tester.class); public static void main(String[] args) {
//启动Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取service组件
UserService service = (UserService) context.getBean("userService");
//以普通的方式调用UserService对象的三个方法
User user = service.get(1L);
service.save(user);
try {
service.delete(1L);
} catch (Exception e) {
if(log.isWarnEnabled()){
log.warn("Delete user : " + e.getMessage());
}
}
}
}

控制台输出如下:

 INFO [spring.aop.aspect.ServiceAspect:40] before execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
INFO [spring.aop.service.UserService:19] getUser method . . .
INFO [spring.aop.aspect.ServiceAspect:60] around execution(User cn.ysh.studio.spring.aop.service.UserService.get(long)) Use time : 42 ms!
INFO [spring.aop.aspect.ServiceAspect:48] after execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
INFO [spring.aop.aspect.ServiceAspect:40] before execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
INFO [spring.aop.service.UserService:26] saveUser method . . .
INFO [spring.aop.aspect.ServiceAspect:60] around execution(void cn.ysh.studio.spring.aop.service.UserService.save(User)) Use time : 2 ms!
INFO [spring.aop.aspect.ServiceAspect:48] after execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
INFO [spring.aop.aspect.ServiceAspect:40] before execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
INFO [spring.aop.service.UserService:32] delete method . . .
INFO [spring.aop.aspect.ServiceAspect:65] around execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long)) Use time : 5 ms with exception : spring aop ThrowAdvice演示
INFO [spring.aop.aspect.ServiceAspect:48] after execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
WARN [studio.spring.aop.Tester:32] Delete user : Null return value from advice does not match primitive return type for: public boolean cn.ysh.studio.spring.aop.service.UserService.delete(long) throws java.lang.Exception
xml配置方式,其实也一样简单:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- 系统服务组件的切面Bean -->
<bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 声明一个切面,并注入切面Bean,相当于@Aspect -->
<aop:aspect id="simpleAspect" ref="serviceAspect">
<!-- 配置一个切入点,相当于@Pointcut -->
<aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
<!-- 配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
<aop:before pointcut-ref="simplePointcut" method="before"/>
<aop:after pointcut-ref="simplePointcut" method="after"/>
<aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
<aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
</aop:aspect>
</aop:config> </beans>
通常情况下,表达式中使用”execution“就可以满足大部分的要求。表达式格式如下:

1)execution(* *(..))

表示匹配所有方法

2)execution(public * com. savage.service.UserService.*(..))
表示匹配com.savage.server.UserService中所有的公有方法
3)execution(* com.savage.server..*.*(..))
表示匹配com.savage.server包及其子包下的所有方法
除了execution表示式外,还有within、this、target、args等Pointcut表示式。一个Pointcut定义由Pointcut表示式和Pointcut签名组成

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 
  • modifiers-pattern:方法的操作权限
  • :返回值
  • :方法所在的包
  • :方法名
  • :参数名
  • :异常

ret-type-patterndeclaring-type-patternname-patternparm-patternthrows-pattern

其中,除ret-type-pattern和name-pattern之外,其他都是可选的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。

Pointcut定义时,还可以使用&&、||、!

运算,如

  1. @Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
  2. private void logSender(){}
  3. @Pointcut("execution(* com.savage.aop.MessageReceiver.*(..))")
  4. private void logReceiver(){}
  5. @Pointcut("logSender() || logReceiver()")
  6. private void logMessage(){}

来源:http://ntzrj513.blog.163.com/blog/static/27945612201362232315/

来源:http://www.eclipse.org/aspectj/doc/released/runtime-api/org/aspectj/lang/Signature.html

Spring AOP 注解和xml实现 --转载的更多相关文章

  1. Spring aop注解失效

    问题 在spring 中使用 @Transactional . @Cacheable 或 自定义 AOP 注解时,对象内部方法中调用该对象的其他使用aop机制的方法会失效. @Transactiona ...

  2. Spring AOP注解为什么失效?90%Java程序员不知道

    使用Spring Aop注解的时候,如@Transactional, @Cacheable等注解一般需要在类方法第一个入口的地方加,不然不会生效. 如下面几种场景 1.Controller直接调用Se ...

  3. spring aop注解方式与xml方式配置

    注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...

  4. 基于注解的Spring AOP的配置和使用--转载

    AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  5. 死磕Spring之AOP篇 - Spring AOP注解驱动与XML配置

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  6. spring aop注解配置

    spring aop是面向切面编程,使用了动态代理的技术,这样可以使业务逻辑的代码不掺入其他乱七八糟的代码 可以在切面上实现合法性校验.权限检验.日志记录... spring aop 用的多的有两种配 ...

  7. spring Aop 注解

    个人理解: spring Aop 是什么:面向切面编程,类似于自定义拦截操作,支持拦截之前操作@Before,拦截之后操作@After,拦截环绕操作@Around. 什么情况下使用spring Aop ...

  8. 【学习笔记】Spring AOP注解使用总结

    Spring AOP基本概念 是一种动态编译期增强性AOP的实现 与IOC进行整合,不是全面的切面框架 与动态代理相辅相成 有两种实现:基于jdk动态代理.cglib Spring AOP与Aspec ...

  9. 6.spring:AOP(注解)

     spring Aop AOP面向切面编程,与OOP面向对象编程相辅相成 AOP中最基本的单元是切面 问题: 代码混乱:越来越多的业务需求(日志&验证)加入后,原有的业务方法急剧膨胀,每个方法 ...

随机推荐

  1. ASP.NET Core1.0 带来的新特性

    1.采用新的文件系统,不再通过工程文件(.sln和.csproj)来定义项目文件清单. 解决方案文件还是*.sln,但项目文件变成*.xproj了.在项目文件夹下新增的文件会被自动添加到项目中,不用再 ...

  2. C# Sqlite 序列

    sqlite 不能直接创建自定义函数,不能像 sql server中那样方便创建并使用.不过我们照样可以创建它,创建成功后,我们照样可以随心所欲(比如批量更新等) 序列是一个数据库中很常用的操作,在其 ...

  3. js 数组常用方法

    var arr =[0,1,2,3,4,5,6,7,8,9]; 1,shift() 删除数组的第一个元素,返回删除的值  //这里返回0 2,unshift(1,2)  把参数添加到数组的前面,返回值 ...

  4. SAP Basis常用事务代码

    事务码 描述(中英文)     SBIT Menu 菜单     SBTA Test background processing 后台处理测试     SBTU Background processi ...

  5. ArcGIS制图之Sub Points点抽稀

    简介 Sub Points工具是 Esri 中国自主开发的一个插件,该工具优先考虑点在空间分布上的均匀合理性,并结合点数据中包含的 "优先级" 属性进行筛选.通过获取每个点在一定范 ...

  6. 编写简单的C/S聊天程序

    找了点资料看了下,于是自己动手做了一个练习一下, 主要用到TServerSocket和TClientSocket这个控件. 客户端: var Form1: TForm1; NewTime:string ...

  7. Sharepoint 2013 开启App和配置App

    在任何站点中,点Add App,然后点Sharepoint Store,如果没有Enable apps,打开app store的时候出出现错误: Sorry, apps are turned off. ...

  8. SharePoint 2010 External List Paging – Server Side

    http://lightningtools.com/bcs/sharepoint-2010-external-list-paging-server-side/ When you are using a ...

  9. Android DiskLruCache 硬盘缓存

    概述 记得在很早之前,我有写过一篇文章Android高效加载大图.多图解决方案,有效避免程序OOM,这篇文章是翻译自Android Doc的,其中防止多图OOM的核心解决思路就是使用LruCache技 ...

  10. 复杂对象的本地化(以Person为例)

    Person.h #import <Foundation/Foundation.h> @interface Person : NSObject <NSCoding> /// 姓 ...