Java 代理模式
被问到了,就补一下。
简述代理模式
代理是英文(Proxy)翻译过来的,在我们的实际生活中,最常见的代理模式应该是朋友圈中的微商了。在起初的时候,厂商直接对标顾客,没有微商在中间赚差价,结构就是如图。
之后慢慢的微商汇总了优质资源,顾客没必要自己去挑厂商货比三家,只需要从微商那里买就行了。所有就有了如下的结构:
而程序设计都是从生活中的实例出现的,所以Java中也产生了代理模式。
在Java中存在几种代理模式:
- 静态代理
- 动态代理
本文将一一介绍
静态代理
当我们看到了微商不厌其烦刷朋友圈的广告,耐不住优惠从微商那里买了一双,微商可能还会向我们推一些别的优惠活动之类的,那么这个过程我们用代码模拟一下。
首先我们需要有一个鞋子的接口,通用的接口是代理模式实现的基础。定义一个sell接口,代表鞋子可以被卖出去的能力。
1package com.proxy.weishang;
2
3// 定义鞋子接口
4public interface Shoes {
5 void sell();
6}
然后再来一个真正的鞋子类,需要实现Shoes的接口
1package com.proxy.weishang;
2
3// 真正的鞋子类
4public class RealShoes implements Shoes {
5 @Override
6 public void sell() {
7 System.out.println("卖出了一双鞋子哦,美汁汁~");
8 }
9}
和一个代理类(微商类)
1package com.proxy.weishang;
2
3// 微商 代理
4public class MicroSell implements Shoes {
5 RealShoes realShoes;
6
7 public MicroSell(RealShoes realShoes) {
8 this.realShoes = realShoes;
9 }
10
11 public void setRealShoes(RealShoes realShoes) {
12 this.realShoes = realShoes;
13 }
14
15
16 @Override
17 public void sell() {
18 beforeSell();
19 realShoes.sell();
20 afterSell();
21 }
22
23 public void beforeSell() {
24 System.out.println("买之前宣传:帅哥,买双鞋子吗?高仿阿迪199两双");
25 }
26
27 public void afterSell() {
28 System.out.println("买之后推销:帅哥,再来双高仿耐克?");
29 }
30}
接下来我们测试下
1package com.proxy;
2
3import com.proxy.weishang.MicroSell;
4import com.proxy.weishang.RealShoes;
5
6public class Main {
7
8 public static void main(String[] args) {
9 MicroSell microSell = new MicroSell(new RealShoes());
10 microSell.sell();
11 }
12}
在微商卖出鞋子的前后,执行了
beforeSell
和afterSell
疯狂推销,那么使用代理模式的好处就在于这,从之前最基本的厂商对顾客,鞋子只有单一的sell能力,而使用代理模式之后,我们并没有改变鞋子的sell能力就可以对其进行功能的拓展和附加。
总结一下静态代理的优点:
- 无需修改被代理的对象
- 无损拓展功能
- 解耦合
缺点当然也存在:
- 要为每一个接口实现代理类,一旦接口增加方法,目标对象与代理对象都要维护。
动态代理
动态代理也是代理,他和静态代理的功能和目的是没有区别的,唯一的区别就在于动态代理是动态生成的,省去为接口实现代理类的操作。
何为动态生成
其实就是Java在内存中创建了一个实现接口的代理,而不需要我们自己定义。多说无益,看代码
我们仍然使用微商的例子来进行讲解。当微商想卖你莆田耐克的时候:
1package com.proxy.dynamicProxy;
2
3// 定义鞋子接口
4public interface Shoes {
5 void sell();
6}
1package com.proxy.dynamicProxy;
2
3// Nike鞋
4public class NikeShoes implements Shoes {
5 @Override
6 public void sell() {
7 System.out.println("卖出去一双莆田耐克,美滋滋~");
8 }
9}
1package com.proxy.dynamicProxy;
2
3import java.lang.reflect.InvocationHandler;
4import java.lang.reflect.Method;
5
6public class dynamicMicroSell implements InvocationHandler {
7 Object shoes;
8
9 public dynamicMicroSell(Object shoes) {
10 this.shoes = shoes;
11 }
12
13 public void setShoes(Object shoes) {
14 this.shoes = shoes;
15 }
16
17 @Override
18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
19 System.out.println("推销前:美女莆田阿迪来一双?");
20 method.invoke(shoes, args);
21 System.out.println("推销后:耐克要吗?");
22 return null;
23 }
24}
测试下
1package com.proxy;
2
3import com.proxy.dynamicProxy.AdidasShoes;
4import com.proxy.dynamicProxy.NikeShoes;
5import com.proxy.dynamicProxy.Shoes;
6import com.proxy.dynamicProxy.dynamicMicroSell;
7
8import java.lang.reflect.Proxy;
9
10public class Main {
11
12 public static void main(String[] args) {
13 NikeShoes nikeShoes = new NikeShoes();
14 dynamicMicroSell nikeSeller = new dynamicMicroSell(nikeShoes);
15 Shoes nikeProxy = (Shoes) Proxy.newProxyInstance(NikeShoes.class.getClassLoader(), NikeShoes.class.getInterfaces(), nikeSeller);
16 nikeProxy.sell();
17 }
18}
可以看到我并没有像静态代理那样重新实现一个代理类,而是实现了
InvocationHandler
接口的invoke方法实现的代理。通过Proxy.newProxyInstance()
创建了一个代理类来执行sell方法。
先不说InvocationHandler到底是什么东西,我们此时如果想要拓展一个阿迪鞋子的接口,应该怎么用动态代理实现?很简单,新建一个AdidasShoes还是实现Shoes接口
1package com.proxy.dynamicProxy;
2
3public class AdidasShoes implements Shoes {
4 @Override
5 public void sell() {
6 System.out.println("卖出去一双莆田阿迪,美滋滋~");
7 }
8}
其他不需要变化,在main中通过dynamicMicroSell和Proxy.newProxyInstance()动态生成代理类就可以了
1package com.proxy;
2
3import com.proxy.dynamicProxy.AdidasShoes;
4import com.proxy.dynamicProxy.NikeShoes;
5import com.proxy.dynamicProxy.Shoes;
6import com.proxy.dynamicProxy.dynamicMicroSell;
7
8import java.lang.reflect.Proxy;
9
10public class Main {
11
12 public static void main(String[] args) {
13 NikeShoes nikeShoes = new NikeShoes();
14 dynamicMicroSell nikeSeller = new dynamicMicroSell(nikeShoes);
15 Shoes nikeProxy = (Shoes) Proxy.newProxyInstance(NikeShoes.class.getClassLoader(), NikeShoes.class.getInterfaces(), nikeSeller);
16 nikeProxy.sell();
17
18 AdidasShoes adidasShoes = new AdidasShoes();
19 dynamicMicroSell adidasSeller = new dynamicMicroSell(adidasShoes);
20 Shoes adidasProxy = (Shoes) Proxy.newProxyInstance(AdidasShoes.class.getClassLoader(), NikeShoes.class.getInterfaces(), adidasSeller);
21 adidasProxy.sell();
22
23 }
24}
运行如图
动态代理的优点是很明显的,它不需要为每一个接口都创建代理类,大大减少重复工作。
动态代理的秘密
在我们使用静态代理的时候,是通过new MicroSell()
创建代理实例,动态代理肯定也有创建实例的动作,要找到在哪里创建了代理实例,我们需要跟进到Proxy.newProxyInstance()一探究竟
1public static Object newProxyInstance(ClassLoader loader,
2 Class<?>[] interfaces,
3 InvocationHandler h)
4 throws IllegalArgumentException
5{
6 Objects.requireNonNull(h);
7
8 final Class<?>[] intfs = interfaces.clone();
9 final SecurityManager sm = System.getSecurityManager();
10 if (sm != null) {
11 checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
12 }
13
14 /*
15 * Look up or generate the designated proxy class.
16 */
17 Class<?> cl = getProxyClass0(loader, intfs);
18
19 /*
20 * Invoke its constructor with the designated invocation handler.
21 */
22 try {
23 if (sm != null) {
24 checkNewProxyPermission(Reflection.getCallerClass(), cl);
25 }
26
27 final Constructor<?> cons = cl.getConstructor(constructorParams);
28 final InvocationHandler ih = h;
29 if (!Modifier.isPublic(cl.getModifiers())) {
30 AccessController.doPrivileged(new PrivilegedAction<Void>() {
31 public Void run() {
32 cons.setAccessible(true);
33 return null;
34 }
35 });
36 }
37 return cons.newInstance(new Object[]{h});
38 } catch (IllegalAccessException|InstantiationException e) {
39 throw new InternalError(e.toString(), e);
40 } catch (InvocationTargetException e) {
41 Throwable t = e.getCause();
42 if (t instanceof RuntimeException) {
43 throw (RuntimeException) t;
44 } else {
45 throw new InternalError(t.toString(), t);
46 }
47 } catch (NoSuchMethodException e) {
48 throw new InternalError(e.toString(), e);
49 }
50}
可以看到通过cl这个class反射调用其构造函数返回了一个实例
1private static Class<?> getProxyClass0(ClassLoader loader,
2 Class<?>... interfaces) {
3 if (interfaces.length > 65535) {
4 throw new IllegalArgumentException("interface limit exceeded");
5 }
6
7 // If the proxy class defined by the given loader implementing
8 // the given interfaces exists, this will simply return the cached copy;
9 // otherwise, it will create the proxy class via the ProxyClassFactory
10 return proxyClassCache.get(loader, interfaces);
11}
直接通过缓存获取,如果获取不到,注释说会通过 ProxyClassFactory 生成。
1private static final class ProxyClassFactory
2 implements BiFunction<ClassLoader, Class<?>[], Class<?>>
3{
4 // prefix for all proxy class names
5 private static final String proxyClassNamePrefix = "$Proxy";
6
7 // next number to use for generation of unique proxy class names
8 private static final AtomicLong nextUniqueNumber = new AtomicLong();
9
10 @Override
11 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
12
13 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
14 for (Class<?> intf : interfaces) {
15 /*
16 * Verify that the class loader resolves the name of this
17 * interface to the same Class object.
18 */
19 Class<?> interfaceClass = null;
20 try {
21 interfaceClass = Class.forName(intf.getName(), false, loader);
22 } catch (ClassNotFoundException e) {
23 }
24 if (interfaceClass != intf) {
25 throw new IllegalArgumentException(
26 intf + " is not visible from class loader");
27 }
28 /*
29 * Verify that the Class object actually represents an
30 * interface.
31 */
32 if (!interfaceClass.isInterface()) {
33 throw new IllegalArgumentException(
34 interfaceClass.getName() + " is not an interface");
35 }
36 /*
37 * Verify that this interface is not a duplicate.
38 */
39 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
40 throw new IllegalArgumentException(
41 "repeated interface: " + interfaceClass.getName());
42 }
43 }
44
45 String proxyPkg = null; // package to define proxy class in
46 int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
47
48 /*
49 * Record the package of a non-public proxy interface so that the
50 * proxy class will be defined in the same package. Verify that
51 * all non-public proxy interfaces are in the same package.
52 */
53 for (Class<?> intf : interfaces) {
54 int flags = intf.getModifiers();
55 if (!Modifier.isPublic(flags)) {
56 accessFlags = Modifier.FINAL;
57 String name = intf.getName();
58 int n = name.lastIndexOf('.');
59 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
60 if (proxyPkg == null) {
61 proxyPkg = pkg;
62 } else if (!pkg.equals(proxyPkg)) {
63 throw new IllegalArgumentException(
64 "non-public interfaces from different packages");
65 }
66 }
67 }
68
69 if (proxyPkg == null) {
70 // if no non-public proxy interfaces, use com.sun.proxy package
71 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
72 }
73
74 /*
75 * Choose a name for the proxy class to generate.
76 */
77 long num = nextUniqueNumber.getAndIncrement();
78 String proxyName = proxyPkg + proxyClassNamePrefix + num;
79
80 /*
81 * Generate the specified proxy class.
82 */
83 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
84 proxyName, interfaces, accessFlags);
85 try {
86 return defineClass0(loader, proxyName,
87 proxyClassFile, 0, proxyClassFile.length);
88 } catch (ClassFormatError e) {
89 /*
90 * A ClassFormatError here means that (barring bugs in the
91 * proxy class generation code) there was some other
92 * invalid aspect of the arguments supplied to the proxy
93 * class creation (such as virtual machine limitations
94 * exceeded).
95 */
96 throw new IllegalArgumentException(e.toString());
97 }
98 }
99}
可知代理类名为String proxyName = proxyPkg + proxyClassNamePrefix + num
,即包名+$Proxy+id序号
生成代理类的核心代码
1byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
2return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
通过修改Java字节码的形式定义class,这就是动态代理自动生成的秘密。来看下动态代理的类名
总结
代理模式被运用于spring框架的aop面向切面编程中,个人业务需求可以应用在日志记录、性能统计等场景中。
最后几句话总结下:
- 代理模式的好处在于不修改现有代码的基础上进行拓展功能
- 不管是动态还是静态代理都要实现接口,本质是面向接口编程
- 静态代理需要自己实现Proxy类,动态由Proxy.newInstance()反射动态生成
- 两者区别在于是否需要自己手动实现Proxy类
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。