【代码笔记】Java——远程监控、反射、代理、内省机制
- 远程控制的基本原理
- 远程控制(RemoteControl)拥有控制端和被控端双方。 控制方通过请求,取得对远端的操作,实现远端的事件回放功能,同时,应该看得到远端桌面的画面。而被控方必须在建立ServerSocket之后进行对外来请求的鉴听。
-
1 /** 2 * 客户端发送事件 3 * @param e 4 */ 5 public void sendEvent(InputEvent e){ 6 try { 7 SinglClient.getInstance().getOos().writeObject(e); 8 SinglClient.getInstance().getOos().flush(); 9 } catch (IOException e1) { 10 e1.printStackTrace(); 11 } 12 }
-
1 /** 2 * 客户端图片接收 3 * @throws Exception 4 */ 5 public void readImage() throws Exception{ 6 int length = SinglClient.getInstance().getOis().readInt(); 7 byte[] bytes = new byte[length]; 8 SinglClient.getInstance().getOis().readFully(bytes); 9 //转换为图片 10 ImageIcon image = new ImageIcon(bytes); 11 label.setIcon(image); 12 label.repaint(); 13 }
-
1 package com.edp.client; 2 3 import java.awt.Dimension; 4 import java.awt.Toolkit; 5 import java.awt.event.InputEvent; 6 import java.awt.event.KeyAdapter; 7 import java.awt.event.KeyEvent; 8 import java.awt.event.MouseAdapter; 9 import java.awt.event.MouseEvent; 10 import java.io.IOException; 11 12 import javax.swing.ImageIcon; 13 import javax.swing.JFrame; 14 import javax.swing.JLabel; 15 16 17 /** 18 * 监控端的屏幕展示界面 19 * @author minGe_000 20 * 21 */ 22 public class ControlFrame extends JFrame{ 23 private JLabel label; 24 public void initFrame() { 25 //建立和服务器的连接 26 SinglClient.getInstance().getConenction(); 27 //得到屏幕大小 28 Dimension dimen = Toolkit.getDefaultToolkit().getScreenSize(); 29 this.setSize(dimen); 30 this.setResizable(false); 31 this.setDefaultCloseOperation(3); 32 this.setLocationRelativeTo(null); 33 this.setUndecorated(true); 34 label = new JLabel(); 35 this.add(label); 36 this.setVisible(true); 37 new Thread(){ 38 public void run() { 39 try{ 40 while(true){ 41 readImage(); 42 Thread.sleep(100); 43 } 44 }catch(Exception e){ 45 e.printStackTrace(); 46 } 47 }; 48 }.start(); 49 //给窗体添加相应的监听 50 MouseAdapter adapter = new MouseAdapter() { 51 public void mouseMoved(MouseEvent e) { 52 super.mouseMoved(e); 53 //发送对象 54 sendEvent(e); 55 } 56 @Override 57 public void mouseClicked(MouseEvent e) { 58 super.mouseClicked(e); 59 //发送对象 60 sendEvent(e); 61 } 62 @Override 63 public void mouseReleased(MouseEvent e) { 64 super.mouseReleased(e); 65 //发送对象 66 sendEvent(e); 67 } 68 @Override 69 public void mousePressed(MouseEvent e) { 70 super.mousePressed(e); 71 //发送对象 72 sendEvent(e); 73 } 74 @Override 75 public void mouseDragged(MouseEvent e) { 76 super.mouseDragged(e); 77 //发送对象 78 sendEvent(e); 79 } 80 }; 81 this.addMouseListener(adapter); 82 this.addMouseMotionListener(adapter); 83 this.addMouseWheelListener(adapter); 84 //给窗体添加键盘监听 85 KeyAdapter key = new KeyAdapter() { 86 @Override 87 public void keyPressed(KeyEvent e) { 88 super.keyPressed(e); 89 //发送对象 90 sendEvent(e); 91 } 92 @Override 93 public void keyReleased(KeyEvent e) { 94 super.keyReleased(e); 95 //发送对象 96 sendEvent(e); 97 } 98 }; 99 this.addKeyListener(key); 100 } 101 /** 102 * 客户端发送事件 103 * @param e 104 */ 105 public void sendEvent(InputEvent e){ 106 try { 107 SinglClient.getInstance().getOos().writeObject(e); 108 SinglClient.getInstance().getOos().flush(); 109 } catch (IOException e1) { 110 e1.printStackTrace(); 111 } 112 } 113 /** 114 * 客户端图片接收 115 * @throws Exception 116 */ 117 public void readImage() throws Exception{ 118 int length = SinglClient.getInstance().getOis().readInt(); 119 byte[] bytes = new byte[length]; 120 SinglClient.getInstance().getOis().readFully(bytes); 121 //转换为图片 122 ImageIcon image = new ImageIcon(bytes); 123 label.setIcon(image); 124 label.repaint(); 125 } 126 }
- 反射机制
-
Person类——用于定义Person对象的属性和方法
Car类——用于定义Car对象的属性和方法
Class类——用于定义类对象的属性方法
- 得到Class三种方法:
-
第一种方式:Object类中的getClass方法
第二种方式:类.class
第三种方式:通过Class类的forName方法获取
-
-
public Constructor<T> getConstructor(Class<?>... parameterTypes)
得到制定的公有(public)的类
public Constructor<?>[] getConstructors()
得到所有的公有(public)的类
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
得到所有的一个指定类
public Constructor<?>[] getDeclaredConstructors() 得到所有的类
-
1 package Reflect; 2 3 4 5 /** 6 7 * 通过一个对象获得完整的包名和类名 8 9 * */ 10 11 class Demo{ 15 } 16 17 18 19 class hello{ 20 21 public static void main(String[] args) { 22 23 Demo demo=new Demo(); 24 25 System.out.println(demo.getClass().getName()); 26 27 } 28 29 }
运行结果:Reflect.Demo -
1 package Reflect; 2 3 4 5 class Person{ 6 7 8 9 public String getName() { 10 11 return name; 12 13 } 14 15 public void setName(String name) { 16 17 this.name = name; 18 19 } 20 21 public int getAge() { 22 23 return age; 24 25 } 26 27 public void setAge(int age) { 28 29 this.age = age; 30 31 } 32 33 @Override 34 35 public String toString(){ 36 37 return "["+this.name+" "+this.age+"]"; 38 39 } 40 41 private String name; 42 43 private int age; 44 45 } 46 47 48 49 class hello{ 50 51 public static void main(String[] args) { 52 53 Class<?> demo=null; 54 55 try{ 56 57 demo=Class.forName("Reflect.Person"); 58 59 }catch (Exception e) { 60 61 e.printStackTrace(); 62 63 } 64 65 Person per=null; 66 67 try { 68 69 per=(Person)demo.newInstance(); 70 71 } catch (InstantiationException e) { 72 73 // TODO Auto-generated catch block 74 75 e.printStackTrace(); 76 77 } catch (IllegalAccessException e) { 78 79 // TODO Auto-generated catch block 80 81 e.printStackTrace(); 82 83 } 84 85 per.setName("Rollen"); 86 87 per.setAge(20); 88 89 System.out.println(per); 90 91 } 92 93 }
运行结果:
Rollen 20
- 获得普通方法:
-
public Method getMethod(String name,Class<?>... parameterTypes)
public Method[] getMethods()
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
public Method[] getDeclaredMethods()
-
获取属性的方法:
-
public Field getField(String name)
public Field[] getFields()
public Field getDeclaredField(String name)
public Field[] getDeclaredFields()
- 获取类信息其他方法
-
public int getModifiers()
public Annotation[] getAnnotations()
public ClassLoader getClassLoader()
public Class<?>[] getInterfaces()
public String getName()
public boolean isAnnotation()
public boolean isEnum()
public boolean isInterface()
- 代理(Proxy):为其他对象提供一种代理以控制对这个对象的访问
-
我们书写执行一个功能的函数时,经常需要在其中写入与功能不是直接相关但很有必要的代 码,如日志记录,信息发送,安全和事务支持等,这些枝节性代码虽然是必要的,但它会带 来以下麻烦:
- 枝节性代码游离在功能性代码之外,它下是函数的目的,这是对OO是一种破坏
- 枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合,而这是OO系统所竭 力避免的
- 枝节性代码带来的耦合度会造成功能性代码移植困难,可重用性降低
- 从法理上说,枝节性代码应该`监视'着功能性代码,然后采取行动,而不是功能性代码 `通知'枝节性代码采取行动,这好比吟游诗人应该是主动记录骑士的功绩而不是骑士主 动要求诗人记录自己的功绩
- 常见的代理
-
毫无疑问,枝节性代码和功能性代码需要分开来才能降低耦合程度,符合现代OO系统的要 求,我们可以使用代理模式完成这个要求。
代理模式的作用是:为其它对象提供一种代理以控制对这个对象的访问。在某些情况下,一 个客户不想直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。 代理模式一般涉及到三个角色:
- 抽象角色:声明真实对象和代理对象的共同接口
- 代理角色:代理对象内部包含有真实角色的引用,从而可以操作真实角色,同时代理对象 与真实对象有相同的接口,能在任何时候代替真实对象,同时代理对象可以在执行真实对 象前后加入特定的逻辑以实现功能的扩展。
- 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
常见的代理有:
- 远程代理(Remote Proxy):对一个位于不同的地址空间对象提供一个局域代表对象,如RMI中的stub
- 虚拟代理(Virtual Proxy):根据需要将一个资源消耗很大或者比较复杂的对象,延迟加 载,在真正需要的时候才创建
- 保护代理(Protect or Access Proxy):控制对一个对象的访问权限。
- 智能引用(Smart Reference Proxy):提供比目标对象额外的服务和功能。
通过代理类这一中间层,能够有效控制对实际委托类对象的直接访问,也可以很好地隐藏和 保护实际对象,实施不同的控制策略,从而在设计上获得了更大的灵活性。
- 动态代理:
-
Dynamic Prox
动态代理相比于静态代理,更具有灵活性
动态代理不用显式地让它实现与真实主题类(RealSubject)相同的接口(interface),而是把这种实现推迟到运行时。
- 所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一 组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实 例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工 作。
-
动态代理关联的API
java.lang.reflect包下的类:
InvocationHandler类:invoke(Object obj,Method method,Object[] objs)
Proxy类:newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
-
一个典型的动态代理创建对象过程可分为以下三个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 InvocationHandler handler = new InvocationHandlerImpl(被代理对象);
2、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface inter= (Interface)newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
3、通过Interface对象执行指定事物
inter.method();
- 代理模式与装饰者模式的区别
-
代理模式和装饰者模式很像,在典型的例子上,如spring的AOP、远程代理类、JDK的proxy, 都是代理模式。JDK里的输入/输出器是很典型的装饰器模式!但在有些场景上,对设计模式 入门的新手,还是有点难区分,UML类图基本没区别,都是实现同一个接口,一个类包装另一 个类。 两者的定义:
- 装饰器模式:能动态的新增或组合对象的行为
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问
装饰模式是“新增行为”,而代理模式是“控制访问”。关键就是我们如何判断是“新增行 为”还是“控制访问”。你在一个地方写装饰,大家就知道这是在增加功能,你写代理,大 家就知道是在限制。
-
代理模式:在不改变接口的前提下,控制对象的访问
例子:孙悟空扮演并代替高家三小姐
孙悟空扮演高家三小姐,所以可以说孙悟空与高家三小姐具有共同的接口。如果猪八戒只想 见见高家三小姐的娇好面容,或者谈谈天说说地,那么高家三小姐的“代理”孙悟空是允许 的,但猪八戒想亲亲嘴,那么是不行的。这是保护代理模式的应用。只有代理对象认为合适 时,才会将客户端的请求传递给真实主题对象。
装饰模式:在不改变接口的前提下,动态扩展对象的功能
孙悟空有七十二般变化,在二郎神眼里,他永远是那只猢狲。装饰模式以对客户透明的方式 动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有 什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。他的每 一种变化都给他带来一种附加的本领。他变成鱼儿时,就可以到水里游泳;他变成雀儿时, 就可以在天上飞行。而不管悟空怎么变化,在二郎神眼里,他永远是那只猢狲。装饰模式以 对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在 装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能 加以扩展。
- 内省机制
-
Ø定义:1.是java语言对Bean类属性、事件的一种缺省(=默认)的处理机制
2.通过反射的方式操作JavaBean的属性
例如Student类中有name属性,则可以通过反射机制调用getName和setName方法设置新值
Ø补充:通过getName和setName方法访问name属性,这就是默认的规则,java的API中有提供访问bean的get和set
方法的接口,都存于java.bean包下面
- 实现内省三步走
-
1. Introspector的静态方法getBeanInfo,获取BeanInfo对象信息
2. 通过BeanInfo对象调用getPropertyDescriptors获取PropertyDescriptor(bean的所有属性描述)
3. 调用getWriteMethod和getReadMethod获取当前属性的get和set机制函数
-
技术归纳:
properties文件
java.util.Properties类
类名.class.getResourceAsStream("文件路径");
相关函数:
load函数:加载properties文件
getProperty函数:获取properties文件字段属性
- 一般的做法是通过类 Introspector 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
-
/** * 内省演示例子: 把某个对象的所有属性名称和值都打印出来 */ public class IntrospectorDemo { String name; String height; public static void main(String[] args) throws Exception{ IntrospectorDemo demo = new IntrospectorDemo(); demo.setName( "Winter Lau" ); demo.setHeight(null); // 如果不想把父类的属性也列出来的话, // 那 getBeanInfo 的第二个参数填写父类的信息 BeanInfo bi = Introspector.getBeanInfo(demo.getClass(), Object. class ); PropertyDescriptor[] props = bi.getPropertyDescriptors(); for ( int i=0;i<props.length;i++){ System.out.println(props[i].getName()+ "=" + props[i].getReadMethod().invoke(demo, null )); } } public String getName() { return name; } public void setName(String name) { this .name = name; } public String getHeight() { return height; } public void setHeight(String height) { this.height = height; } }
-
Web开发框架Struts中的FormBean就是通过内省机制来将表单中的数据映射到类的属性上,因此要求FormBean的每个属性要有getter/setter方法。但也并不总是这样,什么意思呢?就是说对一个Bean类来讲,我可以没有属性,但是只要有getter/setter方法中的其中一个,那么Java的内省机制就会认为存在一个属性,比如类中有方法setMobile,那么就认为存在一个mobile的属性,这样可以方便我们把Bean类通过一个接口来定义而不用去关心具体实现,不用去关心 Bean中数据的存储。比如我们可以把所有的getter/setter方法放到接口里定义,但是真正数据的存取则是在具体类中去实现,这样可提高系统的扩展性。
总结:
将 Java 的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面提到的 Struts ,还有用于处理 XML 文件的 Digester 项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性。