最近在看动态代理设计模式,但是看完之后还是模模糊糊不太清楚,于是决定自己写一个小的demo来加深印象。
举个例子:
现在有一个学生列表,我们可以从服务器拉取学生列表,也可以向服务器添加一个学生名单。
这些方法最终实现的步骤都是相似的:都需要向服务器发起请求,只是请求url和参数可能不一样,如果每个方法都单独写的话,那么以后每增加一个方法都需要加一段冗余代码。所以这里就可以通过动态代理来实现。
首先,创建一个自定义注解用于标记请求链接:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GET {
String value();
}
接着,定义学生相关类:
public class Student {
public long id;
public String name;
public Student(long id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public interface StudentService {
@GET("student/getStudents")
void getStudents();
@GET("student/addStudent")
void addStudent(Student student);
}
最后,实现一个动态代理类:
public class RequestProxy {
public static RequestProxy create() {
return new RequestProxy();
}
public <T> T getService(Class<T> service) {
//noinspection unchecked
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class[]{service},
RequestProxy.this::invoke);
}
private Object invoke(Object o, Method method, Object[] objects) {
GET methodAnnotation = method.getAnnotation(GET.class);
if (methodAnnotation != null) {
String value = methodAnnotation.value();
System.out.println("------request start------");
System.out.println(method.getName() + "方法被调用,向" + value + "发送请求");
System.out.println("请求参数:" + contentToString(objects));
System.out.println("------request end------\n");
}
return null;
}
private String contentToString(Object[] objects) {
if (objects == null || objects.length == 0) return null;
StringBuilder builder = new StringBuilder("[");
for (Object o : objects) {
builder.append(o.toString()).append(",");
}
return builder.substring(0, builder.length() - 1) + "]";
}
}
测试代码:
public class Test {
public static void main(String[] args) {
StudentService studentService = RequestProxy.create().getService(StudentService.class);
studentService.getStudents();
studentService.addStudent(new Student(1, "Bob"));
}
}
最终输出:
------request start------
getStudents方法被调用,向student/getStudents发送请求
请求参数:null
------request end------
------request start------
addStudent方法被调用,向student/addStudent发送请求
请求参数:[Student{id=1, name='Bob'}]
------request end------
Demo写的比较简陋,核心在invoke方法中,每个动态代理类的方法都会走到这个方法中去,具体的实现就需要看具体的业务逻辑了。