JAVA基金会 (三)反射 反思的深度分析

      上一页已经推出反映的一些基本概念,这主要是通过一个例子反映谈的过程,以及样品的实际应用。

这个样例是这种设计思路:从一个属性文件里读取一段字符串,然后,依据该字符串生成相应的类实例对象;这之后另一个增强版的样例,能够依据类里面的setter()方法将类的成员变量(引用类型)也进行初始化,Spring框架是这么实现的。

项目结构例如以下:


本样例包含三个类

1.reflect.properties属性文件,里面为key-value键值对。例如以下

name=javax.swing.JFrame
useraction=com.tgb.reflect.UserAction

2.UserAction.java。Action类

<span style="font-size:14px;">public class UserAction {

		public void addUser()
		{
			System.out.println("加入用户");
		}
}</span>

3.ObjectPoolFactory.java,该类负责读取文件,实例化对象

<span style="font-size:14px;">public class ObjectPoolFactory {

		//定义一个对象池,採用key-value key为对象名、value为对象
		private Map<String, Object> objectPool=new HashMap<>();
		
		//定义一个创建对象的方法。參数为字符串 类名
		private Object createObject(String clazzName)
			throws InstantiationException,IllegalAccessException,ClassNotFoundException
		{
			//依据字符串来获取相应的class对象
			Class<?

> clazz=Class.forName(clazzName); return clazz.newInstance(); } //从属性文件里读取key-value初始化类的实例,也能够利用dom4j从配置文件里读取 public void initPool(String fileName) throws InstantiationException,IllegalAccessException,ClassNotFoundException { try(FileInputStream fis=new FileInputStream(fileName)) { Properties pros=new Properties(); //从输入流载入属性文件 pros.load(fis); //循环属性文件里的key for(String name:pros.stringPropertyNames()) { //取出key-value,依据value创建对象,并放入对象池中 objectPool.put(name, createObject(pros.getProperty(name))); } } catch (Exception e) { e.printStackTrace(); } } public Object getObject(String name) { return objectPool.get(name); } public void test() throws InstantiationException, IllegalAccessException, ClassNotFoundException { String path=this.getClass().getResource("/com/tgb/reflect/reflect.properties").toString(); path=path.substring(path.indexOf("/")+1); System.out.println(path); ObjectPoolFactory opf=new ObjectPoolFactory(); opf.initPool(path); UserAction userAction=(UserAction)opf.getObject("useraction"); userAction.addUser(); } public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { ObjectPoolFactory opf=new ObjectPoolFactory(); opf.test(); }</span>

      此类中。主要是createObject()这种方法创建实例对象。然后又调用Class类的forName()方法,该方法返回的是一个类的Class对象。再利用Class对象的newInstance()返回它所代表的类的实例。

      我们用一个map对象来存储一个已经创建好的对象,作为对象池使用。Class<?>这里使用了类型通配符。代表的意思是Class对象的类型,这次Class对象未知因此使用了类型通配符,这里事实上使用类型參数也是能够的,如Class<T>,至于类型參数与类型通配符差别以后会介绍。


那么跟类载入器有什么关系呢?

      我们能够看一下JDK源代码。forName()这种方法重载了两个,有一个是须要提供类载入器这个參数的,如

<span style="font-size:14px;">    @CallerSensitive
    public static Class<?

> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { if (loader == null) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader ccl = ClassLoader.getClassLoader(Reflection.getCallerClass()); if (ccl != null) { sm.checkPermission( SecurityConstants.GET_CLASSLOADER_PERMISSION); } } } return forName0(name, initialize, loader); }</span>

      这种方法是forName()參数最多。重载之前的方法,它为我们提供了默认的类载入器,里面还有关于一些安全方面的处理推断。
执行结果例如以下:


我们能够看到上面程序UserAction的addUser()已经运行,表名创建实例成功。

      接下来我们完好该方法,实现对UserAction里面的一个引用属性赋值,改动UserAction例如以下添加了一个setter()方法,这里你也就知道setter()方法的作用,Spring进行诸如时就是依据setter()来给依赖属性注入的。

UserAction.java

<span style="font-size:14px;">public class UserAction {
		//依赖属性
		private UserManager userManager;
		
		public void addUser()
		{
			System.out.println("运行UserAction的addUser()方法");
		}
		//属性的set方法
		public void setUserManager(UserManager userManager) {
			this.userManager = userManager;
		}
}</span>
在工厂类里面主要多了一个初始化属性的方法。其余有略微修改但基本类似,例如以下

<span style="font-size:14px;">//初始化类的属性。也能够利用dom4j从配置文件里读取
		public void initProperty()
				throws InstantiationException,IllegalAccessException,ClassNotFoundException
		{
			try
			{
				//循环属性文件里的key
				for(String name:pros.stringPropertyNames())
				{
					if (name.contains("%")) {
						String[] namesArray=name.split("%");
						Object target=getObject(namesArray[0]);
						
						Class<?

> targetClass=target.getClass(); String mName="set"+namesArray[1].substring(0,1).toUpperCase()+namesArray[1].substring(1); //得到目标对象userManager属性的set方法 //第一个參数为方法名、第二个为set方法中传入的參数类型,即UserManager.class=userManager.getClass() Method m=targetClass.getMethod(mName,getObject(name).getClass()); //调用set方法给属性赋值 m.invoke(target,getObject(name)); } } } catch (Exception e) { e.printStackTrace(); } }</span>


      通过看凝视大家也能够理解,和上面类似这里仅仅只是是找到属性相应的实例。然后,通过set方法把这个实例给属性赋值的。

属性文件改为了例如以下变量值:

name=javax.swing.JFrame
useraction=com.tgb.reflect.UserAction
useraction%userManager=com.tgb.reflect.UserManager

      第三句用一个%切割,前面代表类后面代表该类的属性。该属性主要用于拼接set方法名。由于得到set方法时须要用到这个作为參数。



      至此,反射的基本内容就介绍完了,相信大家通过上面的一些概念和简单的实例已经理解了反射的原理是怎么实现的,非常多框架也是利用这一个过程通过xml配置文件来实例化各种类,所不同的是框架对于xml里面的标签已经作为限制,道理和读取属性文件是一样的,xml文件包括的信息会更丰富一些,这样在解析xml和实例化对象时也会更复杂一些。通过配置文件来处理类之间的各种关系。

      我们通常接触到的AOP、IOC、容器、另一些注解原理都依赖反射实现。多了解一些反射的机制是非常有优点的。

非常多内容在内部都是有联系的,把它们都相互联系起来比較着学习和应用才会掌握的更好。



版权声明:本文博客原创文章,博客,未经同意,不得转载。

posted @ 2015-07-19 20:27  blfshiye  阅读(212)  评论(0编辑  收藏  举报