-
静态代理
2020-10-20 13:41:31静态代理 静态代理很好理解,实现了相同接口,对原实现类包装。 通过操作传入的原实现类对象来打到增强效果。 代理类包含了原实现类的操作,所以调用代理对象即可。 通俗的讲:静态代理就是写出一个被代理类的双胞...静态代理
- 静态代理很好理解,实现了相同接口,对原实现类包装。
- 通过操作传入的原实现类对象来打到增强效果。
- 代理类包含了原实现类的操作,所以调用代理对象即可。
通俗的讲:静态代理就是写出一个被代理类的双胞胎类,有共同的接口父亲,只是代理类拿着兄弟的对象在自己内部操作兄弟的行为,同时做些自己的事。
案例:结婚 婚庆公司
静态代理:
- 和被代理目标类实现相同接口(获取相同接口方法)
- 代理类是对目标类的增强辅助
- 代理类需要传入被操作的目标类对象
- 代理类实现相同接口方法的时候需要在其中加入目标类的方法调用
- 调用代理类的接口方法即可实现增强
// 静态代理模式演示 public class staticProxy { public static void main(String[] args) { // 开始代理 代理对象获取代理目标 WeddingCompany weddingCompany = new WeddingCompany(new YouAction()); weddingCompany.HappuMarry(); } }
结婚接口:
// 定义结婚接口动作 public interface Marry { void HappuMarry(); }
定义结婚人动作:
// 真实结婚角色 public class YouAction implements Marry{ @Override public void HappuMarry() { // 定义你的结婚动作 System.out.println("你在结婚,开心"); } }
定义代理公司动作:
// 婚庆代理公司 public class WeddingCompany implements Marry { // 定义被代理目标 private Marry target; public WeddingCompany(Marry target) { this.target = target; } // 代理公司行为 @Override public void HappuMarry() { before(); this.target.HappuMarry(); after(); } // 定义代理公司增强部分 public void before(){ System.out.println("代理公司,收定金。"); System.out.println("代理公司,布置现场。"); System.out.println("代理公司,主持。"); } public void after(){ System.out.println("代理公司,完成任务"); System.out.println("代理公司,收尾款"); } }
代理公司,收定金。 代理公司,布置现场。 代理公司,主持。 你在结婚,开心 代理公司,完成任务 代理公司,收尾款
简短版本静态代理案例:
// 静态代理测试 public class StaticProxy { public static void main(String[] args) { HelloProxy helloProxy = new HelloProxy(); helloProxy.sayHello(); } } // 代理 class HelloProxy implements HelloInterface{ // 被代理对象 接口获取 一般是一对一的,直接固定创建即可 private HelloInterface helloInterface = new Hello(); @Override public void sayHello() { System.out.println("静态代理输出--1"); System.out.println("静态代理输出--2"); helloInterface.sayHello(); System.out.println("静态代理输出--3"); System.out.println("静态代理输出--4"); } } // 被代理 class Hello implements HelloInterface{ @Override public void sayHello() { System.out.println("被代理类输出--hello"); } } // 共同接口 interface HelloInterface{ void sayHello(); }
-
Java多线程02_Thread与静态代理
2020-08-27 12:22:16Java多线程02_Thread与静态代理 静态代理示例: 真实对象和代理对象都要 实现同一个接口 代理是对方法的 增强 public class StaticProxy { public static void main(String[] args) { WeddingCompany ...Java多线程02_Thread与静态代理
静态代理示例:
真实对象和代理对象都要 实现同一个接口
代理是对方法的 增强
public class StaticProxy { public static void main(String[] args) { WeddingCompany weddingCompany = new WeddingCompany(new BLU()); weddingCompany.HappyMarry(); } } interface Marry{ void HappyMarry(); } class BLU implements Marry{ @Override public void HappyMarry() { System.out.println("BLU要结婚了,超开心"); } } class WeddingCompany implements Marry{ private Marry target; public WeddingCompany(Marry target) { this.target = target; } @Override public void HappyMarry() { Before(); this.target.HappyMarry(); After(); } private void Before() { System.out.println("结婚准备....."); } private void After() { System.out.println("结婚结束....."); } }
静态代理和Thread对比
new WeddingCompany(new BLU()).HappyMarry(); new Thread(()->System.out.println("I love you")).start();
-
静态代理和动态代理
2019-10-08 11:53:04静态代理和动态代理的实现和讲解目录
一、静态代理
静态代理其实就是指设计模式中的代理模式,代理模式为其他对象提供一种代理以控制对这个对象的访问。
Subject定义了RealSubject和Proxy的公共接口,这样就在任何使用RealSubject的地方都可以使用Proxy
abstract class Subject { public abstract void Request(); }
RealSubject 定义Proxy所代表的真实实体。
class RealSubject extends Subject { @Override public void Request() { System.out.println("真实的请求"); } }
Proxy保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。
class Proxy extends Subject { private RealSubject real; @Override public void Request() { if (null == real) { real = new RealSubject(); } real.Request(); } }
说明:静态代理模式固然在访问无法访问的资源,增强现有的接口业务功能方面有很大的优点,但是大量使用这种静态代理,会使我们系统内的类的规模增大,并且不易维护;并且由于Proxy和RealSubject的功能本质上是相同的,Proxy只是起到了中介的作用,这种代理在系统中的存在,导致系统结构比较臃肿和松散。
二、动态代理
2.1 动态代理简介
动态代理是反射的一个非常重要的应用场景。动态代理常被用于一些Java框架中。例如Spring的AOP,Dubbo的SPI接口,就是基于Java动态代理实现的。
为了解决静态代理的问题,就有了创建动态代理的想法:在运行状态中,需要代理的地方,根据Subject和RealSubject,动态地创建一个Proxy,用完之后,就会销毁,这样就可以避免了Proxy角色的class在系统中冗杂的问题了。
Java动态代理基于经典代理模式,引入了一个InvocationHandler,InvocationHandler负责统一管理所有的方法调用。动态代理步骤:
-
获取RealSubject上的所有接口列表;
-
确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;
-
根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码;
-
将对应的字节码转换为对应的Class对象;
-
创建InvocationHandler实例handler,用来处理Proxy所有方法调用;
-
Proxy的class对象以创建的handler对象为参数,实例化一个proxy对象。
上面可以看出,JDK动态代理的实现是基于实现接口的方式,使得Proxy和RealSubject具有相同的功能。
但其实还有一种思路:通过继承。即:让Proxy继承RealSubject,这样二者同样具有相同的功能,Proxy 还可以通过重写 RealSubject中的方法,来实现多态。CGLIB就是基于这种思路设计的。
在Java的动态代理机制中,有两个重要的类(接口),一个是InvocationHandler接口、另一个则是Proxy类,这一个类和一个接口是实现我们动态代理所必须用到的。
2.2 InvocationHandler 接口
InvocationHandler接口定义:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个Handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。
我们来看看InvocationHandler这个接口的唯一一个方法invoke方法:
Object invoke(Object proxy, Method method, Object[] args)throws Throwable
参数说明:
-
proxy:代理的真实对象。
-
method :所要调用真实对象的某个方法的 Method 对象
-
args:所要调用真实对象某个方法时接受的参数
2.3 Proxy 类
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是newProxyInstance这个方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法的作用就是得到一个动态的代理对象。参数说明:
-
loader :一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载。
-
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
-
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
2.4 动态代理实例
首先我们定义了一个Subject类型的接口,为其声明了两个方法:
public interface Subject { void hello(String str); String bye(); }
接着,定义了一个类来实现这个接口,这个类就是我们的真实对象,RealSubject类:
public class RealSubject implements Subject { @Override public void hello(String str) { System.out.println("Hello " + str); } @Override public String bye() { System.out.println("Goodbye"); return "Over"; } }
下一步,我们就要定义一个动态代理类了,前面说个,每一个动态代理类都必须要实现InvocationHandler这个接口,因此我们这个动态代理类也不例外
public class InvocationHandlerDemo implements InvocationHandler { // 这个就是我们要代理的真实对象 private Object subject; // 构造方法,给我们要代理的真实对象赋初值 public InvocationHandlerDemo(Object subject) { this.subject = subject; } @Override public Object invoke(Object object, Method method, Object[] args) throws Throwable { // 在代理真实对象前我们可以添加一些自己的操作 System.out.println("Before method"); System.out.println("Call Method: " + method); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 Object obj = method.invoke(subject, args); // 在代理真实对象后我们也可以添加一些自己的操作 System.out.println("After method"); System.out.println(); return obj; } }
最后,来看看我们的Client类:
// 我们要代理的真实对象 Subject realSubject = new RealSubject(); // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 InvocationHandler handler = new InvocationHandlerDemo(realSubject); /* * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数 * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象 * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上 */ Subject proxySubject =(Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); System.out.println(proxySubject.getClass().getName()); proxySubject.hello("World"); String result = proxySubject.bye(); System.out.println("Result is: " + result);
控制台输出:
com.sun.proxy.$Proxy0 Before method Call Method: public abstract void dynamic.Subject.hello(java.lang.String) hello World After method Before method Call Method: public abstract java.lang.String dynamic.Subject.bye() Goodbye After method Result is: over
打印的com.sun.proxy.$Proxy0是动态代理对象的全类名,为什么我们返回的这个代理对象的类名是这样的呢?首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象。
原因就是:在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。
同时我们一定要记住,通过Proxy.newProxyInstance创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的 InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
接着我们来看看这两句:
subject.hello("World"); String result = subject.bye();
这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的handler中的invoke方法去执行,而我们的这个handler对象又接受了一个RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用handler中的invoke方法去执行。
我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们可以在该方法前后添加自己的一些操作,同时我们看到我们的这个method对象是这样的:
public abstract void dynamic.Subject.hello(java.lang.String) public abstract java.lang.String dynamic.Subject.bye()
正好就是我们的Subject接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。
-