前言
代理是什么
事故现场: 我家的宠物今天生病了, 而我又没有相关的医学知识, 所以我只让我的宠物多喝热水吗?
结果显然是不行的, 所以我需要去找宠物医生这些更专业的人来帮我的宠物治病.
这个时候, 代理就出现了, 而宠物医生就是代理, 而我就是目标对象.
总结起来就是代理代替目标对象执行相关操作, 即是对目标对象的一种功能扩展.
使用代理模式的条件
1, 两个角色: 执行者, 被代理对象
2, 注重过程, 必须要做, 被代理的对象没时间做或者不想做, 不专业
3, 执行者必须拿到被代理对象的个人资料
1. 静态代理
代码实现:
- /**
- * 我和宠物医生都是人, 都有治疗技能, 但是宠物医生比我更专业
- */
- interface IPerson{
- void treat(Pet pet); // 治疗技能
- }
- /**
- * 宠物类
- */
- class Pet{
- private String name;
- public Pet(String name){
- this.name = name;
- }
- public String getName() {
- return name;
- }
- }
- /**
- * 目标对象实现 "IPerson" 接口
- */
- class Self implements IPerson{
- private Pet pet;
- public Self(Pet pet){
- this.pet = pet;
- }
- public void treat(Pet pet){
- System.out.println(pet.getName() + ", 你要多喝点水");
- }
- }
- /**
- * 代理对象与目标对象实现同一接口
- */
- class PetDoctor implements IPerson{
- // 接收目标对象
- private IPerson targetObj;
- public PetDoctor(IPerson targetObj){
- this.targetObj = targetObj;
- }
- @Override
- public void treat(Pet pet) {
- System.out.println("对" + pet.getName() + "进行检查");
- targetObj.treat(pet);
- System.out.println("对" + pet.getName() + "进行治疗");
- }
- }
代码测试:
- public static void main(String[] args){
- // 我的宠物
- Pet pet = new Pet("多多");
- // 目标对象
- IPerson target = new Self(pet);
- // 代理对象
- IPerson proxy = new PetDoctor(target);
- proxy.treat(pet);
- }
运行结果:
宠物医生对多多进行检查
我对多多说, 你要多喝点水
宠物医生对多多进行治疗
结果很明显, 医生比我更专业, 我只会让我的宠物喝水, 但医生会先检查再进行专业的治疗, 所以说代理是让更专业的对象帮你做事.
2. 动态代理
动态代理又分为 jdk 动态代理和 cglib 动态代理, 两者的区别是 jdk 动态代理的实现是基于接口, 而 cglib 动态代理是基于继承, 但两者做的是同一件事, 那就是字节码重组.
基本流程都是根据目标对象的资料, 通过反射获取该对象的信息, 然后根据信息按照特定的写法重写一个 java 类, 再进行编译并动态加载到 JVM 中运行, 所以说动态代理在底层其实就是实现了字节码重组.
jdk 动态代理实例演示
Person 接口
- // 定义 Person 接口, 技能是煮饭
- public interface Person {
- void cook();
- }
我自己, 也就是被代理的对象, 但我只会做可乐鸡翅
- public class Oneself implements Person {
- @Override
- public void cook() {
- System.out.println("我会做可乐鸡翅");
- }
- }
动态代理类, 也是一个厨师, 因为初始对于做菜比我更专业
- public class Kitchener implements InvocationHandler{
- // 需要代理的目标对象
- private Object object;
- public Kitchener(Object object){
- this.object = object;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("我会做糖醋排骨");
- method.invoke(object,args); // 这是我会做的, 其余两样是代理对象初始会做的
- System.out.println("我会做九转大肠");
- return null;
- }
- }
测试代码
- public class TestJdk {
- public static void main(String[] args){
- // 创建目标代理对象
- Oneself oneself = new Oneself();
- InvocationHandler kitchener = new Kitchener(oneself);
- /*
- * 通过 Proxy 的 newProxyInstance 方法来创建我们的代理对象, 做的就是字节码重组的工作, 新生成一个 java 类在编译再加载到 JVM 运行
- * 第一个参数是类加载器
- * 第二个参数是我们这里为代理对象提供的接口, 也就是代理对象所实现的接口, 所以说在 jdk 动态代理中被代理对象需要实现一个接口
- * 第三个参数 handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
- */
- Person proxy = (Person) Proxy.newProxyInstance(kitchener.getClass().getClassLoader(),
- oneself.getClass().getInterfaces(),
- kitchener);
- System.out.println(proxy.getClass()); // (1)
- proxy.cook();
- }
- }
测试结果
class com.sun.proxy.$Proxy0 //(2)
我会做糖醋排骨
我会做可乐鸡翅
我会做九转大肠
可以看到 (1) 行代码打印出来的是一个代理类, 而代理对象通过生成 java 类再编译加载运行对用户来说是无感知的, 我们只知道返回回来的是一个代理对象, 然后由代理对象去帮我们做事.
而 cglib 代理的实现原理也是一样的, 只不过一个是基于接口, 一个是基于继承, 原理都是通过反射获取对象信息再根据对象信息创建 java 类编译加载运行, 所以 cglib 暂时就不展开了, 后期可以自己手写一个动态加深理解.
学习了动态代理后, 在本人的工作中是没使用过的, 但却是了解 spring 的 AOP 实现的必要基础, 因为 spring 的 AOP 实现就是基于动态代理实现的.
来源: https://www.cnblogs.com/xiguadadage/p/10188634.html