使用一个代理将对象包装起来, 而后用该代理对象取代原始对象。任何对原始对象的调用都要经过代理。代理对象决定是否以及什么时候将方法调用转到原始对象上。java
简单例子:api
小强意外成为了一个网红明星,此时他请了一个专业的经纪人进行各类事务的管理,小强的演出活动啊都得通过经纪人的赞成。此时小强是被代理对象,经纪人是代理对象。数组
此时快乐大本营想请小强去参加节目,须要找到小强的经纪人,小强的经纪人而后进行排挡,找小强去完成唱歌,才艺展现等动做。ide
代理对象和被代理对象(目标对象)实现同一接口。优势是不用修改目标对象的源码,扩展其功能。测试
建立一个接口,这个接口是代理工厂(代理类)和Nike工厂(被代理类)的公共接口,this
经过代理工厂的方法调用同时也调用了Nike工厂的同名方法,完成不修改被代理类的源码而扩展其功能。.net
共同接口代理
interface ClothFactory{ void produceCloth(); }
代理类code
// 代理类 class ProxyClothFactory implements ClothFactory { private ClothFactory factory;//用被代理类对象进行实例化 public ProxyClothFactory(ClothFactory factory) { this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工厂作一些准备工做"); factory.produceCloth(); System.out.println("代理工厂作一些后续的工做"); } }
被代理类对象
// 被代理类 class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("nike工厂生产运动服"); } }
测试代码
public class StaticProxyTest { public static void main(String[] args) { // 建立被代理类对象 NikeClothFactory nikeClothFactory = new NikeClothFactory(); // 建立代理类对象 ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory); proxyClothFactory.produceCloth(); } }
输出结果
代理工厂作一些准备工做
nike工厂生产运动服
代理工厂作一些后续的工做
动态代理是指客户经过代理类来调用其它对象的方法,而且是在程序运行时根据须要动态建立目标类的代理对象。
代理对象不须要实现接口,可是要求被代理对象(目标对象)必须实现接口,不然不能使用动态代理。
Proxy
:专门完成代理的操做类,是全部动态代理类的父类。经过此类为一个或多个接口动态地生成实现类。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
直接建立一个动态代理对象
参数详解:
ClassLoader loader
:和被代理对象使用相同的类加载器Class<?>[] interfaces
:和被代理对象具备相同的行为,实现相同的接口InvocationHandler h
:获得InvocationHandler接口的实现类实例实现动态代理,要解决的问题
具体步骤以下:
建立被代理类和接口
接口:Human接口
//共同接口 interface Human{ String getBelief(); void eat(String food); }
被代理类:SuperMan类
// 被代理类 class SuperMan implements Human{ @Override public String getBelief(){ return "我能飞"; } @Override public void eat(String food){ System.out.println("我喜欢吃"+food); } }
建立一个用来生产代理类的工厂,该工厂有一个静态方法getProxyInstance
能够返回一个代理类的对象,该方法需传入被代理类对象。
class ProxyFactory{ // 调用此方法,返回一个代理类的对象。须要传入被代理类对象 public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); // 哪一个类加载的?被代理类 // 被代理类实现了哪一个接口?获得被代理类实现的所有接口 // 获得InvocationHandler接口的实现类实例 return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } }
建立一个实现接口InvocationHandler
的类,实现inoke方法,以完成代理的具体操做。当咱们经过代理类的对象调用方法a时,就会自动的调用以下方法invoke(),将被代理类要执行的方法a的功能声明在invoke()中。
class MyInvocationHandler implements InvocationHandler{ private Object obj;//须要使用被代理类的对象进行赋值 public void bind(Object obj){ this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // obj:被代理类对象 // method:即为代理类对象调用的方法,此方法做为被代理类对象要调用的方法 // args: 方法调用时所须要的参数 Object returnValue = method.invoke(obj,args); //该返回值做为当前类中的invoke()的返回值 return returnValue; } }
测试类
public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan();//建立一个被代理类对象 // 动态建立代理类 Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan); String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("肉夹馍"); } }
输出结果
咱们发现,只须要知道接口和被代理类,就能实现建立一个代理类。把上面静态代理部分的例子拿过来
NikeClothFactory nike = new NikeClothFactory(); ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(nike); proxyInstance1.produceCloth(); //nike工厂生产运动服
动态代理
特色:字节码随用随建立,随用随加载;
做用:不修改源码的基础上对方法加强。
提供者: JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。
实现步骤
建立一个接口,该接口能够理解成对生产厂家的规范
/** * 对生产厂家要求的接口 */ public interface IProducer { /** * 销售 * @param money */ public void saleProduct(float money); /** * 售后 * @param money */ public void afterService(float money); }
代理类:是一个生产者,它实现了接口,具备接口中的方法,也就是说符合生产厂家的要求,产品销售和售后是ok的。
/** * 一个生产者 */ public class Producer implements IProducer{ /** * 销售 * @param money */ public void saleProduct(float money){ System.out.println("销售产品,并拿到钱:"+money); } /** * 售后 * @param money */ public void afterService(float money){ System.out.println("提供售后服务,并拿到钱:"+money); } }
模拟一个消费者,若是该消费者若是没有经过代理商去买电脑,而是直接去到厂家处购买。但现实中,不多有人这样购买。通常都是厂家把产品交给代理商进行销售以及提供售后服务,顾客只需跟代理商进行交易,而不直接与厂家进行联系。
没有代理以前
Producer producer1 = new Producer(); producer.saleProduct(10000f);//销售产品,并拿到钱:10000.0
有了代理,代理要从中提取20%的手续费。那么生产者只能获得80%的钱
public class Client { public static void main(String[] args) { final Producer producer = new Producer(); IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //提供加强的代码 Object returnValue = null; //1.获取方法执行的参数 Float money = (Float)args[0]; //2.判断当前方法是否是销售 if("saleProduct".equals(method.getName())) { returnValue = method.invoke(producer, money*0.8f); } return returnValue; } }); proxyProducer.saleProduct(10000f); } }
涉及的参数:
Proxy类
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) ClassLoader:类加载器 代理谁写谁的类加载器,它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。 Class[]:字节码数组,它是用于让代理对象和被代理对象有相同方法。固定写法。 InvocationHandler:用于提供加强的代码 它是让咱们写如何代理。咱们通常都是些一个该接口的实现类,一般状况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。
InvocationHandler接口
public Object invoke(Object proxy, Method method, Object[] args) 做用:执行被代理对象的任何接口方法都会通过该方法 方法参数的含义 proxy 代理对象的引用 method 当前执行的方法 args 当前执行方法所需的参数
提供者:第三方的 cglib。
要求:被代理类不能是最终类(用 final 修饰的类)。
涉及的类:Enhancer
如何建立代理对象:使用Enhancer类中的create(Class,Callback)方法
create方法的参数:
Class:字节码,它是用于指定被代理对象的字节码。
Callback:如何代理,即就是用于提供加强的代码。咱们通常都是写一个该接口的实现类,一般状况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。咱们通常写的都是该接口的子接口实现类:MethodInterceptor
仍是消费者买产品的例子,只不过不让它实现接口。
举例:
生产者
/** * 一个生产者 */ public class Producer { /** * 销售 * @param money */ public void saleProduct(float money){ System.out.println("销售产品,并拿到钱:"+money); } /** * 售后 * @param money */ public void afterService(float money){ System.out.println("提供售后服务,并拿到钱:"+money); } }
消费者
/** * 模拟一个消费者 */ public class Client { public static void main(String[] args) { final Producer producer = new Producer(); Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() { /** * 执行被代理对象的任何方法都会通过该方法 * @param proxy * @param method * @param args * 以上三个参数和基于接口的动态代理中invoke方法的参数是同样的 * @param methodProxy :当前执行方法的代理对象 * @return * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //提供加强的代码 Object returnValue = null; //1.获取方法执行的参数 Float money = (Float)args[0]; //2.判断当前方法是否是销售 if("saleProduct".equals(method.getName())) { returnValue = method.invoke(producer, money*0.8f); } return returnValue; } }); cglibProducer.saleProduct(12000f); } }
参考连接: