您的位置:首页 > 运维架构

AOP之基于Annotation的零配置方式的实现

2014-06-17 13:49 225 查看

一、什么是 AOP

说到面向对象这个概念,大家都已经耳熟能详了,无论是Java,.Net,PHP等等都是基于面向对象的思想进行开发的语言或者说是平台. 那我们是否能够再思考,在面向对象这个基本思想的前提下再进一步的进行扩展和延伸呢,那么在这里就会提出AOP这个概念或者说是架构思想。

AOP:Aspect Oriented Programming,意为面向切面/局部的程序设计,它是面向对象的程序设计的一种延伸。其核心内容就是所谓的“横切关注点”。

二、AOP的作用

AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓 “方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

例如,操作日志的记录,这种操作并不是业务逻辑调用的必须部分,但是,我们却往往不得在代码中显式进行调用,并承担由此带来的后果(例如,当日志记录的接口发生变化时,不得不对调用代码进行修改)。

这种问题,使用传统的OO方法是很难解决的。

AOP的目标,便是要将这些“横切关注点”与业务逻辑代码相分离,下图1.1就非常清晰的表明了这种横向结构:



                                                                      图 1.1

三、AOP的应用场景

# Authentication 权限

# Caching 缓存

# Transactions 事务

# Error handling 错误处理

# Context passing 内容传递

# Lazy loading 懒加载

# Debugging 调试

# logging, tracing, profiling and monitoring 记录跟踪 优化 校准

# Performance optimization 性能优化

# Persistence 持久化

# Resource pooling 资源池

# Synchronization 同步

四、AOP的实现原理

对于应用软件系统来说,日志操作和权限控制都是非常常见的例子。

为了得到好的程序结构,通常使用OO的方法,将日志记录过程或者权限控制封装在一个类中,这个类包含了日志写入或权限验证的代码。

例如下面的代码段:

public class BusinessLogic
{
private readonly LogOperation log = new LogOperation();


public void SomeOperation()
{

//验证安全性;Securtity关注点;

//执行前记录日志;Logging关注点;
log.WriteLog();
DoSomething();

//保存逻辑运算后的数据;Persistence关注点;

//执行结束记录日志;Logging关注点;
log.WriteLog();
}

}


 


public class LogOperation
{
public void WriteLog()
{
//日志写入操作
}

}




这种做法在OO设计中,是常见的做法。但是,这种做法会带来以下一些问题:

1、不清晰的业务逻辑:从某种意义上来说,日志记录过程并不是业务逻辑执行的一部分,这个工作是属于系统的,但是,在这种情况下,我们不得不把系统的日志记录过程和业务逻辑执行过程掺杂在一起,造成代码的混乱。

2、代码浪费:使用这种方法,我们必须所有的业务逻辑代码中用LogOperation类,使得同样校验的代码充斥在整个软件中,显然不是很好的现象。

3、紧耦合:使用这种方法,我们必须在业务逻辑代码中显式引用LogOperation类,这就造成了业务逻辑代码同LogOperation类的紧耦合,这意味着,当LogOperation发生变化时,例如,当系统进化时,需要对WriteLog的方法进行改动时,可能会影响到所有引用代码。下面所有的问题都是因此而来。

4、不易扩展:在这里,我们只是在业务逻辑中添加了日志记录过程,哪一天,当我们需要添加额外的功能,例如日志记录功能的时候,我们不得不同样在所有的业务逻辑代码中添加这个功能。

5、不灵活:有的时候,由于某些特定的需要,我们需要暂时禁止,或者添加某项功能,采用传统的如上述的做法,我们不得不采用修改源代码的方式来实现。

为了解决这些问题,我们通常会采用诸如设计模式等方式,对上面的方案进行改进,这往往需要很高的技巧。利用AOP,我们可以很方便的解决上述问题。

下面用图1.2来说明一下AOP实现原理:



                                                        图 1.2

在一个复杂的系统,它由许多关注点组合实现,如业务逻辑、性能,数据存储、日志和调度信息、授权、安全、线程、错误检查等,还有开发过程中的关注点,如易懂、易维护、易追查、易扩展等,图1.3演示了由不同模块实现的一批关注点组成一个系统。



                                              图 1.3

五、AOP的具体实现

在介绍Spring中Aop具体实现之前,我们先要了解下Aop的一些基本概念.

基本概念

要想了解AOP,首先得了解几个重要的基本概念:

切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。比如说事务管理就是J2EE应用中一个很好的横切关注点例子。切面用Spring的Advisor或拦截器实现。

连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。

切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。

目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。

AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。

编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

各种通知(Advice)类型

为了符合各种流程处理,通知类型提供了5种,可以对目标方法进行全方位处理:

Before advice:在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。

After advice:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。

After returnadvice:在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<aop:after-returning>元素进行声明。

Around advice:包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。

Afterthrowing advice:在方法抛出异常退出时执行的通知。ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。

图1.4给出了几种通知类型更新清晰的表述



                                                                     图 1.4

Aop的Annotation实现

(1)首先启用 Spring 对 @AspectJ 切面配置的支持。

<beans xmlns="http://www.springframework.org/schema/beans"


xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


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/aop


http://www.springframework.org/schema/beans/spring-aop-3.0.xsd">


<!-- 启动对@AspectJ注解的支持 –>


<aop:aspectj-autoproxy/>


<bean class="org.springframeword.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />


</beans>


  (2)定义 Before 增强处理

//定义一个切面
@Aspect
public class BeforeAdviceTest {
//匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点
@Before("execution(* com.wicresoft.app.service.impl.*.*(..))")
public void authorith(){
System.out.println("模拟进行权限检查。");
}
}


上面使用@Before Annotation 时,直接指定了切入点表达式,指定匹配 com.wicresoft.app.service.impl包下所有类的所有方法执行作为切入点。

(3)定义 AfterReturning 增强处理。

// 定义一个切面
@Aspect
public class AfterReturningAdviceTest {
// 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点
@AfterReturning(returning="rvt", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")
public void log(Object rvt) {
System.out.println("模拟目标方法返回值:" + rvt);
System.out.println("模拟记录日志功能...");
}
}


 

(4)定义 AfterThrowing 增强处理。

// 定义一个切面
@Aspect
public class AfterThrowingAdviceTest {
// 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点
@AfterThrowing(throwing="ex", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")
public void doRecoverActions(Throwable ex) {
System.out.println("目标方法中抛出的异常:" + ex);
System.out.println("模拟抛出异常后的增强处理...");
}
}


 

(5)定义 After 增强处理

// 定义一个切面
@Aspect
public class AfterAdviceTest {
// 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点
@After("execution(* com.wicresoft.app.service.impl.*.*(..))")
public void release() {
System.out.println("模拟方法结束后的释放资源...");
}
}


After 增强处理与AfterReturning 增强处理有点相似,但也有区别:

AfterReturning 增强处理处理只有在目标方法成功完成后才会被织入。

After 增强处理不管目标方法如何结束(保存成功完成和遇到异常中止两种情况),它都会被织入。

(6)Around 增强处理

Around 增强处理近似等于 Before 增强处理和  AfterReturning 增强处理的总和。它可改变执行目标方法的参数值,也可改变目标方法之后的返回值。

// 定义一个切面
@Aspect
public class AroundAdviceTest {
// 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点
@Around("execution(* com.wicresoft.app.service.impl.*.*(..))")
public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable {
System.out.println("执行目标方法之前,模拟开始事物...");
// 执行目标方法,并保存目标方法执行后的返回值
Object rvt = jp.proceed(new String[]{"被改变的参数"});
System.out.println("执行目标方法之前,模拟结束事物...");
return rvt + "新增的内容";
}
}


小结

AOP 广泛应用于处理一些具有横切性质的系统级服务,AOP 的出现是对 OOP 的良好补充,它使得开发者能用更优雅的方式处理具有横切性质的服务。当然这个需要结合实际项目,是否需要在架构中使用AOP的架构思想去使系统更加的完善,开发更加便捷,清晰。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: