前言
相信各位小伙伴在准备面试的时候,AOP都是无法绕过的一个点,经常能看到动态代理、JDK动态代理、CGLIB动态代理这样的字眼。其实动态代理是代理模式的一种。代理模式有静态代理、强制代理、动态代理。所以在认识AOP之前需要了解代理模式。
代理模式定义
代理模式(Proxy Pattern):为其他对象提供一种代理以控制这个对象的访问。
- Subject抽象主题角色,也叫做抽象主题类。可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
- RealSubject具体主题角色,也叫做被委托角色,被代理角色,是业务逻辑的具体执行者。
- Proxy代理主题角色,也叫做委托类、代理类。他负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做到预处理和善后工作。
代理模式的优点
- 职责清晰
- 高扩展性
- 智能化
UML
我的理解
跳板机不管是对于运维老哥还是对于我们来讲,都是日常的工作中不可或缺的一个工具。为了保证生产服务器的安全,我们是无法通过xshell等工具直接进行连接的。如果需要操作生产的服务器,则需要通过跳板机。并且跳板机还能记录我们的操作,用来做安全审计。防止出现,某位老哥一气之下反手就是个sudo rm -rf /*
直接凉凉。
我们回过头看看代理模式的定义:为其他对象提供一种代理以控制这个对象的访问。实际上跳板机就是生产服务器的一个代理Proxy
,为了实现控制生产服务器的访问权限。你需要通过跳板机来操作生产服务器。
- 为其他对象提供一种代理以控制这个对象的访问
- 给你提供一个跳板机来访问生产服务器,目的是来控制生产服务器的访问
Proxy
的职责:Proxy
是对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现。并且在真实主题角色处理完毕前后做到预处理和善后工作。你通过操作跳板机。跳板机将你输入的命令在生产服务器上进行执行。并且能记录下执行的命令和执行的结果。
代码演示
Server
public interface Server {
/**
* 执行
* @param command 命令
*/
void exec(String command);
}
ProdServer
public class ProdServer implements Server {
@Override
public void exec(String command){
System.out.println(command + ":执行成功");
}
}
JumpServer
public class JumpServer {
private Server server;
public JumpServer(Server server) {
this.server = server;
}
public void exec(String command){
System.out.println("xxx在:" + LocalDateTime.now() + " 执行了:" + command);
server.exec(command);
System.out.println("xxx在:" + LocalDateTime.now() + " 执行完了:"+ command + " 结果是XXX");
}
}
Client
public class Client {
public static void main(String[] args) {
JumpServer jumpServer = new JumpServer();
jumpServer.exec("pwd");
}
}
运行结果
xxx在:2020-04-04T16:43:19.277 执行了:pwd
pwd:执行成功
xxx在:2020-04-04T16:43:19.278 执行完了:pwd 结果是XXX
通过上面的代码,简单的实现了代理模式。在网络上代理服务器设置分为透明代理和普通代理。
- 透明代理就是用户不用设置代理服务器地址,就可以直接访问.也就是说代理服务器对用户来说是透明的,不用知道它存在的。
- 普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。
当运维老哥给了一台服务器的账号和密码,你成功登录,并完成了相应的操作。你以为给你的是生产的服务器。实际就可能是个跳板机。为了安全起见,是不可能将实际服务器的IP让你知道的。很显然跳板机对你来讲就是透明的。
所以我们调整一下代码,将其变成普通代理
JumpServer
public class JumpServer {
private Server server;
public JumpServer(Server server) {
this.server = server;
}
public void exec(String command){
server.exec(command);
}
}
Client
public class Client {
public static void main(String[] args) {
ProdServer prodServer = new ProdServer();
JumpServer jumpServer = new JumpServer(prodServer);
jumpServer.exec("pwd");
}
}
执行结果
xxx在:2020-04-04T16:52:23.282 执行了:pwd
pwd:执行成功
xxx在:2020-04-04T16:52:23.283 执行完了:pwd 结果是XXX
强制代理
对于现实情况,我们可以通过不开放公网访问的权限来实现,强制使用代理操作服务器。我们可以用代码简单的模拟下。
JumpServer
public class ProdServer implements Server {
private JumpServer jumpServer;
@Override
public void exec(String command){
hasProxy();
System.out.println(command + ":执行成功");
}
private void hasProxy(){
if(jumpServer == null){
throw new RuntimeException("请使用跳板机!");
}
}
public JumpServer setJumpServer() {
this.jumpServer = new JumpServer(this);
return this.jumpServer;
}
}
Client未设置跳板机
public class Client {
public static void main(String[] args) {
ProdServer prodServer = new ProdServer();
prodServer.exec("pwd");
}
}
这个时候需要访问生产服务器,就需要先设置跳板机了,才能进行操作。
动态代理
对静态代理来说,我们需要手动生成代理类。但是如果需要代理的类太多了,那这个肯定是不可取的。所以我们可以使用JDK动态代理来帮我们完成工作。