温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java中代理模式有什么用

发布时间:2021-07-01 14:55:14 来源:亿速云 阅读:120 作者:Leah 栏目:大数据

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

总结:

    静态代理是我们自己写的代理类,是在运行前就编译好的,只能代理某一类情况,扩展起来不方便,需修改代码,继续增加代理类。

动态代理

JDK代理

    代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(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:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

代理步骤

  1. 定义一个事件管理器类实现invocationHandle接口,并重写invoke(代理类,被代理的方法,方法的参数列表)方法。 

  2. 实现被代理类及其实现的接口。

  3. 调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代理实例。 

  4. 通过该代理实例调用方法。

创建抽象对象

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代理。

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中代理模式有什么用的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI