在代理模式中,客户端不直接访问实际对象,而是通过代理对象来访问,代理对象通常与实际对象实现相同的接口,这使得客户端可以使用代理对象调用实际对象的方法,同时可以通过代理对象来实现对实际对象的访问控制和增加额外的功能,而不会对客户端产生任何影响。
在 Java 中,代理模式通常有两种实现方式:静态代理和动态代理。
静态代理是通过创建一个实现与被代理类相同接口的代理类来实现的。代理类持有被代理对象的引用,并通过调用其方法来实现对被代理对象的访问控制和增加额外的功能。
动态代理是在运行时动态创建代理对象,它不需要事先创建代理类,而是在运行时动态生成代理对象,通过 Java 反射机制来实现对被代理对象的访问控制和增加额外的功能。
静态代理
假设我们有一个接口 `UserService`,它有一个方法 `save`,代表保存用户的操作。我们希望在用户保存之前和之后做一些额外的事情,比如记录日志、验证用户信息等等。这时候就可以使用静态代理来实现。
首先,我们定义 `UserService` 接口:
public interface UserService {
void save(User user);
}
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
// 保存用户的操作
System.out.println("保存用户 " + user.getName() + " 成功!");
}
}
接下来,我们定义一个代理类 `UserServiceProxy`,它也实现了 `UserService` 接口,但是在保存用户之前和之后都做了一些额外的操作:
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void save(User user) {
System.out.println("开始保存用户 " + user.getName() + " ...");
userService.save(user);
System.out.println("保存用户 " + user.getName() + " 完成!");
}
}
在上面的代理类中,我们通过一个构造方法将实际的 `UserService` 对象传入,然后在 `save` 方法中先输出日志,然后调用实际对象的 `save` 方法,最后再输出日志。
最后,我们可以编写一个测试类来测试这个代理:
public class UserServiceTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService userServiceProxy = new UserServiceProxy(userService);
userServiceProxy.save(new User("Tom"));
}
}
动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Subject {
void doSomething();
}
class RealSubject implements Subject {
public void doSomething() {
System.out.println("RealSubject do something");
}
}
class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = method.invoke(target, args);
System.out.println("after");
return result;
}
}
public class Test {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
DynamicProxy proxy = new DynamicProxy(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), proxy);
proxySubject.doSomething();
}
}
在上述例子中,我们定义了一个接口 Subject,和实现类 RealSubject。我们通过实现 InvocationHandler 接口来创建代理类 DynamicProxy,然后通过调用 Proxy.newProxyInstance() 方法来创建代理对象 proxySubject,这个代理对象实现了 Subject 接口,并且在代理对象调用 doSomething() 方法时会先输出 "before",然后调用被代理对象的 doSomething() 方法,最后输出 "after"。
需要注意的是,代理对象必须实现与被代理对象相同的接口,因此在创建代理对象时需要传入被代理对象实现的接口信息。
//也可以直接写在InvocationHandler类里一个创建方法
public Demo create(Demo demo){
object = demo;
return (Demo) Proxy.newProxyInstance(demo.getClass().getClassLoader(), demo.getClass().getInterfaces(),this);
}
`Proxy.newProxyInstance` 是 Java 动态代理的核心方法之一,它用于创建一个代理对象。
该方法接收三个参数:
- `ClassLoader` 类加载器,用于加载代理类的定义。
- `Class<?>[]` 代理类要实现的接口,用于确定代理类实现哪些接口。
- `InvocationHandler` 代理对象要执行的方法逻辑。
该方法返回一个代理对象,该代理对象实现了指定的接口,并且所有接口方法的调用都会转发到传入的 `InvocationHandler` 实例的 `invoke` 方法中。