Java中代理模式有什么用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
代理模式在实际开发中的遇到的比较多,Spring的AOP还有RPC中用到。学习设计思想也是很有必要,弄明白其原理,对日后工作和学习中有很大的帮助。
代理模式可以理解为:在不改变源码的情况下,实现对目标对象的功能扩展、增强。
代理模式可分为两种:静态代理和动态代理;动态代理有分为JDK代理和cglib代理。
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
拿海淘购物举例,我们需要找海淘平台让他们帮我们在海外商场购买物美价廉的商品,然后付款,收货。代码如下:
先定义一个抽象对象角色。
public interface Item { void shopping(); }
目标(被代理)对象角色实现抽象对象角色。
public class Person implements Item { @Override public void shopping() { System.out.println("购物"); } }
代理对象角色和目标(被代理)对象角色实现同一接口,代理对象内部含有目标(被代理)对象的引用。
public class PersonProxy implements Item { private Person person; public PersonProxy(Person person) { this.person = person; } public void begin() { System.out.println("登陆海淘平台,挑选中意的商品"); } public void end() { System.out.println("提交订单,付款,等待收获"); } @Override public void shopping() { begin(); person.shopping(); end(); } }
测试:
public class Ceshi { public static void main(String[] args) { Person person = new Person(); PersonProxy personProxy = new PersonProxy(person); personProxy.shopping(); } }
运行结果:
登陆海淘平台,挑选中意的商品 购物 提交订单,付款,等待收获 Process finished with exit code 0
总结:
静态代理是我们自己写的代理类,是在运行前就编译好的,只能代理某一类情况,扩展起来不方便,需修改代码,继续增加代理类。
代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(Proxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
代理类所在包:java.lang.reflect.Proxy,可以看出JDk代理是通过Java反射实现的。
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,
:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,
:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h
:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
代理步骤:
定义一个事件管理器类实现invocationHandle接口,并重写invoke(代理类,被代理的方法,方法的参数列表)方法。
实现被代理类及其实现的接口。
调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代理实例。
通过该代理实例调用方法。
创建抽象对象
public interface Animal { void living(); void eating(); }
创建目标(被代理)对象
public class Person implements Animal { @Override public void living() { System.out.println("谈恋爱"); } @Override public void eating() { System.out.println("吃早餐"); } }
创建代理生成器
public class ProxyInstance implements InvocationHandler { private Object target; public Object createProxy(Object target) { this.target = target; Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return o; } public void begin() { System.out.println("起床"); } public void end() { System.out.println("睡觉"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { begin(); Object obj = method.invoke(target, args); end(); return obj; } }
测试
@Test public void demo() { Person person = new Person(); Animal proxy = (Animal) new ProxyInstance().createProxy(person); proxy.living(); proxy.eating(); }
结果
起床 谈恋爱 睡觉 起床 吃早餐 睡觉 Process finished with exit code 0
从上看可以看出,JDK代理,可以对不同的方法进行动态的代理增强。(对eating和living进行增强代理,区别于静态代理,需要手动去对不同的方法进行增强代理)
我们再试一下其他类
public class Cat implements Animal { @Override public void living() { System.out.println("晒太阳"); } @Override public void eating() { System.out.println("吃猫粮"); } } public class Dog implements Animal { @Override public void living() { System.out.println("拆家"); } @Override public void eating() { System.out.println("吃狗粮"); } }
测试
@Test public void demo1() { Cat cat = new Cat(); Animal proxy = (Animal) new ProxyInstance().createProxy(cat); proxy.living(); proxy.eating(); } @Test public void demo2() { Dog dog = new Dog(); Animal proxy = (Animal) new ProxyInstance().createProxy(dog); proxy.living(); proxy.eating(); }
结果
起床 晒太阳 睡觉 起床 吃猫粮 睡觉 Process finished with exit code 0 起床 拆家 睡觉 起床 吃狗粮 睡觉 Process finished with exit code 0
从上面可以看出来,JDK代理可以对不同的类动态的生成代理对象。
原理:
JDK代理是通过实现接口,并通过反射根据类名进行动态生成代理对象,并根据方法名,由动态生成的代理对象对相应的方法进行增强。(可以参考源码)
动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:cglib代理。
cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用cglib实现.
cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
cglib子类代理实现方法:
1. 需要引入cglib的jar文件,Spring的核心包中已经包括了cglib功能。
2. 引入功能包后,就可以在内存中动态构建子类。
3. 代理的类不能为final,否则报错。
4. 目标对象的方法如果为final/static,就无法实现代理。
创建目标类,没有实现接口
public class Person { public void living() { System.out.println("谈恋爱"); } }
创建代理对象
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { //目标对象 private Object target; public CglibProxy(Object target) { this.target = target; } //生成代理对象的方法 public Object createProxy(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); Object o = enhancer.create(); return o; } //在目标方法前加强 public void begin() { System.out.println("起床"); } //在目标方法后加强 public void end() { System.out.println("睡觉"); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { begin(); //代理执行目标方法 Object invoke = method.invoke(target, objects); end(); return invoke; } }
测试
public class Demo { @Test public void test(){ Person p = new Person(); Person proxy = (Person) new CglibProxy(p).createProxy(); proxy.living(); } }
结果
起床 谈恋爱 睡觉 Process finished with exit code 0
再给Person类添加其他方法
public class Person { public void living() { System.out.println("谈恋爱"); } public void working(){ System.out.println("去公司上班"); } }
测试
public class Demo { @Test public void test(){ Person p = new Person(); Person proxy = (Person) new CglibProxy(p).createProxy(); proxy.living(); proxy.working(); } }
结果
起床 谈恋爱 睡觉 起床 去公司上班 睡觉 Process finished with exit code 0
我们在试一下其他的类,看看能不能一样实现代理
public class Cat { public void living(){ System.out.println("晒太阳"); } public void working(){ System.out.println("抓老鼠"); } }
测试
public class Demo { @Test public void test(){ Person p = new Person(); Person proxy = (Person) new CglibProxy(p).createProxy(); proxy.living(); proxy.working(); } @Test public void test1(){ Cat cat = new Cat(); Cat proxy = (Cat) new CglibProxy(cat).createProxy(); proxy.living(); proxy.working(); } }
结果
起床 晒太阳 睡觉 起床 抓老鼠 睡觉 Process finished with exit code 0
看完上述内容,你们掌握Java中代理模式有什么用的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。