前言:上一篇博客自己動手編寫spring IOC源碼受到了大家的熱情關注,在這里博客十分感謝。特別是給博主留言建議的@瑪麗的竹子等等。本篇博客我們繼續,還是在原有的基礎上進行改造。下面請先欣賞一下博主畫的一張aop簡圖(沒有藝術天分,畫的不好莫見怪)
解析:往往在我們的系統的多個核心流程中會有一部分與之關系不大的相同的橫切流程,例如權限認證,事務管理。因此我們一般會抽象出這些相同的比較次要的交給spring aop的Handler來統一處理這些橫切流程也就是上圖中綠色部分。接下來我們看一下本例結構圖:
解析:1,我們的Hostess對象是Master接口的實現,主要實現了WalkDog()和shopping()兩個方法,而WalkDog()方法則是調用的是Dog接口的實現類的bark()方法。
2,我們整個程序的入口Client調用的Hostess對象的兩個核心方法,HumanHandler處理的Hostess對象的橫切流程。
1 public class Client { 2 3 public static void main(String[] args) { 4 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 5 6 Master master = (Master)context.getBean("humanProxy"); 7 8 System.out.println(""); 9 System.out.println(""); 10 System.out.println(""); 11 System.out.println(""); 12 13 master.shopping(); 14 master.WalkDog(); 15 16 } 17 }
1 package human; 2 3 import dog.Dog; 4 5 public class Hostess implements Master { 6 7 private Dog dog; 8 9 public void setDog(Dog dog) { 10 this.dog = dog; 11 } 12 13 @Override 14 public void WalkDog() { 15 16 dog.bark(); 17 } 18 19 @Override 20 public void shopping(){ 21 System.out.println("瘋狂購物中"); 22 } 23 24 }
解析:通過以上代碼我們不難發現我們的程序只是調用核心業務,而往往核心業務的周圍有很多繁瑣的相對於比較次要的橫切業務。利用本例中遛狗,購物之前,我們需要再家做一些前提准備。例如:整理一下着裝,鎖上房門等等,回家之后有需要換鞋之類的。因此我們還需要一個handler來處理這些業務。

1 package aop; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class HumanHandler implements InvocationHandler { 7 8 private Object target;// 目標是不固定 9 10 public void setTarget(Object target) { 11 this.target = target; 12 } 13 14 /* 15 * return 返回是原來目標方法所返回的內容 method 就是要執行的方法 16 */ 17 @Override 18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 19 // TODO Auto-generated method stub 20 before(); 21 // 具體的業務邏輯代碼 22 Object returnValue = method.invoke(target, args); 23 24 after(); 25 return returnValue; 26 } 27 28 private void before() { 29 // 前置任務 30 System.out.println("[代理執行前置任務]整理着裝"); 31 System.out.println("[代理執行前置任務]帶上鑰匙"); 32 System.out.println(""); 33 System.out.println("[核心業務開始]*****************"); 34 } 35 36 private void after() { 37 // 后置任務 38 System.out.println("[核心業務結束]*****************"); 39 System.out.println(""); 40 System.out.println("[代理執行后置任務]開門"); 41 System.out.println("[代理執行后置任務]換鞋"); 42 } 43 44 }
解析:有了handler我們還需要一個代理工廠

1 package org.springframework.aop.framework; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Proxy; 5 6 7 public class ProxyFactoryBean { 8 9 private Object target; 10 11 private InvocationHandler handler; 12 13 public ProxyFactoryBean(Object target,InvocationHandler handler){ 14 this.target = target; 15 this.handler = handler; 16 } 17 18 //返回本類的一個實例 19 public Object getProxyBean() throws IllegalArgumentException, InstantiationException, IllegalAccessException, ClassNotFoundException{ 20 Object obj = Proxy.newProxyInstance( 21 target.getClass().getClassLoader(), 22 target.getClass().getInterfaces(), 23 handler); 24 return obj; 25 } 26 }
接下來我們來看一下本例的具體配置

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans> 3 <bean id="hostess" class="human.Hostess" scope="prototype"> 4 <property name="dog" ref="dog1"></property> 5 </bean> 6 7 <bean id="dog1" class="dog.Taidi" scope="prototype"></bean> 8 9 <bean id="dog2" class="dog.Labuladuo" scope="prototype"></bean> 10 11 <bean id="humanHandler" class="aop.HumanHandler"> 12 <property name="target" ref="hostess"></property> 13 </bean> 14 15 <bean id="humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> 16 <property name="handlerName" ref="humanHandler"></property> 17 <property name="target" ref="hostess"></property> 18 </bean> 19 20 21 </beans>
最后一步也是關鍵,本類中使用到的實例需要我們通過讀取上面這份配置文件然后通過反射構造出來。

1 package aop; 2 3 import java.io.File; 4 import java.lang.reflect.InvocationHandler; 5 import java.lang.reflect.Method; 6 import org.dom4j.Document; 7 import org.dom4j.Element; 8 import org.dom4j.io.SAXReader; 9 import org.springframework.aop.framework.ProxyFactoryBean; 10 11 public class ClassPathXmlApplicationContext implements ApplicationContext { 12 13 private String fileName; 14 15 public ClassPathXmlApplicationContext(String fileName){ 16 this.fileName = fileName; 17 } 18 19 @Override 20 public Object getBean(String beanid) { 21 22 System.out.println("傳遞過來的ID:"+beanid); 23 24 //獲取本類的當前目錄 25 String currentPath = this.getClass().getResource("").getPath().toString(); 26 SAXReader reader = new SAXReader();//DOM4J解釋器 27 Document doc = null;//xml文檔本身 28 Object obj = null;//目標表創建出來的實例 29 try { 30 doc = reader.read( new File(currentPath+fileName) ); 31 String xpath = "/beans/bean[@id='"+beanid+"']"; 32 Element beanNode = (Element) doc.selectSingleNode(xpath); 33 String className = beanNode.attributeValue("class"); 34 35 36 if ("org.springframework.aop.framework.ProxyFactoryBean".equals(className)){ 37 38 39 Element interceptorNamesNode = 40 (Element) beanNode.selectSingleNode("property[@name='handlerName']"); 41 42 String handlerName_value = interceptorNamesNode.attributeValue("ref"); 43 44 Element targetNode = (Element) beanNode.selectSingleNode("property[@name='target']"); 45 String targetName_value = targetNode.attributeValue("ref"); 46 47 return forProxyFactoryBean(targetName_value,handlerName_value); 48 } 49 50 obj = Class.forName(className).newInstance(); 51 52 53 //查找下一代 54 Element propertyNode = (Element) beanNode.selectSingleNode("property"); 55 if (propertyNode!=null){ 56 //注入 57 //System.out.println("發現property節點,准備注入"); 58 //需要注入的屬性名 59 String propertyName = propertyNode.attributeValue("name"); 60 //System.out.println("需要注入的屬性:"+propertyName); 61 62 //注入方法 63 String executeMethod = "set"+(propertyName.substring(0, 1)).toUpperCase()+propertyName.substring(1,propertyName.length()); 64 //System.out.println("需要執行注入方法:"+executeMethod); 65 66 //需要注入的對象實例 67 String di_object_name = propertyNode.attributeValue("ref"); 68 //System.out.println("注入的對象是:"+di_object_name); 69 70 //定義我們的需要注入的對象實例[遞歸算法:1.層級是不知道多少層的 2.自己調用自己 3.最后1層會自己結束] 71 Object di_object = getBean(di_object_name); 72 //System.out.println("xxx:"+di_object); 73 74 //Method method = obj.getClass().getMethod(executeMethod,di_object.getClass().getInterfaces());// new Method(executeMethod); 75 Method []methods = obj.getClass().getMethods(); 76 77 for (Method m : methods) { 78 if(executeMethod.equals(m.getName()) ) { 79 m.invoke(obj, di_object); 80 break; 81 } 82 } 83 84 } 85 else{ 86 System.out.println("沒有屬性,結束即可"); 87 } 88 89 } catch (Exception e) { 90 e.printStackTrace(); 91 } 92 93 System.out.println("返回實例:"+obj); 94 return obj; 95 } 96 97 98 public Object forProxyFactoryBean(String targetName_value,String handlerName_value) throws Exception{ 99 System.out.println("目標對象"+targetName_value); 100 101 Object target = getBean(targetName_value); 102 103 System.out.println("代理對象"+handlerName_value); 104 105 InvocationHandler handler = (InvocationHandler) getBean(handlerName_value); 106 107 return new ProxyFactoryBean(target,handler).getProxyBean(); 108 } 109 }
下面是運行結果
總結:1 spring aop將我們的系統分為兩部分,一核心業務,二橫切業務。我們的只需關注核心業務,橫切業務統一交給代理去處理。
2 本例依舊是利用反射調用橫切方法實現aop,還是那句話,我們自己寫的自然是漏洞百出,只是為了說明問題。作為一個開源的框架,如果對spring源碼感興趣的朋友可以自行查看。
另:博主的個人博客也在努力搭建中,歡迎與各位學習交流,謝謝!個人博客地址:http://www.singletonh.top/