深入解析反射机制 (二)

上一篇中已经介绍了一些关于反射的基本概念,这篇主要通过一个实例说一说反射的过程,以及实际中应用的例子。

这个例子是这样的设计思路:从一个属性文件中读取一段字符串,然后,根据该字符串生成对应的类实例对象;这之后还有一个增强版的例子,可以根据类里面的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、容器、还有一些注解原理都依赖反射实现,多了解一些反射的机制是很有好处的。很多内容在内部都是有联系的,把它们都相互联系起来比较着学习和应用才会掌握的更好。

时间: 2024-10-13 17:21:02

深入解析反射机制 (二)的相关文章

浅说Java中的反射机制(二)

写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编译执行java文件时,会生成一个.class文件,反射就是一个反编译的过程,它可以通过.class文件得到一个java对象.一个类会有很多组成部分,比如成员变量.成员方法.构造方法等,反射可以通过加载类(加载类是个什么东西?一直搞不清楚),解剖出类的各个组成部分. 为什么要用反射? 我们需要访问一个

深入解析反射机制 (一)

在谈论到反射这个问题时,你是否有如下疑问? 无论是在.NET还是Java中反射的原理和机制是一样的,理解了一种另一种就可以迎刃而解,想要理解反射首先需要了解底层的一些概念和运行,理解了反射有助于你理解程序的运行原理,目前很多框架(java..NET)中都引入了反射这一个技术,反射其实也不是什么新的技术只是几个不同的操作过程集成到一起关联起来了. 从表面上我们看到的效果是这样的:通过传入一个字符串可以得到某个类的对象,在这一个过程中做了很多事情.你是否有下面的一些疑问存在?       JDK.J

java的反射机制原理

一  反射机制的概念: 指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法.这种动态获取信息,以及动态调用对象方法的功能叫java语言的反射机制. 二  反射机制的应用: 生成动态代理,面向切片编程(在调用方法的前后各加栈帧). 三  反射机制的原理: 1  首先明确的概念: 一切皆对象----类也是对象. 2  然后知道类中的内容 :modifier  constructor  field  method. 3  其次明白加载: 当An

java基础篇---反射机制

一.JAVA是动态语言吗? 一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,JAVA和C++一样,都不是动态语言. 但JAVA它却有着一个非常突出的动态相关机制:反射.通过反射,Java可以于运行时加载.探知和使用编译期间完全求和的类.生成其对象实体,调用其方法或者对属性设值.所以Java算是一个半动态的语言吧. 反射的概念: 在Java中的反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方

Java反射机制分析指南

一.JAVA是动态语言吗? 一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,JAVA和C++一样,都不是动态语言. 但JAVA它却有着一个非常突出的动态相关机制:反射.通过反射,Java可以于运行时加载.探知和使用编译期间完全求和的类.生成其对象实体,调用其方法或者对属性设值.所以Java算是一个半动态的语言吧. 反射的概念: 在Java中的反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方

Java 反射机制分析指南

一.JAVA是动态语言吗? 一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,JAVA和C++一样,都不是动态语言. 但JAVA它却有着一个非常突出的动态相关机制:反射.通过反射,Java可以于运行时加载.探知和使用编译期间完全求和的类.生成其对象实体,调用其方法或者对属性设值.所以Java算是一个半动态的语言吧. 反射的概念: 在Java中的反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方

JAVA的反射机制学习笔记(二)

上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了,自己的步伐完全被打乱了~不能继续被动下去,得重新找到自己的节奏. 4.获取类的Constructor 通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例 Class<T>类提供了几个方法获取类的构造器. public Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反

XStream解析XML文本并用反射机制转换为对象

xml文本格式是网络通信中最常用的格式,最近特别研究了一下如何解析xml文本并转换为对象,现在分享一下我最近的学习成果~ 先列一下本例中需要解析的xml文本: Xml代码   <results name="list"> <row pubtime="2016-04-13 16:40:13" author="APP"  id="140" title="什么是公告" content="

第十二章 类加载器和反射机制

12 类加载器和反射机制 12.1 类加载器 负责将.class文件加载到内存中,并为之生成对应的Class对象. 1.类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载.连接.初始化三个步骤来实现对这个类的初始化. 加载 就是指将calss文件读入到内存,并为之穿件一个Class对象. 任何类被使用时系统都会建立一个Class对象. 连接 验证    是否有正确的内部结构,并和其他类协调一致 准备    负责为类的静态成员分配内存,并设置默认初始化值 解析    将类