在学习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-------------------