概述
代理模式是结构化设计模式中的一种,平时我们生活中都有类似的代理模式,比如代购,网上购物,打官司。
定义:为对象提供一种代理以控制对象的访问,这种方式就称为代理模式。
代理模式的结构图中,
Subject:抽象主题类,声明真实主题的共同接口方法。
RealSubject:真实主题类,定义了代理所表示的对象。客户端通过代理类间接的调用真实主题对象的方法。
Proxy:代理类,持有真实主题的类的对象的引用,在其实现的接口中,调用真实主题对象的相应接口的方法。
Client:客户端类。
代理模式分类
代理模式按照代理类的加载时机不同可以分为静态代理和动态代理。其中,动态代理按照,被代理的类是否实现了某个接口又可以划分为Jdk动态代理和Cglib动态代理。
1.静态代理
就是在编译前就已经明确了代理类,也就是在编译前就已经写好了代理类,并且代理类和被代理类要实现相同的接口或者继承相同的父类。
下面通过代码演示,如何使用静态代理:
1.首先定义一个接口,也就是抽象主题
public interface IUserDao {
void save();
}
2.定义一个真实主题
public class UserDao implements IUserDao {
@Override
public void save() {
LogUtil.i("保存数据");
}
}
3.定义一个代理类
public class UserDaoProxy implements IUserDao {
private IUserDao userDao;
public UserDaoProxy(IUserDao userDao){
this.userDao = userDao;
}
@Override
public void save() {
LogUtil.e("开始事务");
userDao.save();
LogUtil.i("提交事务");
}
}
4.新建一个客户端类,并在客户端类中调用代理对象的方法
public class Client {
public static void main(String[] args){
//新建真实对象
IUserDao userDao = new UserDao();
//通过真实主题对象构造一个代理对象
IUserDao userDaoProxy = new UserDaoProxy(userDao);
userDaoProxy.save();
}
}
最后的打印结果是:
10-11 test.cn.example.com.androidskill E/MY_LOG: UserDaoProxy.java::17::save-->>开始事务
10-11 test.cn.example.com.androidskill I/MY_LOG: UserDao.java::13::save-->>保存数据
10-11 test.cn.example.com.androidskill I/MY_LOG: UserDaoProxy.java::19::save-->>提交事务
- JDK动态代理
代理类是在Proxy.newProxy()方法调用后创建的,也就是说是动态生成的,这里就体现了动态代理中的动态的含义。
下面通过具体示例来演示,如何使用Jdk的动态代理
1.还是使用上面静态代理的演示示例中的抽象主题,IUserDao接口和真实主题类UserDao。
2.创建客户端
public class Client {
public static void main(String[] args){
//动态代理
//首先创建真实主题
IUserDao realUserDao = new UserDao();
//创建InvocationHandler对象
InvocationHandler h = new DynamicUserDaoHandler(realUserDao);
//创建代理对象
IUserDao dynamicProxy = (IUserDao) Proxy.newProxyInstance(h.getClass().getClassLoader(),realUserDao.getClass().getInterfaces(),h);
//调用代理对象的方法
dynamicProxy.save();
}
private class DynamicUserDaoHandler implements InvocationHandler{
private Object realSubject;
public DynamicUserDaoHandler(IUserDao subject){
this.realSubject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
LogUtil.i("动态代理---开始事务");
method.invoke(realSubject,args);
LogUtil.i("动态代理---提交事务");
return null;
}
}
}
打印结果如下:
10-11 test.cn.example.com.androidskill I/MY_LOG: ProxyPatternActivity.java::51::invoke-->>动态代理---开始事务
10-11 test.cn.example.com.androidskill I/MY_LOG: UserDao.java::13::save-->>保存数据
10-11 test.cn.example.com.androidskill I/MY_LOG: ProxyPatternActivity.java::53::invoke-->>动态代理---提交事务
Jdk动态代理中,涉及到Proxy类的newProxyInstance方法,这个方法中,需要传入三个参数
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
...
}
这个方法中,
第一个参数 ClassLoader loader:指定一个动态加载代理类的类加载器
第二个参数是要传入被代理对象的所有接口。可以通过被代理对象的getClass().getInterfaces()方法获取。
getInterfaces()方法和Java的反射机制有关。它能够获得这个对象所实现的所有接口。
第三个参数InvocationHandler,这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。
关于InvocationHandler接口的方法的参数的解释
public interface InvocationHandler{
public Object invoke(Object proxy,Method method,Object[] args){
// 参数 proxy 是创建的代理对象
// 参数 method,是代理对象调用的方法
// 参数 args,是代理对象调用的方法传入的参数
}
}
每一次代理对象调用一次方法,则InvocationHandler接口的invoke方法就会执行一次。
- Cglib动态代理
Cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类,也就是生成的.class文件,而我们在android中加载的是优化后的.dex文件,也就是说我们需要可以动态生成.dex文件代理类,cglib在android中是不能使用的。如果想要在Android中使用可以使用CGLib-for-Android这个库,下面演示如何在android项目中使用CGLib-for-Android这个库
1.首先下载CGLib-for-Android这个库, 下载地址
2.将下载的cglib-for-android.jar包导入项目
android studio 中,当导入下载的jar包,导入后,无法使用时,或者导入好,右键,找不到 add as library选项时,看看下载的jar包是否完整,自己下载的cglib-for-android.jar包未下载完整,导致导入到android项目中后,一直无法使用,坑了自己半个小时
3.编写代理拦截器类
import android.content.Context;
import leo.android.cglib.proxy.Enhancer;
import leo.android.cglib.proxy.MethodInterceptor;
import leo.android.cglib.proxy.MethodProxy;
import test.cn.example.com.util.LogUtil;
public class MyInterceptor implements MethodInterceptor {
private final Context context;
public MyInterceptor(Context context){
this.context = context;
}
public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer(context);
enhancer.setSuperclass(clazz);
enhancer.setInterceptor(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Object[] objects, MethodProxy methodProxy) throws Exception {
LogUtil.i("cglib for android 动态代理 事物开始");
Object result = methodProxy.invokeSuper(o, objects);
LogUtil.i("cglib for android 动态代理 事物提交");
return result;
}
}
4.编写客户端
public class Client {
public static void main(String[] args){
//代理类是Cglib自动生成的,生成的代理类是目标类的子类
UserDao proxy1 = (UserDao) new MyInterceptor(this).getProxy(UserDao.class);
LogUtil.i(""+proxy1);
proxy1.save();
}
}
打印日志如下:
通过第一行的打印日志可以知道,生成的代理对象就是被代理对象的子类
10-11 I/MY_LOG: ProxyPatternActivity.java::89::onClick-->>test.cn.example.com.androidskill.model.UserDao$Enhancer$@8234b17
10-11 I/MY_LOG: MyInterceptor.java::26::intercept-->>cglib for android 动态代理 事物开始
10-11 I/MY_LOG: UserDao.java::13::save-->>保存数据
10-11 I/MY_LOG: MyInterceptor.java::28::intercept-->>cglib cglib for android 动态代理 事物提交
Cglib代理前提条件:
目标类不能为final
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
以上便是三种代理模式的使用示例。
代理模式使用场景
看到这里,估计有些读者会好奇,到底什么时候使用代理模式呢?
a.动态代理在Android实际开发中用的并不是很多,但在设计框架的时候用的就比较多了,插件化和热更新就是使用的动态代理技术。
b.在J2EE中,像Spring,Hibernate等都有通过动态代理来实现方法增强、方法拦截等需要,通过代理的方式优雅的实现AOP编程。
c.一般在接手前面的人项目时,如果不想改变源码,可以通过代理类来进行增强。
d.也可以通过代理来进行日志记录功能、性能统计等等。
在使用动态代理模式时,要根据具体情况使用,一般按照如下规则进行抉择:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
Jdk动态代理的实现原理
下面继续看看Jdk动态代理是如何生成代理类的
通过Proxy.newProxyInstance方法就可以创建一个代理对象,下面看看这方法
//Proxy.java
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
InvocationHandler h)throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
//关键代码
Class<?> cl = getProxyClass0(loader, intfs);
try {
//关键代码2
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
cons.setAccessible(true);
}
//关键代码3
return cons.newInstance(new Object[]{h});
}
...
}
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
这个方法中,通过getProxyClass0方法获取代理类的Class,具体是通过在proxyClassCache.get(loader, interfaces)方法获取的,这个proxyClassCache的赋值是这样的
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
所以proxyClassCache.get(loader, interfaces)方法回先通过传入的loader查找jvm是缓存了代理对象的Class,如果存在,则直接返回这个Class,如果不存在,则调用Proxy.ProxyClassFactory的apply方法去生成代理类的Class。由于proxyClassCache是WeakCache类型的,下面看看WeakCache的get方法:
//WeakCache.java
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
//关键代码
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
这个方法中,关键代码处,调用subKeyFactory.apply(key, parameter)方法来生成代理类的,由于这个subKeyFactory就是通过WeakCache类的构造方法传入的,通过上面proxyClassCache
的赋值,可以知道,传入了一个new ProxyClassFactory()对象赋值给了subKeyFactory,所以,subKeyFactory.apply方法其实是调用的Proxy.ProxyClassFactory类的apply方法,下面看看这个方法
//Proxy
public class Proxy implements java.io.Serializable {
...
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
proxyPkg = "";
}
{
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
}
这个方法内部,最终是通过一个native方法generateProxy来完成代理类的Class创建。
得到了代理类的Class对象后,就在Proxy类的newProxyInstance方法的方法,通过newIntance()方法完成代理对象的创建。以上过程便是Jdk动态代理对象生成过程的分析,具体更加细致的分析,大家可以看这篇文章 JDK动态代理实现原理----JDK1.8
总结:
1.如果想对原来的代码不做修改,在原有的代码中新增一些控制逻辑,可以考虑使用代理模式,但是选择使用哪种代理模式,需要根据实际情况来选择,对于动态代理,如果被代理的类实现了接口,则可以使用Jdk动态代理,如果被代理的类未实现接口,则需要使用Cglib动态代理。
2.静态代理和动态代理的差别:
静态代理,是在编译期就已经明确了代理类,也就是程序运行前,代理类的.class已经存在,
而动态代理在编译期还不能明确,需要在执行阶段通过反射动态的创建。
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道代理什么,而动态代理是不知道要代理的东西的,要在运行时才知道。
3.Jdk动态代理需要被代理的类实现接口,而Cglib动态代理则不需要被代理的类实现接口,它是通过派生被代理类的子类来实现的。