Static agent
public interface UserService {
String getASingleUser(String username);
}
public class OkHttpUserService implements UserService {
@Override
public String getASingleUser(String username) {
System.out.println(String.format(Locale.US, "OkHttp do call: getASingleUser(%s)", username));
return String.join("-"."UserInfo", username); }}public class RetrofitUserService implements UserService {
UserService service;
RetrofitUserService(UserService obj) {
this.service = obj;
}
@Override
public String getASingleUser(String username) {
System.out.println(String.format(Locale.US, "Proxy do call: getASingleUser(%s)", username));
String userParam = username.toUpperCase(Locale.US);
System.out.println(String.format(Locale.US, "Proxy do something before OkHttp call, args: %s -> %s", username, userParam));
String userIfo = service.getASingleUser(userParam);
String response = userIfo.toUpperCase(Locale.US);
System.out.println(String.format(Locale.US, "Proxy do something after OkHttp call, ret: %s -> %s", userIfo, response));
return response;
}
}
OkHttpUserService okHttpUserService = new OkHttpUserService();
RetrofitUserService proxy = new RetrofitUserService(okHttpUserService);
String userInfo = proxy.getASingleUser("google");
System.out.println(String.format(Locale.US, "Final result: %s", userInfo));
Copy the code
Proxy do call: getASingleUser(google)
Proxy do something before OkHttp call, args: google -> GOOGLE
OkHttp do call: getASingleUser(GOOGLE)
Proxy do something after OkHttp call, ret: UserInfo-GOOGLE -> USERINFO-GOOGLE
Final result: USERINFO-GOOGLE
Copy the code
Proxy objects and by proxy objects implement the same interface, the proxy object will hold by proxy objects at the same time, the complete control of the by proxy objects and access the traditional agency model is also known as static agent proxy objects like the agent or lawyer in real life, can do some work before the customer access to the real object, also can do some work, after the visit You can help real objects reject unreasonable requests, and you can hand things off to real objects
A dynamic proxy
There is also a proxy pattern in Java that generates proxy classes and proxy objects at run time. This proxy pattern is also called dynamic proxy. The proxy bridge is implemented by InvocationHandler, for example:
public interface UserService {
String getASingleUser(String username);
}
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(String.format(Locale.US, "Proxy do invoke: invoke(proxy, %s, %s)", method.getName(), Arrays.toString(args)));
String username = (String) args[0];
String userParam = username.toUpperCase(Locale.US);
System.out.println(String.format(Locale.US, "Proxy do something before OkHttp call, args: %s -> %s", username, userParam));
System.out.println(String.format(Locale.US, "OkHttp do call: %s(%s)", method.getName(), userParam));
String userIfo = String.join("-"."UserInfo", userParam);
String response = userIfo.toUpperCase(Locale.US);
System.out.println(String.format(Locale.US, "Proxy do something after OkHttp call, ret: %s -> %s", userIfo, response));
returnresponse; }}; UserService proxy = (UserService) Proxy.newProxyInstance(OkHttpUserService.class.getClassLoader(),newClass<? >[] { UserService.class }, handler); String userInfo = proxy.getASingleUser("google");
System.out.println(String.format(Locale.US, "Final result: %s", userInfo));
Copy the code
Proxy do invoke: invoke(proxy, getASingleUser, [google])
Proxy do something before OkHttp call, args: google -> GOOGLE
OkHttp do call: getASingleUser(GOOGLE)
Proxy do something after OkHttp call, ret: UserInfo-GOOGLE -> USERINFO-GOOGLE
Final result: USERINFO-GOOGLE
Copy the code
As you can see, we don’t need to write the Proxy class. All method calls to the Proxy object are forwarded to the Invoke () callback of InvocationHandler. We simply fetch the Proxy object through the Proxy’s static method newProxyInstance(). How does the system generate classes for proxy objects and create proxy objects? By reflection, of course, we first inherit the Proxy and implement the given interface:
visit(V14, accessFlags, dotToSlash(className), null,
JLR_PROXY, typeNames(interfaces));
Copy the code
Then add the object’s hashCode(), equals(), toString() methods:
addProxyMethod(hashCodeMethod);
addProxyMethod(equalsMethod);
addProxyMethod(toStringMethod);
Copy the code
Then add all the methods in the given interface:
for(Class<? > intf : interfaces) {for (Method m : intf.getMethods()) {
if(! Modifier.isStatic(m.getModifiers())) { addProxyMethod(m, intf); }}}Copy the code
The InvocationHandler member variable h is used to delegate the implementation of the method to the InvocationHandler member variable H. This completes the generation of the proxy class:
generateConstructor();
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for the Method object
visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName,
LJLR_METHOD, null.null);
// Generate code for proxy method
pm.generateMethod(this, className);
}
}
generateStaticInitializer();
Copy the code
To create the proxy object, call the constructor of the newly generated proxy class:
return cons.newInstance(new Object[]{h});
Copy the code
Now, even if we don’t see what the generated Proxy class looks like, we know what its structure is. It inherits the Proxy and implements the interface we’re given. Its constructor takes the InvocationHandler argument, It overrides the hashCode(), equals(), toString() methods and forwards them all to InvocationHandler’s invoke() handler, which implements all the methods of our given interface, And forward them all to the Invoke () processing of InvocationHandler, so the proxy class must look something like this:
public final classThe $1extends Proxy implements UserService {
private static Method m0;
private static Method m1;
private static Method m2;
private static Method m3;
static {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
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.frank.UserService").getMethod("getASingleUser", Class.forName("java.lang.String"));
}
public $1(InvocationHandler var1) {
super(var1);
}
public final int hashCode(a) {
return ((Integer) super.h.invoke(this, m0, (Object[]) null)).intValue();
}
public final boolean equals(Object var1) {
return ((Boolean) super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
}
public final String toString(a) {
return (String) super.h.invoke(this, m2, (Object[]) null);
}
public final String getASingleUser(String var1) {
return (String) super.h.invoke(this, m3, newString[]{var1}); }}Copy the code
Visible dynamic proxy sometimes more flexible than static agent, we do not need to manually implement proxy class, also do not need to manually create a proxy object, can realize the function of the agent, but the dynamic proxy by using the reflection of the technology, so the performance is not so good static agent In brief dynamic proxy technology make it possible for flexible implementation agent, Retrofit also leverages dynamic proxy technology to elegantly encapsulate HTTP requests