简介
- 给目标对象(被代理对象)提供一个代理对象,并由代理对象控制对目标对象(被代理对象)的引用,代理对象相当于一个中介。
- 通过引入代理对象的方式来间接访问目标对象,防止直接访问对象给系统带来不必要的复杂性。
- 相当于,代理对象拥有被代理对象的所有功能,并且代理对象还添加了额外的功能来控制被代理对象。
静态代理
代理类与被代理类实现同一个接口,将被代理类组合到代理类中并添加控制操作。
实例:
共同的接口
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),日志记录、性能统计、安全控制、异常处理等