Java代理设计模式
定义
Java代理模式(Proxy Pattern)是一种常用的设计模式,它为其他对象提供一种代理以控制对这个对象的访问。简单来说,我们使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
对应角色
- Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题。
- Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。
- RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
优缺点
优点
- 职责清晰:真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。
- 高扩展性:具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
缺点
- 类个数增加:代理模式会造成系统中类的个数增加,比不使用代理模式增加了代理类,系统的复杂度增加。
- 性能降低:在客户端和目标对象之间,增加了一个代理对象,造成请求处理速度变慢。
应用场景
- 需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理。
- 需要按需创建开销很大的对象的时候,可以使用虚拟代理。
- 需要控制对象访问权限的时候,可以使用保护代理。
实例
// 抽象主题角色
interface Subject {void request();
}// 真实主题角色
class RealSubject implements Subject {public void request() {System.out.println("RealSubject request");}
}// 代理主题角色
class ProxySubject implements Subject {private RealSubject realSubject;public ProxySubject(RealSubject realSubject) {this.realSubject = realSubject;}public void request() {System.out.println("ProxySubject preRequest");realSubject.request();System.out.println("ProxySubject postRequest");}
}// 客户端
public class Client {public static void main(String[] args) {RealSubject realSubject = new RealSubject();ProxySubject proxySubject = new ProxySubject(realSubject);proxySubject.request();}
}
JDK动态代理(Springboot实现)
首先,我们定义一个接口UserService,它有一个方法getUserById用于根据用户ID获取用户信息。
public interface UserService {String getUserById(Integer id);
}
然后,我们创建一个实现了UserService接口的类UserServiceImpl,并在其中实现getUserById方法。
@Service
public class UserServiceImpl implements UserService {@Overridepublic String getUserById(Integer id) {return "User with id: " + id;}
}
接下来,我们创建一个类UserServiceInvocationHandler,它实现了InvocationHandler接口。在这个类中,我们重写了invoke方法,在其中添加了日志记录功能。
public class UserServiceInvocationHandler implements InvocationHandler {private Object target;public UserServiceInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());Object result = method.invoke(target, args);System.out.println("After method: " + method.getName());return result;}
}
最后,在我们的主类中,我们使用Proxy.newProxyInstance方法来创建一个代理对象,并使用这个代理对象来调用getUserById方法。
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {@Autowiredprivate UserService userService;public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@Overridepublic void run(String... args) throws Exception {UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),new UserServiceInvocationHandler(userService));System.out.println(proxy.getUserById(1));}
}
当我们运行这个程序时,会看到控制台输出以下内容:
Before method: getUserById
After method: getUserById
User with id: 1
Cglib动态代理(SpringBoot实现)
首先,我们定义一个类UserService,它有一个方法getUserById用于根据用户ID获取用户信息。
public class UserService {public String getUserById(Integer id) {return "User with id: " + id;}
}
接下来,我们创建一个类UserServiceInterceptor,它继承了MethodInterceptor接口。在这个类中,我们重写了intercept方法,在其中添加了日志记录功能。
public class UserServiceInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("Before method: " + method.getName());Object result = methodProxy.invokeSuper(o, objects);System.out.println("After method: " + method.getName());return result;}
}
最后,在我们的主类中,我们使用Enhancer类来创建一个代理对象,并使用这个代理对象来调用getUserById方法。
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@Overridepublic void run(String... args) throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);enhancer.setCallback(new UserServiceInterceptor());UserService proxy = (UserService) enhancer.create();System.out.println(proxy.getUserById(1));}
}
当我们运行这个程序时,会看到控制台输出以下内容:
Before method: getUserById
After method: getUserById
User with id: 1