前言
这两天复习Retrofit源码时,看到了一些设计模式,平时写业务逻辑或多或少会使用的,简单记录一下^^--^^
目录
一、普通代理
定义一个接口IGamePlayer,所有喜欢网络游戏的玩家,然后定义一个具体的实现类GamePlayer,实现每个游戏爱好者为了玩游戏要执行的操作。
public interface IGamePlayer{
public void login(String user,String pwd);//登录
public void killBoss();//打怪升级
}
//玩家
public class GamePlayer implements IGamePlayer{
private String name = "";
public GamePlayer(String name){
this.name = name;
}
public void login(String user,String pwd){
//登录游戏
}
public void killBoss(){
//打怪
}
}
玩家打怪升级
IGamePlayer player = new GamePlayer("张三");
player.login("张三","密码");
player.killBoss();
这时候玩家累了,去找了一个代练
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer gamePlayer){
this.gamePlayer = gamePlayer;
}
public void login(String user,String password){
this.gamePlayer.login(user,password);
}
public void killBoss(){
this.gamePlayer.killBoss();
}
}
找到代练后,给他账号、密码,代练也要清楚知道是谁要我帮他打怪,然后开始
IGamePlayer player = new GamePlayer("张三");
//player去找代练
IGamePlayer proxy = new GamePlayerProxy(player);
proxy.login("user","pwd");
proxy.killBoss();
代练代理了真正玩家角色,负责真正账号、角色的应用。实际结果都是作用于玩家的账号,可以理解为玩家。这种你清楚这个代练,代练也清楚你,代打起来你也放心。这里是怎么做到让代练也清楚你呢?通过构造函数指定实际玩家角色。
其中好处就是可以减轻你的负担。这也是代理模式的优点。但是上面这样写,有点问题,每次找代理打游戏的时候,你都要事先热身,再交给代练吗?也就是这里 的 new GamePlayer("张三"),这样的话,高层业务逻辑即调用者知道了真实角色是谁,为了使高层业务对真实角色的影响,可以将GamePlayerProxy的构造函数改成传账户、密码,然后内部new GamePlayer。
二、强制代理
强制代理比较另类,一般都是通过代理找到真实的角色,但是强制代理必须要通过真实角色才能找到代理角色,就像你去找明星合作,明星会给经纪人,也就是代理人的信息给你,让你们去沟通,强制代理类似如此。
public interface IGamePlayer{
public void login(String user,String pwd);//登录
public void killBoss();//打怪升级
public IGamePlayer getProxy();//获得代理
}
public class GamePlayer implements IGamePlayer{
private String name = "";
private IGamePlayer proxy = null; //玩家的代理
public GamePlayer(String name){
this.name = name;
}
public IGamePlayer getProxy(){
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
public void killBoss(){
if(this.isProxy()){
//代理成功
}else{
//请找我的代理干活
}
}
public void login(String user,String pwd){
if(this.isProxy()){
}else{
//代理失败
}
}
private boolean isProxy(){
return this.proxy == null;
}
}
其实和之前的普通代理刚开始写法差不多,唯一的区别就是做了角色限制,代理的管理由真实角色完成。
IGamePlayer player = new GamePlayer("张三");
IGamePlayer proxy = player.getProxy();
proxy.login("user","pwd");
proxy.killBoss();
三、动态代理及源码解析
//动态代理类
public class GamePlayIH implements InvocationHandler{
//被代理者
Class cls = null;
//被代理的实例
Object obj = null;
//我要代理谁
public GamePlayIH(Object obj){
this.obj = obj;
}
//调用被代理的方法
public Object invoke(Object proxy,Mehtod method,Object[] args){
Object result = method.invoke(this.obj,args);
if(method.getName().equals("login")){
//有人登录了你的账号
}
return result;
}
}
//调用
IGamePlayer palyer = new GamePlayer("张三");
InvocationHandler handler = new GamePlayIH(player);
IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(palyer.getClass().getClassLoader(),new Class[]{IGamePlayer.class},handler);
动态代理都会用,底层是如何实现的呢?Proxy创建代理的源码如下
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
*1\. 生成代理类Class对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
......
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 2.创建Proxy代理实例
return cons.newInstance(new Object[]{h});
........
}
**主要就是通过getProxyClass0生成代理类对象Class,然后反射创建代理类对象并返回。**getProxyClass0方法如下
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
直接从proxyClassCache中根据ClassLoader和指定接口interfaces匹配取出,如果没有的话,则通过ProxyClassFactory创建,其中proxyClassCache是WeachCache类型对象,初始化如下
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
可以继续跟进get方法查看一下是如和取数据的
public V get(K key, P parameter) {
//代理类接口不能为空
Objects.requireNonNull(parameter);
expungeStaleEntries();
//生成CacheKey对象
Object cacheKey = CacheKey.valueOf(key, refQueue);
//根据cacheKey获取键值对valuesMap,valuesMap的key是接口列表的包装类,value是动态代理类的包装类
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//生成 代理类对应的 key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//代理类的包装类
Supplier<V> supplier = valuesMap.get(subKey);
//生成动态代理的工厂类
Factory factory = null;
while (true) {
if (supplier != null) {
// 关注这里 即使supplier为空 后面supplier会被创建
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
.....
}
}
}
可以看到get方法内调用了Factory的get方法
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
//通过valueFactory的apply方法创建动态代理类
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
.....
return value;
}
}
Factory的get方法里主要是通过ProxyClassFactory的apply方法来生成动态代理类的
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//1.获取代理类接口的Class对象,并校验是否是接口
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
......
}
//2.要生成的代理类的包名
String proxyPkg = null; // package to define proxy class in
//类的修饰符
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
for (Class<?> intf : interfaces) {
//3.根据代理类接口生成包名和访问权限修饰符
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
// 4\. 准备代理类类名
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//5.生成字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 6.生成代理类对象 native方法
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
主要方法就是上述代码中的ProxyGenerator。generateProxyClass()和defineClass0()方法,一个是根据类名、接口、类访问权限,按照class文件格式生成字节流,一个是根据字节流生成代理类对象。
动态代理优点:
不修改源码的基础上对方法增强,在此过程中会建立一个新的类对象
动态代理缺点:
有时候我们写的类中,可能许多内部的方法会相互之间调用,但如果我想对它们进行同时增强的话,用动态代理是做不到的。因为动态代理只会增强最先调用的方法,后续内部之间的相互调用的方法,是不会被增强的。
比如
public Object invoke(Object proxy,Mehtod method,Object[] args){
Object result = method.invoke(this.obj,args);
if(method.getName().equals("login")){
//有人登录了你的账号
}else if(method.getName().equals("killBoss")){
}
return result;
} public void login(String user,String pwd){
if(this.isProxy()){
killBoss();
}else{
//代理失败
}
}
在对login方法增强的时候,如果login方法内调用了killBoss()方法,动态代理是不会对这个killBoss()方法增强
四、代理模式的作用以及应用场景
现在增加一个需求,需要在所有类的方法前后增加校验或者是打印功能,那你会怎么做呢?肯定是利用代理模式,为现有的类增加代理类,在代理类中实现前后打印功能,这就是它的作用。
代理模式可以在不修改代理对象代码基础上,通过扩展代理类,进行一些功能的扩展,动态代理的话,只需要去实现代理的扩展InvocationHandler即可。
代理模式(除JDK提供外的动态代理方式还有 CGLB基于ASM技术的动态代理)常见于AOP切面编程,例如ASpectJ。