Java模拟Sping,实现其IOC和AOP核心

Posted 松饼人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java模拟Sping,实现其IOC和AOP核心相关的知识,希望对你有一定的参考价值。

接着上一篇,在上一篇完成了有关IOC的注解实现,这一篇用XML的方式实现IOC,并且完成AOP。

 

简易的IOC框图

 

注解的方式实现了左边的分支,那么就剩下右边的XML分支:

XmlContext:
这个类是也是AbstractApplicationContext的子类,和AnnotationContext相似,只不过这里是要解析XML文件而不是注解:
(关于XML文件的解析之前给过一篇博客:【Java】XML文件的解析
对于XML文件的处理,是不太容易的,会产生很多问题,后面只是实现核心步骤,很多属性就不考虑了!
首先给出XmlBean,和AnnotationBean一样,都是继承自BeanElement

 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?这两个构造方法到底执行哪个?
再比如说:

 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属性

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"Sping AOP Capabilities and Goals

Sping IOC

对 Spring 的核心(AOP 和 IOC)的理解(大白话)

IOC与AOP介绍

sping 03--21

Spring核心概念