--------- 、、期待与您交流! ----------
一、什么是代理
概念:为其他对象提供一功能以控制对这个对象的访问,这个功能就是代理。生活中的代理
就是一个角色代表别一个角色来完成某些特定的功能。
1、代理对象在客户端和目的对象之间起着中介的作用,比如某一批发商从总商以每斤2元购得100斤苹果,再以4元每斤卖给第三方客户,那么这个批发商就相当于代理,它可以对苹果(对象)进行一系列的包装,比如价格涨了2元,这就是包装后的结果。
2、在程序中,如我们要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如:异常处理、日志、计算方法的运行时间、事务管理、等等,这时候运用代理也是非常方便的。
二、面向方面的编程
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
三、代理模式
代理模式中常见三种角色: 1. 抽象角色 2. 代理角色 3. 真实角色(实际被代理角色)
1.静态代理
(1)、抽象角色
(1)、抽象角色package com.itheima.proxytest1;/** * @author Administrator *先定义一个水果的接口,看见有的人是定义一个抽象类,并提供抽象方法,也 *可以实现,但因为Java只支持继承,而可以实现多个接口,所以笔者觉得还是 *定义成接口好些。 *有价格和颜色两种抽象方法 */public interface Friut{ public abstract double price(); public abstract String color();}
(2)、真实角色1
package com.itheima.proxytest1;/** * @author Administrator *定义一个苹果的类,并实现Friut接口,从而作为代理的目标,也就是 *被代理的真实的对象。 */public class Apple implements Friut{ @Override public double price() { System.out.println("2元"); return 2; } @Override public String color() { System.out.println("青色"); return "青色"; }}
(3)、真实角色2
package com.itheima.proxytest1;/** * @author Administrator *再定义一个葡萄的类,并实现Friut接口,从而作为代理的另一目标,也就是 *被代理的又一真实的对象,效果和苹果一样。 */public class Grape implements Friut{ @Override public double price() { System.out.println("1.5元"); return 1.5; } @Override public String color() { System.out.println("红色"); return "红色"; }}
(4)、代理角色
package com.itheima.proxytest1;/** * @author Administrator *这是代理角色,通过实现抽象方法Friut,那么就能作为任意水果Friut的 *对象的代理。 */public class ProxyFriut implements Friut{ //建立一个真实对象的引用,以便对传进来的真实对象进行附加操作。 private Friut friut; private Advice advice = new MyAdvice(); //private Advice advice;错误 //默认代理苹果Apple ProxyFriut() { this.friut = new Apple(); } //传进来什么水果就返回什么水果, ProxyFriut(Friut friut){ this.friut = friut; } @Override public double price() { //附加操作 advice.beforeMethod(); //调用真实对象的price()方法 double price = this.friut.price(); //附加操作 advice.afterMehotd(); return price; } @Override public String color() { //附加操作 advice.beforeMethod(); //调用真实对象的color()方法 String color = this.friut.color(); //附加操作 advice.afterMehotd(); return color; }}
(5)、事务处理抽象角色
package com.itheima.proxytest1;/** * @author Administrator *因为在调用被代理对象方法前后进行修饰时,我们并不知道 *要修饰了内容,为了提高扩展性,我们就再定义一个Advice *接口,专门用于事务处理。事务处理的内容的不同,就通过 *实现这个接口的并创建不同类来实现。 */public interface Advice{ //方法前 public abstract void beforeMethod(); //方法后 public abstract void afterMehotd();}
(6)、事务处理角色
package com.itheima.proxytest1;/** * @author Administrator *通过实现Advice接口,并覆盖其抽象方法,从而可以对被代理 *的对象进行“修饰”;如下只是一些简单操作。 */public class MyAdvice implements Advice{ @Override public void beforeMethod() { System.out.println("beforMethod:进货:"); } @Override public void afterMehotd() { System.out.println("afterMethod:出货:"); }}
(7)、客户(测试类)
package com.itheima.proxytest1;/** * @author Administrator *定义一个客户,用于测试 */public class Client{ public static void main(String[] args) { //首先把代理找出来,默认代理苹果 ProxyFriut proxy = new ProxyFriut(); //客户就通过代理调用方法。而实际上就是在调用真实对象的方法。 proxy.color(); proxy.price(); }}/*-----------------------------------运行结果:beforMethod:进货:青色afterMethod:出货:beforMethod:进货:2元afterMethod:出货:-----------------------------------*/
2.动态代理
(1)、抽象角色
package com.itheima.proxytest2;public interface Friut { public abstract double price(); public abstract String color();}
(2)、真实角色1
package com.itheima.proxytest2;public class Apple implements Friut{ @Override public double price() { return 2; } @Override public String color() { return "青色"; }}
(3)、真实角色2
package com.itheima.proxytest2;public class Grape implements Friut{ @Override public double price() { // TODO Auto-generated method stub return 1.5; } @Override public String color() { // TODO Auto-generated method stub return "青色"; }}
(4)、代理角色(调用处理器)
package com.itheima.proxytest2;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler{ private Friut friut; private Advice advice = new MyAdvice(); MyInvocationHandler(Friut friut) { this.friut = friut; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); Object retVal = method.invoke(friut, args); System.out.println(retVal); advice.afterMehotd(method); return retVal; }}
(5)、事务处理抽象角色
package com.itheima.proxytest2;import java.lang.reflect.Method;public interface Advice{ void beforeMethod(Method method); void afterMehotd(Method method);}
(6)、事务处理角色
package com.itheima.proxytest2;import java.lang.reflect.Method;public class MyAdvice implements Advice{ @Override public void beforeMethod(Method method) { System.out.println("beforMethod:进货:" + method.getName()); } @Override public void afterMehotd(Method method) { System.out.println("afterMethod:出货:" + method.getName()); }}
(7)、客户端(测试类)
真正创建代理类在这里面,当然实际开发过程,创建代理类会定义成方法再封装到一个类里面。
并且可以把InvocationHandler对象以ni名内部类的形式封装到代理对象中,显得更简便。之后会以代码展现。
package com.itheima.proxytest2;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class Client { public static void main(String[] args) throws Exception { //创建需要被代理的角色 Friut friutType1 = new Grape(); //创建调用处理器,并将要被代理的角色传递进去。 InvocationHandler handler = new MyInvocationHandler(friutType1); /* 获取动态代理类的Class对象,它需要传递一个类加载器和一个接口的Class对象。 Class clazz = Proxy.getProxyClass(Friut.class.getClassLoader(), Friut.class); 获得动态代理类的唯一参数的构造方法,该参数是一个调用处理器。 Constructor constructor = clazz.getConstructor(InvocationHandler.class); 有了该构造方法就可以创建实际对象,该实际对象就是要被代理的对象。 Friut friut = (Friut)constructor.newInstance(handler); 这是下面的具体写法。 */ Friut friut1 = (Friut)Proxy.newProxyInstance(friutType1.getClass().getClassLoader(),friutType1.getClass().getInterfaces(), handler); //调用被代理角色的方法 friut1.color(); friut1.price(); }}/*---------------------------------运行结果: beforMethod:进货:color 青色 afterMethod:出货:color beforMethod:进货:price 1.5 afterMethod:出货:price---------------------------------*/
3.比较两种代理方式:
①静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理的.class文件就已经存在了。 动态代理:在程序运行时,运用反射机制动态创建而成。
②如果我还要满足一个功能,该功能获取进货,和出货所需的时间,一般不会选择修改原代码,那么这时候,如果采用静态代理的形式,我们需要再创建一个代理对象专门用来统计,而如果采用动态代理我们只要再定义一个调用处理器即可,而再创建代理对象时只需要把新定义好的一个调用处理器。
③发现,如果苹果再实现了一个Infomation的接口,而Grape只实现Friut接口,这时,如果要再代理苹果,那么在ProxyFriut必须要再定义一个代理,否则ProxyFriut必须再实现Infomatin接口,那么代码中必须有Apple对象,而当那天代理商觉得苹果不好卖,不代理苹果了,这时我们应该取消这个代理链接,而实际并非如此,所以静态代理中,现每一个代理类只能为一个接口服务,这样一来程序开发
中必然会产生过多的代理。这时用动态代理,就不会产生这种情况了,
4、关于动态代理
Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
(1)Interface InvocationHandler:该接口中仅定义了一个方法
Object:invoke(Object obj,Method method, Object[] args)。
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法(如:上例中的color,price),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2)Proxy:该类即为动态代理类,作用类似于上例中的ProxyFriut,
其中主要包含以下内容:
①Protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
其代码实现如下:
/*由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用*/private Proxy() {}/*由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用*/protected Proxy(InvocationHandler h) {this.h = h;}
②Static Class getProxyClass (ClassLoader loader, Class[] interfaces):
获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
③Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
返回代理类的一个实例,返回后的代理类可以当作被代理类使用。
其代码实现如下:
public static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException{ // 检查 h 不为空,否则抛异常 if (h == null) { throw new NullPointerException(); } /*获得与制定类装载器和一组接口相关的代理类类型对象,说明其内部关键还是 getProxyClass*/ Class cl = getProxyClass(loader, interfaces); // 通过反射获取构造函数对象并生成代理类实例 try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); }}
(3)生成动态代理的步骤
①通过实现 InvocationHandler 接口创建自己的调用处理器;
②通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
③通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
④通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
--------- 、、期待与您交流! ----------