静态代理和动态代理

如何理解代理?

        代理是一种软件设计模式,它允许一个对象(称为代理对象)控制对另一个对象(称为真实对象)的访问。代理对象可以通过在真实对象的前后添加额外逻辑来增强或限制真实对象的行为。

简单来说代理可以帮助原对象进行功能性增强(在原有的功能上添加新的功能)。

例如:明星约谈先关业务后达成,收钱后,开始准备唱歌,而唱歌需要前需要准备场地和先关设备。此时如果该明星有以为代理(或者说经纪人)可以帮明星进行相关的业务洽谈,收取演唱费用,以及准备相关的场地和设施,明星就只负责唱歌就可以了,对明星而言这样效率更高业务能力更强了(可以理解为增强)。

静态代理案例:

使用静态代理模式来实现对明星对象的代理。静态代理是指在编译时就已经确定代理类和被代理类的关系,代理类与被代理类实现相同的接口或继承相同的父类。

具体步骤如下:

  1. 首先定义一个接口或者父类,作为明星对象和代理对象的共同接口。在这个示例中,可以定义一个名为Star的接口,其中包含了明星对象和代理对象都需要实现的方法,比如singSongs()talk()等。

  2. 创建一个真正的明星对象类Realstar,实现Star接口,并实现其中的方法。这个类是真正执行唱歌、谈话等操作的类,即被代理的对象。

  3. 创建一个代理对象类StarProxy,同样实现Star接口,并实现其中的方法。这个类中含有一个成员变量,用于存储真正的明星对象,以便在代理对象中调用。同时,还可以在代理对象中实现一些额外的操作,比如收费、约谈项目等。

  4. 在代理对象的方法中,可以根据需要在执行真正的明星对象方法之前或之后进行一些额外的操作。例如,在singSongs()方法中,可以在调用真正的明星对象的singSongs()方法之前输出一些准备信息,在方法之后输出一些结束信息。

  5. 在测试类中,实例化一个真正的明星对象Realstar,并设置其用户信息。然后,实例化一个代理对象StarProxy,并设置其用户代理信息和真正的明星对象。可以通过调用代理对象的方法来间接调用真正的明星对象的方法,同时享受到代理对象中实现的一些额外操作。

        通过这样的设计,可以在不修改真正的明星对象的情况下,增加一些额外的功能和管理操作。代理对象作为真正明星对象的中介,可以处理一些与明星工作相关的事务,起到解耦和管理的作用。

        为贴合实际便于理解定义一个people类

public class People {
    private String name;
    private int age ;

    public People() {
    }

    public People(String name) {
        this.name = name;
    }

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

明星接口

public interface Star {
    public String singSongs();
    public void pay();
    public void talk();
}

真正明星实现类

public class Realstar implements Star {
    @Autowired
    private People people;
    public void setUser(People people) {
        this.people = people;
    }
    @Override
    public String singSongs() {
        System.out.println(people.getName()+"准备唱歌");
        return people.getName()+"表演结束,说感谢粉丝观看!";
    }
    @Override
    public void pay() {
        int money=1000;
        System.out.println("为明星"+people.getName()+"付款"+money);
    }
    @Override
    public void talk() {
        System.out.println(people.getName()+"约谈演唱会的事情!");
    }
}

明星代理实现类

代理或者在此处贴合情景叫经纪人,经纪人也实现明星接口,可以理解为,经纪人是为明星做代理工作的(做一些工作管理,减轻明星负担),所以代理(或者说经纪人)需要知道明星有哪些技能好做的工作,所以说明星本来要做的工作,现在有代理去分担一部分,唱歌的主要工作还由明星本人来,代理帮助明星做一些其他工作提高效率(可以理解为代理的作用是加强了明星的业务能力)。

public class StarProxy implements Star {
@Autowired
    private People peopleProxy;
@Autowired
    private Realstar realstar;
    public void setRealstar(Realstar realstar) {
        this.realstar = realstar;
    }
    public void setUserProxy(People peopleProxy) {
        this.peopleProxy = peopleProxy;
    }
    @Override
    public String singSongs() {
        return realstar.singSongs();
    }
    @Override
    public void pay() {
        int money=2000;
        System.out.println(peopleProxy.getName()+"代理收费"+money);
    }
    @Override
    public void talk() {
        System.out.println(peopleProxy.getName()+"为明星代理约谈项目");
    }
}

 测试类

public class Test001 {
    public static void main(String[] args) {
//实例化明星实例
        Realstar realstar=new Realstar();
        realstar.setUser(new People("周华健",53));
//实例化代理实实例
        StarProxy starProxy=new StarProxy();
        starProxy.setUserProxy(new People("经纪人李明",33));
        starProxy.setRealstar(realstar);
        starProxy.talk();
        starProxy.pay();
        System.out.println(  starProxy.singSongs()
);

    }
}

上述代码简单梳理:

  1. People类是一个普通的JavaBean,用来表示一个人的信息,包括姓名和年龄。

  2. Realstar类实现了Star接口,它是一个真正的明星实现类。在这个类中,使用了@Autowired注解将People对象注入进来,通过setter方法设置。该类实现了明星接口中的方法,包括singSongs()pay()talk()。在singSongs()方法中,打印输出明星的准备信息,并返回演唱结束的提示信息;在pay()方法中,打印输出为明星付款的金额;在talk()方法中,打印输出约谈演唱会事宜的信息。

  3. StarProxy类也实现了Star接口,它是一个明星代理实现类,相当于经纪人。在这个类中,同样使用了@Autowired注解将People对象和Realstar对象注入进来。它实现了明星接口中的方法,其中singSongs()方法调用了Realstar对象的singSongs()方法,实现了对明星唱歌的代理;pay()方法打印输出代理收费的金额;talk()方法打印输出为明星代理约谈项目的信息。

通过这样的设计,可以实现对真正明星对象的代理,代理对象可以在执行真正明星对象的方法之前或之后进行一些额外的操作,如收费、管理和代办事务等。同时,通过依赖注入,注入了People对象,使得真正明星对象和代理对象都可以获取到People对象的信息,以便在方法中使用。

运行效果:

静态代理和动态代理_第1张图片

动态代理(JDK方式)

        动态代理类是在运行时动态生成的代理类,用于代理目标对象的方法调用。下面我将讲解如何创建一个动态代理类以及如何使用它。 

动态代理类的创建步骤如下:

        1. 创建一个实现了 `InvocationHandler` 接口的类,该类用于处理代理对象方法的调用逻辑。在该类中,需要重写 `invoke()` 方法。

        2. 在 `invoke()` 方法中,根据需要对不同的方法进行特定的处理。比如可以在方法调用前后进行额外的操作,或者对方法的参数进行校验等。

        3. 使用 `Proxy.newProxyInstance()` 方法创建代理对象。该方法需要传入三个参数:
                   - `ClassLoader`: 类加载器,用于加载代理类。可以使用目标对象的类加载器或者其他类加载器。
                   - `Interfaces`: 代理类需要实现的接口列表。目标对象必须实现这些接口。
                   - `InvocationHandler`: 处理代理对象方法调用的逻辑。传入上一步创建的         `InvocationHandler` 实例。

4. 将返回的代理对象强制转换为目标对象所实现的接口类型(如果有多个接口,则选择其中一个)。

使用动态代理类的步骤如下:

        1. 创建目标对象,即要被代理的对象。

        2. 创建上述提到的实现了 `InvocationHandler` 接口的类的实例,并在构造方法中传入目标对象。

        3. 调用实现类的 `creatStar()` 方法获取代理对象。这个方法内部会根据传入的目标对象创建代理对象。

        4. 通过代理对象调用方法,实际上是调用了实现类中的 `invoke()` 方法。

        总结来说,动态代理类在运行时生成,可以处理目标对象的方法调用,并且可以在方法调用前后执行额外的操作。使用动态代理类时,需要自定义一个 `InvocationHandler` 接口的实现类,并将目标对象传入该类中,然后通过 `Proxy.newProxyInstance()` 方法创建代理对象。最后,通过代理对象调用方法即可实现代理功能。

代理类:


//动态代理
public class Starproxy02 {
    @Autowired
    Realstar realstar;

    public Starproxy02(Realstar realstar) {
        this.realstar = realstar;
    }

    //    创建代理
    public Object creatStar(){
        Star proxyObject=(Star)
                Proxy.newProxyInstance(realstar.getClass().getClassLoader(), realstar.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("singSongs")){
                            System.out.println("动态代理调用singSongs");

                            return realstar.singSongs();

                        }else {
                            Object invoke = (String) method.invoke(realstar,args);
                            return invoke;
                        }
                    }
                });
        return proxyObject;
    }


}

测试类:



public class AnnoTest {
    public static void main(String[] args) {


        Realstar realstar=new Realstar();
        realstar.setUser(new People("周华健",53));
        Starproxy02 starproxy02=new Starproxy02(realstar);
        Star star1= (Star) starproxy02.creatStar();
        System.out.println(star1.singSongs());
        star1.talk();

    }
}

 这段代码是使用 JDK 动态代理实现的代理类。下面对代码进行分析和讲解:

  1. Starproxy02 类是代理类,它实现了动态代理的逻辑。在构造方法中接收一个 Realstar 对象作为参数,表示要代理的目标对象。

  2. creatStar() 方法用于创建代理对象。在这个方法中,通过调用 Proxy.newProxyInstance() 方法创建代理对象。该方法需要传入三个参数:

    • ClassLoader: 类加载器,用来加载代理类。
    • Interfaces: 代理类需要实现的接口列表。
    • InvocationHandler: 处理代理对象方法调用的逻辑。
  3. InvocationHandler 是一个接口,定义了一个 invoke() 方法,用于处理代理对象的方法调用。在这段代码中,创建了一个匿名内部类实现了 InvocationHandler 接口。在这个实现类中,重写了 invoke() 方法。

  4. invoke() 方法中,首先通过判断 method.getName().equals("singSongs") 判断方法是否是 singSongs() 方法,如果是,则打印输出 "动态代理调用singSongs",然后调用真实对象的 singSongs() 方法并返回结果。

  5. 如果不是 singSongs() 方法,说明是其他方法,直接通过反射调用真实对象的相应方法,并返回结果。

  6. AnnoTest 类的 main() 方法中,创建了一个 Realstar 对象 realstar,并将其传入 Starproxy02 的构造方法中初始化代理类。然后通过调用 creatStar() 方法得到代理对象 star1

  7. 最后,通过调用代理对象的方法,可以看到对于 singSongs() 方法,会输出 "动态代理调用singSongs",并且调用了真实对象的 singSongs() 方法;对于其他方法,则直接调用真实对象的相应方法。

这段代码通过 JDK 提供的动态代理机制,实现了对 Realstar 类的代理。在代理过程中,利用了 InvocationHandler 接口的 invoke() 方法动态处理方法调用。使用动态代理能够更加灵活地处理不同的目标对象,避免了手动编写大量的代理类。

 

 运行效果:

静态代理和动态代理_第2张图片

动态代理(cglib方式)

CGLIB(Code Generation Library)是一个基于字节码生成库的第三方库,用于生成和修改Java类的字节码。它可以实现对类的动态代理,即创建目标类的子类作为代理类。

CGLIB的动态代理与基于接口的 JDK 动态代理有所不同。JDK 动态代理要求目标对象实现至少一个接口,而 CGLIB 动态代理则可以对没有实现接口的类进行代理。CGLIB 动态代理通过生成目标类的子类来实现代理,子类继承了目标类的行为,并且可以覆盖或增加新的方法。

使用 CGLIB 的动态代理,你需要引入 CGLIB 库并使用它提供的 Enhancer 类来创建代理对象。以下是一个示例:


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibDynamicProxy implements MethodInterceptor {

    public Object createProxy(Object target) {
        // 创建 Enhancer 对象,用于生成代理类
        Enhancer enhancer = new Enhancer();
        // 设置目标类作为父类
        enhancer.setSuperclass(target.getClass());
        // 设置回调对象为当前对象
        enhancer.setCallback(this);
        // 创建代理类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在方法调用前进行一些处理
        System.out.println("Before method invocation");

        // 调用目标对象的方法
        Object result = proxy.invokeSuper(obj, args);

        // 在方法调用后进行一些处理
        System.out.println("After method invocation");

        return result;
    }
}

上述示例中,`CglibDynamicProxy` 实现了 `MethodInterceptor` 接口,并重写了 `intercept` 方法。在 `createProxy` 方法中,创建了一个 `Enhancer` 对象,并设置了目标类和回调对象。在 `intercept` 方法中,可以实现对目标类方法的增强逻辑。`proxy.invokeSuper(obj, args)` 方法通过反射调用目标对象的方法。

使用 CGLIB 创建代理对象的示例代码如下:


RealStar realStar = new RealStar();
CglibDynamicProxy proxy = new CglibDynamicProxy();
Star starProxy = (Star) proxy.createProxy(realStar);

starProxy.singSongs();  // 调用代理对象的方法

在上述代码中,`RealStar` 是目标对象,`CglibDynamicProxy` 是代理对象,通过 `proxy.createProxy(realStar)` 创建了代理对象,并将其转型为 `Star` 接口类型。然后可以调用代理对象的方法,代理对象会在方法调用前后添加额外的处理逻辑。

需要注意的是,CGLIB 动态代理的原理是生成目标类的子类,因此目标类不能是 `final` 类型,并且被代理的方法不能是 `final` 或 `static` 的。同时,如果目标类的方法是 `final` 类型的,那么无法对该方法进行代理。

动态代理和静态代理的区别

动态代理和静态代理是设计模式中的两种代理模式,它们在实现上有一些区别。

    ​    ​   1.静态代理: 静态代理是在编译时就已经确定的代理关系,代理类和目标类都需要实现同一个接口或继承同一个父类。代理类在编译时就已经确定了目标类,并且在代理类中手动编写了对目标方法的调用逻辑。

示例代码中的StarProxy类就是一个静态代理的例子,它在编译时就已经确定了对Realstar类的代理关系,通过在代理类中手动调用Realstar类的方法实现代理。

静态代理的优点是可以在代理类中加入额外的逻辑处理,如安全性检查、日志记录等。但缺点是代理类和目标类耦合度高,每个目标类都需要对应一个代理类,如果目标类很多,代理类的数量也会相应增多,导致代码冗余。

    ​    ​    ​2.动态代理: 动态代理是在运行时生成代理类的代理方式,不需要手动编写代理类,而是利用Java提供的相关API动态生成代理类。动态代理可以代理任意的接口或类,无需提前确定目标类。

动态代理利用反射机制,在运行时动态生成代理类,代理类中的方法调用会通过InvocationHandler接口中的invoke()方法动态转发到目标类的方法。因此,动态代理能够更加灵活地处理不同的目标对象,不需要为每个目标对象都手动编写一个专门的代理类。

你可能感兴趣的:(Java随笔,java,开发语言)