使用Spring实现AOP源码
在面向对象系统中,我们经常需要一些任务活动,如记录,交易的安全性,缓存等,这些活动是必要的,但不是业务逻辑的一部分,被称为"横切关注点"。
(横切关注==跨整个系统的常用功能)
从业务逻辑中分离横切关注点,为写出一个精心设计的解耦系统迈出了一大步。让我们关注思考如何处理横切关注点的分离。
继承Inheritance
继承的是那些通用功能,继承需要我们设计一个基类,如果我们要重用到多个地方可能需要修改这个基类。继承Inheritance == 难以在后来修改(无弹性代码)
委托Delegation
委托是一个处理横切关注的好办法,委托和组合分享一个通用功能,但是我们需要在很多地方调用这个委托对象,显得繁琐。委托 == 繁琐
AOP允许我们以模块化关注横向点并形成对象,称为Aspect,这样使用Aspect能够创建一个干净解耦的代码。
AOP相关概念
- Concerns关注 – 这是基于功能的模块部分,有两种类型关注:. 1. 核心关注 2. 跨切面关注(Aspect). 核心关注是有关业务逻辑,比如生成工资单,让员工记录,做银行转帐。跨切面关注是配合业务的一些活动任务,如日志 缓存等。
- Joinpoint连接点 – Joinpoint是在执行时的切入点,Aspect也就是跨切面关注的一些功能要加入主要业务功能的地方,一个连接点可以是一个被调用的方法。
- Advice建议 – 每个Aspect都有一个目标,它的任务是什么,这个任务会被切入到哪个连接点,这些都被称为Advice. Advice能够定义Aspect什么时候执行任务,是在核心关注也就是主要业务活动的之前 之后或者前后执行?
- Pointcut 切入点– 一个系统有很多连接点,但是并不是所有连接点都需要被选择切入Aspect的,Aspect从切入点能够获得帮助,选择哪个连接点介入。
- Aspect方面 – Advice 和 Pointcut定义了一个方面Aspect.Advice定义了Aspect的任务和什么时候执行它,而切入点Pointcut定义在哪里具体地方切入,也就是说,Aspect定义了它是什么东西 什么时候切入和在哪里切入。
- Target目标 – 目标是一个被切入的地方,它一般是核心关注,业务功能。
- Proxy代理 – 当一个advice应用到目标对象时,这时一个代理对象将被创建. AOP容器创建和管理代理对象的生命周期。
- Weaving织入 – Weaving是一个混合横向方面到目标业务对象的过程,织入可以是在编译时间,也可以在运行时间使用classload,Spring AOP缺省是在运行时间。
下面以Cache为例子说明AOP使用:
package com.aranin.spring.aop;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
public class MyCache {
private LinkedHashMap<String, Object> cacheMap = new LinkedHashMap<String, Object>();
private LinkedHashMap<String, Date> timeStampMap = new LinkedHashMap<String, Date>();
/**
* defines the max size of hashmap
*/
private long maxsize = 10; //should come from properties file or some configuration
/**
* how long the object should be stored before it is evicted from cache
*/
private long objectLifeTime = 10000;
private boolean lock = false;
public LinkedHashMap<String, Object> getCacheMap() {
return cacheMap;
}
public void setCacheMap(LinkedHashMap<String, Object> cacheMap) {
this.cacheMap = cacheMap;
}
public LinkedHashMap<String, Date> getTimeStampMap() {
return timeStampMap;
}
public void setTimeStampMap(LinkedHashMap<String, Date> timeStampMap) {
this.timeStampMap = timeStampMap;
}
public long getMaxsize() {
return maxsize;
}
public void setMaxsize(long maxsize) {
this.maxsize = maxsize;
}
public long getObjectLifeTime() {
return objectLifeTime;
}
public void setObjectLifeTime(long objectLifeTime) {
this.objectLifeTime = objectLifeTime;
}
public boolean isLock() {
return lock;
}
public void setLock(boolean lock) {
this.lock = lock;
}
/**
* This method is used to retrive the object from cache
* @param key
* @return
*/
public Object get(String key){
return this.getCacheMap().get(key);
}
/**
* this method is used for putting an object in cache
* @param key
* @param object
*/
public void put(String key, Object object){
//get the curr date
Date date = new Date(System.currentTimeMillis());
//set object in cacheMap
this.getCacheMap().put(key,object);
//put timestamp in cache
this.getTimeStampMap().put(key, date);
}
public void delete(String key){
this.getCacheMap().remove(key);
this.getTimeStampMap().remove(key);
}
public void clearAll(){
this.setCacheMap(new LinkedHashMap<String, Object>());
this.setTimeStampMap(new LinkedHashMap<String, Date>());
}
/**
* remove last 2 entries
* not worried about object life time
* this is just an example
*/
public void resize(){
System.out.println("inside resize");
long size = this.getCacheMap().size();
System.out.println("size + " + size);
if(size == this.getMaxsize()){
System.out.println("max size has reached");
Map.Entry<String, Date> firstEntry = this.getTimeStampMap().entrySet().iterator().next();
System.out.println("removing : " + firstEntry.getKey() + " value : " + firstEntry.getValue());
this.timeStampMap.remove(firstEntry.getKey());
Map.Entry<String, Object> firstCEntry = this.getCacheMap().entrySet().iterator().next();
System.out.println("removing : " + firstCEntry.getKey() + " value : " + firstCEntry.getValue());
this.cacheMap.remove(firstCEntry.getKey());
}
System.out.println("leaving resize with size : " + this.getCacheMap().size());
}
}
Advice代码:
package com.aranin.spring.aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class ResizeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("invoking " + method.getName() + " on " + target.getClass() + " Object");
if(method.getName().equals("put")){
System.out.println("before invoking " + method.getName());
((MyCache)target).resize();
}
}
}
配置:
<?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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="resizeAdvice" class="com.aranin.spring.aop.ResizeAdvice" />
<bean id="myCache" class="com.aranin.spring.aop.MyCache" />
<bean id="myAOPCache"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="myCache" />
<property name="interceptorNames">
<list>
<value>resizeAdvice</value>
</list>
</property>
</bean>
</beans>
设定了myCache的拦截器是resizeAdvice,
客户端运行代码:
package com.aranin.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MyCacheClient {
public static void main(String[] args){
ApplicationContext springcontext = new FileSystemXmlApplicationContext("D:/samayik/SpringDemos/src/main/resources/springaopdemo.xml");
MyCache myCache = (MyCache)springcontext.getBean("myAOPCache");
myCache.put("1", "1");
myCache.put("2", "2");
myCache.put("3", "3");
myCache.put("4", "4");
myCache.put("5", "5");
myCache.put("6", "6");
myCache.put("7", "7");
myCache.put("8", "8");
myCache.put("9", "9");
myCache.put("10", "10");
System.out.println((String)myCache.get("1"));
System.out.println((String)myCache.get("2"));
System.out.println((String)myCache.get("10"));
myCache.put("11", "11");
System.out.println((String)myCache.get("1"));
System.out.println((String)myCache.get("2"));
System.out.println((String)myCache.get("10"));
System.out.println((String)myCache.get("11"));
}
}