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)