前言
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
举个例子,生活中的房产中介,婚介中心等都是代理
- 代理模式角色分为 3 种:
- Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;
- RealSubject(真实主题角色):真正实现业务逻辑的类;
- Proxy(代理主题角色):用来代理和封装真实主题;
- 代理模式按照职责(使用场景)来分类,至少可以分为以下几类:
- 远程代理
- 虚拟代理。
- Copy-on-Write 代理。
- 保护(Protect or Access)代理。
- Cache代理。
- 防火墙(Firewall)代理。
- 同步化(Synchronization)代理。
- 智能引用(Smart Reference)代理等等。
- 如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:
- 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
- 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件
静态代理
下面由我抛出这些代码来一一讲解
接口
1 2 3 4 5 6 7 8 9
| package com.proxy.proxy1;
public interface Methods { public void code(); public void debug(); }
|
接口的实现
1 2 3 4 5 6 7 8 9 10 11 12
| public class Programmer implements Methods { @Override public void code() { System.out.println("我会敲代码code"); }
@Override public void debug() { System.out.println("我会debug"); } }
|
出我们的代理程序员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.proxy.proxy1;
public class Proxy implements Methods{ private Programmer programmer;
public String name;
public void setName(String name) { this.name = name; }
public Proxy(Programmer programmer){ this.programmer = programmer; }
public void who(){
System.out.println("我是程序员"+name); }
public void sql(){ System.out.println("我还会SQL注入"); } public void xss(){ System.out.println("我还会XSS"); }
@Override public void code() { who(); System.out.println("我会敲代码code"); sql(); xss(); }
@Override public void debug() { who(); System.out.println("我会debug"); sql(); xss(); } }
|
主方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.proxy.proxy1;
public class User { public static void main(String[] args) { Programmer programmer1 = new Programmer(); Proxy proxy = new Proxy(programmer1); proxy.setName("CQJKL1"); proxy.code(); System.out.println("========"); proxy.debug(); } }
|
结果
为什么要用静态代理?
通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
在上面的代码中,我们实现了基本程序员所会的code和debug,并且增加了具体程序员的名字和两个安全相关内容(SQL注入和XSS)
在不改变原接口的情况下,我们增强了某些程序员的能力
缺点
- 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
- 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
- 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
- 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
因此便可以引出动态代理了
动态代理
先抛出代码再说
接口
1 2 3 4 5 6
| interface Programmer{
void code();
void debug(); }
|
接口的实现
1 2 3 4 5 6 7 8 9 10 11 12
| class ordinaryProgrammer implements Programmer{
@Override public void code() { System.out.println("我会敲代码"); }
@Override public void debug() { System.out.println("我会debug"); } }
|
主方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;
public class demo1 { public static void main(String[] args) { ordinaryProgrammer ordinaryProgrammer = new ordinaryProgrammer(); ordinaryProgrammer.code(); ordinaryProgrammer.debug(); System.out.println("=====================分割线=======================");
System.out.println("程序员:cqjkl"); Programmer cqjkl = (Programmer) Proxy.newProxyInstance(ordinaryProgrammer.getClass().getClassLoader(), ordinaryProgrammer.getClass().getInterfaces(),((proxy, method, args1) -> {
if (method.getName().equals("code")) {
System.out.println("cqjkl不会敲代码,只会SQL注入"); return null; }
if (method.getName().equals("debug")) {
System.out.println("cqjkl不会debug,只会XSS"); return null; } return null;
})); cqjkl.code(); cqjkl.debug(); System.out.println("=======================实现了两个不同的具体程序员======================");
System.out.println("程序员:CQJKL"); InvocationHandler handler = (((proxy, method, args1) -> { if (method.getName().equals("code")) {
System.out.println("CQJKL正在撕代码,并且改变了code这个方法"); return null; }
if (method.getName().equals("debug")) {
System.out.println("CQJKL正在debug,并且改变了debug的方法"); return null; } return null; })); Programmer CQJKL = (Programmer)Proxy.newProxyInstance(ordinaryProgrammer.getClass().getClassLoader(), ordinaryProgrammer.getClass().getInterfaces(),handler);
CQJKL.debug(); CQJKL.code();
} }
|
很明显,这里我们通过代理实现了两个不同类对同一接口的使用,完成了静态代理十分繁杂的情况
对InvocationHandler和Proxy.newProxyInstance的理解
借用上面的一部分代码进行讲解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| System.out.println("程序员:CQJKL");
InvocationHandler handler = (((proxy, method, args1) -> { if (method.getName().equals("code")) {
System.out.println("CQJKL正在撕代码,并且改变了code这个方法"); return null; }
if (method.getName().equals("debug")) {
System.out.println("CQJKL正在debug,并且改变了debug的方法"); return null; } return null; }));
Programmer CQJKL = (Programmer)Proxy.newProxyInstance(ordinaryProgrammer.getClass().getClassLoader(), ordinaryProgrammer.getClass().getInterfaces(),handler);
CQJKL.debug(); CQJKL.code();
|
还有一种方法,直接把代理写好了,直接传值进去就行
等我有空的时候再把那段代码写好了拿进文章来QWQ
参考
https://juejin.cn/post/6844903978342301709
https://segmentfault.com/a/1190000011291179
https://www.cnblogs.com/whirly/p/10154887.html
https://www.jianshu.com/p/95970b089360