动态代理
1.代理模式
1.1什么是代理模式
-
代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,
一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之
间起到中介的作用。 -
例如: 有 A, B(代理类), C (目标类)三个类, A 原来可以调用 C 类的方法, 现在因为某种原因 C 类不允许A 类调用其方法,但 B 类可以调用 C 类的方法。 A 类通过 B 类调用 C 类的方法。这里 B 是 C
的代理。 A 通过代理 B 访问 C 。
1.2代理模式作用
- 功能增强: 在原有的功能上,增加了额外的功能。 新增加的功能,叫做功能增强。
- 控制访问: 代理类不让你直接访问目标,例如中介不让租户直接访问房东。
1.3代理模式分类
-
静态代理
-
动态代理
2.静态代理
2.1什么是静态代理
- 静态代理是指,代理类在程序运行前就已经定义好.java 源文件,其与目标类的关系在
程序运行前就已经确立。 在程序运行前代理类已经编译为.class 文件。 - (1)代理类是自己手工实现的,自己手动创建一个java类,表示代理类。
(2)同时你所要代理的目标类是确定的。
特点: 1)实现简单 2)容易理解。
2.2静态代理缺点
- 当一个项目中,目标类和代理类很多时候,有以下的缺点:
(1)代码复杂,难于管理
代理类和目标类实现了相同的接口, 每个代理都需要实现目标类的方法, 这样就出现了大量的代
码重复。如果接口增加一个方法,除了所有目标类需要实现这个方法外,所有代理类也需要实现
此方法。增加了代码维护的复杂度 ,耦合度也高。
(2)代理类依赖目标类,代理类过多
代理类只服务于一种类型的目标类,如果要服务多个类型。势必要为每一种目标类都进行代理,
静态代理在程序规模稍大时就无法胜任了,代理类数量过多。
3.动态代理
3.1什么是动态代理
- 动态代理是指代理类对象在程序运行时由JVM 根据反射机制动态生成的。 动态代理不
需要定义代理类的.java 源文件。
动态代理其实就是 jdk 运行期间, 动态创建 class 字节码并加载到 JVM。
3.2动态代理的实现方式
-
jdk动态代理(理解): 使用java反射包中的类和接口实现动态代理的功能。是基于接口的代理类
反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy。 -
cglib动态代理(了解): cglib是第三方的工具库, 创建代理对象。
cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改。因为cglib是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的。cglib的要求目标类比较宽松, 只要能继承就可以了。cglib在很多的框架中使用, 比如 mybatis ,spring框架中都有使用。
3.3jdk动态代理需要用到的方法和类
-
(1) InvocationHandler 接口
通 过 代 理 对 象 执 行 目 标 接 口 中 的 方 法 , 会 把 方 法 的 调 用 分 派 给 调 用 处 理 器
(InvocationHandler)的实现类,执行实现类中的 invoke()方法,我们需要把功能代理写在 invoke()方法中public interface InvocationHandler {//代理类必须实现这个接口,并重写invoke()方法 /** * 该方法在目标类的方法被执行的时候,会被调用 * 调用目标类方法时,会先执行该invoke方法 * @param proxy 真正的代理对象,即Proxy.newProxyInstance()方法的返回结果 * @param method 目标类中的方法,jdk自动帮我们提供对象 * @param args 目标类中方法的参数(可变参数),也是jdk自动帮我们的提供的 * @return null * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
-
(2) Method 类
表示目标类中的方法
InvocationHandler实现类的invoke()方法的第二个参数为 Method 类对象,该类有一个方法也叫 invoke(),可以调用目标方法。这两个 invoke()方法,虽然同名,但无关
public Object invoke(Object obj, Object... args){} //obj:表示目标对象 //args:表示目标方法参数,就是InvocationHandler实现类的invoke方法的第三个参数(可变参数)
该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对象确定。
在代码中,一般的写法为
method.invoke(target, args);
其中, method 为上一层 invoke 方法的第二个参数。这样,即可调用目标类的目标方法。 -
( 3) Proxy 类
通 过 JDK 的 java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 使 用 其 静 态 方 法newProxyInstance(),依据目标对象、业务接口及调用处理器三者,自动生成一个动态代理对象。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {} //loader:目标类的类加载器,通过目标对象的反射可获取 //interfaces:目标类实现的接口数组,通过目标对象的反射可获取 //handler: 调用处理器。
3.4 jdk 动态代理实现
jdk 动态代理是代理模式的一种实现方式,其只能代理接口。实现步骤:
1、新建一个接口,作为目标接口
2、为接口创建一个实现类,是目标类
3、创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码
4、 创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型。
3.5jdk动态代理的例子
- 目标接口
public interface TargetClass {
void sayHello();
}
-
目标接口的实现类
public class TargetClassImpl implements TargetClass{ @Override public void sayHello() { System.out.println("Hi,dynamic proxy"); } }
-
创建类实现 java.lang.reflect.InvocationHandler 接口
/** * ProxyClass是动态代理的一部分,还不是真正的代理类,该类是协助真正的代理类去工作 */ public class ProxyClass implements InvocationHandler { private Object target;//这里要传入一个目标类 public ProxyClass(Object target) { this.target = target; } /** * 该方法在目标类的方法被执行的时候,会被调用 * 调用目标类方法时,会先执行该invoke方法 * @param proxy 真正的代理对象,即Proxy.newProxyInstance()方法的返回结果 * @param method 目标类中的方法,jdk自动帮我们提供对象 * @param args 目标类中方法的参数(可变参数),也是jdk自动帮我们的提供的 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标方法执行前。。。。。。"); //此代码是真正的目标方法sayHello() Object result = method.invoke(target,args); //这些增加的方法表示增强功能作用 System.out.println("目标方法执行后。。。。。。"); return result; } }
-
创建动态代理对象来调用目标类
public class Client { public static void main(String[] args) { //使用代理模式 //Proxy.newProxyInstance()这个方法的返回结果才是我们的真正的代理对象 TargetClass targetClass = (TargetClass)Proxy.newProxyInstance(TargetClass.class.getClassLoader(), new Class<?>[] {TargetClass.class}, new ProxyClass(new TargetClassImpl())); targetClass.sayHello(); } }