java反射机制

java反射机制

java语言允许通过程序化的方式间接对Class的对象实例操作,Class文件由类加载器装载后,在JVM中将形成一份描述class结构的元信息对象,通过该元信息对象可以获知class的结构信息,通过该源信息对象可以获知class的结构信息,如构造函数、属性和方法等

举一个反射的小例子:

car类主要包含三个属性,和一个无参构造函数与一个带参构造函数

public class Car {
  private String brand;
  private String color;
  private int maxspeed;

  
  public Car() {
    System.out.println("init car");
  }

  public Car(String brand, String color, int maxspeed) {
    this.brand = brand;
    this.color = color;
    this.maxspeed = maxspeed;
  }
  
  public void introduce(){
    System.out.println("品牌:"+brand+"颜色:"+color+"最大速度:"+maxspeed);
  }
  public String getBrand() {
    return brand;
  }

  public void setBrand(String brand) {
    this.brand = brand;
  }

  public String getColor() {
    return color;
  }

  public void setColor(String color) {
    this.color = color;
  }

  public int getMaxspeed() {
    return maxspeed;
  }

  public void setMaxspeed(int maxspeed) {
    this.maxspeed = maxspeed;
  }
}

这里利用反射获取构造器实例化对象

public class ReflectTest {
  //通过默认构造方法初始化car对象
  public static Car initDefaultConst() throws Throwable{
    //1.通过类装载器获取Car类对象
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    Class clazz =loader.loadClass("com.xiongda.spring.reflect.Car");
    
    //2.获取类的默认构造器对象并实例化Car
    Constructor cons =clazz.getDeclaredConstructor(null);
    Car car =(Car) cons.newInstance();
    
    //3.通过反射方法设置属性
    Method setBrand =clazz.getMethod("setBrand", String.class);
    setBrand.invoke(car, "奔驰");
    Method setColor =clazz.getMethod("setColor", String.class);
    setColor.invoke(car, "黑色");
    Method setMaxspeed =clazz.getMethod("setMaxspeed", int.class);
    setMaxspeed.invoke(car, 200);
    
    return car;
  }
  //通过带参构造方法初始化car对象
  public static Car initParamConst() throws Throwable{
  //1.通过类装载器获取Car类对象
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    Class clazz =loader.loadClass("com.xiongda.spring.reflect.Car");
    
    //2.获取类的带参构造器对象并实例化Car
    Constructor cons =clazz.getDeclaredConstructor(String.class,String.class,int.class);
    Car car =(Car) cons.newInstance("宝马","红色",180);
    
    return car;
  }
  
  
  public static void main(String[] args) throws Throwable {
    Car car1 =ReflectTest.initDefaultConst();
    Car car2 =ReflectTest.initParamConst();
    car1.introduce();
    car2.introduce();
  }
}

 

在传统方法中,我们一般是使用构造函数设置属性或者set方法设置属性

 

类装载器ClassLoader
类装载器就是寻找类的字节码文件并构造出类在JVM内部表示的对象组件,主要工作由ClassLoader及其子类负责,ClassLoader是一个重要的java运行时系统组件,它负责在运行时查找和装入class字节码文件

工作机制:
1.装载:查找和导入class文件
2.链接:执行校验,准备和解析步骤
3.初始化:对类的静态变量、静态代码块执行初始化工作

重要方法:
1.Class loadClass(String name)
2.Class defineClass(String name,byte[] b, int off, int len)
3.Class findSystemClass(String name)
4.Class findLoadedClass(String name)
5.ClassLoader getParent()

 

Class反射对象描述类语义结构,可以从Class对象中获取构造函数,成员变量,方法等类元素的反射对象,并以编程的方式通过这些反射对象对目标对象进行操作。这些反射对象类在java.reflect包中定义

 

三个最主要的反射类:
Constructor
Method
Field

 

在Spring中,通过IOC可以将实现类、参数信息等配置在其对应的配置文件中,那么当需要更改实现类或参数信息时,,只需要修改配置文件即可,我们还可以对其对象所需要的其它对象进行注入,这种注入都是在配置文件中做的

 

Spring的IOC的实现原理利用的就是java的反射机制,Spring的工厂类会帮我们完成配置文件的读取、利用反射机制注入对象等工作,我们可以通过bean的名称获取对应的对象

基本的javabean

public class JavaBean {
  private String username;
  private String password;
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  
}

读取配置文件,然后通过Java反射机制实例化javabean

public class BeanFactory {
  private Map<String, Object> beanMap = new HashMap<>();
  
  /**
   * bean工厂的初始化
   * @param xml
   */
  public void init(String sxml){
    InputStream ins= null;
    Document doc = null;
      try {
        //1.创建读取配置文件的reader对象
        SAXReader reader =new SAXReader();
        //2.获取当前线程中的类装载器对象
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //3.从class目录下获取指定的xml文件
         ins = classLoader.getResourceAsStream(sxml);
         if(ins == null){
           System.out.println("获取流失败");
         }
         doc = reader.read(ins);
        Element root = doc.getRootElement();
        Element foo;
        
        //4.遍历xml文件中的Bean实例
        for(Iterator i =root.elementIterator("bean");i.hasNext();){
          foo=(Element) i.next();
          
          //5.针对每一个bean实例,获取bean的属性id和class
          Attribute id =foo.attribute("id");
          Attribute cls=foo.attribute("class");
          
          
          //6.利用java反射机制,通过class的名称获取class对象
          Class bean = Class.forName(cls.getText());
          
          //7.获取对应class的信息
          java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
          
          //8.获取其属性描述
          java.beans.PropertyDescriptor pd[] =info.getPropertyDescriptors();
          
          //9.创建一个对象,并在接下来的代码中为对象的属性赋值
          Object obj = bean.newInstance();
          
          //10.遍历该bean的property属性
          for(Iterator ite = foo.elementIterator("property");ite.hasNext();){
            Element foo2 = (Element) ite.next();
            
            //11.获取该property的name属性
            Attribute name = foo2.attribute("name");
            String value = null;
            
            //12.获取该property的子元素value的值
            for(Iterator ite1 =foo2.elementIterator("value");ite1.hasNext();){
              Element node = (Element) ite1.next();
              value = node.getText();
             
              break;
            }
            
            //13.利用java的反射机制调用对象的某个set方法,并将值设置进去
            for(int k=0;k<pd.length;k++){
              if(pd[k].getName().equalsIgnoreCase(name.getText())){
                Method mset = null;
                mset = pd[k].getWriteMethod();
                mset.invoke(obj, value);
              }
            }
          }
          
          //14.将对象放入beanMap中,其中key为id值,value为对象
          beanMap.put(id.getText(), obj);
        }
      } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
          | InvocationTargetException | DocumentException | IntrospectionException e) {
        // TODO Auto-generated catch block
        if(doc ==null){
          System.out.println("获取失败");
        }
        e.printStackTrace();
      } 
  }
  
  public Object getBean(String beanName){
    Object obj =beanMap.get(beanName);
    return obj;
  }
  
  public static void main(String[] args) throws Throwable {
    BeanFactory factory = new BeanFactory();
    factory.init("config.xml");
    JavaBean  javabean =(JavaBean)factory.getBean("javaBean");
    System.out.println("username  "+javabean.getUsername());
    System.out.println("password  "+javabean.getPassword());
    
  }
}

xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="javaBean" class="com.xiongda.spring.reflect.JavaBean">
        <property name="username">
            <value>极客学院</value>
        </property>
        <property name="password">
            <value>1234567890</value>
        </property>
    </bean>
</beans>

注意:如果你是用eclipse的话,你这个xml文件一定要放在bin目录里面,否则会报出一个空指针异常。但是后来我发现,用IDEA的话就不存在这个问题。(解决这东西花了不少时间,至今还未明白为啥会存在这种区别)

(备注:使用SAXReader需要导包dom4j)

posted @ 2018-06-24 22:31  xtu熊大  阅读(286)  评论(0编辑  收藏  举报