简介
- 给目标对象(被代理对象)提供一个代理对象,并由代理对象控制对目标对象(被代理对象)的引用,代理对象相当于一个中介。
- 通过引入代理对象的方式来间接访问目标对象,防止直接访问对象给系统带来不必要的复杂性。
- 相当于,代理对象拥有被代理对象的所有功能,并且代理对象还添加了额外的功能来控制被代理对象。
静态代理
代理类与被代理类实现同一个接口,将被代理类组合到代理类中并添加控制操作。
实例:
共同的接口
public interface Sourceable {
public void method();
}
被代理类
public class Source implements Sourceable {
public void method() {
System.out.println("the original method!");
}
}
代理类
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!");
}
}
测试类
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的时候,但是只有管理员才能有权限使用这个功能。
用户类
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 + '\'' +
'}';
}
}
用户操作接口
public interface UserService {
public void showUsersInfo(User user);
}
用户操作类(被代理类)
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;
}
}
用户操作代理类(代理类)
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;
}
}
}
测试类
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),日志记录、性能统计、安全控制、异常处理等