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

Java 动态代理

2015-10-15 16:37 381 查看
首先,什么是代理?

代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。

举例:春季回家买票让人代买

Java中的代理模式:

定义

代理模式(Proxy Pattern)是对象的结构型模式,代理模式给某一个对象提供了一个代理对象,并由代理对象控制对原对象的引用。

代理模式不会改变原来的接口和行为,只是转由代理干某件事,代理可以控制原来的目标,例如:代理商,代理商只会买东西,但并不会改变行为,不会制造东西。让我们通过下面的代码好好理解一下这句话。

分类

静态代理和动态代理

先看静态代理利例子:



package com.proxy;
/*
* 用户操作接口
*/
public interface  UserDao {
public abstract void add();
public abstract void delete();
public abstract void update();
public abstract void find();
}
package com.proxy;

public class UserDapImpl implements UserDao{
@Override
public void add() {
System.out.println("添加功能");
}
@Override
public void delete() {
System.out.println("删除功能");
}
@Override
public void update() {
System.out.println("更新功能");
}
@Override
public void find() {
System.out.println("查找功能");
}

}
package com.proxy;

public class UserDaoImplProxy implements UserDao{

@Override
public void add() {
System.out.println("权限校验");
System.out.println("添加功能");
System.out.println("日志记录");
}

@Override
public void delete() {
System.out.println("权限校验");
System.out.println("删除功能");
System.out.println("日志记录");
}

@Override
public void update() {
System.out.println("权限校验");
System.out.println("修改功能");
System.out.println("日志记录");
}

@Override
public void find() {
System.out.println("权限校验");
System.out.println("查找功能");
System.out.println("日志记录");
}

}
package com.proxy;

public class UserDaoDemo {
public static void main(String[] args) {
//用户管理 的基本操作
UserDao ud=new UserDapImpl();
ud.add();
ud.delete();
ud.update();
ud.find();
System.out.println("--------------");
//实际上,对用户的操作,并不是所有的人都可以,我们来个权限验证
//而这个权限验证我们并不自己做,而是交给别人做
UserDao ud2=new UserDaoImplProxy();
ud2.add();
ud2.delete();
ud2.update();
ud2.find();
System.out.println("--------------");

}
}


缺点:

同样,它的优点也成了它致命的缺点。

1、静态代理很麻烦,需要大量的代理类

当我们有多个目标对象需要代理时,我就需要建立多个代理类,改变原有的代码,改的多了就很有可能出问题,必须要重新测试。

2、重复的代码会出现在各个角落里,违背了一个原则:重复不是好味道

我们应该杜绝一次次的重复。

3、在编译期加入,系统的灵活性差

动态代理:在程序运行过程中产生的这个对象

而程序运行过程中产生对象其实就是反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理

在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib

Proxy类中的方法创建动态代理类对象

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

最终会调用InvocationHandler的方法

InvocationHandler

Object invoke(Object proxy,Method method,Object[] args)

所谓 Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

再看动态代理的例子:



UserDao.java,UserDaoImpl.java跟上面的相同.

package com.dynaproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 采用JDK动态代理必须实现InvocationHandler接口,采用Proxy类创建相应的代理类
*
*/
public class MyInvocationHandler implements InvocationHandler{

private Object target;//目标对象,代理对象
public MyInvocationHandler(Object target){
this.target=target;
}

/**
* 反射,这样你可以在不知道具体的类的情况下,根据配置的参数去调用一个类的方法。在灵活编程的时候非常有用。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("权限校验");//代理所做的事情
Object result=method.invoke(target, args);
System.out.println("日志记录");//代理所做的事情
return result; // 返回的是代理对象
}

}


package com.dynaproxy;

import java.lang.reflect.Proxy;

public class Test {
public static void main(String[] args) {
UserDao ud=new UserDapImpl();
//准备对ud做一个代理对象
MyInvocationHandler handler=new MyInvocationHandler(ud);
//生成ud的代理对象
UserDao proxy=(UserDao)Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler);
//用代理对象做 权限工作  及完成ud的操作
proxy.add();
proxy.delete();
proxy.update();
proxy.find();

}
}


优点:大大地减少了代码的冗余,比如,再来个studentDao,对学生进行管理,也是需要权限验证的,那么,你直接再建一个Proxy 对象代理即可,因为在InvocationHandler 里是通过你要实现的接口的Class对象实现逻辑的,不是硬编码。

看到这里,大概知道了动态代理是干什么的了。可是还依然对Java JDK里的

Proxy类和InvocationHandler接口仍然不够了解,都学到这里了,知其然当然要知其所以然啦。

于是,上网看大神更上一层楼

/article/1542173.html点击打开链接

------------------------二次学习Proxy类和InvocationHandler接口更深理解的分界线------------------------------

JDK的自带的动态代理动态生成了字节码(当然当中还用了反射),只是他的生成字节码的方式

必须和接口绑定,而cglib,并不是正对接口来而是针对普通类来动态的生成字节码

public static Object newProxyInstance(ClassLoader loader,Class<?>[interfaces,InvocationHandler h)
分解步骤:以创建某一接口 Foo 的代理为例子:
InvocationHandler handler = new MyInvocationHandler(...);
//生成代理类的Class对象(字节码)
Class proxyClass = Proxy.getProxyClass(
Foo.class.getClassLoader(), new Class[] { Foo.class });
//通过反射,根据Class对象获取构造方法,再 newInstance,传入hanlder(proxy构造函数
指定参数)
Foo f = (Foo) proxyClass.
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
通过网上大神对newProxyInstance的源码深度解析点击打开链接,以及以上newProxyInstance的生成原理,

动态代理动态体现在运行时生成代理类字节码,然后由InvocationHandler要代理的接口(从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。

总结一下动态代理实现过程:

1. 通过getProxyClass0()生成代理类。

2. 通过Proxy.newProxyInstance()生成代理类的实例对象,创建对象时传入InvocationHandler类型的实例。

3. 调用新实例的方法,即此例中的add(),即原InvocationHandler类中的invoke()方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: