Java AOP动态代理实现方式
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
}ห้องสมุดไป่ตู้
}
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(){
public void setInUse(boolean inUse) { }
实际应用举例之Web框架
问题提出:开发web应用程序时经常需要对上传的文件进行处理。
对于用来上传文件的表单其ENCTYPE通常设置为multipart/form-data, 这种类型的表单提交到服务器后是无法通过ServletRequest的 getParameter方法来获取表单中的某个域的值,而必须通过解析上传的 数据流来获取参数值。因此就要求Web框架本身要屏蔽表单之间的差 异,使框架使用者无需关系客户端的表单类型。
也就是说要拦截的函数必须是在某个接口中 定义的方法。
允许:
Connection.close() ServletRequest.getParameter(String param)
不允许:
String.length()
现在可以开始了
需要准备几个Java源文件如下:
1. Test.java 2. TestImpl.java 3. TestProxy.java 测试接口类 测试接口实现类 接口代理类
Java动态接口代理技术
问题提出
•
视窗操作系统中的钩子(Hook)方法 通过编写动态链接库并注册成为系统钩子用于拦截 某些Window API或者某个系统消息
• 在Java中如何拦截某个方法的执行.…..
•
如果可以拦截,那么拦截了又有什么用?
方法拦截的目的
• 屏蔽某个函数的执行
• 动态改写函数的代码
3. 必须提供一个方法用来获取原有接口的实例
该方法不是简单的返回接口实例,而是通过Proxy类的 newProxyInstance来生成一个代理对象。
提供给调用者的方法
要接管某个接口实现类的某个函数,那么就要求 不允许直接将该实现未经代理处理后直接返回给调用 者。在我们这个例子中不允许直接返回类TestImpl的实 例,而应该通过代理类用于获取代理对象实例的方法 。 也就是类似于在Tester类中我们使用的是getTest2 而不是getTest1方法的缘故。
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(); }
//判断是否调用了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; this.inUse = inUse; } }
实际应用举例之连接池应用
数据库连接池
一个好的数据库连接池应该具备下面两个条件 1.无需改变用户使用习惯 2.自动连接回收功能
对于连接池来讲,连接的获取肯定必须是通过连接 池所提供的方法进行,但是要允许用户直接调用 Connection.close来关闭连接,要不就是改变了用户使用 习惯。因为close是接口Connection的一个方法所以可以 使用我们前面介绍的方法来接管该方法。
2. 类TestImpl的sayHello方法也被调用了!
Java的实现机制
Java通过一个类Proxy以及一个接口InvocationHandler 来实现函数接管的功能,这两个类都是在 java.lang.reflect包中。
对接管对象如本例中的TestProxy的要求: 1. 必须实现接口InvocationHandler。 2. 需要保存原有接口的实例(TestProxy的属性iTest)
数据库连接代理实现 1
public class _Connection implements InvocationHandler { private final static String CLOSE_METHOD_NAME = "close";
private Connection conn = null;
谢 谢
}
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;
数据库连接代理实现 2
public boolean isInUse() { return inUse; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
Object obj = null;
解决方案:使用接口代理技术来接管所有用于参数获取的方法,例
如:getParameter,getParameterValues,getParameterNames等。当接管 到这几个方法后首先判断该请求是否为文件上传类型的信息流,如果 不是则直接调用代理对象的对应方法,否则从数据流中解析出参数信 息并返回。
return new TestImpl();
} private static Test getTest2(){ return new TestProxy(new TestImpl()).getTest(); } }
执行结果!
Hello JAVA接口代理 ==================== Before invoke sayHello("JAVA接口代理")
Hello JAVA接口代理
After invoke sayHello("JAVA接口代理") 其中红色为函数接管后(TestProxy)加入的打印信息!
怎么回事???
在执行语句getTest2().sayHello(“JAVA接口代理”);的 时候发生了两件事:
1. 类TestProxy的invoke被调用了!
4. Tester.java
测试类,main方法所在类
Test.java源码
package demo;
public interface Test { public void sayHello(String name); }
TestImpl.java源码
package demo;
public class TestImpl implements Test { public void sayHello(String name) { System.out.println("Hello "+name); }
通过在某个函数执行前和执行后增加代码来增强原有 函数的功能
• 跟踪函数被调用的情况
疑问
我为什么要拦截呢???? 我直接改写要拦截的那个函 数不就可以了嘛?
错!因为并不是每个方法你都可以修 改的,例如其他厂商开发的包,例如 数据库的JDBC驱动程序包,是不是每 个厂商的代码你都要插一腿呢?
局限性
只能拦截接口的方法!!!