JDK实现动态代理
思维导图:点击打开
1.代理模式概念
代理模式是指:为其他对象提供一种代理用以控制这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标类之间起到中介的作用。——————百度百科《代理模式》
代理对象
客户端---->中介---->目标对象
生活中的例子:
- 房产中介
- 代购
- 媒婆
2.代理模式作用
2.1控制访问
在代理中控制客户端能否调用目标对象的方法。
比如媒婆安排相亲
2.2增强功能
完成目标对象的调用时,附加一些额外功能,这些额外功能就是增强功能。
比如媒婆要求中介费用
3.代理模式分类
代理模式分为静态代理和动态代理
3.1静态代理
3.1.1描述
静态代理是指:代理类在程序运行前就已经定义好了.java源文件,代理类和目标类的关系在程序运行前就已经确立了。在程序运行前代理类就已经编译成了.class文件。
3.1.2优缺点
1.优点
- 容易理解
- 使用比较方便
2.缺点
-
目标类较多时,会产生大量代理类
-
当接口改变时,影响的目标类较多
3.2动态代理
3.2.1描述
动态代理是指:代理类对象是在运行时,由JVM根据反射机制动态生成的。动态代理不需要定义代理类的.java源文件。
动态代理其实就是JDK运行期间,动态创建class字节码文件并加载到JVM
3.2.2优点
- 不用创建类文件
- 当修改了接口中的方法时,不会影响代理类
- 不用给不同的目标随时创建代理
3.2.3常用实现方式
1.JDK动态代理:
- 使用java反射包中的类和接口实现动态代理的功能。
- 反射包:java.lang.reflect
- 使用的类:
- InvocationHandler
- Method
- Proxy
2.CGLIB动态代理:
- cglib时第三方的工具库,用于创建代理对象。
- cglib的原理是继承,cglib通过继承目标类,创建其子类,在子类中重写父类中同名的方法,实现功能的修改。
- 因为cglib需要继承目标类,所以要求目标类不能是final的,方法也不能是final的。
- cglib对于目标类的要求比较宽松,只要能继承就可以。
- cglib在很多框架中使用,如mybatis,spring框架。
4.JDK的动态代理
jdk的动态代理是基于Java的反射机制实现的。使用jdk中的接口和类来实现代理对象的动态创建。
jdk的动态代理要求目标对象必须实现接口。
4.1 InvocationHandler,Method和Proxy
4.1.1InvocationHandler接口
InvocationHandler接口叫做调用处理器,该接口只有一个抽象方法:invoke()。
在InvocationHandler接口的实现类中,实现调用目标方法并增强功能的语句就写在invoke()方法里。
public Object invoke(Object proxy, Method method, Object[] args)
proxy:代表生成的代理对象
method:代表目标方法
args:代表目标方法的参数
这三个参数都是jdk运行是赋值的,无需程序员给出。
4.1.2Method类
InvocationHandler接口的invoke()方法中,第二个参数就是Method类对象,该类中也有一个invoke()方法,可以调用目标方法。这两个invoke()方法虽然同名,但是没有关系。
public Object invoke(Object obj, Object... args)
该方法的作用是:调用执行obj对象所属类的方法,该方法由调用者Method对象确定。
在代码中,一般写法为:
method.invoke(target,args);
其中,method是InvocationHandler接口invoke方法的第二个参数。
4.1.3Proxy类
通过JDK的java.lang.reflect.Proxy类实现动态代理,会使用其静态方法newProxyInstance(),根据目标对象、业务接口以及调用处理器,自动生成一个动态代理对象。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
loader:目标类的类加载器,通过目标对象的反射可以获取【目标对象.getClass().getClassLoader()】
interfaces:目标类实现的接口数组,同样可以通过目标对象的反射获取【目标对象.getClass().getInterfaces()】
h:调用处理器,也就是InvocationHandler接口实现类对象
4.2动态代理实现步骤
- 创建目标接口,定义目标类要完成的功能
- 创建目标类来实现目标接口
- 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
- 调用目标类的方法
- 增强功能
- 使用Proxy类的静态方法,创建代理对象。并把返回值也就是代理对象转为接口类型。
4.2.1创建目标接口
定义目标类要完成的功能
package com.tsccg.service;
/**
* @Author: TSCCG
* @Date: 2021/09/03 16:31
* 目标接口
*/
public interface PhoneSell {
float sell(int amount);
}
4.2.2创建目标类来实现目标接口
package com.tsccg.factory;
import com.tsccg.service.PhoneSell;
/**
* @Author: TSCCG
* @Date: 2021/09/03 16:40
* 目标类
*/
public class PhoneFactory implements PhoneSell {
@Override
public float sell(int amount) {
System.out.println("目标类,执行目标方法");
return 50.0F;
}
}
4.2.3创建InvocationHandler接口的实现类
在invoke方法中完成代理类的功能
- 调用目标类的方法
- 增强功能
package com.tsccg.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @Author: TSCCG
* @Date: 2021/09/03 16:43
* 必须实现代理类要做的功能
* 1.调用目标类方法
* 2.增强功能
*/
public class MySellHandler implements InvocationHandler {
//代表目标对象
private Object target = null;
public MySellHandler(Object target) {
//给目标对象赋值
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.执行目标类方法
Object result = null;
result = method.invoke(target,args);
//2.增强功能,在目标类方法调用后,所做的其他功能都是增强功能
if (result != null) {
Float price = (Float)result;
price += 25;
result = price;
}
System.out.println("商家,增强服务:优惠券");
//3.返回增强的价格
return result;
}
}
4.2.4使用Proxy类的静态方法,创建代理对象
使用Proxy类的静态方法,创建代理对象。并把返回值也就是代理对象转为接口类型。
package com.tsccg;
import com.tsccg.factory.PhoneFactory;
import com.tsccg.handler.MySellHandler;
import com.tsccg.service.PhoneSell;
import java.lang.reflect.Proxy;
/**
* @Author: TSCCG
* @Date: 2021/09/03 17:04
* 使用Proxy类的静态方法创建代理对象
*/
public class MainShop {
public static void main(String[] args) {
//1.创建目标对象
PhoneSell factory = new PhoneFactory();
//2.创建InvocationHandler接口实现类对象,传入目标对象
MySellHandler handler = new MySellHandler(factory);
//3.创建代理对象
PhoneSell proxy = (PhoneSell)Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),handler);
//4.通过代理对象执行目标方法
float price = proxy.sell(1);
System.out.println("通过动态代理对象调用方法:" + price);
}
}
执行结果:
目标类,执行目标类的方法
商家,增强服务:优惠券
通过动态代理对象调用方法:75.0