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、容器、另一些注解原理都依赖反射实现。多了解一些反射的机制是非常有优点的。
非常多内容在内部都是有联系的,把它们都相互联系起来比較着学习和应用才会掌握的更好。
版权声明:本文博客原创文章,博客,未经同意,不得转载。
JAVA基金会 (三)反射 反思的深度分析的更多相关文章
- java基础(三):反射、反序列化破解单列模式和解决方式
单例模式指的是一个类只有一个对象,通过一些措施达到达到这个目的.但是反射和反序列化可以获得多个不同的对象. 先简单的认识一下单例模式 一:单例模式 通过私有构造器,声明一个该类的静态对象成员,提供一个 ...
- 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)
写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...
- 【JVM】深度分析Java的ClassLoader机制(源码级别)
原文:深度分析Java的ClassLoader机制(源码级别) 为了更好的理解类的加载机制,我们来深入研究一下ClassLoader和他的loadClass()方法. 源码分析 public abst ...
- 深度分析Java的枚举类型—-枚举的线程安全性及序列化问题
原文:深度分析Java的枚举类型--枚举的线程安全性及序列化问题 枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和clas ...
- java集合框架(1) hashMap 简单使用以及深度分析(转)
java.util 类 HashMap<K,V>java.lang.Object java.util.AbstractMap<K,V> java.util.Hash ...
- Java的三种代理模式&完整源码分析
Java的三种代理模式&完整源码分析 参考资料: 博客园-Java的三种代理模式 简书-JDK动态代理-超详细源码分析 [博客园-WeakCache缓存的实现机制](https://www.c ...
- java反射(三)--反射与操作类
一.反射与操作类 在反射机制的处理过程之中不仅仅只是一个实例化对象的处理操作,更多的情况下还有类的组成的操作,任何一个类的基本组成结构:父类(父接口),包,属性,方法(构造方法,普通方法)--获取类的 ...
- Java 序列化和反序列化(三)Serializable 源码分析 - 2
目录 Java 序列化和反序列化(三)Serializable 源码分析 - 2 1. ObjectStreamField 1.1 数据结构 1.2 构造函数 2. ObjectStreamClass ...
- 深度分析 Java 的 ClassLoader 机制(源码级别)
写在前面:Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中,JVM在加载类的时候,都是通过ClassLoa ...
随机推荐
- java Quartz定时器任务与Spring task定时的几种实现,
java Quartz定时器任务与Spring task定时的几种实现 基于java 的定时任务实现, Quartz 时间详细配置 请查阅 http://www.cnblogs.com/si ...
- Driver 初始化顺序
Linux系统使用两种方式去加载系统中的模块:动态和静态. 静态加载:将所有模块的程序编译到Linux内核中,由do_initcall函数加载 核心进程(/init/main.c)kernel_ini ...
- 关于Velocity加减法等四则运算的迷思
曾今有一个FreeMarker摆在我面前. 我没有好好珍惜, 遇到了Velocity我才想起失去的美好... 需求是把PC网页点击. 手机网页点击.App点击相加得到总点击量显示出来: $articl ...
- mysql sqlserver Oracle字符串连接
mysql 例mysql> select CONCAT('My', 'S', 'QL'); sqlserver select name+'aa' from student; oracle sel ...
- WPF换肤之七:异步
原文:WPF换肤之七:异步 在WinForm时代,相信大家都遇到过这种情形,如果在程序设计过程中遇到了耗时的操作,不使用异步会导致程序假死.当然,在WPF中,这种情况也是存在的,所以我们就需要寻找一种 ...
- WPF命中测试示例(一)——坐标点命中测试
原文:WPF命中测试示例(一)--坐标点命中测试 命中测试也可被称为碰撞测试,在WPF中使用VisualTreeHelper.HitTest()方法实现,该方法用于获取给定的一个坐标点或几何形状内存在 ...
- HDU 4126 Genghis Khan the Conqueror MST+树形dp
题意: 给定n个点m条边的无向图. 以下m行给出边和边权 以下Q个询问. Q行每行给出一条边(一定是m条边中的一条) 表示改动边权. (数据保证改动后的边权比原先的边权大) 问:改动后的最小生成树的权 ...
- Hadoop学习笔记(两)设置单节点集群
本文描写叙述怎样设置一个单一节点的 Hadoop 安装.以便您能够高速运行简单的操作,使用 Hadoop MapReduce 和 Hadoop 分布式文件系统 (HDFS). 參考官方文档:Hadoo ...
- Gulp实现服务器
Gulp实现web服务器 Gulp实现web服务器 阅读目录 一:gulp实现web服务器配置: 二:添加实时刷新(livereload)支持 回到顶部 一:gulp实现web服务器配置: 对于前端开 ...
- HBuilder之初体验
听闻HTML5定稿了,所以特意去了解了下.文章有提到HTML5的一款IDE(HBuilder,貌似出来好久了,孤陋寡闻....),于是来到官网http://dcloud.io/ ,被演示图震惊了!果然 ...