Java动态代理简单应用
概念
代理模式是基本的设计模式之一,它是开发者为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。
Java动态代理比代理的思想更进一步,因为它可以动态地创建代理并动态地处理对代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器(InvocationHandler)上,调用处理器的工作是揭示调用的类型并确定相应的策略。
Java动态代理实现机制采用了反射的思想,有关于反射的基础知识,可以参见博客Java发射机制浅析。
原理
Spring核心AOP实现技术之一就是采用Java动态代理机制,权限认证、日志以及事务管理都可以采用动态代理,使用动态代理机制可以减少代码重复,降低系统耦合度。Java动态代理API位于java.lang.reflect包下,实现动态代理主要涉及以下两个关键步骤:
1.实现InvocationHandler接口
InvocationHandler接口仅定义了一个方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
proxy:代理对象
method:被代理类方法对象
args:被代理方法参数数组
proxy参数表示代理对象,以防开发者需要区分请求的来源,但是在许多情况下,开发者并不关心这一点。InvocationHandler通常需要持有被代理对象的引用,在动态代理上所做的所有调用都会被重定向到InvocationHandler接口的invoke方法上。在invoke方法内部调用被代理对象的method方法,开发者可以在方法调用前后加上需要的逻辑。如果只是想对某些方法进行代理,开发者可以在invoke方法内添加相应的过滤条件,通过method参数可以获取方法本身的很多信息。
2.通过Proxy创建动态代理类实例
Proxy是所有动态代理类的父类,并提供一个静态方法newProxyInstance负责创建动态代理类的实例。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException
loader:类加载器(通常可以从已经被加载的对象中获取其类加载器)
interfaces:代理类需要实现的接口列表(通常使用被代理类实现的接口列表)
h:InvocationHandler接口的一个实现
newProxyInstance方法会根据传入的接口列表interfaces在虚拟机中动态的生成一个Proxy的子类,并实现了传入的接口列表interfaces的所有方法,然后返回该类的一个实例。因此返回的对象能够转型到传入的任意一个接口,通过该对象对接口内所有方法的调用都会重定向到第三个参数h的invoke方法。
Java动态代理底层原理实现比较复杂。总而言之,是根据newProxyInstance方法传入的类加载器和接口,生成代理类的二进制字节码,再利用反射机制创建代理类的实例,在代理类的内部对被代理类方法的调用都会重定向到InvocationHandler中的invoke方法,在invoke方法内再对被代理方法的调用进行封装预处理,加上开发者自己的逻辑。
应用
下面演示两个简单的示例:方法性能监测和日志管理
实体类
public class User {
private String name;
public User(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接口
public interface UserDao {
void addUser(User user);
void deleteUser(String name);
void updateUser(String name);
}
实现类
public class UserDaoImpl implements UserDao {
@Override
public void addUser(User user) {
System.out.println("add user named " + user.getName() + "...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void deleteUser(String name) {
System.out.println("delete user named " + name + "...");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void updateUser(String name) {
System.out.println("update user named " + name + "...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
方法性能监测
监测方法运行性能效率,此处简单以方法运行时间作为评价标准。传统的做法是在每个方法执行前后各自记录时间,然后计算运行时间。采用动态代理机制,可以将以上操作统一到InvocationHandler中,并创建代理类监测所有的方法。
InvocationHandler实现类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class PerformanceInterceptor implements InvocationHandler { private Object proxied; public PerformanceInterceptor(Object proxied) {
this.proxied = proxied;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object obj = method.invoke(proxied, args);
long endTime = System.currentTimeMillis();
System.out.println("Method " + method.getName() + " execution time: " + (endTime - startTime) * 1.0 / 1000 + "s");
return obj;
} }
测试类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; import org.junit.Test; public class PerformanceInterceptorTest { @Test
public void testInvoke() {
UserDao userDao = new UserDaoImpl();
Class<?> cls = userDao.getClass();
InvocationHandler handler = new PerformanceInterceptor(userDao);
UserDao proxy = (UserDao) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
proxy.addUser(new User("tom"));
proxy.deleteUser("tom");
proxy.updateUser("tom");
} }
测试结果
add user named Tom...
Method addUser execution time: 1.0s
delete user named tom...
Method deleteUser execution time: 1.5s
update user named tom...
Method updateUser execution time: 2.0s
日志管理
有时候开发者需要在方法开始执行和执行结束时打印一些信息,使用动态代理就可以对所有方法或者某些方法进行简单日志管理,并写入到指定文件。
InvocationHandler实现类
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat; public class LogInterceptor implements InvocationHandler { private Object proxied; public static final String path = "run.log"; public LogInterceptor(Object proxied) {
this.proxied = proxied;
} public String beforeMethod(Method method) {
return getFormatedTime() + " Method:" + method.getName() + " start running\r\n";
} public String afterMethod(Method method) {
return getFormatedTime() + " Method:" + method.getName() + " end running\r\n";
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
write(path, beforeMethod(method));
Object object = method.invoke(proxied, args);
write(path, afterMethod(method));
return object;
} public String getFormatedTime() {
DateFormat formater = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return formater.format(System.currentTimeMillis());
} public void write(String path, String content) {
FileWriter writer = null;
try {
writer = new FileWriter(new File(path), true);
writer.write(content);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != writer) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} }
测试类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; import org.junit.Test; public class LogInterceptorTest { @Test
public void testInvoke() {
UserDao userDao = new UserDaoImpl();
Class<?> cls = userDao.getClass();
InvocationHandler handler = new LogInterceptor(userDao);
UserDao proxy = (UserDao) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
proxy.addUser(new User("tom"));
proxy.deleteUser("tom");
proxy.updateUser("tom");
} }
测试结果
在工程根目录下生成run.log文件,内容如下:
2015-10-07 05:41:02 Method:addUser start running
2015-10-07 05:41:03 Method:addUser end running
2015-10-07 05:41:03 Method:deleteUser start running
2015-10-07 05:41:05 Method:deleteUser end running
2015-10-07 05:41:05 Method:updateUser start running
2015-10-07 05:41:07 Method:updateUser end running
总结
尽管Java动态代理机制设计的已经非常出色,美中不足之处的是,Java动态代理只能代理实现接口的类。若想要对没有实现任何接口的类进行代理,这种情况确实罕见,就需要使用cdlib。Java动态代理机制是实现AOP编程的重要技术之一,动态代理并非开发者日常使用的工具,但是在某些特殊的场景,使用动态代理可以非常好地解决某些类型的问题。
Java动态代理简单应用的更多相关文章
- java动态代理的基本思想以及简单的实现
代理模式 本人参考于代理模式及Java实现动态代理 不作为商业用途,只是借鉴于其思路.侵权即删. 原理:给某个对象提供一个代理对象,并且由代理对象控制原对象的访问,即不直接操控原对象,而是通过代理对 ...
- Java动态代理原理及其简单应用
概念 代理对象和被代理对象一般实现相同的接口,调用者与代理对象进行交互.代理的存在对于调用者来说是透明的,调用者看到的只是接口.代理对象则可以封装一些内部的处理逻辑,如访问控制.远程通信.日志.缓存等 ...
- java:java静态代理与动态代理简单分析
java静态代理与动态代理简单分析 转载自:http://www.cnblogs.com/V1haoge/p/5860749.html 1.动态代理(Dynamic Proxy) 代理分为静态代理和动 ...
- 《Java设计模式》之代理模式 -Java动态代理(InvocationHandler) -简单实现
如题 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式可细分为如下, 本文不做多余解释 远程代理 虚拟代理 缓冲代理 保护代理 借鉴文章 ht ...
- java的代理和动态代理简单测试
什么叫代理与动态代理? 1.以买火车票多的生活实例说明. 因为天天调bug所以我没有时间去火车票,然后就给火车票代理商打电话订票,然后代理商就去火车站给我买票.就这么理解,需要我做的事情,代理商帮我办 ...
- Java动态代理全面分析
代理模式 解说:给某一个对象提供一个代理,并由代理对象控制对原对象的引用: 代理模式需要以下几个角色: 1 主题:规定代理类和真实对象共同对外暴露的接口: 2 代理类:专门代理真实对象的类: 3 ...
- java动态代理原理
我们经常会用到Java的动态代理技术, 虽然会使用, 但是自己对其中的原理却不是很了解.比如代理对象是如何产生的, InvocationHandler的invoke方法是如何调用的?今天就来深究下Ja ...
- java动态代理浅析
最近在公司看到了mybatis与spring整合中MapperScannerConfigurer的使用,该类通过反向代理自动生成基于接口的动态代理类. 于是想起了java的动态代理,然后就有了这篇文章 ...
- Java 动态代理
被代理的接口特点: 1. 不能有重复的接口,以避免动态代理类代码生成时的编译错误. 2. 这些接口对于类装载器必须可见,否则类装载器将无法链接它们,将会导致类定义失败. 3. 需被代理的所有非 pub ...
随机推荐
- Google Dataflow
十分钟了解分布式计算:Google Dataflow 介绍 Google Cloud Dataflow是一种构建.管理和优化复杂数据处理流水线的方法,集成了许多内部技术,如用于数据高效并行化处理的Fl ...
- JS定时跳转URL并输出剩余秒数
1. [代码][JavaScript]代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <scrip ...
- Web API 2
Asp.Net Web API 2 官网菜鸟学习系列导航[持续更新中] 前言 本来一直参见于微软官网进行学习的, 官网网址http://www.asp.net/web-api.出于自己想锻炼一下学 ...
- AngularJS的工作原理
AngularJS的工作原理 个人觉得,要很好的理解AngularJS的运行机制,才能尽可能避免掉到坑里面去.在这篇文章中,我将根据网上的资料和自己的理解对AngularJS的在启动后,每一步都做了些 ...
- Vnix项目正式启动
历经3年的学习时间,我从Puppy Linux到各种常见的Linux发行版,从Gentoo Linux再到LFS,期间学会了LiveCD.中文化定制.服务器搭建.Google Key Search.C ...
- CLR的组成和运转
CLR的组成和运转 clr基本 CLR(Common Language Runtime)是一个可由多种编程语言使用的“运行时”.(例如:c#,c++/cli,vb,f#,ironpython,iron ...
- 企业架构研究总结(33)——TOGAF架构内容框架之架构制品(上)
4. 架构制品(Architectural Artifacts) 架构制品是针对某个系统或解决方案的模型描述,与架构交付物和构建块相比,架构制品既不是架构开发方法过程各阶段的合约性产物,亦不是企业中客 ...
- 对 Linux 新手有用的 20 个命令
你打算从Windows换到Linux上来,还是你刚好换到Linux上来?哎哟!!!我说什么呢,是什么原因你就出现在我的世界里了.从我以往的经验来说,当我刚使用Linux,命令,终端啊什么的,吓了我一跳 ...
- js 冒泡 捕获
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> ...
- Object-c学习之路五(@protocol协议)
今天模拟Button的delegate来联系一下protocol. Button类 // Button.h // Protocal // // Created by WildCat on 13-7-2 ...