Java设计模式之代理模式(动态代理-基础篇)

上一篇博客(Java设计模式之代理模式(基础篇)1),博猪简单的引入了代理模式,本文将对动态代理进行分析,后几篇博客博猪将就与代理模式相关联或相似的模式进行介绍与比较分析以及对远程代理和缓存代理进行学习笔记整理。


动态代理的相关类和接口

此小节内容详情以JDK源码为主,具体可查看JDK源码。

  • java.lang.reflect.Proxy
    Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
// 获取指定代理对象所关联的调用处理器 
static InvocationHandler getInvocationHandler(Object proxy);

// 获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces);

// 判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl);

// 用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h);
  • java.lang.reflect.InvocationHandler
    这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
// 该方法负责集中处理动态代理类上的所有方法调用,第一个参数既是代理类实例,第二个参数是被调用的方法对象,第三个方法是调用参数.调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行.
Object invoke(Object proxy, Method method, Object[] args);
  • java.lang.ClassLoader
    这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。 每次生成动态代理类对象时都需要指定一个类装载器对象

  • 动态代理类的继承图

    图1 动态代理类的继承图

动态代理的使用方法

  • 通过实现java.lang.reflect.InvocationHandler接口创建自己的调用处理器;

  • 通过为Proxy类指定java.lang.ClassLoader对象和一组interface来创建动态代理类;

  • 通过反射机制获取动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

动态代理对象的创建过程

方法1
具体方法
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发.其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
InvocationHandler handler = new InvocationHandlerImpl(......);

// 通过Proxy为包括Interface接口在内的一组接口动态创建代理类的类对象.
Class clazz = Proxy.getProxyClass(classLoader, new Class[]{ Interface.class, ......});

// 通过反射从生成的类对象获得构造函数对象.
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

// 通过构造函数对象创建动态代理类实例.
Interface Proxy = (Interface)constructor.newInstance(new Object[]{handler});
方法2
简化方法
// InvocationHandlerImpl实现了InvocationHandlerImpl接口,并能实现方法调用从代理类到委托类的分派转发.
InvocationHandler handler = new InvocationHandlerImpl(......);

// 通过Proxy直接创建动态代理类实例.
Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[]{Interface.class}, handler);

动态代理的例子

客户只和代理主题类产生耦合关联,具体主题类有哪些与客户无关,如果主题类的操作流程不变,那么操作流程可以使用代理处理这些不变的工作流程。新增的具体主题类不应该影响和改变原有系统的代码功能,因此可以使用中间件建立主题类和真实主题类的关系,使它们在代码层面上的耦合完全消除,达到即插即用,只需新增具体主题类和增添配置项就能向原系统增加新的具体主题类而不改变原有系统任何类和方法。

  • 抽象主题角色类(Subject.java)
package com.wchy.patterns.proxydemo.dao;

/**
 * 抽象主题,定义主要功能
 * @author wchy
 *
 */
public interface Subject 
{
    public abstract void operation();
}
  • 具体主题角色类
    具体主题1(RealSubject.java)
package com.wchy.patterns.proxydemo.dao.impl;

import org.apache.log4j.Logger;

import com.wchy.patterns.proxydemo.dao.Subject;

/**
 * 具体主题
 * @author wchy
 *
 */
public class RealSubject implements Subject 
{
    private static final Logger LOGGER = Logger.getLogger(RealSubject.class);

    @Override
    public void operation() 
    {
        LOGGER.debug("Real subject operation.");
    }

}

具体主题2(RealSubjectA.java)

package com.wchy.patterns.proxydemo.dao.impl;

import org.apache.log4j.Logger;

import com.wchy.patterns.proxydemo.dao.Subject;

public class RealSubjectA implements Subject 
{

    private static final Logger LOGGER = Logger.getLogger(RealSubjectA.class);

    @Override
    public void operation() 
    {
        LOGGER.debug("Real subject A operation.");
    }

}
  • 具体主题3,用于即插即用的新增具体主题(RealSubjectB.java)
package com.wchy.patterns.proxydemo.dao.impl;

import org.apache.log4j.Logger;

import com.wchy.patterns.proxydemo.dao.Subject;

public class RealSubjectB implements Subject 
{

    private static final Logger LOGGER = Logger.getLogger(RealSubjectB.class);

    @Override
    public void operation() 
    {
        LOGGER.debug("Real subject B operation.");
    }

}
  • 中间件(realsubject.properties)
RealSubject=com.wchy.patterns.proxydemo.dao.impl.RealSubject
RealSubjectA=com.wchy.patterns.proxydemo.dao.impl.RealSubjectA
RealSubjectB=com.wchy.patterns.proxydemo.dao.impl.RealSubjectB
RealSubjectC=com.wchy.patterns.proxydemo.dao.impl.RealSubject
  • 动态代理主题类(SubjectBroker.java)
package com.wchy.patterns.proxydemo.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import com.wchy.patterns.proxydemo.dao.Subject;
import com.wchy.patterns.proxydemo.utils.ProxyDemoUtil;

public class SubjectBroker implements InvocationHandler 
{

    private static final Logger LOGGER = Logger.getLogger(SubjectBroker.class);

    // 实际代理目标
    private Object target = null;

    // 真实subject实例对象
    private static Map<String, Subject> subjectMap = null;

    // 所有真实Subject的类名
    private static Map<String, String> realClassNameMap = null;

    static 
    {
        try 
        {
            realClassNameMap = ProxyDemoUtil.countRealSubjects();
            subjectMap = ProxyDemoUtil.newInstanceSubjects(realClassNameMap);
        } 
        catch (ClassNotFoundException e) 
        {
            e.printStackTrace();
        }
    }

    private SubjectBroker(Subject target) 
    {
        this.target = target;
    }

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

    /**
     * 实际Subject代理,外部通过此方法得倒被代理的接口实现对象.
     * @param realSubjetName
     * @return
     */
    public static Subject getRealSubject(String realClassName) 
    {
        preOperation(realClassName);
        // 真实subject实例对象
        Set<Entry<String, String>> entrySet = realClassNameMap.entrySet();
        Iterator<Entry<String, String>> iterator = entrySet.iterator();
        while (iterator.hasNext()) 
        {
            Entry<String, String> entry = iterator.next();
            if (entry.getValue().equals(realClassName)) 
            {
                Subject subject = subjectMap.get(entry.getKey());
                SubjectBroker sb = new SubjectBroker(subject);
                Object object = Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), sb);
                nextOperation((Subject)object);
                return (Subject)object;
            }
        }
        nextOperation(null);
        return null;
    }

    public static Map<String, String> getRealClassNames() 
    {
        return realClassNameMap;
    }

    private static void preOperation(String realClassName) 
    {
        LOGGER.debug("The realClassName is : " + realClassName);
    }

    private static void nextOperation(Subject subject) 
    {
        LOGGER.debug("The Subject is : " + subject);
    }
}
  • 客户类(Client.java)
package com.wchy.patterns.proxydemo.client;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import com.wchy.patterns.proxydemo.dao.Subject;
import com.wchy.patterns.proxydemo.proxy.SubjectBroker;

import junit.framework.TestCase;

/**
 * 客户
 * @author wchy
 *
 */
public class DemoTest extends TestCase 
{
    public void testcase01() 
    {
        Subject subject = null;
        Map<String, String> realClassNameMap = SubjectBroker.getRealClassNames();
        Set<Entry<String, String>> entrySet = realClassNameMap.entrySet();
        Iterator<Entry<String, String>> iterator = entrySet.iterator();
        while (iterator.hasNext()) 
        {
            Entry<String, String> entry = iterator.next();
            subject = SubjectBroker.getRealSubject(entry.getValue());
            subject.operation();
        }

        SubjectBroker.getRealSubject("com.wchy.patterns.proxydemo.dao.impl.RealSubject").operation();
    }
}
  • 解析中间件配置文件的单例类(ConfigManager.java)
package com.wchy.patterns.proxydemo.common;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import org.apache.log4j.Logger;

public class ConfigManager 
{
    private static final Logger LOGGER = Logger.getLogger(ConfigManager.class);
    /**
     * 属性文件全名.
     */
    private static final String filePath = ConstantUtil.CONFIGROOTPATH + File.separator + ConstantUtil.PROPFILENAME;

    /**
     * 属性文件对应的文件对象变量.
     */
    private File file = null;

    /**
     * 属性文件的最后修改日期.
     */
    private long lastModifiedTime = 0;

    /**
     * 属性文件所对应的属性对象变量.
     */
    private Properties props = null;

    /**
     * 饿汉式单例类的实例
     */
    private static ConfigManager instance = new ConfigManager();

    /**
     * 私有构造子,用以保证外界无法直接实例化.
     */
    private ConfigManager() 
    {
        file = new File(filePath);
        lastModifiedTime = file.lastModified();
        if (lastModifiedTime == 0) 
        {
            LOGGER.debug(filePath + " file does not exist.");
        }
        props = new Properties();
        try 
        {
            props.load(new FileInputStream(filePath));
        } 
        catch (Exception e) 
        {
            LOGGER.error(e);
        }
    }

    /**
     * 静态工厂方法.
     * @return
     */
    synchronized public static ConfigManager getInstance() 
    {
        return instance;
    }

    /**
     * 读取一个特定的属性项.
     * @param name 属性项的项名.
     * @param defaultVal 属性项的默认值.
     * @return 属性项的值(此项存在),默认值(此项不存在).
     */
    public final Object getConfigItem(String name, Object defaultVal) 
    {
        long newTime = file.lastModified();

        if (newTime == 0) 
        {
            if (lastModifiedTime == 0) 
            {
                LOGGER.debug(filePath + " file does not exist!");
            } 
            else 
            {
                LOGGER.debug(filePath + " file was deleted!");
            }
            return defaultVal;
        } 
        else if (newTime > lastModifiedTime) 
        {
            props.clear();
            try 
            {
                props.load(new FileInputStream(filePath));
            } 
            catch (Exception e) 
            {
                LOGGER.error(e);
            }
        }
        lastModifiedTime = newTime;
        Object val = props.getProperty(name);
        if (val == null) 
        {
            return defaultVal;
        } 
        else 
        {
            return val;
        }
    }

    public final Properties getProperties() 
    {
        return props;
    }
}
  • 字符串常量物理意义化常量类(ConstantUtil.java)
package com.wchy.patterns.proxydemo.common;

public final class ConstantUtil 
{
    private ConstantUtil() 
    {
    }
    /**
     * The root path of the config files. 
     */
    public static final String CONFIGROOTPATH = "config";
    /**
     * The file name of the realsubject config file.
     */
    public static final String PROPFILENAME = "realsubject.properties";

    /**
     * The interface name of the realsubject.
     */
    public static final String INTERFACENAME = "com.wchy.patterns.proxydemo.dao.Subject";

    public static final String SEPARATEDMARK = " ";
}

  • 动态代理主题类的工具类(ProxyDemoUtil.java)
package com.wchy.patterns.proxydemo.utils;

import java.lang.reflect.Type;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import com.wchy.patterns.proxydemo.common.ConfigManager;
import com.wchy.patterns.proxydemo.common.ConstantUtil;
import com.wchy.patterns.proxydemo.dao.Subject;

public class ProxyDemoUtil 
{
    private ProxyDemoUtil() 
    {
    }

    /**
     * 统计配置文件里实现了Subject接口的类的类名.
     * @param props
     * @return
     * @throws ClassNotFoundException
     */
    public static Map<String, String> countRealSubjects() throws ClassNotFoundException 
    {
        // 存放所有真实Subject的类名
        Map<String, String> realClassNameMap = new HashMap<String, String>();

        Properties props = ConfigManager.getInstance().getProperties();
        Enumeration<?> keys = props.propertyNames();
        while (keys.hasMoreElements()) 
        {
            String key = (String)keys.nextElement();
            String value = props.getProperty(key);
            Class<?> cls = Class.forName(value);
            Type[] intfs = cls.getGenericInterfaces();
            for(Type type : intfs) 
            {
                String intfName = type.toString();
                intfName = intfName.substring(intfName.indexOf(ConstantUtil.SEPARATEDMARK) + 1);
                if (ConstantUtil.INTERFACENAME.equals(intfName)) 
                {
                    if (!realClassNameMap.containsKey(key)) 
                    {
                        realClassNameMap.put(key, value);
                    }
                    break;
                }
            }
        }
        return realClassNameMap;
    }

    public static Map<String, Subject> newInstanceSubjects(Map<String, String> realClassNameMap) throws ClassNotFoundException 
    {
        if (realClassNameMap == null || realClassNameMap.size() <= 0) 
        {
            return null;
        }
        else 
        {
            // 真实subject实例对象
            Map<String, Subject> subjectMap = new HashMap<String, Subject>();
            Set<Entry<String, String>> entrySet = realClassNameMap.entrySet();
            Iterator<Entry<String, String>> iterator = entrySet.iterator();
            while (iterator.hasNext()) 
            {
                Entry<String, String> entry = iterator.next();
                Class<?> cls = Class.forName(entry.getValue());
                try 
                {
                    Object object = cls.newInstance();
                    subjectMap.put(entry.getKey(), (Subject)object);
                } 
                catch (InstantiationException e) 
                {
                    e.printStackTrace();
                } 
                catch (IllegalAccessException e) 
                {
                    e.printStackTrace();
                }
            }
            return subjectMap;
        }
    }
}

  • 对于代理模式基础不是很了解的客官,可参考我的上一篇博客:Java设计模式之代理模式(基础篇)1

总结

  • Proxy仅支持interface代理,但并不影响其在java中的作用,让Java动态代理的实现很方便清晰快捷。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值