代理

静态代理

首先设计接口,接口是代理模式实现的基础

public interface Movie 
{
    void play();
}

然后假设我们有一个片厂,他管理影片《情书》

public class ShowMovie implements Movie 
{
    @Override
    public void play() 
    {
        System.out.println("普通影厅正在播放的电影是《情书》");
    }
}

那么对于一个影院,他购买了《情书》的播放权,作为代理商,在播放前后,需要插入广告等信息

public class MovieStaticProxy implements Movie 
{
    Movie movie;

    public MovieStaticProxy(Movie movie) 
    {
        this.movie = movie;
    }

    @Override
    public void play() 
    {
        playStart();
        movie.play();
        playEnd();
    }

    public void playStart() 
    {
        System.out.println("电影开始前正在播放广告");
    }
    public void playEnd() 
    {
        System.out.println("电影结束了,接续播放广告");
    }
}

最后写测试类

public class StaticProxyTest 
{
    public static void main(String[] args) 
    {
        Movie showMovie = new ShowMovie();
        Movie movieStaticProxy = new MovieStaticProxy(showMovie);
        movieStaticProxy.play();
    }
}

那么输出的内容为

电影开始前正在播放广告
正在播放的电影是《情书》
电影结束了,接续播放广告

通过这种静态代理的方式,我们可以发现代理商可以在代理对象的基础上进行自行的修改
降低系统的耦合度

缺点:代理对象需要与目标对象实现一样的接口,所以会有很多代理类,后期维护困难

JDK动态代理

与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需手动配置

我们在创建一个影厅

public class LoveMovie implements Movie
{
    @Override
    public void play()
    {
        System.out.println("VIP影厅正在播放的电影是《你的名字》");
    }
}

比如,想要使用动态代理,我们需要将代理商改写为下面的形式

public class MyInvocationHandler implements InvocationHandler 
{

    private Object object;

    public MyInvocationHandler(Object object) 
    {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
    {
        playStart();
        Object invoke = method.invoke(object, args);
        playEnd();
        return invoke;
    }

    public void playStart() 
    {
        System.out.println("电影开始前正在播放广告");
    }
    public void playEnd()
    {
        System.out.println("电影结束了,接续播放广告");
    }
}

实现

public class DynamicProxyTest
{
    public static void main(String[] args)
    {
        Movie showMovie = new ShowMovie();
        Movie loveMovie = new LoveMovie();
        Food buyFood = new BuyFood();
        InvocationHandler invocationHandler = new MyInvocationHandler(showMovie);
        InvocationHandler invocationHandler1 = new MyInvocationHandler(loveMovie);
        InvocationHandler invocationHandler2 = new MyInvocationHandler(buyFood);
        Movie dynamicProxy = (Movie) Proxy.newProxyInstance(ShowMovie.class.getClassLoader(),
                ShowMovie.class.getInterfaces(), invocationHandler1);

        Movie dynamicProxy1 = (Movie) Proxy.newProxyInstance(LoveMovie.class.getClassLoader(),
                LoveMovie.class.getInterfaces(), invocationHandler);

        Food dynamicProxy2 = (Food) Proxy.newProxyInstance(BuyFood.class.getClassLoader(),
                BuyFood.class.getInterfaces(), invocationHandler2);

        dynamicProxy.play();
        dynamicProxy1.play();
        dynamicProxy2.eat();
    }
}

输出

电影开始前正在播放广告
VIP影厅正在播放的电影是《你的名字》
电影结束了,接续播放广告
电影开始前正在播放广告
普通影厅正在播放的电影是《情书》
电影结束了,接续播放广告
电影开始前正在播放广告
欢迎购买炸鸡
电影结束了,接续播放广告

由此可以看到,通过动态代理我们不需要再代理处指定Object的类型,而是可以交给jdk判断

cglib动态代理

这个方法可以解决关于类的动态代理

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

import java.lang.reflect.Method;

public class CglibProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        playStart();
        Object object = methodProxy.invokeSuper(o, objects);
        playEnd();
        return object;
    }

    public void playStart() {
        System.out.println("电影开始前正在播放广告");
    }

    public void playEnd() {
        System.out.println("电影结束了,接续播放广告");
    }
}

测试类

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {
    public static void main(String[] args) {
        // //在指定目录下生成动态代理类
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class");
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(CaptainAmerica2MovieImpl.class);
        //设置回调函数
        enhancer.setCallback(new CglibProxyInterceptor());
        //这里的creat方法就是正式创建代理类
        CaptainAmerica2MovieImpl captainAmerica2Movie = (CaptainAmerica2MovieImpl)enhancer.create();
        //调用代理类的play方法
        captainAmerica2Movie.play();
        System.out.println("cglib动态代理《美国队长2》:"+captainAmerica2Movie.getClass());
    }
}

代理
https://dreamerland.cn/2024/03/16/springboot/反射代理/
作者
Silva31
发布于
2024年3月16日
许可协议