上一篇博客(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 文件中。 每次生成动态代理类对象时都需要指定一个类装载器对象动态代理类的继承图
动态代理的使用方法
通过实现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动态代理的实现很方便清晰快捷。