代理模式
在学习Spring Aop时,使用了动态代理,所以学习了代理模式,静态代理,动态代理,Cglib动态代理,整理blog记录自己的学习笔记
静态代理
1.定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通过代理对象访问目标对象,防止直接访问目标对象造成系统复杂性提升.
2.原理

Subject : 接口,为RealSubject和ProxySubject提供一致性的接口
RealSubject : 目标访问类,实际的主体,实现Subject接口的类
ProxySubject : 代理类,处理Client的请求,只有当代理类无法回复请求时,才会调用目标访问类
Client : 请求者
3.适用场景
Virtual Proxy : 对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象。
Remote Proxy : 远程代理使得客户端可以访问远程主机上的对象,远程代理可以把网络的细节隐藏起来,使得客户端不必考虑网络的存在,客户端完全可以认为调用的远程代理对象在本地,而不是在远程
Cache Proxy : 某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,从而可以避免某些方法的重复执行,优化系统性能。
Access Proxy : 可以在调用RealSubject时做访问限制,通过代理过滤不可以访问的对象
4.实现
Printable.java
public interface Printable {
void setPrintName(String name);
String getPrintName();
void print(String str);
}
Printer.java
public class Printer implements Printable {
private String name;
public Printer() {
heavyJob("正在生成Printer的实例");
}
public Printer(String name) {
this.name = name;
heavyJob("正在生成(Printer)" + name + "的实例");
}
@Override
public void setPrintName(String name) {
this.name = name;
}
@Override
public String getPrintName() {
return this.name;
}
@Override
public void print(String str) {
System.out.println("==" + name + "==");
System.out.println(str);
}
public void heavyJob(String msg) {
System.out.println(msg);
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(".");
}
System.out.println("over");
}
}
PrintProxy.java
public class PrintProxy implements Printable {
private String name;
private Printer real;
public PrintProxy(String name) {
this.name = name;
}
@Override
public synchronized void setPrintName(String name) {
if (real != null) {
real.setPrintName(name);
}
this.name = name;
}
@Override
public String getPrintName() {
return this.name;
}
@Override
public void print(String str) {
realize();
real.print(str);
}
public synchronized void realize() {
if (real == null) {
real = new Printer(name);
}
}
}
Main.java
public class Main {
public static void main(String[] args) {
Printable p = new PrintProxy("kristin");
System.out.println("name: " + p.getPrintName());
p.setPrintName("kkk");
System.out.println("new name: " + p.getPrintName());
p.print("hello world");
}
}
Printable相当于Subject
Printer相当于RealSubject
PrinterProxy相当于ProxySubject
Main相当于Client
5.优缺点
优点:
协调调用者与被调用者,降低耦合度
作为客户端对象与目标对象的中介,可以有效地保护目标对象
缺点:
在客户端与目标对象之间增加了代理对象,处理请求的速度可能会变慢
如果代理的实现复杂,可能会增加系统实现的复杂性
如果想要为多个类进行代理,需要创建多个代理类,维护难度加大
JDK动态代理
1.定义
代理类在程序运行时被创建,并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的.
2.原理
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
3.实现
UserDao.java
public interface UserDao {
void add();
}
UserDaoImpl.java
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("------------add------------");
}
}
MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public void setProxy(Object target) {
this.target = target;
}
@Override
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;
}
}
MyProxy.java
public class MyProxy {
public static void main(String[] args) {
UserDao user = new UserDaoImpl();
MyInvocationHandler handler = new MyInvocationHandler();
handler.setProxy(user);
// UserDao userDao = (UserDao) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), handler);
UserDao userDao = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(), new Class[]{UserDao.class}, handler);
userDao.add();
}
}
Output
-------------before------------- ------------add------------ -------------after-------------
4.应用
如果想对代理类的所有方法都加上日志,可以通过动态代理可以对代理类的所有方法进行统一的处理,而不用一一更改每一个方法.
Cglib动态代理
1.定义
cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个类,并覆盖其中方法实现增强
2.原理
代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理.
3.应用
UserDao.java
public interface UserDao {
void add();
}
UserDaoImpl.java
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("the method is running");
}
}
Interceptor.java
public class Interceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("-------------------before------------------");
proxy.invokeSuper(obj, args);
System.out.println("--------------------after-------------------");
return null;
}
}
Main.java
public class Main {
@org.junit.jupiter.api.Test
public void test() {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDaoImpl.class);
enhancer.setCallback(new Interceptor());
UserDao userDao = (UserDao) enhancer.create();
userDao.add();
}
}
Output
CGLIB debugging enabled, writing to 'E:\code' -------------------before------------------ the method is running --------------------after-------------------

浙公网安备 33010602011771号