静态代理
概述
- 静态代理中,我们对目标对象的每个方法的增强都是手动完成的(后面会具体演示代码),非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。
- 实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。
- 上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, 静态代理在 编译时 就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
静态代理角色分析
-
抽象角色 : 一般使用接口或者抽象类来实现
-
真实角色 : 被代理的角色
-
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
-
客户 : 使用代理角色来进行一些操作
- 接口(抽象角色)
//抽象角色:租房 public interface Rent { public void rent(); }
.真实角色
//真实角色: 房东,房东要出租房子 public class Host implements Rent{ public void rent() { System.out.println("房屋出租"); } }
代理角色
//代理角色:中介 public class Proxy implements Rent { private Host host; public Proxy() { }; public Proxy(Host host) { this.host = host; } //租房 public void rent(){ seeHouse(); host.rent(); fare(); } //看房 public void seeHouse(){ System.out.println("带房客看房"); } //收中介费 public void fare(){ System.out.println("收中介费"); }
客户访问代理角色
//客户类,一般客户都会去找代理! public class Client { public static void main(String[] args) { //房东要租房 Host host = new Host(); //中介帮助房东 Proxy proxy = new Proxy(host); //你去找中介! proxy.rent(); } }
静态代理的好处与缺点:
好处:
-
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
-
公共的业务由代理来完成 . 实现了业务的分工 ,
-
公共业务发生扩展时变得更加集中和方便 .
缺点:
-
类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
动态代理
相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类(CGLIB 动态代理机制)。
从 JVM 角度来说,动态代理是在 运行时 动态生成类字节码,并加载到 JVM 中的。
说到动态代理,Spring AOP、RPC 框架应该是两个不得不提的,它们的实现都依赖了动态代理。
动态代理在我们日常开发中使用的相对较少,但是在框架中的几乎是必用的一门技术。学会了动态代理之后,对于我们理解和学习各种框架的原理也非常有帮助。
就 Java 来说,动态代理的实现方式有很多种,比如 JDK 动态代理、CGLIB 动态代理 等等。
-
动态代理的角色和静态代理的一样 .
-
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
-
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
-
-
基于接口的动态代理----JDK动态代理
-
基于类的动态代理--cglib
-
现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
-
我们这里使用JDK的原生代码来实现,其余的道理都是一样的!
-
-
JDK的动态代理需要了解两个类
-
核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看
Proxy.newProxyInstance的三个参数
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
ClassLoader loader获取被代理类的类加载器。
Class<?>[] interfaces获取被代理类的实现接口的数组。
InvocationHandler h在invok方法中对方法做增强处理。
invoke方法的三个参数
// proxy : 代理类 method : 代理类的调用处理程序的方法对象.
// 处理代理实例上的方法调用并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
-
Object proxy
当前代理对象 -
Method method
当前方法 -
Object[] args
方法传递的参数
代码实现
接口(抽象角色)
//抽象角色:租房 public interface Rent { public void rent(); }
实体类(真实角色)
//真实角色: 房东,房东要出租房子 public class Host implements Rent{ public void rent() { System.out.println("房屋出租"); } }
代理角色
public class ProxyInvocationHandler implements InvocationHandler { private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this); } // proxy : 代理类 method : 代理类的调用处理程序的方法对象. // 处理代理实例上的方法调用并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); //核心:本质利用反射实现! Object result = method.invoke(rent, args); fare(); return result; } //看房 public void seeHouse(){ System.out.println("带房客看房"); } //收中介费 public void fare(){ System.out.println("收中介费"); } }
客户端访问代理角色
//租客 public class Client { public static void main(String[] args) { //真实角色 Host host = new Host(); //代理实例的调用处理程序 ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setRent(host); //将真实角色放置进去! Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类! proxy.rent(); } }
深化理解
核心:*一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、
我们来使用动态代理实现代理我们后面写的UserService!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!