Java - 代理模式

Java - 代理模式,第1张

文章目录
  • 1. 代理模式理解
  • 2. 静态代理
  • 3. 动态代理
  • 4. 动态代理的两种实现
  • 5. 参考资料

1. 代理模式理解

生活示例

你要买一个二手房,你要是自己找房源、谈判、过户的话,这一套程序太麻烦。往往你会找一个房屋中介,中介帮你找房源、帮你谈判、帮你跑过户手续。
在这个过程中你就是“委托人”,这个房屋中介就是“代理人”,你只需要找到房屋中介把你的购房需求、相关材料、身份z等给他,他就可以帮你完成后续过程,你不用跑来跑去。

示例分析

  1. 有两个角色:委托人和代理人
  2. 委托人和代理人要达到的目的一样(申请专利或者购买房子)
  3. 委托人需要去找到代理人(要让代理人帮忙干活,肯定得先找到它)
  4. 主要是代理人抛头露面,委托人在后台

Java代码实现

package proxy;

/**
 * @Date: 2022/4/24 19:51
 * @author: ZHX
 * @Description:接口
 */
public interface BuyHouse {
    void buy();
}

package proxy;

/**
 * @Date: 2022/4/24 19:52
 * @author: ZHX
 * @Description: 委托类
 */
public class Consignor implements BuyHouse {
    @Override
    public void buy() {
        System.out.println("委托类:买房、签合同、交钱");
    }
}
package proxy;

/**
 * @Date: 2022/4/24 19:52
 * @author: ZHX
 * @Description: 代理类,要实现委托类相同的所有接口,相当于对委托类进行封装/套壳
 */
public class Proxy implements BuyHouse{

    private Consignor origin;

    public Proxy(Consignor origin) {
        this.origin = origin;
    }

    @Override
    public void buy() {
        //代理类新增的
        System.out.println("代理类:提供卖家信息,联系卖家");

        //帮委托类做事,调用委托类原有的方法
        origin.buy();

        //代理类新增的
        System.out.println("代理类:将买家信息统计");
    }
}

package proxy;

/**
 * @Date: 2022/4/24 20:00
 * @author: ZHX
 * @Description: 测试
 */
public class Test {

    public static void main(String[] args) {
        Consignor consignor = new Consignor();

        Proxy proxy = new Proxy(consignor);

        proxy.buy();

    }
}

在Java中使用代理模式主要是为了在不侵入原代码的前提下进行功能增强。

例如: 想在某个类的某个方法执行之前打印一下日志或者记录下开始时间,但是又不好将打印日志和时间的逻辑写入原来的方法里。这时就可以创建一个代理类实现和原方法相同的方法,通过让代理类持有真实对象,然后代码调用的时候直接调用代理类的方法,来达到增强业务逻辑的目的。

2. 静态代理

静态代理就是程序员手动定义代理类,在程序运行时代理类的.class文件已经有了。

缺点:

如果需要定义多个代理类,如果都手写那不是累死了!例如我们需要给所有访问数据库的方法添加一个方法执行的耗时,这样持久层的类/接口我们会写好多,每个类里面的增删改查方法也有好多,那怎么办呢? 这就需要使用动态代理了。

3. 动态代理

动态代理就是让程序自动生成代理类,不需要我们自己定义代理类了。代理类的定义是在程序运行时动态生成。

Java 代码 实现

* 类说明
1. public interface BuyHouse 								接口
2. public class Consignor implements BuyHouse 				委托类
3. public class GenerateProxy 								自定义的生成代理类的工具类
4. class MyInvocationHandler implements InvocationHandler	调用处理器类
5. public class Test										测试类
package proxy_dynamic;

/**
 * @Date: 2022/4/24 19:51
 * @author: ZHX
 * @Description: 接口
 */
public interface BuyHouse {
    void buy();

    void call();
}
package proxy_dynamic;

/**
 * @Date: 2022/4/24 19:52
 * @author: ZHX
 * @Description: 委托类
 */
public class Consignor implements BuyHouse {
    @Override
    public void buy() {
        System.out.println("委托类:买房、签合同、交钱");
    }

    @Override
    public void call() {}
}
package proxy_dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Date: 2022/4/24 20:33
 * @author: ZHX
 * @Description: 通过java反射包中的Proxy,实现动态代理。
 */
public class GenerateProxy {

    //方式一 只生成代理类.class ,无法加载到内存。
    // 因为类加载器中的加载方法不让我们用,当然自己写一个类加载器也是能实现的。
    public static Object generateProxy0() {
        //1.使用JDK提供的ProxyGenerator生成代理类的.class文件 。我们保存到本地看看。
        byte[] bytes = ProxyGenerator.generateProxyClass("com.alibaba.proxy.Wrapper", new Class[]{BuyHouse.class});
        FileOutputStream fos = new FileOutputStream(new File("C:\Users\ZHX\Desktop\a.class"));
		
		//2.加载到内存
		//no
		
        fos.write(bytes);
        fos.close();
        return null;
    }
	
    /*
    方式二 Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h) 
    1.创建类实现接口InvocationHandler
    */
    public static Object generateProxy1() {
        //Java帮我们整合了,创建代理类.class、加载到内存、执行 一条龙。
        //其实Proxy.newProxyInstance 就是调用的 ProxyGenerator.generateProxyClass("自动生成类名","接口"),得到的字节数组,直接加载到内存。
        Object proxy = Proxy.newProxyInstance(
                GenerateProxy.class.getClassLoader(),   //类加载器
                new Class[]{BuyHouse.class},            //要实现那些接口
                new MyInvocationHandler(new Consignor()));
        return proxy;
    }

    /*
    方式二 Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
    2.用匿名内部类实现InvocationHandler接口
    */
    public static Object generateProxy2() {
        Object proxy = Proxy.newProxyInstance(
                GenerateProxy.class.getClassLoader(),   //类加载器
                new Class[]{BuyHouse.class},            //要实现那些接口
                new InvocationHandler() {

                    private Object origin;

                    /*
                    //传入委托类 ,内名内部类无法创建构造器,所以用一个方法给字段赋值
                    public MyInvocationHandler(Object origin) {
                        this.origin = origin;
                    }
                    */

                    //传入委托类,当代理类不需要用到委托类时,也可以不写。但不用那就相当于用接口创建了一个实现类而已,没啥代理不代理的关系了。
                    public InvocationHandler setOrigin(Object origin) {
                        this.origin = origin;
                        return this; //链式编程
                    }

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        Object result = null;                           //方法返回值一定要写

                        String name = method.getName();

                        if (name.equals("buy")) { //只改造buy方法,其他方法原样调用。
                            System.out.println("代理类:提供卖家信息,联系卖家");
                            result = method.invoke(origin, args);       //方法返回值一定要写,
                            System.out.println("代理类:将买家信息统计");
                        } else {
                            result = method.invoke(origin, args);
                        }

                        return result;                                  //方法返回值一定要写
                    }
                }.setOrigin(new Consignor())  //通过方法传入委托类,
        );
        return proxy;
    }

}
// 代理类的调用处理程序, 将对委托类中的方法进行改造并调用.
class MyInvocationHandler implements InvocationHandler {

    private Object origin;

    //传入委托类
    public MyInvocationHandler(Object origin) {
        this.origin = origin;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object result = null;

        String name = method.getName();

        if (name.equals("buy")) { //只改造buy方法,其他方法原样调用。
            System.out.println("代理类:提供卖家信息,联系卖家");
            result = method.invoke(origin, args);     //这里增强方法中需要使用原方法就调用.
            System.out.println("代理类:将买家信息统计");
        } else {
            result = method.invoke(origin, args);
        }

        return result;
    }
}
package proxy_dynamic;

/**
 * @Date: 2022/4/24 20:00
 * @author: ZHX
 * @Description: 测试
 */
public class Test {

    public static void main(String[] args) {
        BuyHouse o = (BuyHouse) GenerateProxy.generateProxy2();

        o.buy();//实际使用的是代理类的增强方法。(增强代码+原委托类的buy代码)

    }
}
4. 动态代理的两种实现
  1. JDK动态代理:使用JDK提供的工具类实现。
  2. CGLIB动态代理:使用CGLIB外部包实现。

两者区别:

JDK动态代理 委托类必须要实现接口。
CGLIB动态代理 委托类不需要有实现接口。

CGLIB实现步骤:

  1. 引入依赖坐标

    <dependency>
        <groupId>cglibgroupId>
        <artifactId>cglibartifactId>
        <version>2.2.2version>
    dependency>
    
  2. 有一个委托类(业务类)

    package proxy_dynamic;
    
    /**
     * @Date: 2022/4/24 19:52
     * @author: ZHX
     * @Description: 委托类
     */
    public class Consignor {
        public void buy() {
            System.out.println("委托类:买房、签合同、交钱");
        }
    
        public void call() {
        }
    }
    
    
  3. 获取代理对象测试

    package proxy_dynamic;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * @Date: 2022/4/24 20:00
     * @author: ZHX
     * @Description: 测试
     */
    public class Test {
    
        public static void main(String[] args) {
    
            //使用CGLIB动态代理
            Consignor consignor = new Consignor();
    
            Consignor o1 = (Consignor) Enhancer.create(Consignor.class, new MyMethodInterceptor(consignor));
    
            o1.buy();
    
        }	
    }
    
    class MyMethodInterceptor implements MethodInterceptor {
        Object origin;
    
        public MyMethodInterceptor(Object origin) {
            this.origin = origin;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object result = null;
            System.out.println("日志开始........................");
            result = method.invoke(origin, objects);
            System.out.println("日志结束........................");
            return result;
        }
    }
    

总结:

  1. 使用代理技术就是为了在不入侵原有代码的情况下增强业务逻辑。
  2. 静态代理太过于繁琐。
  3. 有接口就用JDK动态代理,没有接口就用CGLIB动态代理
  4. JDK动态代理,按照接口结构创建代理类,能用于多态替换
  5. CGLIB动态代理,按照委托类结构创建代理类。
  6. CGLIB 也提供了按照接口生成代理类的方法。
5. 参考资料

JDK动态代理为什么必须要基于接口?

B站黑马程序员

静态代理和动态代理的区别,什么场景使用

百度百科

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/735512.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存