一、代理模式简介(这里是转载别人的博客):
https://www.cnblogs.com/gonjan-blog/p/6685611.html
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预
处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联
关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用
委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象
来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
二、今天讲的是动态代理模式:
动态代理就是通过代理类是代理类与相关接口不直接发生联系,而在运行期(Runtime)实现动态关联。
首先先回顾一下类反射:
举例: 有一个接口如下:
public interface IRenter {
public abstract void rent();
}
一个实现类:
package cn.hncu.proxy;
public class Renter implements IRenter {
@Override
public void rent() {
System.out.println("真正的房东, 提供房子,收房租");
}
public double aa(int n,double d){
double s=0;
if(n%2==1){
s=n*3*d;
}else{
s=n*n*d;
}
return s;
}
}
通过类反射:用类反射实现类似下面这样的功能:Renter r = new Renter();double s = r.aa(87,3.14);
1 先获取所要执行函数的method对象--m
2再执行m函数---当然得给它传实参和调用对象
//复习一下类反射
@Test
public void demo1() throws Exception{
/*用类反射实现类似下面这样的功能:
Renter r = new Renter();
double s = r.aa(87,3.14);
*/
//1 先获取所要执行函数的method对象--m
Class c=Renter.class;
Class paramTypes[]={int.class,double.class};
Method m = c.getMethod("aa", paramTypes);
//2再执行m函数---当然得给它传实参和调用对象
Object r=c.newInstance();// Object r = new Renter();
Object args[]= {87,3.14};
Object s=m.invoke(r, args);//※※※※ //double s = r.aa(87,3.14);
System.out.println("s="+s);
}
动态代理主要用到java.lang.reflect包中的两个类,InvocationHandler接口和Proxy类。
动态代理相较于静态代理最大的不同就是:动态代理的代理类不需要手动生成,
该代理类是在运行期间动态生成的,这个动态生成的代理类已经实现代理对象的
相关接口(Interface)。在生成代理类的同时,
必须提供一个handler,这个handler有InvocationHandler提供,
同时用来接管实际的工作。
动态代理, 技术入口: java.lang.reflect包中,
Object proxiedObj=Proxy.newProxyInstance(loader, interfaces, h)
1.loader即当前类的加载器:假设我们当前的类是ProxyDemo:那么loader=ProxyDemo.class.getClassLoader(),
2.interfaces即InvocationHandler接口 :
每一个动态代理类都必须实现InvocationHandler接口,InvocationHandler是代理实例(代理的真实对象),的调用处理程序实现的
接口,当通过代理实例调用一个方法的时候,该方法的调用就会指派到它调用处理程序(InvocationHandler接口)的 invoke
方法。
那上面的例子那么这里就是:
r.getClass().getInterfaces(),
//new Class[]{IRenter.class}, //和上一句一样
3.invoke方法声明:
h即是invoke方法 也是监听要代理的方法:
回调函数,通过代理后的对象执行的所有方法(接口中有的)都会自动进入这个方法来实现"所调方法的功能
Object returnValue=method.invoke(r, args);//放行,即执行所代理对象原来的方法,
按上面的例子即是rent()方法
new InvocationHandler() {
@Override//回调函数,通过代理后的对象执行的所有方法(接口中有的)都会自动进入这个方法来实现"所调方法的功能"
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { //proxy即是proxiedObj
System.out.println("前面拦拦,做些事情如收代理费.....");
Object returnValue=method.invoke(r, args);//放行
System.out.println("后面拦拦,做些事情如发个卡片,留个电话...");
return returnValue;
//return method.invoke(r, args); //简单放行
}
});
综上所述;代码如下;
@Test
public void demo2() throws Exception{
final Renter r=new Renter();
Object proxiedObj=Proxy.newProxyInstance(
ProxyDemo.class.getClassLoader(),
r.getClass().getInterfaces(),
//new Class[]{IRenter.class}, //和一句一样
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { //proxy即是proxiedObj
System.out.println("前面拦拦,做些事情如收代理费.....");
Object returnValue=method.invoke(r, args);//放行
System.out.println("后面拦拦,做些事情如发个卡片,留个电话...");
return returnValue;
//return method.invoke(r, args); //简单放行
}
});
IRenter ir=(IRenter) proxiedObj;
ir.rent();
}
结果;
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类(JDK). Proxy类根据
已提供的相关接口和被代理的类就可以利用反射机制得到实现的方法,根据Proxy类的newProxyInstance(...)方法生成代理类。
即技术入口:Object proxiedObj=Proxy.newProxyInstance(loader, interfaces, h)
假设这里需求是要代理一下list:
//代理一下List list = new ArrayList();
@Test
public void demo3() throws Exception{
Object proxyObj=Proxy.newProxyInstance(
ProxyDemo.class.getClassLoader(),
new Class[]{List.class},
new MyInvocation());
List list=(List) proxyObj;
list.add("hncu");
list.add(12);
list.add(3.14);
int len = list.size();
for(int i=0; i<len; i++){
System.out.println(list.get(i));
}
}
}
class MyInvocation implements InvocationHandler{
final List srcList=new ArrayList();//原型对象
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(args==null || args.length==0){
System.out.println("进来了,,你正在调用"+method.getName()+",空参");
}else{
System.out.println("进来了,,你正在调用"+method.getName()+",参数1是:"+args[0]);
}
return method.invoke(srcList, args); //放行
}
}
下面写一个动态代理的工具类,也是以后会经常用到的工具类:
同样用一些简单的类做实例:
两个接口(这里为了方便放一起):
public interface IAnimal {
public void run();
}
public interface IPerson {
public void sayHi();
}
两个实现类
public class Person implements IPerson {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void sayHi() {
System.out.println("您好,我叫"+name);
}
}
public class Dog implements IAnimal {
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("小狗"+name+"正在跑...");
}
}
真正的工具类:
个人认为这里最巧妙的思想也是经常会被忘记的地方:还是构造传参...
通过构造传参,即拿到原型对象,不同类传进来的是什么对象就是什么对象
package cn.hncu.proxy.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtils implements InvocationHandler {
private Object srcObj;
public ProxyUtils(Object srcObj) {
super();
this.srcObj = srcObj;
}
public static Object getProxy(Object srcObj) {
Object proxiedObj = Proxy.newProxyInstance(ProxyUtils.class
.getClassLoader(), srcObj.getClass().getInterfaces(),
new ProxyUtils(srcObj));
return proxiedObj;
}
@Override
// 拦截动作
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("在通用代理工具中,前面拦拦...");
Object returnValue = method.invoke(srcObj, args);
// 放行
System.out.println("在通用代理工具中,后面拦拦...");
return returnValue;
}
}
测试类:
package cn.hncu.proxy.utils;
import org.junit.Test;
public class UseProxyuUtils {
@Test
public void demo(){
Dog dog = new Dog("小布丁");
IAnimal dog2 =(IAnimal) ProxyUtils.getProxy(dog);
dog2.run();
IPerson p2 = (IPerson) ProxyUtils.getProxy( new Person("张三") );
p2.sayHi();
}
}