今天学习到了一个新的设计模式:代理模式。介绍借鉴了runoob的:
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
访问一个类是做一些控制,增加中间层,这种情况一般都出现在账号密码验证、令牌验证之类,需要检验或修改部分信息,但对业务结构没有改变的情况。
Struts2的filter,还有SpringMVC的interceptor,以及Java的注解,本质上都是代理模式。
这里有一个使用了代理模式的例子:
之前为了给系统对接华为云API,设计了一套restful API的封装类,有多种封装类型,VPCUtil(虚拟私有云),ECSUtil(云服务器),SecurityGroupUtil(安全组),PublicIPUtil(公开IP),这几种请求不同的业务,但是都有一个相同点:操作之前要先登录,使用IAMUtil(身份验证)里的封装函数发送账号密码,返回有效期为24小时的token,这个token拿去请求其他业务(何时使用)。
图1. 华为云API模块操作流程图
每次开启服务器时都要获取一次token,并且每次请求华为云都要检查token是否过期,过期则重新更新token。
如果这个操作是写在业务Util里面,每次操作都要在对象函数里面执行一次,像VPCUtil:
1 import java.util.UUID; 2 3 import org.json.JSONArray; 4 import org.json.JSONException; 5 import org.json.JSONObject; 6 7 public class VPCUtil{ 8 9 private String username; 10 11 private String password; 12 13 private String token; 14 15 private String projectId; 16 17 private long overDate; 18 19 public IAMUtil(){ 20 } 21 22 public IAMUtil(String username, String password){ 23 this.username = username; 24 this.password = password; 25 TokenAndProject tap = new IAMUtil().getToken(username,password); 26 this.overDate = System.currentTimeMillis() + 23*60*60*1000; 27 this.token = tap.getToken(); 28 this.projectId = tap.getProjectId(); 29 } 30 31 /** 32 * 33 * @Title createVPC 34 * @Description 创建虚拟私有云 35 * @param idx 36 * @return vpc_id 虚拟私有云的ID 37 */ 38 public String createVPC(int idx, String name_prefix) { 39 this.checkAccount(); 40 JSONObject resjo = null; 41 String res = RequestUtil.requestsPost("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs","{"vpc": {"name": "" + name_prefix + "-" + (1+idx) + "","cidr": "192.168.0.0/16"}}",token); 42 String vpc_id = ""; 43 try { 44 resjo = new JSONObject(res); 45 vpc_id = resjo.getJSONObject("vpc").getString("id"); 46 } catch (JSONException e) { 47 System.out.println(res); 48 e.printStackTrace(); 49 }finally { 50 } 51 return vpc_id; 52 } 53 54 /** 55 * 56 * @Title createVPC 57 * @Description 创建虚拟私有云的子网 58 * @param subNo 192.168.x.y 里面的x 59 * @return vpc_id 虚拟私有云的ID 60 */ 61 public NetWorkIds createSubnet(int idx, String vpc_id, int subNo, String name_prefix) { 62 this.checkAccount(); 63 JSONObject resjo = null; 64 String res = RequestUtil.requestsPost("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/subnets", 65 "{"subnet": {"name": "" + name_prefix + "-" + (subNo == 10 ? "manager" : "service") + "-" + (1+idx) + "","cidr": "192.168." + subNo + ".0/24","gateway_ip": "192.168." + subNo + ".254","vpc_id": "" + vpc_id + ""}}",token); 66 String netId = ""; 67 String subnetId = ""; 68 try { 69 resjo = new JSONObject(res); 70 netId = resjo.getJSONObject("subnet").getString("id"); 71 subnetId = resjo.getJSONObject("subnet").getString("neutron_subnet_id"); 72 } catch (JSONException e) { 73 System.out.println(res); 74 e.printStackTrace(); 75 }finally { 76 } 77 return new NetWorkIds(netId,subnetId); 78 } 79 80 public NetWorkIds createSubnetFilter(int idx, String vpc_id, int subNo, String name_prefix) { 81 this.checkAccount(); 82 NetWorkIds nids = createSubnet(idx, vpc_id, subNo, name_prefix); 83 while(true) { 84 String portsres = getPorts(nids.getNetId(), true); 85 JSONArray ports = null; 86 JSONArray fixed_ips = null; 87 try { 88 ports = new JSONArray(portsres); 89 if(ports.length() <= 0)continue; 90 fixed_ips = ports.getJSONObject(0).getJSONArray("fixed_ips"); 91 if(fixed_ips.length() <= 0)continue; 92 String ip_address = fixed_ips.getJSONObject(0).getString("ip_address"); 93 int ip4 = Integer.parseInt(ip_address.split("\.")[3]); 94 System.out.println("I get the address : " + ip_address); 95 if((ip4 >= 1 && ip4 <= 4) || (ip4 >= 100 && ip4 <= 103)){ 96 nids = new VPCUtil().createSubnet(token, projectId, idx, vpc_id, subNo, name_prefix); 97 }else { 98 break; 99 } 100 } catch (JSONException e) { 101 System.out.println(portsres); 102 e.printStackTrace(); 103 } 104 } 105 106 return nids; 107 } 108 109 public String createVirtualIP(String net_id, String subnet_id, String ip) { 110 this.checkAccount(); 111 String res = RequestUtil.requestsPost("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports", 112 "{"port": {"network_id": "" + net_id + "","device_owner": "neutron:VIP_PORT","name": "" + UUID.randomUUID().toString() + "","fixed_ips": [{"subnet_id": "" + subnet_id + "", "ip_address": "" + ip + ""}]}}", token); 113 String new_port_id = ""; 114 try { 115 new_port_id = new JSONObject(res).getJSONObject("port").getString("id"); 116 } catch (JSONException e) { 117 System.out.println(res); 118 e.printStackTrace(); 119 } 120 return new_port_id; 121 } 122 123 public void createVirtualIPport(String port_id, String ip1, String ip2) { 124 this.checkAccount(); 125 RequestUtil.requestsPut("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports/" + port_id, 126 "{"port": {"allowed_address_pairs": [{"ip_address":"" + ip1 + ""},{"ip_address":"" + ip2 + ""}]}}", token); 127 } 128 129 /** 130 * 131 * @Title getVPCs 132 * @Description 获取虚拟私有云列表 133 * @return vpcsInJSON 虚拟私有云列表的JOSN格式字符串 134 */ 135 public String getVPCs() { 136 this.checkAccount(); 137 String res = RequestUtil.requestsGet("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs", token); 138 try { 139 JSONObject resjo = new JSONObject(res); 140 JSONArray vpcs = resjo.getJSONArray("vpcs"); 141 res = vpcs.toString(); 142 } catch (JSONException e) { 143 System.out.println(res); 144 e.printStackTrace(); 145 } 146 return res; 147 } 148 149 public String getSubnets(String vpc_id) { 150 this.checkAccount(); 151 String res = RequestUtil.requestsGet("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/subnets?project_id" + projectId + (vpc_id == null ? "" : "&vpc_id=" + vpc_id), token); 152 try { 153 JSONObject resjo = new JSONObject(res); 154 JSONArray vpcs = resjo.getJSONArray("subnets"); 155 res = vpcs.toString(); 156 } catch (JSONException e) { 157 System.out.println(res); 158 e.printStackTrace(); 159 } 160 return res; 161 } 162 163 public String getPorts(String network_id) { 164 this.checkAccount(); 165 return getPorts(network_id, false); 166 } 167 168 public String getPorts(String network_id, boolean isDHCP) { 169 this.checkAccount(); 170 String res = RequestUtil.requestsGet("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports?project_id=" + projectId + (network_id == null ? "" : "&network_id=" + network_id) + (isDHCP ? "&device_owner=network:dhcp" : ""), token); 171 try { 172 JSONObject resjo = new JSONObject(res); 173 JSONArray vpcs = resjo.getJSONArray("ports"); 174 res = vpcs.toString(); 175 } catch (JSONException e) { 176 System.out.println(res); 177 e.printStackTrace(); 178 } 179 return res; 180 } 181 182 public String deletePort(String token, String port_id) { 183 this.checkAccount(); 184 System.out.println("deletePort start " + port_id); 185 String res = RequestUtil.requestsDelete("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports/" + port_id, token); 186 System.out.println(res); 187 System.out.println("deletePort end"); 188 return res; 189 } 190 191 public String deleteSubnet(String vpc_id, String network_id) { 192 this.checkAccount(); 193 System.out.println("deleteSubnet start " + network_id); 194 String res = RequestUtil.requestsDelete("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs/"+ vpc_id +"/subnets/" + network_id, token); 195 System.out.println(res); 196 System.out.println("deleteSubnet end"); 197 return res; 198 } 199 200 public String deleteVPC(String vpc_id) { 201 this.checkAccount(); 202 String res = RequestUtil.requestsDelete("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs/" + vpc_id, token); 203 System.out.println(res); 204 return res; 205 } 206 207 private void checkAccount(){ 208 if(tap == null || System.currentTimeMillis() > overDate) { 209 tap = new IAMUtil().getToken(this.username,this.password); 210 this.token = tap.getToken(); 211 this.projectId = tap.getProjectId(); 212 this.overDate = System.currentTimeMillis() + 23*60*60*1000; 213 } 214 } 215 216 } 217 218 class NetWorkIds{ 219 String netId; 220 String subnetId; 221 public NetWorkIds(String netId, String subnetId) { 222 super(); 223 this.netId = netId; 224 this.subnetId = subnetId; 225 } 226 227 public String getNetId() {return netId;} 228 public String getSubNetId() {return subnetId;} 229 }
这里,每个操作前面都要执行checkAccount这个函数。如果这个业务要经常添加功能的话,每个函数都要在这里添加这个前置动作,很容易因为某些原因,把代码写错,也不利于维护。
可以给这上面的所有代码做个代理,并且把username和password、token、projectId的可见性设置为protected,为后面的变量访问做准备。
直接把函数写在代理的注入过程里面(如何解决),就可以在不用写前置代码的前提下执行token校验了;外部类调用代理过的对象。为了让cglib能代理成功,需要声明无参构造函数:
1 import java.util.UUID; 2 3 import org.json.JSONArray; 4 import org.json.JSONException; 5 import org.json.JSONObject; 6 7 public class VPCUtil{ 8 9 protected String username; 10 11 protected String password; 12 13 protected String token; 14 15 protected String projectId; 16 17 protected long overDate; 18 19 public IAMUtil(){ 20 } 21 22 public IAMUtil(String username, String password){ 23 this.username = username; 24 this.password = password; 25 TokenAndProject tap = new IAMUtil().getToken(username,password); 26 this.overDate = System.currentTimeMillis() + 23*60*60*1000; 27 this.token = tap.getToken(); 28 this.projectId = tap.getProjectId(); 29 } 30 31 /** 32 * 33 * @Title createVPC 34 * @Description 创建虚拟私有云 35 * @param idx 36 * @return vpc_id 虚拟私有云的ID 37 */ 38 public String createVPC(int idx, String name_prefix) { 39 this.checkAccount(); 40 JSONObject resjo = null; 41 String res = RequestUtil.requestsPost("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs","{"vpc": {"name": "" + name_prefix + "-" + (1+idx) + "","cidr": "192.168.0.0/16"}}",token); 42 String vpc_id = ""; 43 try { 44 resjo = new JSONObject(res); 45 vpc_id = resjo.getJSONObject("vpc").getString("id"); 46 } catch (JSONException e) { 47 System.out.println(res); 48 e.printStackTrace(); 49 }finally { 50 } 51 return vpc_id; 52 } 53 54 /** 55 * 56 * @Title createVPC 57 * @Description 创建虚拟私有云的子网 58 * @param subNo 192.168.x.y 里面的x 59 * @return vpc_id 虚拟私有云的ID 60 */ 61 public NetWorkIds createSubnet(int idx, String vpc_id, int subNo, String name_prefix) { 62 this.checkAccount(); 63 JSONObject resjo = null; 64 String res = RequestUtil.requestsPost("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/subnets", 65 "{"subnet": {"name": "" + name_prefix + "-" + (subNo == 10 ? "manager" : "service") + "-" + (1+idx) + "","cidr": "192.168." + subNo + ".0/24","gateway_ip": "192.168." + subNo + ".254","vpc_id": "" + vpc_id + ""}}",token); 66 String netId = ""; 67 String subnetId = ""; 68 try { 69 resjo = new JSONObject(res); 70 netId = resjo.getJSONObject("subnet").getString("id"); 71 subnetId = resjo.getJSONObject("subnet").getString("neutron_subnet_id"); 72 } catch (JSONException e) { 73 System.out.println(res); 74 e.printStackTrace(); 75 }finally { 76 } 77 return new NetWorkIds(netId,subnetId); 78 } 79 80 public NetWorkIds createSubnetFilter(int idx, String vpc_id, int subNo, String name_prefix) { 81 this.checkAccount(); 82 NetWorkIds nids = createSubnet(idx, vpc_id, subNo, name_prefix); 83 while(true) { 84 String portsres = getPorts(nids.getNetId(), true); 85 JSONArray ports = null; 86 JSONArray fixed_ips = null; 87 try { 88 ports = new JSONArray(portsres); 89 if(ports.length() <= 0)continue; 90 fixed_ips = ports.getJSONObject(0).getJSONArray("fixed_ips"); 91 if(fixed_ips.length() <= 0)continue; 92 String ip_address = fixed_ips.getJSONObject(0).getString("ip_address"); 93 int ip4 = Integer.parseInt(ip_address.split("\.")[3]); 94 System.out.println("I get the address : " + ip_address); 95 if((ip4 >= 1 && ip4 <= 4) || (ip4 >= 100 && ip4 <= 103)){ 96 nids = new VPCUtil().createSubnet(token, projectId, idx, vpc_id, subNo, name_prefix); 97 }else { 98 break; 99 } 100 } catch (JSONException e) { 101 System.out.println(portsres); 102 e.printStackTrace(); 103 } 104 } 105 106 return nids; 107 } 108 109 public String createVirtualIP(String net_id, String subnet_id, String ip) { 110 this.checkAccount(); 111 String res = RequestUtil.requestsPost("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports", 112 "{"port": {"network_id": "" + net_id + "","device_owner": "neutron:VIP_PORT","name": "" + UUID.randomUUID().toString() + "","fixed_ips": [{"subnet_id": "" + subnet_id + "", "ip_address": "" + ip + ""}]}}", token); 113 String new_port_id = ""; 114 try { 115 new_port_id = new JSONObject(res).getJSONObject("port").getString("id"); 116 } catch (JSONException e) { 117 System.out.println(res); 118 e.printStackTrace(); 119 } 120 return new_port_id; 121 } 122 123 public void createVirtualIPport(String port_id, String ip1, String ip2) { 124 this.checkAccount(); 125 RequestUtil.requestsPut("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports/" + port_id, 126 "{"port": {"allowed_address_pairs": [{"ip_address":"" + ip1 + ""},{"ip_address":"" + ip2 + ""}]}}", token); 127 } 128 129 /** 130 * 131 * @Title getVPCs 132 * @Description 获取虚拟私有云列表 133 * @return vpcsInJSON 虚拟私有云列表的JOSN格式字符串 134 */ 135 public String getVPCs() { 136 this.checkAccount(); 137 String res = RequestUtil.requestsGet("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs", token); 138 try { 139 JSONObject resjo = new JSONObject(res); 140 JSONArray vpcs = resjo.getJSONArray("vpcs"); 141 res = vpcs.toString(); 142 } catch (JSONException e) { 143 System.out.println(res); 144 e.printStackTrace(); 145 } 146 return res; 147 } 148 149 public String getSubnets(String vpc_id) { 150 this.checkAccount(); 151 String res = RequestUtil.requestsGet("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/subnets?project_id" + projectId + (vpc_id == null ? "" : "&vpc_id=" + vpc_id), token); 152 try { 153 JSONObject resjo = new JSONObject(res); 154 JSONArray vpcs = resjo.getJSONArray("subnets"); 155 res = vpcs.toString(); 156 } catch (JSONException e) { 157 System.out.println(res); 158 e.printStackTrace(); 159 } 160 return res; 161 } 162 163 public String getPorts(String network_id) { 164 this.checkAccount(); 165 return getPorts(network_id, false); 166 } 167 168 public String getPorts(String network_id, boolean isDHCP) { 169 this.checkAccount(); 170 String res = RequestUtil.requestsGet("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports?project_id=" + projectId + (network_id == null ? "" : "&network_id=" + network_id) + (isDHCP ? "&device_owner=network:dhcp" : ""), token); 171 try { 172 JSONObject resjo = new JSONObject(res); 173 JSONArray vpcs = resjo.getJSONArray("ports"); 174 res = vpcs.toString(); 175 } catch (JSONException e) { 176 System.out.println(res); 177 e.printStackTrace(); 178 } 179 return res; 180 } 181 182 public String deletePort(String token, String port_id) { 183 this.checkAccount(); 184 System.out.println("deletePort start " + port_id); 185 String res = RequestUtil.requestsDelete("https://vpc.cn-south-1.myhuaweicloud.com/v2.0/ports/" + port_id, token); 186 System.out.println(res); 187 System.out.println("deletePort end"); 188 return res; 189 } 190 191 public String deleteSubnet(String vpc_id, String network_id) { 192 this.checkAccount(); 193 System.out.println("deleteSubnet start " + network_id); 194 String res = RequestUtil.requestsDelete("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs/"+ vpc_id +"/subnets/" + network_id, token); 195 System.out.println(res); 196 System.out.println("deleteSubnet end"); 197 return res; 198 } 199 200 public String deleteVPC(String vpc_id) { 201 this.checkAccount(); 202 String res = RequestUtil.requestsDelete("https://vpc.cn-south-1.myhuaweicloud.com/v1/" + projectId + "/vpcs/" + vpc_id, token); 203 System.out.println(res); 204 return res; 205 } 206 207 private void checkAccount(){ 208 if(tap == null || System.currentTimeMillis() > overDate) { 209 tap = new IAMUtil().getToken(this.username,this.password); 210 this.token = tap.getToken(); 211 this.projectId = tap.getProjectId(); 212 this.overDate = System.currentTimeMillis() + 23*60*60*1000; 213 } 214 } 215 216 } 217 218 class NetWorkIds{ 219 String netId; 220 String subnetId; 221 public NetWorkIds(String netId, String subnetId) { 222 super(); 223 this.netId = netId; 224 this.subnetId = subnetId; 225 } 226 227 public String getNetId() {return netId;} 228 public String getSubNetId() {return subnetId;} 229 }
代理这个Util的类:
1 import java.lang.reflect.Method; 2 3 import org.springframework.cglib.proxy.Enhancer; 4 import org.springframework.cglib.proxy.MethodInterceptor; 5 import org.springframework.cglib.proxy.MethodProxy; 6 7 public class TokenProxy { 8 9 private VPCUtil VPCProxy = null; 10 11 public TokenProxy(String username, String password) { 12 Enhancer enhancer = new Enhancer(); 13 enhancer.setSuperclass(VPCUtil.class); 14 // 设置enhancer的回调对象 15 enhancer.setCallback(new MethodInterceptor() { 16 private TokenAndProject tap = null; 17 private long overDate = 0; 18 @Override 19 public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 20 ((VPCUtil)sub).checkAccount(); 21 Object res = methodProxy.invokeSuper(sub, objects); 22 return res; 23 } 24 }); 25 this.VPCProxy= (VPCUtil)enhancer.create(); 26 } 27 28 public VPCUtil getVPCProxy() { 29 return this.VPCProxy; 30 } 31 32 }
但是如果运行这个类的函数,会出现StackOverFlowError错误:
Exception in thread "main" java.lang.StackOverflowError at zhyx_cloud.VPCUtil$$EnhancerByCGLIB$$a10b0367.checkAccount(<generated>) at zhyx_cloud.TokenProxy$1.intercept(TokenProxy.java:22) at zhyx_cloud.VPCUtil$$EnhancerByCGLIB$$a10b0367.checkAccount(<generated>) at zhyx_cloud.TokenProxy$1.intercept(TokenProxy.java:22) at zhyx_cloud.VPCUtil$$EnhancerByCGLIB$$a10b0367.checkAccount(<generated>) at zhyx_cloud.TokenProxy$1.intercept(TokenProxy.java:22) at zhyx_cloud.VPCUtil$$EnhancerByCGLIB$$a10b0367.checkAccount(<generated>) at zhyx_cloud.TokenProxy$1.intercept(TokenProxy.java:22) at zhyx_cloud.VPCUtil$$EnhancerByCGLIB$$a10b0367.checkAccount(<generated>) at zhyx_cloud.TokenProxy$1.intercept(TokenProxy.java:22)
问题出现在哪呢?实际上cglib代理这个函数时,传进来的第一参数也是经过代理的,执行这个对象的函数的话,这个函数会先进入代理注入环节,然后注入函数里面又会调用到这个函数,这就造成了死循环递归。
就是说,在无法保证递归终点的情况下,注入函数里面不能调用被代理对象本身的函数,那就只能把整个函数都抄过来了(关键代码)。
1 import java.lang.reflect.Method; 2 3 import org.springframework.cglib.proxy.Enhancer; 4 import org.springframework.cglib.proxy.MethodInterceptor; 5 import org.springframework.cglib.proxy.MethodProxy; 6 7 public class TokenProxy { 8 9 private VPCUtil VPCProxy = null; 10 11 public TokenProxy(String username, String password) { 12 Enhancer enhancer = new Enhancer(); 13 enhancer.setSuperclass(VPCUtil.class); 14 // 设置enhancer的回调对象 15 enhancer.setCallback(new MethodInterceptor() { 16 @Override 17 public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 18 if(tap == null || System.currentTimeMillis() > overDate) { 19 TokenAndProject tap = new IAMUtil().getToken(this.username,this.password); 20 VPCUtil myproxy = (VPCUtil)sub; 21 myproxy.token = tap.getToken(); 22 myproxy.projectId = tap.getProjectId(); 23 myproxy.overDate = System.currentTimeMillis() + 23*60*60*1000; 24 } 25 Object res = methodProxy.invokeSuper(sub, objects); 26 return res; 27 } 28 }); 29 this.VPCProxy= (VPCUtil)enhancer.create(); 30 } 31 32 public VPCUtil getVPCProxy() { 33 return this.VPCProxy; 34 } 35 36 }
这里,没有对被代理对象的函数调用,没有循环注入的问题;同时也实现了修改内部属性的功能,使功能照常进行。
这里违反了DRY(don't repeat yourself)规则,但是相对来说也没啥更好的方法;这个方法的代码量和出错率还是较低的,并且运行效果与原先代码相当。
图2. 用0.2版本TokenProxy代理之后,华为云API模块操作流程图
这种方法还是有一些优化空间,像图2所示,其他几个模块也要用这个,可以把这4个模块整合起来代理,这样优化级别就是模块等级了。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。(Java要实现代理,就必须使用反射,这中间会涉及到类的加载,类在cglib中的字节码处理,运行时间较长)
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。(Spring的依赖注入内部不止要做代理,还要维护工厂生产的组件)