静态代理和动态代理案例介绍

1、代理模式概念

我很忙,忙的没空理你,那你要找我呢,就先找我的代理人吧,那代理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,被代理人虽然不想干活,但是代理的人能干活呀。

生活中的房屋中介
在这里插入图片描述

2、代理模式分类

静态代理:

	【定义】
			在程序运行之前,代理类.class文件就已经被创建
	【实现】
			由程序员创建或特定工具自动生成源代码,在对其编译	

动态代理:

	【定义】
			程序运行时通过反射机制动态创建的,对方法的【增强】,不需要修改源码
	【实现】
			基于接口:JDK动态代理
			基于子类:CGLib动态代理

3、静态代理(1)

【1】目标

使用静态代理的方式,完成通过房产中介租房的操作;

【2】实现

生活中的房屋中介抽象:
在这里插入图片描述

步骤:
1、创建项目;
2、定义接口:HouseAgencyCompany及接口中租房的方法:rentingHouse;
3、定义房主类:HouseOwner ,中介类 HouseProxy,均实现接口HouseAgencyCompany;
4、定义租客类:Customer调用HouseProxy完成租房

【2.1】创建项目

新建项目
在这里插入图片描述

【2.2】定义HouseAgencyCompany

/**
 * @Description:中介公司
 */
public interface HouseAgencyCompany {
    /**
     * @Description 租房子
     */
    void rentingHouse();
}

【2.3】定义HouseOwner、HouseProxy

    /**
     * @Description:被代理人(房东)
     */
public class HouseOwner implements HouseAgencyCompany {
    
        @Override
        public void rentingHouse() {
            System.out.println("房东签合同");
        }
    }
    
/**
 * @Description:中介(代理人)
 */
public class HouseProxy implements HouseAgencyCompany {

    /**
     * 被代理人
     */
    private HouseOwner houseOwner;

    public HouseProxy() {
        this.houseOwner = new HouseOwner();
    }

    @Override
    public void rentingHouse() {
        System.out.println("中介带看房子");
        System.out.println("中介约房东");
        houseOwner.rentingHouse();
        System.out.println("中介完成租房");
    }
}

【2.4】定义Customer

import org.junit.jupiter.api.Test;

/**
 * @Description:租客
 */
public class Customer {

    @Test
    public void needHouse(){
        HouseOwner houseOwner  = new HouseOwner();
        houseOwner.rentingHouse();
        System.out.println("==================================");
        HouseAgencyCompany houseAgencyCompany = new HouseProxy();
        houseAgencyCompany.rentingHouse();
    }
}

【2.5】运行结果

在这里插入图片描述

【3】静态代理小结

1、静态代理:在程序运行前手动创建代理类,代理类和目标类需要实现相同接口;

4、动态代理

【1】思考

静态代理有什么问题?
	如果目标类中有多个方法都需要增强,我们得为每一个服务都得创建代理类,工作量太大,不易管理。
	同时接口一旦发生改变,代理类也得相应修改

【2】动态代理概述

代理类在程序运行时创建的方式被成为动态代理。
也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的动态生成的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

【3】jdk动态代理(2)

jdk动态代理:

必须基于接口
  java.lang.reflect.ProxyJava动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其实例。
//方法1: 该方法用于获取指定动态代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
 
//方法2:该方法用于获取关联于指定类装载器和一组接口的动态代理对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
 
//方法3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
 
//方法4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理对象:
//1、类加载器 2、接口数组、调用处理器(增强部分的业务代码)
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

java.lang.reflect.InvocationHandler:

调用处理器接口,它自定义了一个invoke方法,用于集中处理在动态代理对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个实现了该接口的调用处理器对象。

InvocationHandler的核心方法:

//该方法负责集中处理动态代理类上的所有方法调用。
//第一个参数是代理对象,第二个参数是被调用的方法对象,第三个方法是调用参数。
//调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行。
Object invoke(Object proxy, Method method, Object[] args)

【3.1】目标

 使用jdk动态代理的方式,增强租房方法,使得租房方法前后打印输入日志信息;

【3.2】实现

【3.2.1】创建项目

在这里插入图片描述

【3.2.2】修改Customer
    import org.junit.Test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @Description:租客
     */
    public class Customer {
    
        HouseOwner houseOwner = new HouseOwner();
    
        @Test
        public void needHouse(){
           //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理对象:
           //1、目标类的加载器 2、目标类接口 3、调用处理器(增强部分的业务代码)
            HouseAgencyCompany houseProxy = (HouseAgencyCompany) Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(),
                    houseOwner.getClass().getInterfaces(), new InvocationHandler() {
                        //第一个参数是代理对象,第二个参数是被调用的方法对象(rentingHoues),第三个方法是调用参数
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        	//前置增强
                            System.out.println("中介公司让中介带客户看房");
                            Object object = method.invoke(houseOwner, args);
                            //后置增强
                            System.out.println("中介公司让中介完成租房业务");
                            return object;
                        }
                    });
            houseProxy.rentingHoues();
    
        }
    }
    
    
【3.2.3】测试结果

在这里插入图片描述

【3.3】小结

1、JDK动态代理:基于接口的;
2、JDK动态代理实现要点:
		Proxy类
		newProxyInstance静态方法
		InvocationHandler增强方法

【4】cglib动态代理(3)

【4.1】思考

如果目标类没有实现接口呢?
	那么就无法使用JDK的动态代理,因此这种方式有其局限性,必须实现一个接口。
可以使用的方案:
	使用CGLIB动态代理:基于子类(包含本类)

net.sf.cglib.proxy.Enhancer

Enhancer类是CGLib中的一个字节码增强器,作用用于生成代理对象,跟上一章所学的Proxy类相似,常用方式为:

 //方法1:该方法用于为指定目标类、回调对象 1、类的类型,2、调用处理器
 public static Object create(Class type, Callback callback)

net.sf.cglib.proxy.MethodInterceptor

//方法1:
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;

【4.2】实现

【4.2.1】创建项目

在这里插入图片描述

【4.2.2】修改Customer
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

import java.lang.reflect.Method;

/**
 * @Description:
 */
public class Customer {


    @Test
    public void needHouse(){
        System.out.println("=====================");
        //create():该方法用于为指定目标类、回调对象 1、被代理的类型,2、拦截方法
        //代理模式下的租房
        HouseOwner houseOwnerProxy = (HouseOwner) Enhancer.create(HouseOwner.class,
                new MethodInterceptor() {
            //1、代理类 2、执行的目标方法 3、方法参数 4、方法的代理
            @Override
            public Object intercept(Object o, Method method,
                                    Object[] objects, MethodProxy methodProxy) throws Throwable {
                //增强
                System.out.println("中介公司让中介带客户看房");
                //返回结果
                methodProxy.invokeSuper(o, objects);
                
                System.out.println("中介公司让中介完成租房业务");
                return null;
            }
        });

        houseOwnerProxy.renttingHouse();
    }
}
【4.2.4】测试结果

在这里插入图片描述

【4.3】小结

1、Cglib动态代理:基于类,无需实现接口;
2、被代理的目标类不能被final修饰

【5】动态代理小结

通过动态代理可以完成对已有方法的功能的增强:
1、JDK动态代理
	要求:
		被代理对象至少实现一个接口
	应用场景:
		被代理对象有接口

2、CGLIB动态代理
	要求:
		被代理类上不能用static、final修饰
	应用场景:
		被代理对象没有实现接口

5、使用工厂模式+单例模式实现代理模式

案例代码

ProxyFactory 类

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @ClassName: ProxyFactory 
 * @Description: 使用工厂模式 + 单例模式实现 JdkProxy
 * @Author: wang xiao le
 * @Date: 2023/03/18 21:50
 **/
public class ProxyFactory {

    private ProxyFactory() {
    }

    private static class JdkProxyFactoryInstance {
        public static final ProxyFactory INSTANCE = new ProxyFactory();
    }

    public static ProxyFactory getInstance() {
        return JdkProxyFactoryInstance.INSTANCE;
    }

    /**
     * Jdk动态代理
     *
     * @param obj 委托对象
     * @return 代理对象
     */

    public Object getJdkProxy(Object obj) {

        //第一个参数:目标类的加载器,第二个参数:目标类的接口,第三个参数:调用处理器
        return Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    //第一个参数:代理类,
                    //第二个参数:执行的目标方法,这里指的是:rentingHouse
                    //第三参数:方法参数
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前置增强
                        System.out.println("中介公司:我安排中介带你去看房子");
                        Object invoke = method.invoke(obj, args);
                        //后置增强
                        System.out.println("中介公司:欢迎下次光临");
                        return invoke;
                    }
                });

    }


    /**
     * 使用cglib动态代理生成代理对象
     * @param obj 委托对象
     * @return
     */
    public Object getCglibProxy(Object obj) {
        //第一个参数:被代理类的类型 第二个参数:拦截方法
        return Enhancer.create(
                obj.getClass(),
                new MethodInterceptor() {
                    @Override
                    //第一个:代理类,第二个是:执行的目标方法,第三个:方法参数,第四个:方法的代理
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("房东儿子:我爸身体不好,让我来带i看房子");
                        methodProxy.invokeSuper(o, objects);
                        System.out.println("房东儿子:我爸身体不好,后面有什么事情你可以找我");
                        return null;
                    }
                });
    }
}

JdkProxy单元测试


@SpringBootTest
class JdkProxyTests {

    @Test
    public void  needHouse(){
        HouseOwner houseOwner = new HouseOwner();
        System.out.println("=============================");
        //第一个参数:目标类的加载器,第二个参数:目标类的接口,第三个参数:调用处理器
        HouseAgencyCompany houseAgencyCompany = (HouseAgencyCompany) Proxy.newProxyInstance(
                HouseAgencyCompany.class.getClassLoader(),
                houseOwner.getClass().getInterfaces(),
                new InvocationHandler() {
                    //第一个参数:代理类,
                    //第二个参数:执行的目标方法,这里指的是:rentingHouse
                    //第三参数:方法参数
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前置增强
                        System.out.println("中介公司:我安排中介带你去看房子");
                        Object invoke = method.invoke(houseOwner, args);
                        //后置增强
                        System.out.println("中介公司:欢迎下次光临");
                        return invoke;
                    }
                });
        houseAgencyCompany.rentingHouse();
        //=============================
        //中介公司:我安排中介带你去看房子
        //房东:签合同
        //中介公司:欢迎下次光临
    }


    @Test
    void factoryTest() {
        ProxyFactory jdkProxyFactory = ProxyFactory.getInstance();
        HouseOwner houseOwner = new HouseOwner();
        HouseAgencyCompany houseAgencyCompany = (HouseAgencyCompany) jdkProxyFactory.getJdkProxy(houseOwner);
        houseAgencyCompany.rentingHouse();
        //中介公司:我安排中介带你去看房子
        //房东:签合同
        //中介公司:欢迎下次光临
    }
}

CglibProxy单元测试


@SpringBootTest
class CglibProxyTests {

    @Test
    void factoryTest() {
        HouseOwner houseOwner = new HouseOwner();
        //创建代理对象,通过Jdk动态代理对原有对象中的方法进行增强
        ProxyFactory jdkProxyFactory = ProxyFactory.getInstance();
        HouseAgencyCompany houseAgencyCompany = (HouseAgencyCompany) jdkProxyFactory.getCglibProxy(houseOwner);
        //使用增强后的类
        houseAgencyCompany.rentingHouse();

        //房东儿子:我爸身体不好,让我来带i看房子
        //房东:签合同
        //房东儿子:我爸身体不好,后面有什么事情你可以找我
    }


    @Test
    public void  needHouse(){
        //第一个参数:被代理类的类型 第二个参数:拦截方法
        HouseOwner houseOwner = (HouseOwner) Enhancer.create(
                HouseOwner.class,
                new MethodInterceptor() {
                    @Override
                    //第一个:代理类,第二个是:执行的目标方法,第三个:方法参数,第四个:方法的代理
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("房东儿子:我爸身体不好,让我来带i看房子");
                        methodProxy.invokeSuper(o, objects);
                        System.out.println("房东儿子:我爸身体不好,后面有什么事情你可以找我");
                        return null;
                    }
                });
        houseOwner.rentingHouse();
        //房东儿子:我爸身体不好,让我来带i看房子
        //房东:签合同
        //房东儿子:我爸身体不好,后面有什么事情你可以找我
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值