Fork me on GitHub

Spring AOP 框架

AOP简述

AOP全称为Aspect-Oriented Programming ,中文通常翻译为面向方面编程。使用AOP,我们可以对类似于Logging和Security等系统需求进行模块化的组织,简化系统需求与实现之间的对比关系,进而使得整个系统的实现更具模块化。
AOP引入了Aspect的概念,用来以模块化的形式对系统中的横切关注点进行封装。Aspect之对于AOP,就相当于Class之对于OOP。AOP仅是对OOP方法的一种补足。

静态AOP时代(第一代AOP)

  • 优点:Aspect直接以Java字节码的形式编译到Java类中,Java虚拟机可以像通常一样加载Java类运行,不会对整个系统的运行造成任何的性能损失。
  • 缺点:不够灵活

动态AOP时代(第二代AOP)

动态AOP引入了灵活性以及易用性,也不可避免地引入了相应的性能问题;大多数情况下,这种性能损失是可以容忍的。

Java平台上的AOP实现机制

动态代理

Spring AOP 默认情况下采用这种机制实现AOP机能。(动态代理机制只针对接口有效)

动态字节码增强

使用动态字节码增强技术,即使模块类没有实现相应的接口,我们依然可以对其进行扩展,而不用像动态代理那样受限于接口。

Spring AOP概述及其实现机制

Spring AOP 概述

Spring AOP 是 Spring核心框架的重要组成部分,通常认为它与Spring的IoC容器以及Spring框架对其他JavaEE服务的集成共同组成了Spring框架的”质量三角”。

Spring AOP 的实现机制

Spring AOP 属于第二代AOP,采用动态代理机制和字节码生成技术实现。动态代理机制和字节码生成都是在运行期间为目标对象生成一个代理对象,而将横切逻辑织入到这个代理对象中,系统最终使用的是织入了横切逻辑的代理对象,而不是真正的目标对象。

设计模式之代理模式

在代理模式中,通常涉及4种角色

  • ISubject:该接口是对被访问者或者被访问资源的抽象。
  • SubjectImpl:被访问者或者被访问资源的具体实现类
  • SubjectProxy:被访问者或者被访问资源的代理实现类,该类持有一个ISubject接口的具体实例。在这个场景中,我们要对SubjectImpl进行代理,那么SubjectProxy现在持有的就是SubjectImpl的实例。
  • Client:代表访问者的抽象角色,Client将会访问ISubject类型的对象或者资源。在这个场景中,Client将会请求具体的SubjectImpl实例,但Client无法直接请求其真正要访问的资源SubjectImpl,而是必须要通过ISubject资源的访问代理类SubjectProxy进行。

SubjectImpl和SubjectProxy都实现了相同的接口ISubject,而SubjectProxy内部持有SubjectImpl的引用。当Client通过request()请求服务的时候,SubjectProxy将转发该请求给SubjectImpl。从这个角度来说,SubjectProxy反而有多此一举之嫌了。不过,SubjectProxy的作用不只局限于请求的转发,更多时候是对请求添加更多访问限制。
SubjectImpl和SubjectProxy之间的调用关系如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SubjectProxy implements ISubject{
private ISubject subject;
public String request(){
//add pre-process logic if necessary
String originalResult = subject.request();
//add post process logic if necessary
return "Proxy:" + originalResult;
}
public ISubject getSubject(){
return subject;
}
public void setSubject(ISubject subject){
this.subject = subject;
}
}

代理对象SubjectProxy就像是SubjectImpl的影子,只不过这个影子通常拥有更多的功能。

动态代理

动态代理机制的实现主要由一个类和一个接口组成,即java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
InvocationHandler实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class RequestCtrlInvocationHandler implements InvocationHandler{
private static final Log logger = LogFactory.getLog(RequestCtrlInvocationHandler.class);
private Object target;
public RequestCtrlInvocationHandler(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
if(method.getName().equals("request")){
TimeOfDay startTime = new TimeOfDay(0,0,0);
TimeOfDay endTime = new TimeOfDay(5,59,59);
TimeOfDay currentTime = new TimeOfDay();
if(currentTime.isAfter(startTime) && currentTime.isBefore(endTime)){
logger.warn("service is not available now.");
return null;
}
return method.invoke(target, args);
}
return null;
}
}

使用Proxy和RequestCtrlInvocationHandler创建不同类型目标对象的动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
ISubject subject = (ISubject)Proxy.newProxyInstance(
ProxyRunner.class.getClassLoader(),
new Class[]{ISubject.class},
new RequestCtrlInvocationHandler(new SubjectImpl())
);
subject.request();
IRequestable requestable = (IRequestable)Proxy.newProxyInstance(
ProxyRunnable.class.getClassLoader(),
new Class[]{IRequestable.class},
new RequestCtrlInvocationHandler(new RequestableImpl())
);
requestable.request();

动态字节码生成

AOP引用案例

Java异常处理

安全检查

javax.servlet.Filter是Servlet规范为我们提供的一种AOP支持,通过它,我们可以为基于Servlet的Web应用添加相应的资源访问控制。

缓存

使用AOP为系统添加缓存(实例代码没有添加同步逻辑,在生产环境下需要考虑这一点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Aspect
public class CachingAspect{
private static Map cache = new LRUMap(5);
@Around("...")
public Object doCache(ProceedingJoinPoint pjp, Object key) throws Throwable{
if(cache.containsKey(key)){
return cache.get(key);
}else{
Object retValue = pjp.proceed();
cache.put(key,retValue);
return retValue;
}
}
}

「真诚赞赏,手留余香」