动态代理模式和动态代理失效原因简介

代理模式的定义

给目标对象提供一个代理对象,并由代理对象控制目标对象的引用

代理模式的作用

  • 通过代理对象对目标对象进行业务增强
  • 间接访问目标对象,防止直接访问目标对象带来的复杂性【这个作用目前没有理解】

代理模式三要素

  • 抽象接口,定义功能行为
  • 真实对象,实现了抽象对象
  • 代理对象,实现了抽象对象,并包含了真实对象
    静态代理和动态代理,代理对象都必须持有目标对象【被代理对象,也称为目标对象】

    静态代理时,被代理对象和代理对象都必须实现接口,且必须是同一个接口。
    动态代理时,被代理对象必须实现接口,因为代理对象是通过该接口的反射机制创建的,即
    代理对象=Proxy.newProxyInstance(被代理对象.getClass().getClassLoader(),被代理对象.getClass().getInterfaces(), this);

静态代理

Demo实现的功能:代理苹果手机专卖店,在网上买手机,增强了打广告的功能

//抽象对象
public interface PhoneShop {
    void sellPhone(String phoneName);
}
//被代理的目标对象
public class ApplePhoneShop implements PhoneShop {
    @Override
    public void sellPhone(String phoneName) {
        System.out.println("苹果手机专门店正在卖:" + phoneName);
    }
}
//用于增强的代理对象
public class OnlineShop implements PhoneShop {

    private PhoneShop phoneShop;

    public OnlineShop(PhoneShop phoneShop) {
        this.phoneShop= phoneShop;
    }

    @Override
    public void sellPhone(String phoneName) {
        System.out.println("手机广告推广");
        phoneShop.sellPhone(phoneName);
        System.out.println("卖完了分钱");
    }
}
public class StaticProxyDemo {
    public static void main(String[] args) {
        OnlineShop onlineShop = new OnlineShop(new ApplePhoneShop());
        onlineShop.sellPhone("苹果12 pro");
    }
}

运行结果:
在这里插入图片描述
静态代理的缺点:
静态代理的扩展性不好,如果想在接口中增加方法,必须修改代理对象中的代码,明显违背了开闭原则。为了解决静态代理的缺点,由此出现了动态代理。
开闭原则【对扩展开放,对修改关闭】:只额外增加代码模块,不修改原有功能代码

动态代理

Spring动态代理的两种实现方式

java动态代理:通过java.lang.reflect.Proxy类的newProxyInstance方法创建代理对象【反射机制】,再通过代理对象调用被代理对象的方法,实际是通过invoke方法调用
cglib动态代理【Code Generator Library】:修改内存中目标对象类的字节码文件,生成其子类。

为什么使用CGLIB动态代理

JDK动态代理要求代理对象类必须实现接口,不能代理没有实现接口的普通类,因为JDK动态代理创建代理对象时,是通过接口反射机制来实现的:Proxy.newProxyInstance(被代理对象.getClass().getClassLoader(),被代理对象.getClass().getInterfaces(), this);

什么是 CGLIB

CGLIB(Code Generator Library)是一个代码生成库。其被广泛应用于AOP框架。GLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。

JDK动态代理和CGLIB字节码生成的区别

(1)JDK动态代理要求代理对象类必须实现接口,不能代理没有实现接口的普通类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆写其中的方法。
CGlib动态代理不要求代理对象类必须实现接口,CGlib使用的是继承机制,代理类继承委托类。CGlib要求委托类不能是final的,否则会创建代理失败,因为final类是不允许继承的

  1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
  2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  3. 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

JDK动态代理的实现

JDK动态代理的条件:
(1)被代理的对象必须要实现接口
(2)实现InvocationHandler接口
(3)使用Proxy.newProxyInstance产生代理对象

1.实现动态代理的两个核心:InvocationHandler接口、Proxy类

  • InvocationHandler接口,提供了invoke方法,实现该方法主要作用:
    代理对象调用被代理对象的方法通过invoke方法来实现
  • Proxy类用来创建一个代理对象,主要通过它的静态方法newProxyInstance方法
public interface Girl {
    void shopping(String goodsName);
    void movie(String filmName);
}
public class WangMeiNv implements Girl {

    @Override
    public void shopping(String goodsName) {
        System.out.println("今天王美女买了:" + goodsName);
    }
    
    @Override
    public void movie(String filmName) {
        System.out.println("今天王美女看了电影:" + filmName);
    }
}
public class WangMeiNvPoxy implements InvocationHandler {

    private Girl girl;

    public WangMeiNvPoxy() {
    }

    public WangMeiNvPoxy(Girl girl) {
        this.girl = girl;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用方法之前做一些事情!");
        Object ret = method.invoke(girl, args);
        System.out.println("调用方法之后做一些事情!");
        return ret;
    }
	//创建该代理对象
    public Object getNewInstance(){
       return Proxy.newProxyInstance(girl.getClass().getClassLoader(), girl.getClass().getInterfaces(), this);
    }
}

代理对象的使用

@Test
public void test1(){
     Girl wangMeiNv = new WangMeiNv();
     WangMeiNvPoxy wangMeiNvPoxy = new WangMeiNvPoxy(wangMeiNv);
     Girl newInstance = (Girl) wangMeiNvPoxy.getNewInstance();
     newInstance.shopping("超短裙");
     System.out.println("--------------------");
     newInstance.movie("坦坦尼克号");
 }

调用方法之前做一些事情!
今天王美女买了:超短裙
调用方法之后做一些事情!
--------------------------
调用方法之前做一些事情!
今天王美女看了电影:坦坦尼克号
调用方法之后做一些事情!

CGLIB动态代理的实现

参考文章:
https://www.jianshu.com/p/296684ba7fe6
https://blog.csdn.net/gyshun/article/details/81000997

AOP和动态代理的关系

  • AOP面向切面编程是一种编程思想,是一种横向抽取代码的机制,补充了面向对象编程思想只能纵向父子集成的不足。
  • 动态代理只是AOP这种编程思想的实现方式之一
  • Spring的AOP是基于动态代理和反射机制实现的

动态代理失效的情况分析

public interface Girl {
    void shopping(String goodsName);
    void movie(String filmName);
}
public class WangMeiNv implements Girl {

    @Override
    public void shopping(String goodsName) {
       System.out.println("==============这里此次代码分析的重点=================");
       //这里此次代码分析的重点
        this.movie("泰塔尼克号");
    }
    
    @Override
    public void movie(String filmName) {
        System.out.println("今天王美女看了电影:" + filmName);
    }
}
public class WangMeiNvPoxy implements InvocationHandler {

    private Girl girl;

    public WangMeiNvPoxy() {
    }

    public WangMeiNvPoxy(Girl girl) {
        this.girl = girl;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用方法之前做一些事情!");
        Object ret = method.invoke(girl, args);
        System.out.println("调用方法之后做一些事情!");
        return ret;
    }
	//创建该代理对象,这里this代表实现InvocationHandler接口的当前代理对象
    public Object getNewInstance(){
       return Proxy.newProxyInstance(girl.getClass().getClassLoader(), girl.getClass().getInterfaces(), this);
    }
}
@Test
public void test1(){
     Girl wangMeiNv = new WangMeiNv();
     WangMeiNvPoxy wangMeiNvPoxy = new WangMeiNvPoxy(wangMeiNv);
     Girl newInstance = (Girl) wangMeiNvPoxy.getNewInstance();
     newInstance.shopping("超短裙");
     System.out.println("--------------------");
     //newInstance.movie("坦坦尼克号");
 }

调用方法之前做一些事情!
今天王美女买了:超短裙
==============这里此次代码分析的重点=================
今天王美女看了电影:泰塔尼克号
调用方法之后做一些事情!
--------------------

为什么这次shopping方法中直接调用moive方法后,这里的结果和之前的不同呢?
原因:这里的this指带的WangMeiNv类的对象,而不是代理类实例。只有代理实例调用方法时,方法才会通过AOP实现业务增强处理

public class WangMeiNv implements Girl {

    @Override
    public void shopping(String goodsName) {
       System.out.println("==============这里此次代码分析的重点=================");
       //这里此次代码分析的重点
        this.movie("泰塔尼克号");
    } 
    @Override
    public void movie(String filmName) {
        System.out.println("今天王美女看了电影:" + filmName);
    }
}

解决办法:
https://blog.csdn.net/Dongguabai/article/details/80788585

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值