AOP現在對大家來說都不是陌生的詞,網上也有着大量的介紹與講解,在這裡我對AOP的基本知識就不多介紹(雖然懂得也不多
),直入主題,我在學習研究過一段時間後總結一下,發現我們自己也可以寫一個簡單的AOP架構,能夠實作簡單的功能橫切,雖說簡單,但是AOP的核心思想着實能展現出來。本文就是想與大家交流一下我的迷你AOP架構,希望大家能多多分享自己的經驗與想法。
我們首先明确一下架構的目标,架構架構肯定是可以多次套用并能在這個架構的規則下用相對較少的代碼量實作我們想要的功能,那AOP 是針對業務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果。 比如說,要在多個核心功能中同時加入一個日志功能,用傳統的OOP(面向對象程式設計)思想就不能實作,自上而下的關系無法為對象引入公共的行為,那我們就目标明确:這個架構要可以在基于某種規則的配置下給程式動态統一添加功能并且不修改源代碼(大部分情況).
AOP的實作可用多種語言,本架構使用JDK的動态代理API(API裡面已經實作了動态代理的大部分,是以相對編寫簡單...).
一步一步來首先我們簡單編寫核心業務類:
package net.localer.test.dao;
public interface IPersonDAO {
publicvoid addPerson(String name);
publicvoid delPerson(int id);
}
package net.localer.test.dao;
public class PersonDAO implementsIPersonDAO{
publicvoid addPerson(String name) {
System.out.println("添加人員"+name);
}
publicvoid delPerson(int id) {
System.out.println("删除人員");
}
}
PersonDAO 裡面有兩個方法分别是添加和删除人員,但是突然有一天我們需要在兩個方法前面加上一個check步驟。如果采用笨方式修改方法的源代碼是非常浪費精力并且影響進度,同時check放發作為一個單獨的邏輯放在增加和删除人員裡面也是不合情理的。
是以我們需要借助AOP思想來把check方法橫切到所有的方法前面,當然這時候我們就需要一個配置檔案來進行配置,通過這個配置檔案,我們能知道哪個類的哪個方法需要橫切到具體哪個位置,這樣通過修改配置檔案我們可以輕松的給所有方法加上統一的'前奏',以下是配置檔案與Check類
<?xml version="1.0"encoding="UTF-8"?>
<proxy>
<beanid="persondao" class="net.localer.test.dao.PersonDAO"/>
<beanid="check" class="net.localer.test.dao.aop.Check"/>
<!--
id隻是一個辨別,沒有實際意義
source是包含了增強功能的類,target是源系統的類
sourcemethod是增強功能的類中的什麼方法會動态的加入到目标類裡面去
targetmethodprefix是源系統類什麼字首開頭的方法需要加入增強功能類的方法
location是在target的什麼位置去加入增強的代碼,可以取值before after exception around
-->
<aopid="testaop" source="check" target="persondao"sourcemethod="test" targetmethodprefix="add"location="after" />
</proxy>
package net.localer.test.dao.aop;
importnet.localer.aop.config.JoinPoint;
public class Check {
publicvoid test(JoinPoint joinPoint) {
System.out.println("執行檢查");
}
//N多方方法
}
proxy配置檔案裡bean節點對應業務類,在需要的時刻我們會根據class反射出對應的實體,aop節點使我們的重點配置項,其中source,target屬性對應于bean節點的id屬性,如同檔案中所說的,我們要在這個節點配置多個屬性以便架構知道用什麼在哪裡橫切。
配置檔案需要解析,解析之後的資料理應也要存到實體類中去,是以我們還要準備多個實體類來存配置檔案的資訊,實體類中的屬性要與配置檔案中節點屬性相一緻。如下:
package net.localer.aop.config;
public class BeanConfig {
privateString id;
privateString clz;
publicString getId() {
returnid;
}
publicvoid setId(String id) {
this.id= id;
}
publicString getClz() {
returnclz;
}
publicvoid setClz(String clz) {
this.clz= clz;
}
}
package net.localer.aop.config;
public class AopConfig {
privateString id;
//因為source和target都是引用了bean标簽配置的類
//而這個配置的類是不确定的類型,是以用Object來作為屬性
privateObject source;
privateObject target;
privateString sourcemethod;
privateString targetmethodprefix;
privateString location;
publicObject getSource() {
returnsource;
}
publicvoid setSource(Object source) {
this.source= source;
}
publicObject getTarget() {
returntarget;
}
publicvoid setTarget(Object target) {
this.target= target;
}
publicString getSourcemethod() {
returnsourcemethod;
}
publicvoid setSourcemethod(String sourcemethod) {
this.sourcemethod= sourcemethod;
}
publicString getTargetmethodprefix() {
returntargetmethodprefix;
}
publicvoid setTargetmethodprefix(String targetmethodprefix) {
this.targetmethodprefix= targetmethodprefix;
}
publicString getLocation() {
returnlocation;
}
publicvoid setLocation(String location) {
this.location= location;
}
publicString getId() {
returnid;
}
publicvoid setId(String id) {
this.id= id;
}
}
要注意因為source和target都是引用了bean标簽配置的類,而這個配置的類是不确定的類型,是以用Object來作為屬性類型。
同時AopConfig 與BeanConfig 的對象肯定是以集合的方式存在是以我們需要ProxyConfig類,其中的兩個HashMap将AopConfig 與BeanConfig存了起來并以id作為key值,如下:
package net.localer.aop.config;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class ProxyConfig {
privateMap<String,BeanConfig> beanConfig = newHashMap<String,BeanConfig>();
privateMap<String,AopConfig> aopConfig = new HashMap<String,AopConfig>();
//根據bean标簽的id擷取到對應的target屬性的aopConfig。對應關系bean的id屬性,對應到aop标簽的target和source屬性
publicAopConfig getAopConfigByBeanId(String beanId) {
Iterator<AopConfig>it = aopConfig.values().iterator();
AopConfigconfig = null;
while(it.hasNext()) {
config= it.next();
//判斷aop标簽中的target屬性是否和bean标簽的id屬性一緻
if(config.getTarget().equals(beanId)) {
break;
}
}
returnconfig;
}
publicBeanConfig getBeanConfig(String id) {
returnbeanConfig.get(id);
}
publicvoid setBeanConfig(String id, BeanConfig config) {
beanConfig.put(id,config);
}
publicvoid setProxyConfig(String id, AopConfig config) {
aopConfig.put(id,config);
}
publicAopConfig getProxyConfig(String id) {
returnaopConfig.get(id);
}
}
其中的 getAopConfigByBeanId 方法是根據bean标簽的id擷取到對應的target屬性的aopConfig,當然方法體的實作方式是在配置檔案中找到第一個之後就停止不再繼續往下找...
解析XML的過程不再敖述,這裡也不再貼代碼,有了以上的基礎資料做鋪墊之後我們就可以使用JDK的動态代理API完成自己的面向切面程式設計.