简介

  • 给目标对象(被代理对象)提供一个代理对象,并由代理对象控制对目标对象(被代理对象)的引用,代理对象相当于一个中介。
  • 通过引入代理对象的方式来间接访问目标对象,防止直接访问对象给系统带来不必要的复杂性。
  • 相当于,代理对象拥有被代理对象的所有功能,并且代理对象还添加了额外的功能来控制被代理对象。

静态代理

代理类与被代理类实现同一个接口,将被代理类组合到代理类中并添加控制操作。
实例:
共同的接口

1
2
3
public interface Sourceable {
public void method();
}

被代理类

1
2
3
4
5
6
public class Source implements Sourceable {
public void method() {
System.out.println("the original method!");
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Proxy implements Sourceable {
private Source source;
public Proxy(Source source){
this.source = source;
}
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}

测试类

1
2
3
4
5
6
7
public class ProxyTest {
public static void main(String[] args) {
Source target = new Source();
Sourceable proxy = new Proxy(target);
proxy.method();
}
}

动态代理

设计动态代理类时,不需要显式实现与被代理类相同的接口,而是将这种实现推迟到程序运行时由JVM来实现。
通过Java反射机制的method.invoke(),通过调用动态代理类对象方法,从而自动调用目标对象的方法。
实例:假设一个系统中有三个简单角色,”游客”,”会员”,”管理员”,当用一个用户User进行查看其它用户信息功能showUsersInfo的时候,但是只有管理员才能有权限使用这个功能。
用户类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class User {
String name;
String id;
public User(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"namae='" + name + '\'' +
", id='" + id + '\'' +
'}';
}
}

用户操作接口

1
2
3
public interface UserService {
public void showUsersInfo(User user);
}

用户操作类(被代理类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UserServiceIpm implements UserService {
private User user;
public UserServiceIpm(User user) {
this.user = user;
}
@Override
public void showUsersInfo(User user) {
System.out.println(user);
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

用户操作代理类(代理类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UserServiceProxy implements InvocationHandler{
private Object target; //被代理对象
public UserServiceProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理");
target = (UserServiceIpm)target;
String name = ((UserServiceIpm) target).getUser().name;
if (name=="管理员"){
Object o = method.invoke(target,args);
return o;
}else{
System.out.println("没有权限");
return null;
}
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
public static void main(String[] args) {
User u1 = new User("游客","2");
// User u1 = new User("管理员","2");
//创建被代理对象target
UserService target = new UserServiceIpm(u1);
//获取被代理对象类类型
Class<? extends UserService> c = target.getClass();
//获取被代理对象的类加载器
ClassLoader classLoader = c.getClassLoader();
//获取被代理对象实现的接口
Class<?>[] interfaces = c.getInterfaces();
//创建代理类对象h,传被代理类对象target过去
InvocationHandler h = new UserServiceProxy(target);
//被代理类创建代理
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, h);
User user = new User("会员","1");
target.showUsersInfo(user);
System.out.println("--------");
proxy.showUsersInfo(user);
}
}

两者的区别

  • 静态代理
    • 代理单一目标对象
    • 在代理类实现时就指定与被代理类相同的接口(运行前)
    • 优点
      • 降低了系统的耦合度
      • 代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用
    • 缺点
      • 增加了代理对象,因此会造成请求的处理速度变慢
      • 增加了系统实现的复杂度
  • 动态代理
    • 代理多个目标对象
    • 不需要显式实现与被代理类相同的接口,而是将这种实现推迟到程序运行时由JVM来实现。(运行时)
    • 优点
      • 只需要1个动态代理类就可以解决创建多个静态代理的问题
      • 灵活性强,在使用时(调用被代理类对象)才会动态创建动态代理类实例,不需要事先实例化
    • 缺点
      • 效率低,需要先通过Java反射机制,间接调用目标对象方法。
      • 只能针对接口创建代理类,不能针对类创建代理类,即只能动态代理 实现了接口的类。
    • 具体应用场景:面向切面编程(AOP),日志记录、性能统计、安全控制、异常处理等

最后更新: 2020年07月27日 03:53

原始链接: https://www.lousenjay.top/2018/08/15/设计模式-代理模式/