Annotations we use them a lot, and many frameworks provide a lot of annotations for us to use, For example, @route (path = “/test/activity”) for ARouter, @bindView (R.i.User) for Butterknife EditText username; But, have you customized annotations and written your own annotation handler? Reflection sounds fancy, but when you really get to know it, it’s just some API calls; Dynamic proxy is just a reflection technique based on static proxy (proxy mode); This article will give you a better understanding of annotations, reflection, and dynamic proxies.
The sample code for this article is posted on Github.
annotations
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the actions of the code they annotate.
Annotations can be used in many ways, such as:
- Provide information to the compiler: The compiler can use annotations to check for errors or suppress warnings
- Compile or deploy-time processing: you can generate code, XML, files, and so on
- Runtime processing: Annotations can be checked at runtime
Format of annotations
The format of the notes is as follows:
@Persilee
class MyClass {... }
Copy the code
Annotations begin with @ followed by content. Annotations can contain elements such as:
@Persilee(id=Awesome!, value = "lsy")
class MyClass {... }
Copy the code
If there is only one value element, the name can be omitted. If there are no elements, the parentheses can be omitted. For example
@Persilee("lsy") // There is only one value element
class MyClass {... }
@Persilee // No element
class MyClass {... }
Copy the code
If the annotation has the same type, it is a duplicate annotation, such as
@Persilee("lsy")
@Persilee("zimu")
class MyClass {... }
Copy the code
Annotation statement
The definition of an annotation is similar to the definition of an interface, with the @ prefix in front of the keyword interface, as in:
@interface Persilee {
int id(a);
String value(a);
}
Copy the code
Annotation type
Int ID () and String value() are annotation types. They can also define optional defaults, such as:
@interface Persilee {
int id(a);
String value(a) default "lsy";
}
Copy the code
When using annotations, if the annotation type of the defined annotation does not have a default value, it must be assigned, as in:
@Persilee(id = Awesome!) // id must be assigned. For example, @persilee will prompt you that ID must be assigned
class MyClass {... }
Copy the code
Yuan notes
Annotations above annotations are called meta-annotations, such as annotations
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@interface Persilee {
int id(a);
String value(a) default "lsy";
}
Copy the code
There are several meta-annotation types defined in java.lang.annotation (@retention, @target are commonly used), such as
@retention specifies how annotations are stored, as we know from retentionPolicy.java (which is an enumeration), such as:
public enum RetentionPolicy {
SOURCE, // The annotations of the tag remain only at the source level and are ignored by the compiler.
CLASS, // Annotations of the tag are retained by the compiler at compile time, but ignored by the Java Virtual Machine (JVM).
RUNTIME // The annotation of the tag is reserved by the JVM so that the runtime environment can use it.
}
Copy the code
@target specifies the scope in which the annotation can be used. We know the scope from elementType. Java (which is an enumeration) as follows:
public enum ElementType {
TYPE, / / class
FIELD, // Field or attribute
METHOD, / / method
PARAMETER, / / parameters
CONSTRUCTOR, // constructor
LOCAL_VARIABLE, // Local variables
ANNOTATION_TYPE, // Can also be used for annotations
PACKAGE, / / package
TYPE_PARAMETER, // Type parameter
TYPE_USE // Any type
}
Copy the code
TYPE_PARAMETER (type parameter), TYPE_USE (any type name) may not be well understood. If Target is set to @target ({elementtype.type_parameter}), Representations can be used on type parameters of generics (which were introduced in the previous article), such as:
public class TypeParameterClassThe < @Persilee T> {
public <@Persilee T> T foo(T t) {
return null;
}
}
Copy the code
If Target is set to @target ({elementtype.type_use}), it can be used on any type, such as:
TypeParameterClass<@Persilee String> typeParameterClass = new TypeParameterClass<>();
@Persilee String text = (@Persilee String)new Object();
Copy the code
The @documented annotation indicates that a specified annotation is used, and these elements will be Documented using the Javadoc tool.
The @Inherited annotation indicates that annotation types can be Inherited from superclasses.
The @REPEATable annotation indicates that the tagged annotation can be applied to the same statement or type multiple times.
Annotation Application Scenario
Depending on how the @Retention meta-annotation is defined, annotations can be used in one of the following three scenarios:
level | technology | instructions |
---|---|---|
The source code | APT | Classes and all member information in which annotations and annotation declarations can be obtained at compile time are typically used to generate additional helper classes. |
The bytecode | Bytecode enhancement | After the Class is compiled, you can modify the Class data to modify the code logic. You can use annotations to distinguish whether changes are needed or to determine whether changes are made to different logic. |
The runtime | reflection | While the program is running, the annotation and its elements are dynamically retrieved by reflection technology to make different logical judgments. |
Small case study (syntax checking with annotations)
We define a weekDay field that is of type weekDay enumeration type so that we can set the values specified in the enumeration, such as:
class WeekDayDemo {
private static WeekDay weekDay;
enum WeekDay {
SATURDAY,SUNDAY
}
public static WeekDay getWeekDay(a) {
return weekDay;
}
public static void setWeekDay(WeekDay weekDay) {
WeekDayDemo.weekDay = weekDay;
}
public static void main(String[] args) {
setWeekDay(WeekDay.SATURDAY);
System.out.println(getWeekDay());
}
}
Copy the code
As we all know, enumerations in Java are essentially special static member variables. At runtime, all enumerations are loaded into memory as singletons, which consume a lot of memory. So, is there an optimized solution?
Instead of enumerations, we use constants and @intdef meta-annotations, such as:
class IntdefDemo {
private static final int SATURDAY = 0;
private static final int SUNDAY = 1;
private static int weekDay;
@IntDef({SATURDAY, SUNDAY})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@interface WeekDay { // Create a custom WeekDay annotation
}
public static void setWeekDay(@WeekDay int weekDay) { // Use WeekDay annotations to restrict parameter types
IntdefDemo.weekDay = weekDay;
}
public static void main(String[] args) {
setWeekDay(SATURDAY); // Only SATURDAY, SUNDAY can be passed
}
}
Copy the code
APT annotation processor
Annotation Processor APT(Annotation Processor Tools) is used to process annotations. Compiled Java files need to be compiled by Javac into bytecode (Class) files that can be loaded by VMS. Annotation Processor is a tool provided by Javac. Used to process annotation information at compile time.
Now that we have defined @persilee annotations, we can write a simple annotation processor to handle @persilee annotations. We can create a new Java Module and create a PersileeProcessor class, such as:
@SupportedAnnotationTypes("net.lishaoy.anreprdemo.Persilee") // Specify the annotation to work with
public class PersileeProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager(); //
messager.printMessage(Diagnostic.Kind.NOTE, "APT working ...");
for (TypeElement typeElement: set) {
messager.printMessage(Diagnostic.Kind.NOTE,"= = = >" + typeElement.getQualifiedName());
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(typeElement);
for (Element element: elements) {
messager.printMessage(Diagnostic.Kind.NOTE,"= = = >" + element.getSimpleName());
}
}
return false;
}
}
Copy the code
Then, create a new resources directory under the main directory, as shown:
This directory structure is dead, we must write, then in the javax.mail. Annotation. Processing. The Processor file annotation Processor register need to be addressed, such as
net.lishaoy.aptlib.PersileeProcessor
Copy the code
Finally, introduce modules in your app’s build.gradle file, such as
dependencies {
.
annotationProcessor project(':aptlib')
}
Copy the code
When you Build the project, the Task: : app compileDebugJavaWithJavac Task print we log information in the annotation processing program, such as:
Note: APT working...
Note: = = = >.net. Lishaoy. Anreprdemo. Persilee
Note: = = = > MainActivity
Copy the code
Because we only use the @persilee annotation in MainActivity, as follows:
@Persilee(id = Awesome!, value = "lsy")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Copy the code
reflection
In general, when we use a class, we must know what it is and what it does. We instantiate the class directly and then operate with the class object.
Cook cook = new Cook(); // Instantiate an object
cook.cookService("🍅");
Copy the code
Reflection does not know what the initialized class object is at the beginning, nor can it use the new keyword to create the object. Reflection does not know what the class to operate on is at run time, and can obtain the complete construction of the class at run time, and call the corresponding method.
The Java reflection mechanism provides the following functions:
- Constructs an object of any class at run time
- Gets or modifies the member variables and methods that any class has at run time
- Call methods (properties) of any object at run time
Class Class
Class is a Class that encapsulates the information about the corresponding Class of the current object. Each Class we write can be regarded as an object, which is an object of the Java.lang. Class Class, which describes the Class.
Get the Class object
Class objects can be obtained in three ways:
- Gets the class name by class name. Class
- Get the object name from the object.getClass ()
- Get class.forname (Class name) by Class name
Cook cook = new Cook();
Class cookClass = Cook.class;
Class cookClass1 = cook.getClass();
Class cookClass2 = Class.forName("net.lishaoy.reflectdemo.Cook");
Copy the code
Create an instance
We can use reflection to generate instances of objects, such as:
Class cookClass = Cook.class;
Cook cook1 = (Cook) cookClass.newInstance();
Copy the code
Get constructor
The constructor can be obtained as follows:
- Constructor getConstructor(Class[] params) : getConstructor(Class[] params) : getConstructor(Class[] params) : getConstructor(Class[] params)
- Constructor[] getConstructors() : Get all the public constructors of the class
- Constructor getDeclaredConstructor(Class[] params) : getDeclaredConstructor(private included)
- Constructor[] getDeclaredConstructors() : Get all class constructors (regardless of access level)
Let’s create a new Person for our demo, such as:
public class Person {
public String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(a) {
super(a);
}
public String getName(a) {
System.out.println("get name: " + name);
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("set name: " + this.name);
}
public int getAge(a) {
System.out.println("get age: " + age);
return age;
}
public void setAge(int age) {
this.age = age;
System.out.println("set age: " + this.age);
}
private void privateMethod(a){
System.out.println("the private method!");
}
}
Copy the code
A generic class with private properties and methods.
Let’s create a new class GetConstructor to demonstrate how to use the GetConstructor method, as in:
class GetConstructor {
public static void main(String[] args) throws
ClassNotFoundException,
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
InstantiationException {
String className = "net.lishaoy.reflectdemo.entity.Person";
Class<Person> personClass = (Class<Person>) Class.forName(className);
// Get all the constructor objects
Constructor<? >[] constructors = personClass.getConstructors();
for(Constructor<? > constructor: constructors) {
System.out.println("Get all constructor objects:" + constructor);
}
// Get a constructor object
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
System.out.println(Get a constructor object: + constructor);
// Call the constructor's newInstance() method to create the object
Person person = constructor.newInstance("lsy".66);
System.out.println(person.getName() + "," + person.getAge() );
}
}
Copy the code
The output is as follows:
For all the constructor object: public net. Lishaoy. Reflectdemo. Entity. The Person (Java. Lang. String, int)
For all the constructor object: public net. Lishaoy. Reflectdemo. Entity. The Person ()
To obtain a constructor object: public net. Lishaoy. Reflectdemo. Entity. The Person (Java. Lang. String, int)
lsy, 66
Copy the code
Access method
The methods to obtain the method are as follows:
- Method getMethod(String name, Class[] params) : Gets a named public Method using a specific parameter type
- Method[] getMethods() : Gets all public methods of the class
- Method getDeclaredMethod(String name, Class[] params) : use the close-up parameter type to get the named Method of the Class declaration
- Method[] getDeclaredMethods() : Get all methods declared by a class
Let’s create a new GetMethod to show how to get and call a method, like this:
class GetMethod {
public static void main(String[] args) throws
ClassNotFoundException,
NoSuchMethodException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
Class<? > aClass = Class.forName("net.lishaoy.reflectdemo.entity.Person");
// Get all public methods (including methods inherited from their parent)
Method[] methods = aClass.getMethods();
for (Method method: methods) {
System.out.println("Get all public methods:" + method.getName() + "()");
}
System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = = = =");
// Get all methods (excluding superclass methods)
methods = aClass.getDeclaredMethods();
for (Method method: methods) {
System.out.println("Get all methods:" + method.getName() + "()");
}
System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = = = =");
// Get the specified method
Method method = aClass.getDeclaredMethod("setAge".int.class);
System.out.println("Get the specified method :" + method);
// Call the method
Object instance = aClass.newInstance();
method.invoke(instance, 66);
// Call private methods
method = aClass.getDeclaredMethod("privateMethod");
method.setAccessible(true); // This method needs to be called and set to true
method.invoke(instance);
}
}
Copy the code
The result is as follows:
Get all public methods: getName()
Get all public methods: setName()
Get all public methods: setAge()
Get all public methods: getAge()
Get all public methods:wait(a)
Get all public methods:wait(a)
Get all public methods:wait(a)
Get all public methods: equals()
Get all public methods: toString()
Get all public methods: hashCode()
Get all public methods: getClass()
Get all public methods: notify()
Get all public methods: notifyAll()
= = = = = = = = = = = = = = = = = = = = = = = = = = =
Get all methods: getName()
Get all methods: setName()
Get all methods: setAge()
Get all methods: privateMethod()
Get all methods: getAge()
= = = = = = = = = = = = = = = = = = = = = = = = = = =
Access to the specified methods: public void net. Lishaoy. Reflectdemo. Entity. Person. SetAge (int)
set age: 66
the private method!
BUILD SUCCESSFUL in 395ms
Copy the code
Get member variables
The methods for obtaining member variables are as follows:
- Field getField(String name) : Gets the named public Field
- Field[] getFields() : Gets all the public fields of the class
- Field getDeclaredField(String name) : Gets the named Field declared by the class
- Field[] getDeclaredFields() : Gets all fields declared by the class
Let’s create a new GetField class to show how to get a member variable, as follows:
class GetField {
public static void main(String[] args) throws
ClassNotFoundException,
NoSuchFieldException,
IllegalAccessException,
InstantiationException {
Class<? > aClass = Class.forName("net.lishaoy.reflectdemo.entity.Person");
// Get all fields (excluding superclass fields)
Field[] fields = aClass.getDeclaredFields();
for (Field field: fields) {
System.out.println("Get all fields:" + field.getName());
}
System.out.println("= = = = = = = = = = = = = = = =");
// Get the specified field
Field name = aClass.getDeclaredField("name");
System.out.println("Get specified field:" + name.getName());
// Sets the value of the specified field
Object instance = aClass.newInstance();
name.set(instance, "per");
// Gets the value of the specified field
Object o = name.get(instance);
System.out.println("Get the value of the specified field:" + o);
// Set and get the values of private fields
Field age = aClass.getDeclaredField("age");
age.setAccessible(true); // This method needs to be called and set to true
age.set(instance, 66);
System.out.println("Get private field value:" + age.get(instance));
}
}
Copy the code
The result is as follows:
Get all fields: name
Get all fields: age
= = = = = = = = = = = = = = = =
Gets the specified field: name
Gets the value of the specified field: per
Get the value of the private field: 66
BUILD SUCCESSFUL in 395ms
Copy the code
Automatic findViewById with annotations and reflection (Case)
Now that we have a better understanding of annotations and reflection, let’s reinforce our learning with a quick example: Using annotations and reflection to do something similar to Butterknife’s automatic findViewById.
Create an empty Android project, create the Inject directory under the project directory, and in this directory create an InjectView class and BindView custom annotations, such as:
Create InjectView
InjectView does findViewById by reflection:
public class InjectView {
public static void init(Activity activity) {
// Get the activity's class object
Class<? extends Activity> aClass = activity.getClass();
// Get all the member variables of the activity
Field[] declaredFields = aClass.getDeclaredFields();
// Variables so member variables
for (Field field: declaredFields) {
// Check whether the attribute is annotated with @bindView
if(field.isAnnotationPresent(BindView.class)){
// Get the annotation BindView object
BindView bindView = field.getAnnotation(BindView.class);
// Get the id of the annotation type element
int id = bindView.value();
// Find the view based on the resource ID
View view = activity.findViewById(id);
// Set access to private fields
field.setAccessible(true);
try {
// Assign a value to the field
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
Copy the code
Create the @bindView annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
@IdRes int value(a); // @idres can only transmit id resources
}
Copy the code
Use the @bindView annotation
Use @bindView annotations in MainActivity, such as:
public class MainActivity extends AppCompatActivity {
// Use annotations
@BindView(R.id.text_view)
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize InjectView to complete automatic findViewById
InjectView.init(this);
// Test whether R.i.d.ext_view is automatically assigned to textView
textView.setText("Autocomplete findViewById with @BindView annotation.");
}
}
Copy the code
Running results, as shown in figure:
Isn’t it easy? A single class does the automatic findViewById.
A dynamic proxy
Before we look at dynamic proxies, let’s review static proxies.
Static agent
The proxy pattern provides a proxy object to an object and controls references to the original object, such as mediations, which are common in our daily life.
The proxy mode typically has three roles, as shown in the figure below:
- Abstract role: Refers to the public methods provided by the proxy role and the real role, usually as an interface
- Real roles: You need to implement an abstract role interface that defines the business logic that real roles implement for invocation by proxy roles
- Proxy roles: Need to implement abstract role interfaces, are proxies for real roles, implement abstract methods through the business logic methods of real roles, and can attach their own operations
Why use proxy mode
- Objects can be accessed indirectly, avoiding the unnecessary complexity of accessing them directly
- Access is controlled through proxy objects
Static proxy case
The scenario is as follows:
Xiao Ming can buy domestic things on a website, but he can’t buy overseas things, so he finds an overseas agent to help him buy things.
How do you describe it in code? According to the three roles of the proxy pattern, we define 1 interface and 2 classes respectively, for example: OrderService interface (Abstract role), ImplJapanOrderService class (real role), ProxyJapanOrder class (proxy role)
OrderService interface (Abstract role), code as follows:
public interface OrderService {
int saveOrder(a);
}
Copy the code
The ImplJapanOrderService class (real role) looks like this:
// Implement the abstract role interface
public class ImplJapanOrderService implements OrderService {
@Override
public int saveOrder(a) {
System.out.println("Order successfully, order number: 888888");
return 888888;
}
}
Copy the code
ProxyJapanOrder class (proxy role), code as follows:
// Implement the abstract role interface
public class ProxyJapanOrder implements OrderService {
private OrderService orderService; // Hold real characters
public OrderService getOrderService(a) {
return orderService;
}
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
@Override
public int saveOrder(a) {
System.out.print("Japan order,");
return orderService.saveOrder(); // Call the behavior method of the real role
}
}
Copy the code
Create a Client class to test our code as follows:
public class Client {
public static void main(String[] args) {
// Order in Japan
OrderService orderJapan = new ImplJapanOrderService();
ProxyJapanOrder proxyJapanOrder = new ProxyJapanOrder();
proxyJapanOrder.setOrderService(orderJapan);
proxyJapanOrder.saveOrder();
}
}
Copy the code
The result is as follows:
The order from Japan has been placed successfully. The order number is 888888
BUILD SUCCESSFUL in 1s
Copy the code
If you need to buy something from Korea, you need to add a new class called ImplKoreaOrderService (Korean service provider) and a new class called ProxyKoreaOrder (Korean agent). If you need to buy something from other countries, you need to add different classes, then you may have a lot of static proxy objects and code. This leads to complex code and poor maintainability. If so, we need to use dynamic proxy.
A dynamic proxy
Dynamic proxy classes and instances are created at run time. Therefore, we can pass different real actors and implement a single proxy class to perform the behavior of multiple real actors. Of course, it is less efficient than static proxy. How do we implement dynamic proxies? The JDK already provides a Proxy class and an InvocationHandler interface to do this.
Let’s create a ProxyDynamicOrder class with the following code:
public class ProxyDynamicOrder implements InvocationHandler {
private Object orderService; // Hold real characters
public Object getOrderService(a) {
return orderService;
}
public void setOrderService(Object orderService) {
this.orderService = orderService;
}
// Use Proxy to dynamically create real roles
public Object getProxyInstance(a){
return Proxy.newProxyInstance(
orderService.getClass().getClassLoader(),
orderService.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
return method.invoke(orderService, objects); // Perform real character behavior through reflection
}
}
Copy the code
Let’s see how the Client class is called. The code is as follows:
public class Client {
public static void main(String[] args) {
Static proxy mode
// Domestic order
OrderService order = new ImplOrderService();
order.saveOrder();
// Order in Japan
OrderService orderJapan = new ImplJapanOrderService();
ProxyJapanOrder proxyJapanOrder = new ProxyJapanOrder();
proxyJapanOrder.setOrderService(orderJapan);
proxyJapanOrder.saveOrder();
// Order from Korea
OrderService orderKorea = new ImplKoreaOrderService();
ProxyKoreaOrder proxyKoreaOrder = new ProxyKoreaOrder();
proxyKoreaOrder.setOrderService(orderKorea);
proxyKoreaOrder.saveOrder();
// Dynamic proxy mode
// Domestic order
ProxyDynamicOrder proxyDynamicOrder = new ProxyDynamicOrder();
OrderService orderService = new ImplOrderService();
proxyDynamicOrder.setOrderService(orderService);
OrderService orderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();
orderService1.saveOrder();
// Order in Japan
OrderService japanOrderService = new ImplJapanOrderService();
proxyDynamicOrder.setOrderService(japanOrderService);
OrderService japanOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();
japanOrderService1.saveOrder();
// Order from Korea
OrderService koreaOrderService = new ImplKoreaOrderService();
proxyDynamicOrder.setOrderService(koreaOrderService);
OrderService koreaOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();
koreaOrderService1.saveOrder();
// Generate the class file generated by the dynamic proxy
//ProxyUtil.generateClassFile(koreaOrderService.getClass(), koreaOrderService1.getClass().getSimpleName());
}
}
Copy the code
The result is as follows:
The order number is 666666
The order from Japan has been placed successfully. The order number is 888888
Korea agent order, place the order successfully, the order number is 666888
The order number is 666666
The order number is 888888
The order number is 666888
BUILD SUCCESSFUL in 1s
Copy the code
Only a ProxyDynamicOrder proxy class is required to complete the services provided by the ImplOrderService, ImplJapanOrderService, and ImplKoreaOrderService real roles.
Principle of dynamic proxy
We’re proxyDynamicOrder. GetProxyInstance hit a breakpoint () code, found in a debug mode, as shown in figure:
The name of the proxy class is $Proxy0@507. We can not find the $Proxy0@507 class file in the compiled directory, as shown in the following figure:
Proxy. NewProxyInstance ();
@CallerSensitive
public static Object newProxyInstance(ClassLoader var0, Class
[] var1, InvocationHandler var2) throws IllegalArgumentException {
Objects.requireNonNull(var2);
Class[] var3 = (Class[])var1.clone();
SecurityManager var4 = System.getSecurityManager();
if(var4 ! =null) {
checkProxyAccess(Reflection.getCallerClass(), var0, var3);
}
// Get the class object of the proxy class
Class var5 = getProxyClass0(var0, var3);
try {
if(var4 ! =null) {
checkNewProxyPermission(Reflection.getCallerClass(), var5);
}
// Get the constructor for the proxy class
final Constructor var6 = var5.getConstructor(constructorParams);
if(! Modifier.isPublic(var5.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
var6.setAccessible(true);
return null;
}
});
}
// An example of creating a proxy class
return var6.newInstance(var2);
} catch (InstantiationException | IllegalAccessException var8) {
throw new InternalError(var8.toString(), var8);
} catch (InvocationTargetException var9) {
Throwable var7 = var9.getCause();
if (var7 instanceof RuntimeException) {
throw (RuntimeException)var7;
} else {
throw new InternalError(var7.toString(), var7);
}
} catch (NoSuchMethodException var10) {
throw new InternalError(var10.toString(), var10);
}
}
Copy the code
Then, go to getProxyClass0(var0, var3) to see how to get the class object of the proxy class.
private staticClass<? > getProxyClass0(ClassLoader var0, Class<? >... var1) {
if (var1.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
} else {
// The proxy class object is cached
return (Class)proxyClassCache.get(var0, var1);
}
}
Copy the code
Then, let’s see what var1 is. We looked up and found, sure enough, the following:
// Var1 is the InvocationHandler interface we implemented
protected Proxy(InvocationHandler var1) {
Objects.requireNonNull(var1);
this.h = var1;
}
Copy the code
Then, we click proxyClassCache. Get (var0, var1), as shown:
Apply (var1, var2) to get the class object of our proxy class, we enter the Apply implementation class ProxyClassFactory, as follows:
publicClass<? > apply(ClassLoader var1, Class<? >[] var2) {
IdentityHashMap var3 = new IdentityHashMap(var2.length);
Class[] var4 = var2;
int var5 = var2.length;
.
if (var16 == null) {
var16 = "com.sun.proxy.";
}
long var19 = nextUniqueNumber.getAndIncrement();
// Generate the class name of the proxy class
String var23 = var16 + "$Proxy" + var19;
// Generate the bytecode of the proxy class
byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);
try {
// Generate a class object for the proxy class
return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
} catch (ClassFormatError var14) {
throw new IllegalArgumentException(var14.toString());
}
}
Copy the code
Then, we click on the proxy.defineclass0 method as follows:
private static nativeClass<? > defineClass0(ClassLoader var0, String var1,byte[] var2, int var3, int var4);
Copy the code
Is a native method, so we won’t follow it further when it comes to C or C++.
So, where does the proxy Class file actually exist, given the life cycle of a Class, as shown in figure:
The Class file of the agent is reflected in memory, so we can write to the file by byte[]. We create a utility Class to write the Class bytecode in memory to the file, such as:
public class ProxyUtil {
public static void generateClassFile(Class aClass, String proxyName) {
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName,
new Class[]{aClass}
);
String path = aClass.getResource(".").getPath();
System.out.println(path);
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(path + proxyName + ".class");
outputStream.write(proxyClassFile);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Copy the code
Find the file using the output path, for example:
/Users/lishaoying/Documents/APP/Android/practice/annotation_reflect/anRePrDemo/proxyDemo/build/classes/java/main/net/lis haoy/proxydemo/service/impl/
Copy the code
The file code is as follows:
// Implements the ImplKoreaOrderService interface
public final class $Proxy0 extends Proxy implements ImplKoreaOrderService {
// generate various methods
private static Method m1;
private static Method m8;
private static Method m3;
private static Method m2;
private static Method m5;
private static Method m4;
private static Method m7;
private static Method m9;
private static Method m0;
private static Method m6;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
.
// Generates the saveOrder method for the real character
public final int saveOrder(a) throws {
try {
// what is h? And click on it to find the InvocationHandler interface that we passed in
// what is m3? The following static code block is our saveOrder method
return (Integer)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
.
public final Class getClass(a) throws {
try {
return (Class)super.h.invoke(this, m7, (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"));
m8 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("notify");
m3 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("saveOrder");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m5 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait", Long.TYPE);
m4 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait", Long.TYPE, Integer.TYPE);
m7 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("getClass");
m9 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("notifyAll");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m6 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
Copy the code
Complete a simple Retrofit using annotations, reflection, and dynamic proxies
Due to the length of the article, and the amount of code used to complete a simple Retrofit using annotations, reflection, and dynamic proxies, I will not show it here. If you are interested, you can check the source code on GitHub.
Finally, the blog and GitHub address are attached as follows:
Blog address: h.lishaoy.net GitHub address: github.com/persilee