JAVA反射机制分析-------spring的通过反射创建bean实例对象
以及属性注⼊的原理解析
JAVA反射机制
java反射机制是在运⾏状态中,对于任意⼀个类, 能够知道这个类的所有属性和⽅法;对于任意⼀个对象,都能够调⽤他的任意⼀个⽅法和属性。这种动态获取的信息以及动态调⽤对象的⽅法的功能称为JAVA语⾔的反射机制。
巧妙的利⽤java中的反射机制,能够帮助我们进⾏程序开发时达到意想不到的效果,本⽚博⽂将对java中的反射机制进⾏分析以及针对spring中通过配置⽂件完成bean实例对象的创建原理进⾏分析。
⼀:java中获取类对象以及通过类对象获取实例化对象,并获取/修改类属性,⽅法。
⼆:通过读取⾃定义配置⽂件,完成对象的创建以及调⽤
三:模仿spring创建beanFactory的实现过程。
⼀:类对象
1. 什么是类对象,java的反射机制是什么意思?如何获取类对象?
java的反射机制,意思很简单,就是通过类对象获取类的信息。
那什么是类对象呢?每⼀个类型的类都有且仅有⼀个类对象Class,该类对象中记录了这个类包含了哪些属性、哪些⽅法、以及有哪些构造⽅法等信息。那通过类对象,就可以反向获取该类的构造⽅法,就可以完成实例化,也可以访问类中的⽅法和属性。完成赋值、调⽤⽅法等操作。那类对象如何获取呢?很简单,只需要知道他的类名即可。
获取类对象的三种⽅法:
①Class a = Class.forName(pojo.Hero);
②Class b = Hero.class;
③Class c = new Hero().getClass;
并且a=b=c,因为在同⼀个JVM中,或者说在⼀个ClassLoader中,Hero这个类,有且只有⼀个类对象。
注意,在获取类对象时,除了Hero.class这种⽅式,其他的会导致类的属性被初始化,即该类中的静态代码块执⾏或者静态属性被初始化,可以理解为加载类时完成的事情,并且该初始化过程只会被执⾏⼀次。例如在Hero这个类中,添加static静态代码块,控制台输出"初始化",则通过三种⽅式获取类对象,类中的代码块会被执⾏⼀次,并在控制台打印了"初始化"。
2. 通过类对象创建实例化对象、访问属性、访问⽅法。
㈠通过类对象创建实例化对象:直接看代码,⼀个类对象可以通过构造⽅法创建⽆数个实例化对象。
try {
//Class h = Hero.class;
//Class h = Class.forName("test.pojo.Hero");
Class h = new Hero().getClass(); //获取类对象
Constructor c = h.getConstructor(); //根据类对象获取默认构造器
Hero h1 = (wInstance();  //通过构造器调⽤构造⽅法完成实例化
h1.showName();  //通过实例化好的对象即可访问该类中的⽅法
} catch (Exception e) {
e.printStackTrace();
}
㈡通过类对象访问类属性,修改类属性值
getField和getDeclaredField的区别
这两个⽅法都是⽤于获取字段
getField 只能获取public的,包括从⽗类继承来的字段。
getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这⾥只能获取到private的字段,但并不能访问该private字段的值,除⾮加上setAccessible(true))
Hero h = new Hero();
h.setPrice("qq");
System.out.Price());
Field f = h.getClass().getDeclaredField("price"); //根据类对象获取该类的price属性
f.set(h, "tt"); //通过获取的属性,给h对象修改这个属性的值
System.out.Price());
㈢通过类对象访问类的⽅法,并执⾏该⽅法。
Hero h = new Hero();
Hero h1 = new Hero();
Method m = Class.forName("test.pojo.Hero").getMethod("setPrice", String.class);//通过类对象获取该类中的⽅法以及该⽅法中需要传⼊的参数对象
//如果没有参数的⽅法则不需要写第⼆个参数
System.out.Price());
m.invoke(h, "访问⽅法"); //通过m对象,访问h对象的setPrice,并传⼊参数值。此时便对h对象执⾏了该⽅法。
m.invoke(h1, "h1");
System.out.Price());
System.out.Price());
⾄此,已经完成了通过类对象调⽤构造⽅法、调⽤⽅法、修改属性值操作了。
接下来,将演⽰java的反射机制如何使⽤如何利⽤反射,以及在哪些场景中使⽤。
⼆:读取配置⽂件
1. 为什么要使⽤反射?如何利⽤反射机制
假设有⼀个场景,有两个不同的类根据业务需要可能需要随时调换,如果通过new的⽅式,则每次调换时需要修改代码。如果通过反射来完成,我们可以将类全名写在配置⽂件中,通过IO读取配置⽂件来决定实例化哪个对象。我们就不需要修改代码了,直接修改配置⽂件中的类名即可。这只是个⼩例⼦,JAVA的反射机制远⽐这要强⼤。下⾯通过读取配置⽂件的⽅式完成调⽤不同的类中的不同⽅法。
2. 编写配置⽂件,如下,配置⽂件中声明需要创建的类名称、需要调⽤的⽅法名、参数名、以及该参数的值。
通过读取该配置⽂件,完全利⽤反射来完成对象的创建和属性的注⼊。
①创建两个类⽂件,service、service1,在这两个类中分别写两个打印的⽅法,代表两种不同的业务,如果需要创建的是service1的话,则程序需要⾃动把读取到的name值注⼊到该对象的name属性中。
public class Service {
public void show(String s) {
System.out.println("this is show1"+s);
}
}
public class Service1 {
public String name;
public void show(String s) {
System.out.println("this is show2"+s);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
②在项⽬中创建配置⽂件⽂件:
class=example.pojo.Service1
method=show
property=name
name=zhangSan
③编写⼀个⽅法,⽤来读取配置⽂件,并创建对象。输出需要调⽤的⽅法。如果是service1的话,则返回⼀个对象,该对象中已经被注⼊好了name值。
public Object getObj() throws Exception {
File f = new File("F:\\project\\fansheORzhujie\\fanshe\\src\\example\\"); //读取配置⽂件
Properties springConfig = new Properties();
springConfig.load(new FileInputStream(f)); //加载配置⽂件
String className1 = (String) ("class"); //获取配置⽂件中class的值
String method1 = (String) ("method"); //获取配置⽂件中method的值
String pro = (String) ("property");  //获取配置⽂件中的属性名
String value = (String) ("name");  //获取配置⽂件中name的属性值
//根据读取的配置⽂件创建类对象
Class c1 = Class.forName(className1);
//根据类对象创建类实例
Constructor ct1 = c1.getConstructor();
//实例化类对象
Object o1 = wInstance();
//根据反射读取类对象的⽅法名,然后具体某个实例化对象执⾏⽅法
Method m1 = c1.getMethod(method1, String.class);
m1.invoke(o1, "我是类对象中的⽅法"); //给这个⽅法注⼊参数
//根据配置⽂件的创建,本例通过反射,读取配置⽂件中的信息,也完成创建对象(反转控制)和属性的依赖注⼊
//如果类对象时service1,则判断,获取到该类中的name属性,并将实例化的对象中的name值设置为配置⽂件中name的值。
if(c1 == Service1.class) {
Field f1 = c1.getDeclaredField(pro);
f1.set(o1, value);
}
return o1;
}
④编写测试类:
public static void main(String[] args) throws Exception {
Test t = new Test();
Object obj = t.getObj();
Class() == Service1.class) {
Service1 s = (Service1) obj;
System.out.Name());
}
}
⑤输出结果:
⑥这样通过配置⽂件的⽅式,如果此时需要切换创建的对象,则只需要修改配置⽂件中的class值,修改类名,或者需要调⽤别的⽅法,则修改⽅法名。从⽽达到通过配置⽂件调控实例化哪个类的对象。或者,通过配置⽂件实例化⼀个对象,并给该对象注⼊参数值的实现。
spring中的bean的创建就是通过java的反射机制来完成的,基本实现过程与本例类似,spring实现时,是通过不断的读取配置⽂件,将class值,属性名,属性值,实例化成⼀个⼀个的object对象,然后将这些对象放到⼀个map中,key值就是配置⽂件中的id值或者name值,也就是形成了所谓的IOC容器beanFactory,使⽤时,直接通过key值获取,然后强转成需要的类型即可直接使⽤spring帮我们创建好的实例化对象了。接下来我将仿照spring中beanFactory的实现过程进⾏分析。
三:模拟spring创建beanFactory的实现过程
1. 简介
通过spring的配置⽂件,配置bean,使⽤spring类⽂件实例化bean中配置的对象,在测试类中直接返回使⽤。本demo中包含⼀个配置⽂件:l、⼀个spring类⽂件:applicationContext.java、⼀个实体类:product.java、⼀个测试类:text.java。
2.创建实体类与配置⽂件l,放在src路径下,设置好bean的属性。
package beans;
public class Product {
public void show() {
System.out.println("我是实例化对象product");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="p" class="beans.Product">
</bean>
<!-- <bean id="h" class="beans.Hero">
实例化类和实例化对象
</bean> -->
</beans>
3. 接下来我们⾃⼰创建⼀个bean⼯⼚,⽤来⽣产实例化对象配置⽂件中配置好的bean,从⽽达到spring完成创建对象的⽬的。
①先读取到配置⽂件l,并创建⼀个存放bean的私有静态全局变量map集合。
②然后对该⽂件进⾏解析,获取到⽂件中的跟标签。也就是最外⾯的那层标签beans,需要导⼊dom4j-1.6.jar、xml-apis-1.0.b2.jar资源⽂件
③循环跟标签beans中的bean标签,将每⼀个bean中的属性值获取出来,根据class属性通过反射创建类对象并完成实例化,然后将这个对象放在map中,key值是读取到的id的值,value值是实例化好的object对象。配置⽂件中有多少bean标签就会循环创建多少个对象。
④重写该类的构造⽅法,并传⼊参数,参数值为配置⽂件的路径⽂件名。
⑤编写getBean⽅法,传⼊参数值,参数为bean中的id,从⽽达到通过调⽤该⽅法,传⼊配置的id值,获取到bean⼯⼚中的实例化对象。此⽅法也是提供给外部访问map的唯⼀⼊⼝。
请看代码:
package spring;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* spring的配置⼯⼚类⽂件
* @author lemon
*
*/
public class ApplicationContext {
private static Map<String, Object> beans = new HashMap<String, Object>();
public ApplicationContext(String file) {
buildBeanFactory(file);
}
public void buildBeanFactory(String file) {
//读取配置⽂件
InputStream in = ClassLoader().//类的加载容器,把类的实例加载到内存中
getResourceAsStream(file);//这个⽅法直接从src下读取该⽂件
//解析⽂件
//创建解析器对象
SAXReader saxReader = new SAXReader();
Document dom = null;
try {
dom = ad(in);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("解析⽂件时抛出异常");
}
//解析完了该⽂件,接下来需要拿到根元素(bean元素)
Element root = RootElement();
//获取根元素所有的bean元素,并存在放list中,并且将所有的bean标签循环读取,把每⼀个bean都创建成⼀个实例化对象
//然后存放在map中,此时,即完成了spring创建bean的过程。
List<Element> list = root.elements("bean");
for (Element e : list) {
String className = e.attributeValue("class");
String idName = e.attributeValue("id");
Object obj = null;
try {
obj = Class.forName(className).newInstance();
} catch (Exception e1) {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。