黑马程序员_spring的注入的实现

---------- android培训java培训、期待与您交流! ----------

Spring 是Java领域一个著名的开源框架,是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架 。Spring有两大核心技术,一个是控制反转(ioc)也被称为注入(di),另外一个是面向切面的编程(aop),使用拥有者两大技术的sping创建应用程序变的很简单。

 

看了张孝祥老师的java高新视频教程,想自己写一个sping这样的框架,今天实现了sping核心技术中的注入。。。。。。

下面的这个类实现了sping中的注入功能,配置使用的是xml(beans.xml),解析xml文件使用的是dom4j。原理是扫描配置文件,把class属性的内容通过Class的forName这个静态方法得到一个对象,把放进map里面,key是配置文件中对应的id的值。若bean有property 这个属性,把property 上的ref对应的对象,注入到name的值表示的成员变量上,用法和spring差不多,看下配置文件格式就明白了。

 

xml的配置文件格式为:

<?xml version="1.0" encoding="UTF-8"?>

<beans>

<bean id="test" class="com.heima.Test">
<property name="myTest" ref="myTestImpl" />
</bean>
<bean id="myTestImpl" class="com.heima.MyTestImpl" />

</beans>

------------------------------------------------------

BeanFactory类:

 

public class BeanFactory {

 privatestaticBeanFactory beanFactory=newBeanFactory();//使用了singleton模式

 private Map<String, Object> beanMap = new HashMap<String, Object>();

 

 privateBeanFactory(){

  try{

   init();

  }catch(MyFrameworkException e){

   e.printStackTrace();

  }

 }

 

 publicstaticBeanFactory getBeanFactory(){

  return beanFactory;

 }

 

 publicObject getBean(String id){

  return beanMap.get(id);

 }

 

 //扫描xml配置文件

 public void init() throws MyFrameworkException {

  // 拿到配置文件的根元素

  Element root = getRootElement();

  // 拿到所有的bean元素

  List<Element> beans = root.elements("bean");

  // 遍历所有的bean元素,并装进map中,keybeanidvalue为这个bean对象

  for (Element bean : beans) {

   // 检查bean的格式

   checkBeanData(bean);

   // 获得bean这个对象

   Object beanObj = getBeanObject(bean.attribute("class").getText());

   // 装进对象

   beanMap.put(bean.attributeValue("id"), beanObj);

  }

  // 开始注入

  di(beans);

 }

 

 privatevoid di(List<Element> beans)throwsMyFrameworkException{

  //重新读取下beans这个list,这样做可以使xml文件中bean的配置可以不用管顺序,代价是遍历了两次beanslist

  for (Element bean : beans) {

   Element property = getProperty(bean);

   if (property != null) {

    // 得到idbean.attributeValue("id")的对象

    Object beanObj = beanMap.get(bean.attributeValue("id"));

    // 得到bean的参考bean

    // idproperty.attributeValue("ref"),并把这个参考bean设置到bean的字段上

    Object refObj = beanMap.get(property.attributeValue("ref"));

    if (refObj == null) {

     // 如果被参考的bean为空,抛出异常

     throw new MyFrameworkException("找不到id"

       + property.attributeValue("ref") + "Bean,请检查配置文件");

    }

    // 开始反射,使用了直接设置类中的私有字段的暴力反射方式(还可以使用javaBeansetXxx方法设置值)

    Field field = null;

    try {

     field = beanObj.getClass().getDeclaredField(

       property.attributeValue("name"));

     field.setAccessible(true);

     field.set(beanObj, refObj);

    } catch (Exception e) {

     e.printStackTrace();

    }

   }

  }

 }

 

 //检查bean元素的格式是否正确

 private void checkBeanData(Element bean) {

  Attribute classAttr = bean.attribute("class");

  Attribute idAttr = bean.attribute("id");

  if (classAttr == null) {

   try {

    throw new MyFrameworkException("节点bean的属性class不能为空");

   } catch (MyFrameworkException e) {

    e.printStackTrace();

   }

  }

  if (idAttr == null) {

   try {

    throw new MyFrameworkException("节点bean的属性id不能为空");

   } catch (MyFrameworkException e) {

    e.printStackTrace();

   }

  }

 }

 

 //拿到bean元素下的property元素

 private Element getProperty(Element bean) {

  Element property = bean.element("property");

  if (property == null)

   return null;

 

  //如果属性name为空抛出异常

  if (property.attributeValue("name") == null) {

   try {

    throw new MyFrameworkException("节点Propertyname属性不能为空");

   } catch (MyFrameworkException e) {

    e.printStackTrace();

   }

  }

  // 如果属性ref为空抛出异常

  if (property.attributeValue("ref") == null) {

   try {

    throw new MyFrameworkException("节点Propertyref属性不能为空");

   } catch (MyFrameworkException e) {

    e.printStackTrace();

   }

  }

  return property;

 }

 

 //通过bean的名字,获得一个对象

 private Object getBeanObject(String name) {

  Object obj = null;

  try {

   obj = (Object) Class.forName(name).newInstance();

  } catch (InstantiationException e) {

   e.printStackTrace();

  } catch (IllegalAccessException e) {

   e.printStackTrace();

  } catch (ClassNotFoundException e) {

   e.printStackTrace();

  }

  return obj;

 }

 

 //得到xml配置文件的根节点

 private Element getRootElement() {

  SAXReader reader = new SAXReader();

  Element root = null;

  try {

   root = reader.read(

     this.getClass().getClassLoader()

       .getResourceAsStream("beans.xml")).getRootElement();

  } catch (DocumentException e) {

   System.out.println("读取beans.xml失败,你确定把它放在了ClassPath下了吗?");

  }

  return root;

 }

 

 //重新读取

 public void reload() {

  try {

   init();

  } catch (MyFrameworkException e) {

   e.printStackTrace();

  }

 

 }

}

 

在配置文件值使用到的两个测试类还有一个接口如下:

定义MyTest这个接口

package com.itheima;

publicinterfaceMyTest{
 void test();
}

写一个MyTest接口的实现MyTestImpl

package com.itheima;

publicclassMyTestImplimplementsMyTest{
 @Override
 publicvoid test(){
  System.out.println("注入成功");
 }
}

然后写配置文件中的被注入的Test类,也是main方法所在的类

package com.itheima;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

publicclassTest{
 
 privateMyTest myTest;
 
 publicstaticvoid main(String[] args)throwsException{ 
  BeanFactory bf=BeanFactory.getBeanFactory();
  Test test=(Test)bf.getBean("test");
  test.getMyTest().test();
 }

 publicMyTest getMyTest(){
  return myTest;
 }

 publicvoid setMyTest(MyTest myTest){
  this.myTest= myTest;
 }
}

若main方法中这句代码执行后test.getMyTest().test();打印出了注入成功,这句话则说明注入成功了

打印结果是:

注入成功

spring注入功能就完成了。。。。

 

这个程序中最主要的逻辑是这端代码

// 开始反射,使用了直接设置类中的私有字段的暴力反射方式(还可以使用javaBeansetXxx方法设置值)

    Field field = null;

    try {

     field = beanObj.getClass().getDeclaredField(

       property.attributeValue("name"));

     field.setAccessible(true);

     field.set(beanObj, refObj);

    } catch (Exception e) {

     e.printStackTrace();

    }

使用的是暴力反射的方式进行注入的,除此之外还有一种方式也可以做到。通过反射的方式拿到,符合JavaBean标准的类(拥有一系类gets和sets方法)中的私有字段,有两种方式,一种为暴力反射,一种是使用,Java api对JavaBean操作的支持

通过代码,描述一下,现在有一个了目标类,获得他的私有字段x

package com.itheima;
 publicclassTarget{
      privateint x;
      publicint getX(){
         return x;
      }

     publicvoid setX(int x){
         this.x= x;
      }       
}

通过反射拿到Target的x值

 

public static void main(String[] args) throws Exception {               
                Target t = new Target();
                t.setX(5);                
                //
通过暴力反射方式
               
Field f = Target.class.getDeclaredField("x");
                f.setAccessible(true);
                f.get(t);
                System.out.println(f.get(t));
                //通过JavaBean
                PropertyDescriptor pd = new PropertyDescriptor("x", Target.class);
                Method method = pd.getReadMethod();               
                System.out.println(method.invoke(t, null));
        }

 

结果成功获得了x的值(都打印了5),使用PropertyDescriptor 这个类的原理,其实就是根据传进来的x,拼出getX这个方法,并通过反射进行调用。通常我们都使用符合JavaBean标准这种,因为使用暴力反射会破坏程序的封装性,像一些框架ssh只有很少数的情况下,才会使用暴力反射,比如Hibernate只有注解写在私有字段上时才会使用这种方式。

---------- android培训java培训、期待与您交流! ---------- 
详细请查看:
http://edu.csdn.net/

你可能感兴趣的:(spring,bean,exception,object,null,Class)