黑马程序员--反射应用和动态代理
2015-12-22 21:13
666 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
第二讲 反射应用和动态代理
一、 通过配置文件运行类中的方法
这个应用有助于我们理解反射的作用。应用场景是这样的,假设我们有三个项目分别为Student、Teacher、Worker,另外还有一个测试类Test用来测试这三个项目。
我们在学习反射之前的做法是这样的,这种编码称为“硬编码”,即把代码写死了。
这样的做法太麻烦,测试代码需要不断的修改。有了反射之后,就能很好的解决这个问题,一般需要和配置文件配合使用。在这里我们用class.txt来代替配置文件,配置文件一般由键值对组成,并且你需要知道有两个键className和methodName22
反射的解决方案如下。
二、 通过反射绕过泛型检查
在学习反射之前,想给指定泛型的集合添加其他类型的数据是做不到的。比如有个集合是ArrayList<Integer> array=new ArrayList<Integer>( ),想实现array.add(“hello”)是无法实现的。但是学习了反射以后,这个功能就可以实现了。通过查看add的源码我们发现,泛型默认的类型是Object,只不过在JDK5以后为了数据的安全加入了泛型机制,但是这个机制仅仅是给编译期看的,真正运行时依然是Object。通过反编译工具查看代码也可以发现,在反编译器中,泛型被去掉了,由此可见泛型检查在运行期是不存在的。
所以如果我们能拿到集合的源码就可以实现如题的功能了。而反射恰好就可以拿到类的源码。反射的解决方案如下。
三、 写一个给类中私有成员变量赋值
public void setProperty(Object obj, String propertyName value )
这个方法声明的意思是给obj中的成员变量propertyName赋值为value。这里主要指给私有成员变量赋值,因为除此之外的成员变量可以通过创建对象对其赋值。
四、 动态代理
动态代理的应用场景是这样的,假设有一个借口UserDao,他有四个抽象方法add,delete,update,find。另外有一个UserDaoDemo类实现这个借口,实现代码如下
但是真实的应用场景往往不是这样的。写代码要符合他的开闭原则,即对扩展开放,对修改关闭。真实的应用场景是在任何操作前都要检查用户权限,在操作后要生成操作日志。所以以上代码改进为如下代码。
但是假如还有别的类也要实现这样的功能,那么也要在具体实现类中分别加上检查用户权限和生成操作日志,很显然这样的代码不够灵活,这样的实现不好。那么我们可不可以找一个“中介”来给我们实现添加检查用户权限和生成操作日志功能呢。这个“中介”即为动态代理
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。具体用法如下。
第二讲 反射应用和动态代理
一、 通过配置文件运行类中的方法
这个应用有助于我们理解反射的作用。应用场景是这样的,假设我们有三个项目分别为Student、Teacher、Worker,另外还有一个测试类Test用来测试这三个项目。
//学生类 package cn.itcast_02; public class Student { public void show(){ System.out.println("我是学生,我爱学习"); } }<textarea readonly="readonly" name="code" class="java"><br/>
//老师类 package cn.itcast_02; public class Teacher { public void show(){ System.out.println("我是老师,我爱教书"); } }<textarea readonly="readonly" name="code" class="java"><br/>
//工人类 package cn.itcast_02; public class Worker { public void show(){ System.out.println("我是工人,我爱工作"); } }<textarea readonly="readonly" name="code" class="java"><br/>
我们在学习反射之前的做法是这样的,这种编码称为“硬编码”,即把代码写死了。
//测试类1 package cn.itcast_02; public class Test { public static void main(String[] args) { Student s=new Student(); s.show(); Teacher t=new Teacher(); t.show(); Worker w=new Worker(); w.show(); } }<textarea readonly="readonly" name="code" class="java"><br/>
这样的做法太麻烦,测试代码需要不断的修改。有了反射之后,就能很好的解决这个问题,一般需要和配置文件配合使用。在这里我们用class.txt来代替配置文件,配置文件一般由键值对组成,并且你需要知道有两个键className和methodName22
反射的解决方案如下。
className=??? methodName???<textarea readonly="readonly" name="code" class="java"><br/>
//测试类2 package cn.itcast_02; import java.io.FileReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties; public class Test2 { public static void main(String[] args) throws Exception { // Properties加载文件中的键值对 Properties prop = new Properties(); FileReader fr = new FileReader("class.txt"); prop.load(fr); fr.close(); // 获取数据 String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); // 反射 Class c = Class.forName(className); Constructor con = c.getConstructor(); Object obj = con.newInstance(); //调用方法 Method m = c.getMethod(methodName); m.invoke(obj); } }<textarea readonly="readonly" name="code" class="java"><br/>
二、 通过反射绕过泛型检查
在学习反射之前,想给指定泛型的集合添加其他类型的数据是做不到的。比如有个集合是ArrayList<Integer> array=new ArrayList<Integer>( ),想实现array.add(“hello”)是无法实现的。但是学习了反射以后,这个功能就可以实现了。通过查看add的源码我们发现,泛型默认的类型是Object,只不过在JDK5以后为了数据的安全加入了泛型机制,但是这个机制仅仅是给编译期看的,真正运行时依然是Object。通过反编译工具查看代码也可以发现,在反编译器中,泛型被去掉了,由此可见泛型检查在运行期是不存在的。
所以如果我们能拿到集合的源码就可以实现如题的功能了。而反射恰好就可以拿到类的源码。反射的解决方案如下。
//代码实现 package cn.itcast_02; import java.lang.reflect.Method; import java.util.ArrayList; public class ArrayDemo { public static void main(String[] args) throws Exception { ArrayList<integer> array = new ArrayList<integer>(); Class c = array.getClass(); Method m = c.getMethod("add", Object.class); m.invoke(array, "hello"); m.invoke(array, "world"); m.invoke(array, "java"); System.out.println(array); } }<textarea readonly="readonly" name="code" class="java"><br/>
三、 写一个给类中私有成员变量赋值
public void setProperty(Object obj, String propertyName value )
这个方法声明的意思是给obj中的成员变量propertyName赋值为value。这里主要指给私有成员变量赋值,因为除此之外的成员变量可以通过创建对象对其赋值。
//学生类 package cn.itcast_03; public class Student1 { private String name; public int age; @Override public String toString() { return "Student1 [name=" + name + ", age=" + age + "]"; } } <textarea readonly="readonly" name="code" class="java"><br/>
//为成员变量赋值的方法 package cn.itcast_03; import java.lang.reflect.Field; public class Tool { public void setProperty(Object obj, String propertyName, Object value) throws Exception { //获取obj的字节码文件对象 Class c = obj.getClass(); //获取成员变量propertyName Field field = c.getDeclaredField(propertyName); //取消访问检查 field.setAccessible(true); //赋值 field.set(obj, value); } }<textarea readonly="readonly" name="code" class="java"><br/>
//测试类 package cn.itcast_03; public class ReflectDemo1 { public static void main(String[] args) throws Exception { // 给私有成员变量赋值 Student1 s = new Student1(); Tool t = new Tool(); t.setProperty(s, "name", "刘亦菲"); System.out.println(s); } }<textarea readonly="readonly" name="code" class="java"><br/>
四、 动态代理
动态代理的应用场景是这样的,假设有一个借口UserDao,他有四个抽象方法add,delete,update,find。另外有一个UserDaoDemo类实现这个借口,实现代码如下
//用户操作接口 package cn.it 4000 cast_04; public interface UserDao { public abstract void add(); public abstract void delete(); public abstract void update(); public abstract void find(); }<textarea readonly="readonly" name="code" class="java"><br/>
//用户操作实现类1 package cn.itcast_04; public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("增加"); } @Override public void delete() { System.out.println("删除"); } @Override public void update() { System.out.println("修改"); } @Override public void find() { System.out.println("查找"); } }<textarea readonly="readonly" name="code" class="java"><br/>
但是真实的应用场景往往不是这样的。写代码要符合他的开闭原则,即对扩展开放,对修改关闭。真实的应用场景是在任何操作前都要检查用户权限,在操作后要生成操作日志。所以以上代码改进为如下代码。
//用户操作实现类2 package cn.itcast_04; public class UserDaoImpl2 implements UserDao { @Override public void add() { System.out.println("检查用户权限"); System.out.println("增加"); System.out.println("生成操作日志"); } @Override public void delete() { System.out.println("检查用户权限"); System.out.println("删除"); System.out.println("生成操作日志"); } @Override public void update() { System.out.println("检查用户权限"); System.out.println("修改"); System.out.println("生成操作日志"); } @Override public void find() { System.out.println("检查用户权限"); System.out.println("查找"); System.out.println("生成操作日志"); } }<textarea readonly="readonly" name="code" class="java"><br/>
但是假如还有别的类也要实现这样的功能,那么也要在具体实现类中分别加上检查用户权限和生成操作日志,很显然这样的代码不够灵活,这样的实现不好。那么我们可不可以找一个“中介”来给我们实现添加检查用户权限和生成操作日志功能呢。这个“中介”即为动态代理
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。具体用法如下。
//用户操作接口 package cn.itcast_05; public interface UserDao { public abstract void add(); public abstract void delete(); public abstract void update(); public abstract void find(); }<textarea readonly="readonly" name="code" class="java"><br/>
//用户操作实现类3 package cn.itcast_05; public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("增加"); } @Override public void delete() { System.out.println("删除"); } @Override public void update() { System.out.println("修改"); } @Override public void find() { System.out.println("查找"); } }<textarea readonly="readonly" name="code" class="java"><br/>
package cn.itcast_05; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationhandler implements InvocationHandler { private Object target; public MyInvocationhandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("检查用户权限"); Object result = method.invoke(target, args); System.out.println("生成操作日志"); return result; } }<textarea readonly="readonly" name="code" class="java"><br/>
//代理测试类 package cn.itcast_05; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { UserDao ud = new UserDaoImpl(); ud.add(); ud.delete(); ud.update(); ud.find(); System.out.println("----------------"); // 创建动态代理对象 // 准备对ud对象做代理对象 MyInvocationhandler halder = new MyInvocationhandler(ud); UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass() .getClassLoader(), ud.getClass().getInterfaces(), halder); proxy.add(); proxy.delete(); proxy.update(); proxy.find(); } }<textarea readonly="readonly" name="code" class="java"><br/>
相关文章推荐
- 2015年9-10月互联网秋招经典面试题目汇总
- Java多线程、并发基础面试知识汇总
- Google多线程面试题: 4个线程向4个文件里写入数据, 每个线程只能写一个值(待更新)
- 高效程序员的7个共同特征
- 分享:HTML常见面试题
- 分享 :CSS常见面试题
- Java常见面试题
- Java EE面试笔试
- Java就业面试题大全<二>
- Java就业面试题大全<一>
- 程序员遇到BUG的解释
- 程序员 SEO优化
- [职场]工作多久才能换工作?下一个工作年薪该多高?
- 面试 阿里巴巴
- iOS面试题汇总(一)
- 普通程序员通向架构师的最佳路径?
- 程序员必须知道的几个Git代码托管平台
- 普通程序员通向架构师的最佳路径?
- 程序员看中的浏览器
- 2014年最新前端开发面试题(题目列表+答案 完整版)