• 1. Java动态接口代理技术
  • 2. 问题提出视窗操作系统中的钩子(Hook)方法 通过编写动态链接库并注册成为系统钩子用于拦截某些Window API或者某个系统消息 在Java中如何拦截某个方法的执行.….. 如果可以拦截,那么拦截了又有什么用?
  • 3. 方法拦截的目的屏蔽某个函数的执行 动态改写函数的代码 通过在某个函数执行前和执行后增加代码来增强原有函数的功能 跟踪函数被调用的情况
  • 4. 疑问我为什么要拦截呢????我直接改写要拦截的那个函数不就可以了嘛?错!因为并不是每个方法你都可以修改的,例如其他厂商开发的包,例如数据库的JDBC驱动程序包,是不是每个厂商的代码你都要插一腿呢?
  • 5. 局限性只能拦截接口的方法!!! 也就是说要拦截的函数必须是在某个接口中定义的方法。 允许: Connection.close() ServletRequest.getParameter(String param) 不允许: String.length()
  • 6. 现在可以开始了需要准备几个Java源文件如下: Test.java 测试接口类 TestImpl.java 测试接口实现类 TestProxy.java 接口代理类 Tester.java 测试类,main方法所在类
  • 7. Test.java源码package demo; public interface Test { public void sayHello(String name); }
  • 8. TestImpl.java源码package demo; public class TestImpl implements Test { public void sayHello(String name) { System.out.println("Hello "+name); } }
  • 9. TestProxy.java源码(注意红色斜体)package demo; import java.lang.reflect.*; public class TestProxy implements InvocationHandler { Test iTest = null; public TestProxy(Test test) { this.iTest = test; } public Test getTest(){ return(Test)Proxy.newProxyInstance(iTest.getClass().getClassLoader(), iTest.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before invoke sayHello(\""+args[0]+"\")"); Object rst = method.invoke(iTest,args); System.out.println("After invoke sayHello(\""+args[0]+"\")"); return rst; } }
  • 10. Tester.java源码package demo; public class Tester { public static void main(String[] args) { getTest1().sayHello("JAVA接口代理"); System.out.println("===================="); getTest2().sayHello("JAVA接口代理"); } private static Test getTest1(){ return new TestImpl(); } private static Test getTest2(){ return new TestProxy(new TestImpl()).getTest(); } }
  • 11. 执行结果!Hello JAVA接口代理 ==================== Before invoke sayHello("JAVA接口代理") Hello JAVA接口代理 After invoke sayHello("JAVA接口代理")其中红色为函数接管后(TestProxy)加入的打印信息!
  • 12. 怎么回事??? 在执行语句getTest2().sayHello(“JAVA接口代理”);的时候发生了两件事: 类TestProxy的invoke被调用了! 类TestImpl的sayHello方法也被调用了!
  • 13. Java的实现机制 Java通过一个类Proxy以及一个接口InvocationHandler来实现函数接管的功能,这两个类都是在java.lang.reflect包中。 对接管对象如本例中的TestProxy的要求: 必须实现接口InvocationHandler。 需要保存原有接口的实例(TestProxy的属性iTest) 必须提供一个方法用来获取原有接口的实例 该方法不是简单的返回接口实例,而是通过Proxy类的newProxyInstance来生成一个代理对象。
  • 14. 提供给调用者的方法 要接管某个接口实现类的某个函数,那么就要求不允许直接将该实现未经代理处理后直接返回给调用者。在我们这个例子中不允许直接返回类TestImpl的实例,而应该通过代理类用于获取代理对象实例的方法。 也就是类似于在Tester类中我们使用的是getTest2而不是getTest1方法的缘故。
  • 15. 实际应用举例之连接池应用数据库连接池 一个好的数据库连接池应该具备下面两个条件 无需改变用户使用习惯 自动连接回收功能 对于连接池来讲,连接的获取肯定必须是通过连接池所提供的方法进行,但是要允许用户直接调用Connection.close来关闭连接,要不就是改变了用户使用习惯。因为close是接口Connection的一个方法所以可以使用我们前面介绍的方法来接管该方法。
  • 16. 数据库连接代理实现 1public class _Connection implements InvocationHandler { private final static String CLOSE_METHOD_NAME = "close"; private Connection conn = null; private boolean inUse = false; //数据库的忙状态 private long lastAccessTime = System.currentTimeMillis(); _Connection(Connection conn, boolean inUse) { this.conn = conn; this.inUse = inUse; } public Connection getConnection() {//返回数据库连接conn的接管类,以便截住close方法 return (Connection) Proxy.newProxyInstance( conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), this); } void close() throws SQLException { //直接关闭连接 conn.close(); }
  • 17. 数据库连接代理实现 2 public boolean isInUse() { return inUse; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { Object obj = null; //判断是否调用了close的方法,如果调用close方法则把连接置为无用状态 if (CLOSE_METHOD_NAME.equals(m.getName())) setInUse(false); else obj = m.invoke(conn, args); //设置最后一次访问时间,以便及时清除超时的连接 lastAccessTime = System.currentTimeMillis(); return obj; } public long getLastAccessTime() { return lastAccessTime; } public void setInUse(boolean inUse) { this.inUse = inUse; } }
  • 18. 实际应用举例之Web框架问题提出:开发web应用程序时经常需要对上传的文件进行处理。对于用来上传文件的表单其ENCTYPE通常设置为multipart/form-data,这种类型的表单提交到服务器后是无法通过ServletRequest的getParameter方法来获取表单中的某个域的值,而必须通过解析上传的数据流来获取参数值。因此就要求Web框架本身要屏蔽表单之间的差异,使框架使用者无需关系客户端的表单类型。 解决方案:使用接口代理技术来接管所有用于参数获取的方法,例如:getParameter,getParameterValues,getParameterNames等。当接管到这几个方法后首先判断该请求是否为文件上传类型的信息流,如果不是则直接调用代理对象的对应方法,否则从数据流中解析出参数信息并返回。
  • 19. 谢 谢