静态代理
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
目的:
(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂;
(2)通过代理对象对访问进行控制;
代理模式一般会有三个角色:
抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。
静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。
示例代码:
- 代理抽象角色
/**
* 代理抽象角色: 定义了服务的接口
*/
public interface Massage {
void massage();
}
- 代理对象
/**
* 代理对象
*/
public class Agent implements Massage {
private final Massage massage;
public Agent(Massage massage) {
this.massage = massage;
}
//....前置处理
public void before() {
System.out.println("前置处理");
}
//....后置处理
public void after() {
System.out.println("后置处理");
}
@Override
public void massage() {
before();
massage.massage();
after();
}
}
- 真实实现类
/**
* 真实实现类
*/
public class Lucy implements Massage {
@Override
public void massage() {
System.out.println("真干活的人");
}
}
- 静态代理使用
public static void main(String[] args) throws Exception {
//静态代理
Massage message = new Lucy();
Agent agent = new Agent(message);
agent.massage();
}
动态代理
在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提供了 Proxy
来完成这件事情。基本使用如下:
- 代理抽象角色
/**
* 代理抽象角色: 定义了服务的接口
*/
public interface Massage {
void massage();
}
/**
* 代理抽象角色2: 定义了服务的接口
*/
public interface Massage222 {
void massage222();
}
- 真实实现类
/**
* 真实实现类:A
*/
public class A implements Massage, Massage222 {
@Override
public void massage() {
System.out.println("massage====");
}
@Override
public void massage222() {
System.out.println("massage222====");
}
}
- 使用JDK提供的
Proxy
public static void main(String[] args) throws Exception {
//动态代理
A a = new A();
Object o = Proxy.newProxyInstance(MyClass.class.getClassLoader(),
new Class[]{Massage.class, Massage222.class}, new InvocationHandler() {
/**
*
* @param o 代理人 = 中介
* @param method 代理人执行的方法
* @param objects 代理人执行的方法中的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
// System.out.println(o.toString());
return method.invoke(a, objects);
}
});
// 代理人调用每个函数都会调用invoke函数
Massage massage = (Massage) o;
massage.massage();
Massage222 massage2 = (Massage222) o;
massage2.massage222();
}
为什么代理人调用每个函数都会调用invoke函数?
查看JDK中 newProxyInstance
代码
@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);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
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;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
可以看到newProxyInstance
通过 clone
拷贝了抽象接口,然后通过 getProxyClass0
获取了代理类的 class
文件,再通过反射实例化对象返回给了代理人 o
与静态代理不同,这个Class
不是由具体的.java
源文件编译而来,即没有真正的文件,只是在内存中按照Class
格式生成了一个Class
。
这个 class
是 ProxyClassFactory
生成的,查看JDK中的源码:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
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");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
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 + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
可以看到代理对象其实是通过 ProxyGenerator.generateProxyClass
生成的 byte
数组,然后再生成 class
对象
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
可以直接用这个API把代理接口的动态代理的数组数据通过IO流生成 class
文件,查看下动态代理里面的代码
private static void proxy() throws Exception {
String name = Massage.class.getName() + "$Proxy0";
//生成代理指定接口的Class数据
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Massage.class});
FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");
fos.write(bytes);
fos.close();
}
或者在执行newProxyInstance
之前将sun.misc.ProxyGenerator.saveGeneratedFiles
系统属性设置为true,生成的代理类将被自动写入磁盘;class
文件在 \Workspaces\项目名称\com\sun\proxy
路径下
// System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
// System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
查看生成的 class
文件:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.enjoy.lib.Massage;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Massage {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void massage() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.enjoy.lib.Massage").getMethod("massage");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到这个代理类在静态代码块中通过反射获取了抽象接口的所有方法。并且代理类中都有相同的方法。每次调用代理类的方法其实调用的是InvocationHandler
中的invoke
方法
应用
模拟Retrofit
定义注解:
- Field
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
String value();
}
- GET
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
String value() default "";
}
- POST
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(METHOD)
@Retention(RUNTIME)
public @interface POST {
String value() default "";
}
- Query
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {
String value();
}
请求API:
import okhttp3.Call;
public interface BalaWeatherApi {
@POST("/v3/weather/weatherInfo")
Call postWeather(@Field("city") String city, @Field("key") String key);
@GET("/v3/weather/weatherInfo")
Call getWeather(@Query("city") String city, @Query("key") String key);
}
使用:
public class MainActivity extends AppCompatActivity {
private WeatherApi weatherApi;
private static final String TAG = "MainActivity";
private BalaWeatherApi mBalaWeatherApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BalaRetrofit balaRetrofit = new BalaRetrofit.Builder().baseUrl("https://restapi.amap.com").build();
mBalaWeatherApi = balaRetrofit.create(BalaWeatherApi.class);
}
public void myGet(View view) {
okhttp3.Call call = mBalaWeatherApi.getWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
Log.i(TAG, "onResponse enjoy get: " + response.body().string());
response.close();
}
});
}
public void myPost(View view) {
okhttp3.Call call = mBalaWeatherApi.postWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
Log.i(TAG, "onResponse enjoy post: " + response.body().string());
response.close();
}
});
}
}
- BalaRetrofit
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
public class BalaRetrofit {
final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
final Call.Factory callFactory;
final HttpUrl baseUrl;
BalaRetrofit(Call.Factory callFactory, HttpUrl baseUrl) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
}
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//解析这个method 上所有的注解信息
ServiceMethod serviceMethod = loadServiceMethod(method);
//args:
return serviceMethod.invoke(args);
}
});
}
private ServiceMethod loadServiceMethod(Method method) {
//先不上锁,避免synchronized的性能损失
ServiceMethod result = serviceMethodCache.get(method);
if (result != null) return result;
//多线程下,避免重复解析,
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
/**
* 构建者模式,将一个复杂对象的构建和它的表示分离,可以使使用者不必知道内部组成的细节。
*/
public static final class Builder {
private HttpUrl baseUrl;
//Okhttp->OkhttClient
private okhttp3.Call.Factory callFactory; //null
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = factory;
return this;
}
public Builder baseUrl(String baseUrl) {
this.baseUrl = HttpUrl.get(baseUrl);
return this;
}
public BalaRetrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
return new BalaRetrofit(callFactory, baseUrl);
}
}
}
- ServiceMethod
import com.enjoy.enjoyclass04.retrofit.annotation.Field;
import com.enjoy.enjoyclass04.retrofit.annotation.GET;
import com.enjoy.enjoyclass04.retrofit.annotation.POST;
import com.enjoy.enjoyclass04.retrofit.annotation.Query;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.Request;
/**
* 记录请求类型 参数 完整地址
*/
public class ServiceMethod {
private final Call.Factory callFactory;
private final String relativeUrl;
private final boolean hasBody;
private final ParameterHandler[] parameterHandler;
private FormBody.Builder formBodyBuild;
HttpUrl baseUrl;
String httpMethod;
HttpUrl.Builder urlBuilder;
public ServiceMethod(Builder builder) {
baseUrl = builder.mBalaRetrofit.baseUrl;
callFactory = builder.mBalaRetrofit.callFactory;
httpMethod = builder.httpMethod;
relativeUrl = builder.relativeUrl;
hasBody = builder.hasBody;
parameterHandler = builder.parameterHandler;
//如果是有请求体,创建一个okhttp的请求体对象
if (hasBody) {
formBodyBuild = new FormBody.Builder();
}
}
public Object invoke(Object[] args) {
/**
* 1 处理请求的地址与参数
*/
for (int i = 0; i < parameterHandler.length; i++) {
ParameterHandler handlers = parameterHandler[i];
//handler内本来就记录了key,现在给到对应的value
handlers.apply(this, args[i].toString());
}
//获取最终请求地址
HttpUrl url;
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
url = urlBuilder.build();
//请求体
FormBody formBody = null;
if (formBodyBuild != null) {
formBody = formBodyBuild.build();
}
Request request = new Request.Builder().url(url).method(httpMethod, formBody).build();
return callFactory.newCall(request);
}
// get请求, 把 k-v 拼到url里面
public void addQueryParameter(String key, String value) {
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
urlBuilder.addQueryParameter(key, value);
}
//Post 把k-v 放到 请求体中
public void addFiledParameter(String key, String value) {
formBodyBuild.add(key, value);
}
public static class Builder {
private final BalaRetrofit mBalaRetrofit;
private final Annotation[] methodAnnotations;
private final Annotation[][] parameterAnnotations;
ParameterHandler[] parameterHandler;
private String httpMethod;
private String relativeUrl;
private boolean hasBody;
public Builder(BalaRetrofit balaRetrofit, Method method) {
this.mBalaRetrofit = balaRetrofit;
//获取方法上的所有的注解
methodAnnotations = method.getAnnotations();
//获得方法参数的所有的注解 (一个参数可以有多个注解,一个方法又会有多个参数)
parameterAnnotations = method.getParameterAnnotations();
}
public ServiceMethod build() {
/**
* 1 解析方法上的注解, 只处理POST与GET
*/
for (Annotation methodAnnotation : methodAnnotations) {
if (methodAnnotation instanceof POST) {
//记录当前请求方式
this.httpMethod = "POST";
//记录请求url的path
this.relativeUrl = ((POST) methodAnnotation).value();
// 是否有请求体
this.hasBody = true;
} else if (methodAnnotation instanceof GET) {
this.httpMethod = "GET";
this.relativeUrl = ((GET) methodAnnotation).value();
this.hasBody = false;
}
}
/**
* 2 解析方法参数的注解
*/
int length = parameterAnnotations.length;
parameterHandler = new ParameterHandler[length];
for (int i = 0; i < length; i++) {
// 一个参数上的所有的注解
Annotation[] annotations = parameterAnnotations[i];
// 处理参数上的每一个注解
for (Annotation annotation : annotations) {
//todo 可以加一个判断:如果httpMethod是get请求,现在又解析到Filed注解,可以提示使用者使用Query注解
if (annotation instanceof Field) {
//得到注解上的value: 请求参数的key
String value = ((Field) annotation).value();
parameterHandler[i] = new ParameterHandler.FiledParameterHandler(value);
} else if (annotation instanceof Query) {
String value = ((Query) annotation).value();
parameterHandler[i] = new ParameterHandler.QueryParameterHandler(value);
}
}
}
return new ServiceMethod(this);
}
}
}
- ParameterHandler
public abstract class ParameterHandler {
abstract void apply(ServiceMethod serviceMethod, String value);
static class QueryParameterHandler extends ParameterHandler {
String key;
public QueryParameterHandler(String key) {
this.key = key;
}
//serviceMethod: 回调
@Override
void apply(ServiceMethod serviceMethod, String value) {
serviceMethod.addQueryParameter(key,value);
}
}
static class FiledParameterHandler extends ParameterHandler {
String key;
public FiledParameterHandler(String key) {
this.key = key;
}
@Override
void apply(ServiceMethod serviceMethod, String value) {
serviceMethod.addFiledParameter(key,value);
}
}
}