Java动态代理
少年. 人气:0"代理"这个词相信大家并不陌生,简单来说就是代替厂家来售卖商品,代理替代厂家售卖商品,顾客找代理购买商品。也就是说:1)顾客和厂家之间是不可见的,顾客不知道背后的厂家是谁。2)代理可以对顾客进行“定位”,更精确的售卖给需要的客户群体。
代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问,也就是创建一个代理对象作为客户端和目标对象之间的中介,主要目的就是保护目标对象或增强目标对象
通过使用代理模式,通常有以下两个优点:
\1) 可以隐藏被代理类的实现
\2) 可以实现客户与被代理类间的解耦,在不修改被代理类代码的情况下能够做一些额外的处理
静态代理
所谓的动态代理,就是通过声明一个明确的代理类来访问源对象,一个代理只能服务于一种产品,当有n种产品时,就需要n个代理,这样就不利于业务的发展。
举例:我们有两个接口,Mouse和Keyboard,每个接口都有一个实现类
实现类中的代码如下:
public class LogitechMouse implements Mouse{ @Override public void sell() { System.out.println("出售罗技鼠标"); } } public class HHKBKeyboard implements Keyboard{ @Override public void sell() { System.out.println("出售HHKB键盘"); } }
现在我们要做的就是让代理在调用sell()
前输出一句售前了解,调用后输出一句售后服务
那我们只需写两个代理类MouseProxy
和KeyboardProxy
public class MouseProxy implements Mouse { private Mouse mouse; public MouseProxy(Mouse mouse) { this.mouse = mouse; } @Override public void sell() { System.out.println("售前了解"); mouse.sell(); System.out.println("售后服务"); } } public class KeyboardProxy implements Keyboard{ private Keyboard keyboard; public KeyboardProxy(Keyboard keyboard) { this.keyboard = keyboard; } @Override public void sell() { System.out.println("售前了解"); keyboard.sell(); System.out.println("售后服务"); } }
最终执行为:
public class Test { public static void main(String[] args) { Mouse logitechMouse = new LogitechMouse(); MouseProxy mouseProxy = new MouseProxy(logitechMouse); mouseProxy.sell(); Keyboard hhkbKeyboard = new HHKBKeyboard(); KeyboardProxy keyboardProxy = new KeyboardProxy(hhkbKeyboard); keyboardProxy.sell(); } }
输出:
售前了解
出售罗技鼠标
售后服务
售前了解
出售HHKB键盘
售后服务
静态代理的代码非常简单易懂,这种模式虽好,但是也有明显的缺点:
- 会存在大量冗余的代理类,这里只有两个接口,如果有n个接口,那么就要定义n个代理类。
- 不易维护,一旦接口更改,代理类和被代理类都要更改。
那么这个时候就可以使用动态代理来解决了
动态代理
代理类在程序运行时创建代理的方式叫动态代理,也就是说代理类并不是在java代码中定义的,而是在运行的时候动态生成的
JDK动态代理
JDK从1.3版本就开始支持动态代理类的创建。主要核心类只有2个:java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
还是上面的例子,用JDK动态代理如下:
public class JDKProxy implements InvocationHandler { private Object object; public JDKProxy(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("售前了解"); Object invoke = method.invoke(object, args); System.out.println("售后服务"); return invoke; } }
当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,
代理类对象作为proxy参数传入,
参数method标识了我们具体调用的是代理类的哪个方法,
args为这个方法的参数。
这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们可以在中介类的invoke方法中实现输出售前了解,再调用被代理类的方法,再输出售后服务。
执行代码
public class Test { public static void main(String[] args) { Mouse logitechMouse = new LogitechMouse(); JDKProxy jdkProxy = new JDKProxy(logitechMouse); Mouse mouse= (Mouse)Proxy.newProxyInstance(jdkProxy.getClass().getClassLoader(), new Class[]{Mouse.class}, jdkProxy); mouse.sell(); HHKBKeyboard hhkbKeyboard = new HHKBKeyboard(); JDKProxy jdkProxy1 = new JDKProxy(hhkbKeyboard); Keyboard keyboard = (Keyboard)Proxy.newProxyInstance(jdkProxy1.getClass().getClassLoader(), new Class[]{Keyboard.class}, jdkProxy1); keyboard.sell(); } }
可以看到无论多少个接口,只需要一个代理类就可以了。
CGLIB动态代理
代理类:
public class CGLIBProcy implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); private Object object; public CGLIBProcy(Object object) { this.object = object; } public Object getProxy(){ //设置需要创建子类的类 enhancer.setSuperclass(object.getClass()); enhancer.setCallback(this); //创建代理对象 return enhancer.create(); } // o: cglib 动态生成的代理类的实例 // method:实体类所调用的都被代理的方法的引用 // objects 参数列表 // methodProxy:生成的代理类对方法的代理引用 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("售前了解"); Object invoke = method.invoke(object, objects); System.out.println("售后处理"); return invoke; } }
执行代码:
public class Test { public static void main(String[] args) { Mouse logitechMouse = new LogitechMouse(); CGLIBProcy cglibProcy = new CGLIBProcy(logitechMouse); Mouse proxy = (Mouse)cglibProcy.getProxy(); proxy.sell(); cglibProcy = new CGLIBProcy(new HHKBKeyboard()); Keyboard keyboard = (Keyboard)cglibProcy.getProxy(); keyboard.sell(); } }
JDK代理与CGLIB代理的区别
- JDK动态代理实现接口,CGLIB动态继承思想
- JDK动态代理(目标对象存在接口时)执行效率高于CIGLIB
- 如果对象有接口实现,选择JDK代理,如果没有接口实现选择CGILB代理
加载全部内容