【Java】模擬Sping,實現其IOC和AOP核心(二)

接着上一篇,在上一篇完成了有關IOC的註解實現,這一篇用XML的方式實現IOC,而且完成AOP。html

 

簡易的IOC框圖java

 

註解的方式實現了左邊的分支,那麼就剩下右邊的XML分支:正則表達式

XmlContext:
這個類是也是AbstractApplicationContext的子類,和AnnotationContext類似,只不過這裏是要解析XML文件而不是註解:
(關於XML文件的解析以前給過一篇博客:【Java】XML文件的解析
對於XML文件的處理,是不太容易的,會產生不少問題,後面只是實現核心步驟,不少屬性就不考慮了!
首先給出XmlBean,和AnnotationBean同樣,都是繼承自BeanElementspring

 1 public class XmlBean implements BeanElement {  2     private boolean DI;  3     private Object object;  4     private Object proxy;  5     private Map<Field, String> wiredMap;  6     // key:object的爲注入成員 value:依賴的className  7     // 將不能注入的成員先保存起來
 8     
 9     protected XmlBean() { 10         this(true, null, null); 11  } 12 
13     protected XmlBean(Object object, Object proxy) { 14         this(true, object, proxy); 15  } 16 
17     protected XmlBean(boolean dI, Object object, Object proxy) { 18         DI = dI; 19         this.object = object; 20         this.proxy = proxy; 21  } 22 
23     protected void addWiredElement(Field field, String ref) throws RepeatProperty { 24         if (wiredMap == null) { 25             wiredMap = new HashMap<>(); 26  } 27         if (wiredMap.containsKey(field)) { 28             throw new RepeatProperty(object.getClass() + "成員:" + field.getName() + "已定義!"); 29  } 30  wiredMap.put(field, ref); 31  } 32     
33     protected void setDI(boolean DI) { 34         this.DI = DI; 35  } 36 
37     protected Map<Field, String> getWiredMap() { 38         return wiredMap; 39  } 40     
41  @Override 42     @SuppressWarnings("unchecked") 43     public <E> E getProxy() { 44         return (E) proxy; 45  } 46 
47  @Override 48     public Object getObject() { 49         return object; 50  } 51 
52  @Override 53     public boolean isDI() { 54         return DI; 55  } 56 
57 }

 

XmlContext設計模式

 1 public class XmlContext extends AbstractApplicationContext {  2     protected XmlContext() {  3  }  4     
 5     protected XmlContext(String xmlPath) {  6  innerParseXml(xmlPath);  7  }  8     
 9     // 和註解方式中的作法同樣,只不過產生的是XML方式的BeanElement
 10     private XmlBean addXmlBean(Class<?> klass, Object object, String classId, String className) throws BeansException {  11         Object proxy = aopFactory.creatCGLibProxy(klass, object);  12         XmlBean bean = new XmlBean(object, proxy);  13  add(classId, className, bean);  14         return bean;  15  }  16 
 17     protected void innerParseXml(String xmlPath) {  18         // 找到根標籤 
 19         new XMLReader() {  20  @Override  21             public void dealElment(Element element, int index) {  22                 // 處理bean標籤
 23                 new XMLReader() {  24  @Override  25                     public void dealElment(Element element, int index) {  26                         // 獲得id屬性和class屬性的值
 27                         String classId = element.getAttribute("id");  28                         String className = element.getAttribute("class");  29                         try {  30                             // 由class獲得類
 31                             Class<?> klass = Class.forName(className);  32                             // 處理constructor標籤
 33                             new XMLReader() {  34  @Override  35                                 public void dealElment(Element element, int index) {  36                                 // TODO 處理有參數的構造方法,這裏就會遇到許多問題,在這裏我就不處理了,後面會給出解決思路
 37  }  38                             }.parse(element, "constructor-arg");  39                             // 因爲上面沒有處理帶參數的構造方法,這裏直接經過反射機制調用無參構造產生對象  40                             // 而且利用產生的對象生成代理對象,最後獲得Bean放入beanMap中
 41                             Object object = klass.newInstance();  42                             XmlBean bean = addXmlBean(klass, object, classId, className);  43                             
 44                             // 處理property標籤
 45                             new XMLReader() {  46  @Override  47                                 public void dealElment(Element element, int index) {  48                                     try {  49  dealProperty(element, klass, bean);  50                                     } catch (XmlPropertyMustNeedNameException e) {  51  e.printStackTrace();  52                                     } catch (Exception e) {  53  e.printStackTrace();  54  }  55  }  56                             }.parse(element, "property");  57                         } catch (Exception e1) {  58  e1.printStackTrace();  59  }  60  }  61                 }.parse(element, "bean");  62  }  63         }.parse(XMLReader.openDocument(xmlPath), "SimpleSpring");  64  }  65     
 66     private void dealProperty(Element element, Class<?> klass, XmlBean bean)  67             throws XmlPropertyMustNeedNameException, Exception {  68         // 獲得property標籤name屬性的值
 69         String fieldName = element.getAttribute("name");  70         if (fieldName.length() <= 0) {  71             throw new XmlPropertyMustNeedNameException("Bean" + klass.getName() + "的Property標籤必須聲明name屬性!");  72  }  73         // 經過反射機制獲得成員
 74         Field field = klass.getDeclaredField(fieldName);  75         // 獲得該成員的類型
 76         Class<?> fieldType = field.getType();  77         // 獲得value屬性
 78         String value = element.getAttribute("value");  79         // 獲得ref屬性
 80         String ref = element.getAttribute("ref");  81         
 82         // 判斷ref和value是否同時存在 
 83         if (value.length() > 0 && ref.length() > 0) {  84             throw new CanNotJudgeParameterException("value:" + value + " ref:" + ref + "只能存在一個!");  85  }  86         Object arg = null;  87         // value存在,則直接經過類型轉換給成員賦值
 88         if (value.length() > 0) {  89             if (!fieldType.isPrimitive() && !fieldType.equals(String.class)) {  90                 throw new ValueOnlyPrimitiveType("Value只能用於八大基本類型!");  91  }  92             // TypeConversion是我本身寫的,將字符串轉換爲基本類型的工具
 93             arg = TypeConversion.getValue(value, fieldType.getSimpleName());  94             field.setAccessible(true);  95  field.set(bean.getObject(), arg);  96  }  97         if (ref.length() > 0) {  98             // ref屬性存在,因爲存在相互依賴關係,因此如今不作處理,只是將其保存起來  99             // 設置該bean的狀態爲還沒有注入
100             bean.setDI(false); 101  bean.addWiredElement(field, ref); 102  } 103  } 104 
105 }

XmlContext能作的工做也十分有限,只能完成簡單的注入,剩下的注入工做留給下一級處理!數組

在這裏之因此沒有處理constructor標籤,是由於對與構造方法的處理存在許多因素:
好比:多線程

1 public class Test { 2     public Test(String one, int two) { 3  ...... 4  } 5     public Test(int two, String one) { 6  ...... 7  } 8 }

經過XML文件讀取出來的都是字符串,如何區分它是字符串「123」,而不是int類型123?這兩個構造方法到底執行哪一個?
再好比說:app

 1     public Test(int one, int two, Student student) {  2  ......  3  }  4     
 5     public Test(String one, int two, Student student) {  6  ......  7  }  8 
 9     public Test(int two, String one, Student student) { 10  ...... 11     }

經過反射機制,咱們就須要獲得構造方法的集合getConstructors();而後篩選出參數個數符合要求的子集,再遍歷這個子集的每個構造方法,而後遍歷當前構造方法的全部參數,一個一個比對參數類型是否符合要求,直到找到符合要求的那一個爲止,可是,若是說咱們是想執行第三個構造方法,它卻找到的是第一個,徹底就出問題了!
因此Spring的解決辦法是給出一個type屬性ide

1 <bean id="xxx" class="xxx.xxx.Test">
2     <constructor-arg idnex="0" value="1" type="int.class">
3     <constructor-arg idnex="1" value="2" type="java.lang.String">
4     <constructor-arg idnex="2" ref="student">
5 </bean>

 

只有這樣作才能真真區分,因此之後在使用Spring的constructor標籤時,當構造方法有歧義時,必定要給出type屬性,避免出錯,也減小了查找時的遍歷!函數

接下來就是最後一個類,xml分支的最高容器:
ClassPathXmlApplicationContext
上面的XmlContext只是完成了基本的注入問題,還有後續有關於注入之間的依賴關係,甚至是依賴循環(關於依賴循環在個人上一篇中有專門介紹,這裏就再也不介紹了)

 

 1 public class ClassPathXmlApplicationContext extends XmlContext {  2     public ClassPathXmlApplicationContext() {  3  }  4     
 5     public ClassPathXmlApplicationContext(String xmlPath) {  6         super(xmlPath);  7  }  8     
 9     public ClassPathXmlApplicationContext parseXml(String xmlPath) { 10  innerParseXml(xmlPath); 11         return this; 12  } 13     
14  @Override 15     public <T> T getBean(Class<T> klass) throws BeansException { 16         String className = klass.getName(); 17         BeanElement bean = beanMap.get(className); 18         
19         if (bean == null) { 20             throw new BeansException("Bean :" + klass + "不存在!"); 21  } 22         // 在這裏仍是隻考慮XmlBean的注入,不考慮AnnotationBlean註解的完成狀況
23         if (!bean.isDI() && bean instanceof XmlBean) { 24  autowired(className, (XmlBean)bean); 25  } 26         
27         return bean.getProxy(); 28  } 29     
30     private void autowired(String klassName, XmlBean bean) throws BeansException { 31         // 和AnnotationBean的解決思路同樣,先設置狀態爲已注入,防止循環依賴的無限遞歸
32         bean.setDI(true); 33         // 獲得還沒有注入的成員map
34         Map<Field, String> wiredMap = bean.getWiredMap(); 35         if (wiredMap == null || wiredMap.isEmpty()) return; 36         // 遍歷map
37         for (Field field : wiredMap.keySet()) { 38             String ref = wiredMap.get(field); 39             String tagClassName = beanNameMap.get(ref); 40             // ref若是是id則在beanNameMap中找,若是是className就在beanMap中找
41             BeanElement wiredBean = tagClassName == null ? beanMap.get(ref) : beanMap.get(tagClassName); 42             if (bean == null) { 43                 return; 44  } 45             if (!wiredBean.isDI() && wiredBean instanceof XmlBean) { 46  autowired(ref, (XmlBean)wiredBean); 47  } 48             field.setAccessible(true); 49             try { 50  field.set(bean.getObject(), wiredBean.getObject()); 51             } catch (Exception e) { 52                 throw new BeansException(klassName + "依賴關係不正確!"); 53  } 54  } 55  wiredMap.clear(); 56  } 57     
58 }

看過註解方式的話再看XML就會發現二者實際上是一回事,都是經過二者提供的映射關係,利用反射機制完成注入!
只不過二者提供的映射關係在解析起來時各有各的特色!

Xml方式的實現這裏就簡單實現了,來看看使用狀況:

 1 public class StudentA {  2  String name;  3     private StudentB B;  4     
 5     public StudentA() {  6  }  7     
 8  @Override  9     public String toString() { 10         return "A:" + name + "->" + B; 11  } 12     
13 } 14 
15 @Component 16 public class StudentB { 17     private String name; 18     private StudentC C; 19     
20     public StudentB() { 21  } 22 
23  @Override 24     public String toString() { 25         return "B:" + name + "->" + C; 26  } 27     
28 } 29 
30 @Component 31 public class StudentC { 32     private String name; 33     private StudentA A; 34     
35     public StudentC() { 36  } 37 
38  @Override 39     public String toString() { 40         return "C:" + name; 41  } 42     
43 }

xml的配置:

 1 <SimpleSpring>
 2     <bean id="haha" class="com.zc.ioc.demo.StudentA">
 3         <property name="name" value="我是A"></property>
 4         <property name="B" ref="com.zc.ioc.demo.StudentB"></property>
 5     </bean>
 6     <bean class="com.zc.ioc.demo.StudentB">
 7         <property name="name" value="我是B"></property>
 8         <property name="C" ref="com.zc.ioc.demo.StudentC"></property>
 9     </bean>
10     <bean class="com.zc.ioc.demo.StudentC">
11         <property name="name" value="我是C"></property>
12         <property name="A" ref="haha"></property>
13     </bean>
14 </SimpleSpring>

主函數:

1 public static void main(String[] args) throws BeansException { 2         // 或者是使用BeanFactory beanFactory = new ClassPathXmlApplicationContext("/test_simple_spring.xml");
3         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/test_simple_spring.xml"); 4         StudentA bean = applicationContext.getBean(StudentA.class); 5  System.out.println(bean); 6 }

輸出:

那麼試一試註解和Xml方式的混合使用:

 1 @Component  2 public class StudentA {  3     @Value(value="我是A")  4  String name;  5  @Autowired  6     private StudentB B;  7     
 8     public StudentA() {  9  } 10     
11  @Override 12     public String toString() { 13         return "A:" + name + "->" + B; 14  } 15     
16 } 17 
18 @Component 19 public class StudentB { 20     @Value(value="我是B") 21     private String name; 22  @Autowired 23     private StudentC C; 24     
25     public StudentB() { 26  } 27 
28  @Override 29     public String toString() { 30         return "B:" + name + "->" + C; 31  } 32     
33 } 34 @Component 35 public class StudentC { 36     @Value(value="我是C") 37     private String name; 38  @Autowired 39     private StudentD D; 40     
41  @Autowired 42     private StudentA A; 43     
44     public StudentC() { 45  } 46 
47  @Override 48     public String toString() { 49         return "C:" + name + "->" + D; 50  } 51     
52 } 53 
54 public class StudentD { 55     private String name; 56     
57     public StudentD() { 58  } 59     
60  @Override 61     public String toString() { 62         return "D:" + name; 63  } 64     
65 }

 Xml配置:

1 <SimpleSpring>
2     <bean class="com.zc.ioc.demo.StudentD">
3         <property name="name" value="我是D"></property>
4     </bean>
5 </SimpleSpring>

主函數:

1 public static void main(String[] args) throws BeansException { 2         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/test_simple_spring.xml"); 3         StudentD studentD = applicationContext.getBean(StudentD.class); 4  System.out.println(studentD); 5         
6         applicationContext= new AnnotationConfigApplicationContext("com.zc.moedl"); 7         StudentA studentA = applicationContext.getBean(StudentA.class); 8  System.out.println(studentA); 9}

輸出結果:

看起來是沒有問題了,可是若是Xml和註解之間的出現順序不一樣,結果也會不同,還得仔細考慮,並且我作的這個是延遲注入,只有在getBean的時候纔會完成最後的注入,而且如果註解中須要一個Xml的bean注入,而xml的這個bean又依賴於註解中的一個bean,那麼這套方法是不可行的!

AOP
前面屢次談到AOP,以及咱們的Bean是經過原始對象+代理對象,這裏來看看AOP部分的實現:
AOP說到底主要目的不是產生代理對象,而是要經過代理對象執行方法,並對方法進行有效的攔截!
簡單起見,將攔截分爲置前,置後,以及出現異常時的攔截。
而攔截又是什麼時候產生的?
仍是爲了簡單實現,後面都只使用CGLibProxy,有關CGLib的代理我在上一篇有介紹,這裏也就不累贅了。

關於攔截器的產生,我以前的實現方式是給要攔截的方法添加註解,給出攔截Id,而後提供一套方法,給指定Id號的方法建立攔截器,可是,在知道Spring的處理後,這種方式很快被否認了!在工程中,每每不少須要攔截的方法是不容許侵入式修改的,又或者是被打成了jar包,那麼就更不可能對其添加註解,因此給出新的解決思路:
由用戶本身寫一個方法,而後給這個方法添加註解,使其和要攔截的方法產生對映射關係,這樣咱們實際執行的攔截器方法徹底是由用戶提供,並不會干預源代碼!
前面說過只是處理置前,置後,以及出現異常時的攔截,因此會給出三種不一樣的註解,用於區分!
因爲是要使用註解,那麼就要用到包掃描【Java】包、jar包的掃描
包掃描就須要對類進行區分,只處理帶有標識的類,因此還缺乏一個對類的註解:

@Aspect

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.TYPE) 3 public @interface Aspect { 4 }

這個註解只是爲了代表這個類存放着用戶編寫的攔截器方法!

主要的是下面三個註解:
@Before

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.METHOD) 3 public @interface Before { 4     Class<?> klass(); 5  String method(); 6 }

置前攔截方法的註解,klass代表對哪一個類進行置前攔截,method代表對哪一個方法進行攔截,但發現僅僅經過這好像不能找到具體的方法,但仔細想想,置前攔截是對要攔截的方法參數進行判斷,用戶在編寫攔截時必然知道攔截的方法是什麼,參數個數和類型固然也知道,那咱們只要讓用戶寫的方法的參數和要攔截的方法參數保持一致就好了,若是不一致,就異常處理!這樣就能經過用戶編寫的方法,知道被攔截的方法參數,進而定位到具體要攔截的方法!

@After

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.METHOD) 3 public @interface After { 4     Class<?> klass(); 5  String method(); 6     Class<?>[] parameterTypes() default {}; 7 }

置後攔截方法的註解,同置前攔截同樣,klass代表對哪一個類進行置前攔截,method代表對哪一個方法進行攔截。因爲以後攔截是對方法執行結果的操做,用戶寫的方法的參數有且只有一個,且參數類型要與原方法的返回值類型匹配,這樣,咱們就不能像處理@Before時同樣,必須申明被攔截的方法的參數類型,只有這樣才能定位到具體的被攔截方法!

@Throwable

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.TYPE) 3 public @interface Throwable { 4     Class<?> klass(); 5  String method(); 6     Class<?>[] parameterTypes(); 7 }

出現異常時攔截的註解,和@After同樣,它只是處理異常,那麼用戶所提供的方法的參數有且只有一個,類型是執行被攔截方法產生的異常類型,它也必須傳遞被攔截方法的參數,使其定位到具體的被攔截方法!

其實在Spring裏面使用了更爲犀利的手段,它並無使用Class<?> 只是使用了一個字符串就解決了類、方法、參數的定位,只不過它就須要對字符串解析,利用了正則表達式,雖然比個人方法繁瑣,但面向用戶的使用是十分友好的!

AOP框圖

 

看起來我把它作的很複雜,事實上這裏用到的全是接口,是很是靈活的,若是說不想使用這套方式,那麼能夠本身實現Advice接口;若是說攔截器鏈用的時list存儲,之後想更換爲鏈表也是能夠的;攔截器的產生不想使用上面說的註解方式,那麼本身去實現IntercepterFactory接口!

AopFactory

 1 public class AopFactory {  2     private Advice advice;  3     
 4     public AopFactory() {  5  }  6     
 7     public AopFactory setAdvice(Advice advice) {  8         this.advice = advice;  9         return this; 10  } 11     
12     public <E> E creatCGLibProxy(Class<?> klass) throws Exception { 13         return creatCGLibProxy(klass, klass.newInstance()); 14  } 15     
16     public <E> E creatCGLibProxy(Object object) { 17         return creatCGLibProxy(object.getClass(), object); 18  } 19     
20     @SuppressWarnings("unchecked") 21     public <E> E creatCGLibProxy(Class<?> klass, Object object) { 22         Enhancer enhancer = new Enhancer(); 23  enhancer.setSuperclass(klass); 24         enhancer.setCallback(new MethodInterceptor() { 25  @Override 26             public Object intercept(Object proxyObject, Method method, 27                     Object[] args, MethodProxy methodProxy) throws Throwable { 28                 return doInvoke(object, method, args); 29  } 30  }); 31         
32         return (E) enhancer.create(); 33  } 34     
35     private Object doInvoke(Object object, Method method, 36  Object[] args) 37              throws Throwable{ 38         Object result = null; 39         // AdviceAdapter是Advice的適配,什麼都不作
40         advice = advice == null ? new AdviceAdapter() : advice; 41         if (!advice.dealBefore(method, args)) { 42             return result; 43  } 44         try { 45             result = method.invoke(object, args); 46             result = advice.dealAfter(method, result); 47         } catch (Throwable e) { 48  advice.delaThrowable(method, e); 49             throw e; 50  } 51         
52         return result; 53  } 54 
55 }

AopFactory只負責產生代理對象,而代理的攔截就下發給Advice


Advice

1 public interface Advice { 2     boolean dealBefore(Method method, Object[] args); 3  Object dealAfter(Method method, Object result); 4     void delaThrowable(Method method, Throwable e); 5 }

暫時只處理置前、置後、以及異常,之後須要再添加,而不是修改!這就是使用接口的好處!

IntercepterLink

1 public interface IntercepterLink { 2     boolean add(IntercepterMethod tagMethod); 3     public boolean doBefore(Object[] args); 4  Object doAfter(Object result); 5     void doThrowable(Throwable e); 6 }

攔截器鏈,一個方法能夠由多個攔截器,攔截器鏈是攔截器方法的真正執行者!提供了添加攔截器,處理置前、置後、異常,也能夠給個remove,這裏就不寫了。

IntercepterFactory

1 public interface IntercepterFactory { 2     void addBeforeIntercepter(Method tagMethod, IntercepterMethod imd); 3     void addAfterIntercepter(Method tagMethod, IntercepterMethod imd); 4     void addThrowableIntercepter(Method tagMethod, IntercepterMethod imd); 5     
6  IntercepterLink getBeforeIntercepterLink(Method tagMethod); 7  IntercepterLink getAfterIntercepterLink(Method tagMethod); 8  IntercepterLink getThrowableIntercepterLink(Method tagMethod); 9 }

攔截器鏈的建立者和擁有着,其中的IntercepterMethod就是攔截器接口:
IntercepterMethod

1 public interface IntercepterMethod { 2  Object getIntercepterObject(); 3  Method getIntercepterMethod(); 4 }

咱們的攔截器的執行是經過反射機制,那麼就必須知道方法和對象,至於參實是經過CGLib代理機制傳遞過來的,就不用考慮!

準備工做完成,接下來就是真真的處理部分:
IntercepterFactory在上面說是攔截器的建立者和持有者,因此我把它的是實現類進行了分級:
IntercepterLoader

 1 public class IntercepterLoader implements IntercepterFactory {  2     // 能夠看到每一個方法都有本身的置前、置後、異常攔截器鏈
 3     private static  final Map<Method, IntercepterLink> beforeMap;  4     private static  final Map<Method, IntercepterLink> afterMap;  5     private static  final Map<Method, IntercepterLink> exceptionMap;  6 
 7     static {  8         beforeMap = new HashMap<>();  9         afterMap = new HashMap<>(); 10         exceptionMap = new HashMap<>(); 11  } 12     
13     public IntercepterLoader() { 14  } 15     
16  @Override 17     public IntercepterLink getBeforeIntercepterLink(Method tagMethod) { 18         return beforeMap.get(tagMethod); 19  } 20 
21  @Override 22     public IntercepterLink getAfterIntercepterLink(Method tagMethod) { 23         return afterMap.get(tagMethod); 24  } 25 
26  @Override 27     public IntercepterLink getThrowableIntercepterLink(Method tagMethod) { 28         return exceptionMap.get(tagMethod); 29  } 30 
31     private void add(Map<Method, IntercepterLink> map, 32  Method tagMethod, IntercepterMethod imd) { 33         IntercepterLink link = map.get(tagMethod); 34         // 防止多線程的訪問而建立不一樣的攔截器鏈
35         if (link == null) { 36             synchronized (map) { 37                 if (link == null) { 38                     // IntercepterNodeList是我這套機制默認的IntercepterLink實現類
39                     link = new IntercepterNodeList(imd); 40  } 41  } 42             // 該方法還未建立攔截器鏈
43  map.put(tagMethod, link); 44         } else { 45             // 方法相同,則在攔截器鏈上追加
46  link.add(imd); 47  } 48  } 49     
50  @Override 51     public void addBeforeIntercepter(Method tagMethod, IntercepterMethod imd) { 52  add(beforeMap, tagMethod, imd); 53  } 54     
55  @Override 56     public void addAfterIntercepter(Method tagMethod, IntercepterMethod imd) { 57  add(afterMap, tagMethod, imd); 58  } 59 
60  @Override 61     public void addThrowableIntercepter(Method tagMethod, IntercepterMethod imd) { 62  add(exceptionMap, tagMethod, imd); 63  } 64 
65 }

真正意義上的攔截器持有者,它要完成的功能很是簡單!

咱們是要經過註解的方式產生攔截器,因此就有更高級來處理:
IntercepterLoaderFactory

 1 /**
 2 * 使用包掃描,找到帶有@Before、@After、@Throwable的方法,將其添加至攔截器map中  3 */
 4 public class IntercepterLoaderFactory extends IntercepterLoader {  5     public IntercepterLoaderFactory() {  6  }  7     
 8     public IntercepterLoaderFactory parseMethodForPackage(String packageName) {  9         new PackageScanner() {  10  @Override  11             public void dealClass(Class<?> klass) {  12                 // 判斷類是否知足咱們定義的@Aspect
 13                 if (!klass.isAnnotationPresent(Aspect.class)) return;  14                 try {  15                     // 產生方法執行的對象
 16                     Object object = klass.newInstance();  17                     Method[] methods = klass.getDeclaredMethods();  18                     // 遍歷全部方法,處理帶有註解的方法
 19                     for (Method method : methods) {  20                         if (method.isAnnotationPresent(Before.class)) {  21                             parseBeforeIntercepter(klass, object, method, method.getAnnotation(Before.class));  22                         } else if (method.isAnnotationPresent(After.class)) {  23                             parseAfterIntercepter(klass, object, method, method.getAnnotation(After.class));  24                         } else if (method.isAnnotationPresent(Throwable.class)) {  25                             parseExceptionIntercepter(klass, object, method, method.getAnnotation(Throwable.class));  26  }  27  }  28                 } catch (InstantiationException e) {  29  e.printStackTrace();  30                 } catch (IllegalAccessException e) {  31  e.printStackTrace();  32  }  33  }  34  }.scanPackage(packageName);  35         return this;  36  }  37     
 38     /**
 39  * 處理帶@Before註解的方法  40     */
 41     private void parseBeforeIntercepter(Class<?> klass, Object object, Method method, Before before) {  42         if (!method.getReturnType().equals(boolean.class)) {  43             try {  44                 throw new ReturnTypeNotMatch(method + "返回值類型必須是boolean!");  45             } catch (ReturnTypeNotMatch e) {  46  e.printStackTrace();  47  }  48  }  49         // 從@Before註解中獲取被攔截方法的信息
 50         Class<?> targetClass = before.klass();  51         String targetMethodName = before.method();  52         Class<?>[] methodTypes = method.getParameterTypes();  53 
 54         try {  55             Method targetMethod = targetClass.getDeclaredMethod(targetMethodName, methodTypes);  56             // 父類的方法調用,其中的IntercepterMethodDefination是IntercepterMethod的實現類
 57             addBeforeIntercepter(targetMethod, new IntercepterMethodDefination(object, method));  58         } catch (NoSuchMethodException e) {  59             try {  60                 throw new IntercepterMethodParaNotMatch(method + "參數不匹配!");  61             } catch (IntercepterMethodParaNotMatch e1) {  62  e1.printStackTrace();  63  }  64  }  65  }  66 
 67     /**
 68  * 處理帶@After註解的方法  69     */
 70     private void parseAfterIntercepter(Class<?> klass, Object object, Method method, After after) {  71         // 從@After註解中獲取被攔截方法的信息
 72         Class<?> targetClass = after.klass();  73         String targetMethodName = after.method();  74         Class<?>[] targetMethodPara = after.parameterTypes();  75         try {  76             // 經過上述參數獲得被攔截方法,若是得不到,異常處理
 77             Method targetMethod = targetClass.getDeclaredMethod(targetMethodName, targetMethodPara);  78             Class<?> targetMethodReturnType = targetMethod.getReturnType();  79             Class<?> methodReturnType = method.getReturnType();  80             Class<?>[] methodParameters = method.getParameterTypes();  81             // 判斷是否知足置後攔截方法的條件:  82             // 置後攔截的方法返回值類型必須和被攔截方法相同  83             // 置後攔截的方法的參數有且只有一個,且是被攔截的方法返回值類型
 84             if (methodParameters.length != 1
 85                     || !targetMethodReturnType.equals(methodReturnType)  86                     || !methodReturnType.equals(methodParameters[0])) {  87                 try {  88                     throw new IntercepterMethodParaNotMatch("攔截器方法:" + method + 
 89                             " 與被攔截方法" + targetMethod + "不匹配!");  90                 } catch (IntercepterMethodParaNotMatch e) {  91  e.printStackTrace();  92  }  93  }  94             
 95             addAfterIntercepter(targetMethod, new IntercepterMethodDefination(object, method));  96         } catch (NoSuchMethodException | SecurityException e) {  97             try {  98                 throw new IntercepterMethodParaNotMatch("被攔截方法[" + targetMethodName + "]不存在!");  99             } catch (IntercepterMethodParaNotMatch e1) { 100  e1.printStackTrace(); 101  } 102  } 103  } 104 
105     private void parseExceptionIntercepter(Class<?> klass, Object object, Method method, Throwable throwable) { 106             // 從@Throwable 註解中獲取被攔截方法的信息
107             Class<?> targetClass = throwable.klass(); 108             String targetMethodName = throwable.method(); 109             Class<?>[] targetMethodPara = throwable.parameterTypes(); 110             try { 111                 // 經過上述參數獲得被攔截方法,若是得不到,異常處理
112                 Method targetMethod = targetClass.getDeclaredMethod(targetMethodName, targetMethodPara); 113                 Class<?>[] methodParameters = method.getParameterTypes(); 114                 // 判斷是否知足異常攔截方法的條件: 115                 // 異常攔截的方法的參數有且只有一個,且是java.lang.Throwable
116                 if (methodParameters.length != 1
117                         || methodParameters[0].equals(java.lang.Throwable.class)) { 118                     try { 119                         throw new IntercepterMethodParaNotMatch("攔截器方法:" + method + 
120                                 " 與被攔截方法" + targetMethod + "不匹配!"); 121                     } catch (IntercepterMethodParaNotMatch e) { 122  e.printStackTrace(); 123  } 124  } 125                 addAfterIntercepter(targetMethod, new IntercepterMethodDefination(object, method)); 126             } catch (NoSuchMethodException | SecurityException e) { 127                 try { 128                     throw new IntercepterMethodParaNotMatch("被攔截方法[" + targetMethodName + "]不存在!"); 129                 } catch (IntercepterMethodParaNotMatch e1) { 130  e1.printStackTrace(); 131  } 132  } 133  } 134     
135 }

經過這套機制,咱們就能經過註解+包掃描,十分方便地給指定方法添加攔截了!
IntercepterMethodDefination

 1 public class IntercepterMethodDefination implements IntercepterMethod {  2     private Object intercepterObject;  3     private Method intercepterMethod;  4     
 5     protected IntercepterMethodDefination() {  6  }  7 
 8     protected IntercepterMethodDefination(Object intercepterObject, Method intercepterMethod) {  9         this.intercepterObject = intercepterObject; 10         this.intercepterMethod = intercepterMethod; 11  } 12 
13  @Override 14     public Object getIntercepterObject() { 15         return intercepterObject; 16  } 17 
18  @Override 19     public Method getIntercepterMethod() { 20         return intercepterMethod; 21  } 22 
23 }

攔截器方法執行所需的封裝

攔截器咱們也有了,就剩下攔截器鏈了:
個人攔截器鏈使用了鏈表,爲了可以方法地鏈式調用,也就是設計模式之一的職責鏈模式,固然也可使用List,只不過使用鏈表相比於List,在處理時都須要遍歷,沒有什麼差異,可是鏈表比List佔的空間小,List在內部是數組,且數組大小是大於有效元素個數的!
IntercepterNodeList

 1 public class IntercepterNodeList implements IntercepterLink {  2     private IntercepterMethod imd; // 攔截器
 3     private IntercepterNodeList next; // 下一結點
 4     private IntercepterNodeList last; // 尾結點
 5     
 6     protected IntercepterNodeList() {  7         this(null);  8  }  9     
10     protected IntercepterNodeList(IntercepterMethod imd) { 11         this.imd = imd; 12         this.next = null; 13         this.last = this; 14  } 15     
16     /**
17  * 尾插法追加結點 18     */
19  @Override 20     public boolean add(IntercepterMethod imd) { 21         if (next == null) { 22             next = new IntercepterNodeList(imd); 23             last = next; 24         } else { 25             last = last.next = new IntercepterNodeList(imd); 26             last.next = null; 27  } 28         return true; 29  } 30     /**
31  * 鏈式調用處理置前攔截 32     */
33  @Override 34     public boolean doBefore(Object[] args) { 35         boolean isContinue = this.innerInvoke(imd.getIntercepterObject(), imd.getIntercepterMethod(), args); 36         if (this.next != null && isContinue) { 37             isContinue = this.next.doBefore(args); 38  } 39         return true; 40  } 41     
42     @SuppressWarnings("unchecked") 43     private <T> T innerInvoke(Object object, Method method, Object[] args) { 44         T result = null; 45         try { 46             result = (T) method.invoke(object, args); 47         } catch (Exception e) { 48  e.printStackTrace(); 49  } 50         return result; 51  } 52     
53     /**
54  * 鏈式調用處理置後攔截 55     */
56  @Override 57     public Object doAfter(Object result) { 58         result = innerInvoke(imd.getIntercepterObject(), imd.getIntercepterMethod(), new Object[] {result}); 59         if (this.next != null) { 60             result = this.next.doAfter(result); 61  } 62         return result; 63  } 64     
65     /**
66  * 鏈式調用處理異常攔截 67     */
68  @Override 69     public void doThrowable(Throwable e) { 70         innerInvoke(imd.getIntercepterObject(), imd.getIntercepterMethod(), new Object[] {e}); 71         if (this.next != null) { 72             this.next.doThrowable(e); 73  } 74  } 75     
76 }

AOP到這裏就已經結束了,來看看它的使用吧:
被攔截類及其方法:

1 public class Test { 2     public Test() { 3  } 4     
5     public String fun(int arg) { 6         System.out.println("Test的fun方法執行 arg = " + arg); 7         return "fun"; 8  } 9 }

攔截器所在類:

 1 @Aspect  2 public class Action {  3     public Action() {  4  }  5     
 6     @Before(klass=Test.class, method="fun")  7     public boolean beforeFun(int arg) {  8         System.out.println("置前攔截beforeFun:arg = " + arg);  9         return true; 10  } 11     
12     @Before(klass=Test.class, method="fun") 13     public boolean beforeFunOther(int arg) { 14         System.out.println("置前攔截beforeFunOther:arg = " + arg); 15         return true; 16  } 17     
18     @After(klass=Test.class, method="fun", parameterTypes= {int.class}) 19     public String AfterFun(String arg) { 20         System.out.println("置後攔截:arg = " + arg); 21         
22         return "AfterFun"; 23  } 24 }

主函數:

 1 public static void main(String[] args) throws Exception {  2         IntercepterLoaderFactory intercepterLoaderFactory = 
 3                 new IntercepterLoaderFactory().parseMethodForPackage("com.zc.action");  4         
 5         AopFactory aopFactory = new AopFactory();  6         aopFactory.setAdvice(new AdviceHander()  7  .setIntercepterFactory(intercepterLoaderFactory));  8         
 9         Test testProxy = aopFactory.creatCGLibProxy(Test.class); 10         System.out.println(testProxy.fun(10)); 11 }

執行結果:

 

這樣的用法是有些噁心了,可是,別忘了,AOP配合IOC纔是使用的精華:
註解方式的注入:

 1 @Component  2 public class StudentA {  3     @Value(value="我是A")  4  String name;  5  @Autowired  6     private StudentB B;  7     
 8     public String fun(int arg) {  9         System.out.println("StudentA的fun方法執行 arg = " + arg); 10         return "fun"; 11  } 12     
13  @Override 14     public String toString() { 15         return "A:" + name + "->" + B; 16  } 17     
18 } 19 
20 @Component 21 public class StudentB { 22     @Value(value="我是B") 23     private String name; 24  @Autowired 25     private StudentC C; 26     
27     public StudentB() { 28  } 29 
30  @Override 31     public String toString() { 32         return "B:" + name + "->" + C; 33  } 34     
35 } 36 
37 @Component 38 public class StudentC { 39     @Value(value="我是C") 40     private String name; 41  @Autowired 42     private StudentD D; 43     
44  @Autowired 45     private StudentA A; 46     
47     public StudentC() { 48  } 49 
50  @Override 51     public String toString() { 52         return "C:" + name + "->" + D; 53  } 54     
55 }

Xml方式的注入:

1 <SimpleSpring>
2     <bean class="com.zc.ioc.demo.StudentD">
3         <property name="name" value="我是D"></property>
4     </bean>
5 </SimpleSpring>

攔截器:

 1 @Aspect  2 public class Action {  3     public Action() {  4  }  5     
 6     @Before(klass=StudentA.class, method="fun")  7     public boolean beforeFun(int arg) {  8         System.out.println("置前攔截beforeFun:arg = " + arg);  9         return true; 10  } 11     
12     @Before(klass=StudentA.class, method="fun") 13     public boolean beforeFunOther(int arg) { 14         System.out.println("置前攔截beforeFunOther:arg = " + arg); 15         return true; 16  } 17     
18     @After(klass=StudentA.class, method="fun", parameterTypes= {int.class}) 19     public String AfterFun(String arg) { 20         System.out.println("置後攔截:arg = " + arg); 21         return "AfterFun"; 22  } 23     
24 }

主函數:

 1 public static void main(String[] args) throws Exception {  2         new IntercepterLoaderFactory().parseMethodForPackage("com.zc.action");  3         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/test_simple_spring.xml");  4         StudentD studentD = applicationContext.getBean(StudentD.class);  5  System.out.println(studentD);  6         
 7         applicationContext = new AnnotationConfigApplicationContext("com.zc.model");  8         StudentA studentA = applicationContext.getBean(StudentA.class);  9         studentA.fun(10); 10  System.out.println(studentA); 11 }

執行結果:

 

Spring的IOC和AOP就是先到這裏了,有興趣的能夠交流一下感謝您的閱讀(*^_^*)

相關文章
相關標籤/搜索