您的位置:首页 > 编程语言 > Java开发

java动态代理

2017-01-18 00:00 411 查看
摘要: java反射机制实现动态代理

1.为什么需要代理?

我们写一个功能函数的时候,可能常常需要写入与功能相关却又十分必要的代码,如日志记录,信息发送,安全和事务支持等(AOP),这些枝节性代码虽然是必要的,但它会带来以下麻烦:
1. 枝节性代码游离在功能性代码之外,它不是函数的目的,这是对OO(面向对象)是一种破坏
2. 枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合,而这是OO系统所竭力避免的
3. 枝节性代码带来的耦合度会造成功能性代码移植困难,可重用性降低
4. 从目的上面来说,枝节性代码应该监视着功能性代码,然后采取行动,而不是功能性代码 通知枝节性代码采取行动,例如,我们常见的增删改查操作

2.代理模式一般涉及到的角色

抽象角色:声明真实对象和代理对象的共同接口;

真实角色:真实角色所代表的真实对象,是我们最终要引用的对象。

代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

3.静态代理实例

假设我们有一个经销商(ISaler),我们有两个真实角色需要购买电脑(Leader,Manager),而这两个角色可能不会自己去买电脑,而是交给秘书完成(CustomerProxy),而领导和经理只是有购买电脑这个需求

这里ISaler就好比我们上面说的抽象角色,是一个接口里面提供购买等方法

public interface ISaler {
public void buy();
public void modify();
}


我们的Leader、Manager就好比真实角色需要购买电脑

public class Manger implements ISaler{
@Override
public void buy() {
System.out.println("经理要购买。。。");
}
@Override
public void modify() {
}
}


我们的秘书就好比是代理角色,在购买电脑之前,去帮忙完成一系列操作

public class CustomerProxy implements ISaler{
private ISaler saler;
public CustomerProxy(ISaler saler){
this.saler = saler;
}
@Override
public void buy() {
System.out.println("还价。。。。");
saler.buy();
System.out.println("要求售后");

@Override
public void modify() {
// TODO Auto-generated method stub
}
}


但是,这里有一个很不好问题就是,我们的真实角色如果又有新的需求,那么我们的抽象角色中就又需要另外一个抽象方法,这时候,我们的代理角色,我们的真实角色中都需要去实现这个方法,这样子就显得很不动态,于是,就有了我们下面的动态代理

3.java利用反射机制实现动态代理

同样是上面的场景,现在,我们采用动态代理来实现,也就是我们将上面的代理对象换成动态代理对象:代理所有真实对象的所有要求(目标接口中的方法)

InvocationHandler接口(InvocationHandler 是代理实例的调用处理程序 实现的接口)

每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。就是说我们需要去实现invoke方法

public class DynamicProxyObject implements InvocationHandler{
private Object realObject;//需要被代理的真实对象
public DynamicProxyObject(Object reaObject){
this.realObject = reaObject;
}
/**
* @param proxy 由真实对象产生的代理对象
* @param method 真实对象需要执行的方法
* @param args 执行方法时需要的参数值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("进入代理对象,代理准备执行真实对象的要求");
return method.invoke(realObject, args);
}
}


我们在测试类中去进行测试

我们需要创建一个代理对象,这里涉及到Proxy.newProxyInstance()方法,这个方法有三参数(1)、需要一个类加载器,定义我们需要代理的真实对象,例如我们这里的Manger.class.getClassLoader();(2)、需要一个我们代理对象需要实现的接口;(3)、需要InvocationHandler。

InvocationHandler h = new DynamicProxyObject(new Manger());
//创建代理对象
Object proxyObj = Proxy.newProxyInstance(Manger.class.getClassLoader(), Manger.class.getInterfaces(), h);

1. 用真实对象的上层接口去接收这个代理对象强转),**这里千万不能用真实对象去接收,我试过是会报错的,如果用上层接口接收则没有问题,如果看本博客的人知道为什么,欢迎私信**

//这里不能用真实对象去接收,要用真实对象的上层接口接收
ISaler saler = (ISaler)proxyObj;

1. 接下来,我们就可以调用我们接口中的各种方法了

saler.back();

4.java动态代理example

通过动态代理,在真实对象执行方法前记录一次,包含执行的方法名,执行开始时间,在真实对象方法执行后记录一次,执行的方法名,执行结束时间,显示执行方法所需时长

准备一个抽象角色(接口)

public interface ITran {

public void beginTran();

public void commit();

public void callback();
}


准备一个真实角色

public class TransactionManager implements ITran {

@Override
public void beginTran() {
System.out.println("事务开启");

}

@Override
public void callback() {
System.out.println("事务回滚");

}

@Override
public void commit() {
System.out.println("事务提交");

}
}


准备一个代理角色

public class TranProxy implements InvocationHandler{
public Object realObject;
public TranProxy(Object realObject){
this.realObject = realObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//获取程序执行时间
long start = System.currentTimeMillis();
System.out.println(method.getName()+"开始执行" + getTime(new Date(start)));
//方法执行
Object returnValue = method.invoke(realObject, args);
//获得程序结束时间
long end = System.currentTimeMillis();
System.out.println(method.getName()+"执行完成" + getTime(new Date(end)));

System.out.println("耗时" + (end-start) + "ms");
return returnValue;
}
//获得动态代理对象
public static Object getProxy(Object realObject){
return Proxy.newProxyInstance(realObject.getClass().getClassLoader(), realObject.getClass().getInterfaces(), new TranProxy(realObject));
}
//格式化日期
public String getTime(Date date){
return new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss SSS]").format(date);
}
}


在测试类中进行测试

public class Test {

public static void main(String[] args) {
Object proxyObj = TranProxy.getProxy(new TransactionManager());
ITran tran = (ITran)proxyObj;
tran.beginTran();
tran.commit();
}
}


查看程序的运行结果

beginTran开始执行[2017-01-18 16:27:11 262]
事务开启
beginTran执行完成[2017-01-18 16:27:11 309]
耗时47ms
commit开始执行[2017-01-18 16:27:11 309]
事务提交


上面就是一个简单的动态代理的例子,用于管理事务,简单的实现类似日志的这种效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息