【设计模式】代理模式,java 面试官常问的问题
/**
@ProjectName: demo
@Package: com.ymy.proxy
@ClassName: IUserService
@Author: 流星 007
@Description: user interface
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2020/8/29 15:26
@Version: 1.0
*/
public interface IUserService {
Boolean login(String username,String password);
}
package com.ymy.proxy;
/**
@ProjectName: demo
@Package: com.ymy.proxy
@ClassName: UserService
@Author: 流星 007
@Description: user login or register
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2020/8/29 15:10
@Version: 1.0
*/
public class UserService implements IUserService{
/**
login in
@param username
@param password
*/
@Override
public Boolean login(String username,String password){
System.out.println("username:"+username);
System.out.println("password:"+password);
return true;
}
}
package com.ymy.proxy;
/**
@ProjectName: demo
@Package: com.ymy.proxy
@ClassName: UserServiceProxy
@Author: 流星 007
@Description: user proxy
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2020/8/29 15:29
@Version: 1.0
*/
public class UserServiceProxy implements IUserService{
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public Boolean login(String username, String password) {
long startTime = System.currentTimeMillis();
Boolean login = userService.login(username, password);
long endTime = System.currentTimeMillis();
System.out.println("login time : " + (endTime - startTime) +" ms" );
return login;
}
}
package com.ymy.proxy;
/**
@ProjectName: demo
@Package: com.ymy.proxy
@ClassName: Test
@Author: 流星 007
@Description: test
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2020/8/29 15:15
@Version: 1.0
*/
public class Test {
public static void main(String[] args) {
UserServiceProxy userServiceProxy = new UserServiceProxy(new UserService());
Boolean login = userServiceProxy.login("zhgnsan", "123456");
System.out.println("login result : " +login);
}
}
这就是基于组合模式实现的静态代理,代理类和被代理类同时实现一个 IUserService 接口,代理类中的 login 方法去调用被代理类中的 login 方法,这样需要稍微改动一下被代理类,让他们去实现同一个接口。
[](
)组合与继承如何选择
========================================================================
静态代理的实现方式相信大家也应该知道怎么实现了,那什么时候实现组合,什么时候使用继承呢?不知道大家有没有听过这么一句话:组合优于继承;能使用组合就少使用继承,原因是继承的层数过大时维护和扩展会非常难受,但是也不绝对,相对于代理模式而言,如果被代理的类来源于第三方,你无法改动原类,那么继承将是一个不错的原则,一般情况下,我还是推荐组合的这种方式。
[](
)动态代理
===================================================================
静态代理是说完了,还有一种代理模式不知道大家听过没有,那就是动态代理模式,如果熟悉 java aop 的都应该对 java 的动态代理不会陌生,那我们现在来讲讲什么是动态代理。
不知道大家思考过一个问题没有,你们觉得静态代理有什么缺点吗?
可能大家想到了,如果被代理的类过多,成千上百的话,代理类也要成千上百,大大增加了程序员的工作量,他们都是差不多的一个模板,难道就不能只写一次,符合所有的被代理类吗?
当然可以,这个时候动态代理就出现了,动态代理:就是我们不需要在程序运行前为被代理类创建代理类,而是在程序运行中,动态的创建代理类,然后使用这些动态生成的代理类替代原始类,是不是就省去了大量的编码时间?那如何实现呢?
下面请看我表演
package com.ymy.proxy;
/**
@ProjectName: demo
@Package: com.ymy.proxy
@ClassName: IUserService
@Author: 流星 007
@Description: user interface
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2020/8/29 15:26
@Version: 1.0
*/
public interface IUserService {
Boolean login(String username,String password);
}
package com.ymy.proxy;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;
import java.lang.reflect.Method;
/**
@ProjectName: demo
@Package: com.ymy.proxy
@ClassName: dynamicUserUserProxy
@Author: 流星 007
@Description: dynamic User proxy
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2020/8/30 13:33
@Version: 1.0
*/
public class DynamicUserUserProxy {
public Object createProxy(Object object) {
Class[] interfaces = object.getClass().getInterfaces();
DynamicProxyHandler handler = new DynamicProxyHandler(object);
return Proxy.newProxyInstance(object.getClass().getClassLoader(), interfaces, handler);
}
private class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = method.invoke(object, args);
long endTime = System.currentTimeMillis();
System.out.println("login time : " + (endTime - startTime) +" ms" );
return result;
}
}
}
package com.ymy.proxy;
/**
@ProjectName: demo
@Package: com.ymy.proxy
@ClassName: DynamicTest
@Author: 流星 007
@Description: Dynamic Test
csdn:https://blog.csdn.net/qq_33220089
今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
@Date: 2020/8/30 13:36
@Version: 1.0
*/
public class DynamicTest {
public static void main(String[] args) {
DynamicUserUserProxy proxy = new DynamicUserUserProxy();
IUserService userService = (IUserService) proxy.createProxy(new UserService());
Boolean login = userService.login("zhangsan", "123456");
System.out.println("login result :" +login);
}
}
username:zhangsan
password:123456
login time : 0 ms
login result :true
Process finished with exit code 0
我这里实现动态代理是基于 cglib,由静态代理改成动态代理之后是不是觉得干净多了,如果后续还有类需要代理,就不需要在创建代理类了,让程序在运行中自己创建,简直不要太爽。
[](
)代理模式应用场景
=======================================================================
1.日志、鉴权、限流、统计等物业功能的开发。
2.rpc 远程调用。
3.一个类需要做扩展功能的时候。
[](
)总结
=================================================================
什么是代理模式?,就是在不改变原类的情况下新增新的功能,比如日志、流量监控、请求耗时等等操作都可以通过代理模式来解决。
代理模式分为静态代理和动态代理,静态代理可以通过继承和组合实现,动态代理可以通过 cglib 和 jdk 自带的动态代理实现,如果在日常开发过程中,你想要做一些扩展功能,比如:日志、鉴权等,那么就可以考虑一下代理模式哦。
推荐文章:
[【设计模式】单例模式](
)
[【设计模式】工厂模式:你还在使用一堆的 if/else 创建对象吗?](
)
[【设计模式】建造者模式:你创建对象的方式有它丝滑吗?](
)
[【设计模式】原型模式
:如何快速的克隆出一个对象?](
)
[【设计模式】策略模式:我是一个有谋略的类](
)
[【设计模式】观察者模式:一个注册功能也能使用到设计模式?](
)[【设计模式】门面模式:接口就像门面,一眼就能看出你的代码水平](
)
评论