动态代理入门代码

基础概念:
1.什么是动态?
在代码运行时动态的创建代理类对象
2.什么是代理?
代理模式/装饰模式,再不改变原有类的前提下,增强这个类的功能

接口:obj和proxy要实现相同的接口
被代理类:obj实际干活的对象
代理类proxy:包装obj,增强obj,实际底层操作obj干活
增强的点

静态代理的静态:代理类在代码运行之前写好的
动态代理的动态:代理类在代码运行时写的(创建的) 底层:反射
对比:
1>能用动态代理写的,都能用静态代理写
2>在很多场景下,动态代理比静态代理简洁
(因为程序猿无需提前写代理类)牛逼之处
静态代码书写步骤:
1.创建被代理类的对象
2.创建代理对象
3.调用代理
动态代理书写步骤
1.创建被代理类的对象
2.创建代理对象
I.被代理类对象获取自己的类加载器
II.获取被代理类实现的接口
III. new invocationHandler 代理对象被调用之后执行的方法
重写invoke方法
3.调用代理
英文单词翻译
proxy代理
loader类加载器
invoke 调用
newProxyInstance个人理解创建一个代理实例
new创建
Instance实例
Invocation调用
Handler处理器
在这里插入图片描述

静态代理代码步骤
1.先写一个接口,里面有两个抽象方法

public interface Singer {

   void sing(double money);

   void dance(double money);
}

2.写被代理类实现这个接口,重写方法+toString方便打印

class YangChaoYue implements Singer{
    @Override
    public void sing(double money) {
        System.out.println("杨超越唱歌,收钱:" + money);
    }

    @Override
    public void dance(double money) {
        System.out.println("杨超越跳舞,收钱:" + money);
    }

    @Override
    public String toString() {
        return "YangChaoYue{}";
    }
}

3.写代理类,也要实现这个接口

/*
    需求: 可以增强Singer接口的所有实现类对象
    增强的点: 讨价还价功能

    代理类:
        1. 定义一个代理类,实现接口Singer
        2. 该代理类设置一个Singer接口的成员变量,并用构造赋值
        3. 业务: 增强点+被代理对象调用
 */
public class MyProxy implements Singer{
    Singer singer;//被代理的对象

    public MyProxy( Singer singer){
        this.singer = singer;
    }
    @Override
    public void sing(double money) {
        if(money > 100){
            singer.sing(money);
        }else{
            System.out.println("钱不够,不唱");
        }
    }

    @Override
    public void dance(double money) {
        if(money > 200){
            singer.dance(money);
        }else{
            System.out.println("钱不够,不跳");
        }
    }
}

动态代理代码步骤

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
    动态代理
      Object proxy = Proxy.newProxyInstance(loader,interfaces,invocationHandler);
       1. loader 类加载器
       2. interfaces 代理类实现的接口(要跟被代理类相同)
       3. invocationHandler 代理对象被调用之后执行的方法
       return 代理类对象
 */
public class Demo02 {

    public static void main(String[] args) {

        //1. 创建被代理类对象
        YangChaoYue ycy = new YangChaoYue();
        //2. 创建代理
            //被代理类对象获取自己的Class对象,类获取自己的类加载器
        ClassLoader loader = ycy.getClass().getClassLoader();
            //获取被代理类实现的接口
        Class<?>[] interfaces = ycy.getClass().getInterfaces();
		//接口类型参数,必定接收其实现类对象,因为接口不能创建实例
        InvocationHandler h = new InvocationHandler() {
            /*
                invoke 方法: 代理对象每调用一次方法(无论什么方法),invoke就会执行一次!!!

                参数列表
                    proxy :  就是代理对象(就是这里的singer)
                    method(重要) : 当前代理对象调用的方法(当前案例,可以是sing/dance/toString)
                            (比如 singer.sing(105), method就是sing方法对象)
                    args(重要) : 当前代理对象调用的方法的参数
                            (比如 singer.sing(105,"xx"), Object[] args = {105,"xx"} )

                返回值
                    就是代理对象调用方法的返回值,如果某方法的返回值是void无返回值,就是**加粗样式**return null
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                //name可能是sing,也可能是dance,也可能是toString
                if("sing".equals(name)){
                    double money = (double) args[0];
                    if(money > 100){
                        ycy.sing(money);
                    }else{
                        System.out.println("动态: 钱不够,不唱");
                    }
                }else if("dance".equals(name)){
                    double money = (double) args[0];
                    if(money > 200){
                        ycy.dance(money);
                    }else{
                        System.out.println("动态: 钱不够,不跳");
                    }
                }else if("toString".equals(name)){
                 //toString有返回值,就返回一个字符串
                   return "我是代理类";
                }
                //singer和dance没有返回值,就是return null
                return null;
            }
        };

        Singer singer = (Singer)Proxy.newProxyInstance(loader, interfaces, h);
        //3. 调用代理
        singer.sing(105);
        singer.dance(33);
        String str = singer.toString();
        System.out.println(str);
    }
}
**执行结果**
杨超越唱歌,收钱:105.0
动态: 钱不够,不跳
我是代理类

动态代理代码第二遍书写

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Demo03 {

    public static void main(String[] args) {

        //1. 创建被代理的对象
        YangChaoYue ycy = new YangChaoYue();
        //2. 创建代理对象
        ClassLoader loader = ycy.getClass().getClassLoader();
        Class<?>[] interfaces = ycy.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            /*
                invoke方法的特点: 代理每调用一次方法,就会执行一次invoke

                参数列表:
                    proxy : 就是代理(案例: singer)
                    method: 代理当前调用的方法(案例: 可以是sing/dance/toString... )
                    args: 代理当前调用方法的参数(案例: sing方法, Object[] args = {130} )
                返回值:
                    如果代理调用的方法没有返回值,返回null
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                if("sing".equals(name)){
                    double money = (double) args[0];
                    if(money > 100){
                        ycy.sing(money);
                    }else{
                        System.out.println("哼哼,钱不够不唱");
                    }
                }else{
//					除了sing之外的方法,我们调用代理其他方法,代理都会给被代理对象执行
//                  下面调用dance的时候这句代码就 等价于ycy.dance(130);
//                      调用toString的时候 等价于String s = ycy.toString();
//                    method是什么?调用dance它就是dance方法,调用toString就是toString方法
//                    Object接收是因为底层没办法知道你的返回值类型
                    Object o = method.invoke(ycy, args);
                    return o;
                }
                return null;
            }
        };
        Singer singer = (Singer)Proxy.newProxyInstance(loader, interfaces, h);
        //3. 调用代理
        singer.dance(130);
        String str = singer.toString();
        System.out.println(str);
    }
}
**执行结果**
杨超越跳舞,收钱:130.0
YangChaoYue{}

原理分析


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
    # 动态代理
    1. 动态: 代理类在程序运行时创建的
        底层: 肯定和反射有关系
        1). 类加载器: 将字节码加载进内存,jvm依此创建Class对象
 */
public class Demo04 {

    public static void main(String[] args) {

        //1. 创建被代理的对象
        YangChaoYue ycy = new YangChaoYue();
        //2. 创建代理对象
        /*
            原理的第一步: loader + interfaces (继承+ 反射)
            实现接口是用了继承
            类加载器是用了反射
                运行时: jvm底层会定义一个类,去实现interfaces中的接口(动态字节码技术)
                    这个字节码是在运行时动态创建的
                loader此时加载这个字节码, jvm依此创建代理类的Class对象

                总结: loader + interfaces -> 代理类Class对象

                TODO:说一个点:我们的字节码最终在运行时的目的就是产生Class对象,
                TODO:newProxyInstance方法底层已经实现了,生成了指定的代理类
                TODO:我这个目的达到了,我的字节码需要提前写好吗?
                TODO:只要我们能在运行时产生,我们就不用提前写好

            原理的第二步 : InvocationHandler h (多态+ 反射)
                问题: jvm能够创建类实现接口,无法预知业务重写方法
                    重写方法还是需要交给我们程序猿来实现
         */
        ClassLoader loader = ycy.getClass().getClassLoader();
        Class<?>[] interfaces = ycy.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            /*
                invoke方法的特点: 代理无论执行什么方法,invoke都会被调用
                参数列表:
                    proxy : 就是代理本身(无意义)
                    method: 当前代理调用的方法(可以是sing/dance/toString...)
                    args: 当前代理调用的方法参数
                返回值:
                    代理调用方法的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                if("sing".equals(name)){
                    double money = (double) args[0];
                    if(money > 100){
                        ycy.sing(money);
                    }else{
                        System.out.println("哼哼,钱不够不唱");
                    }
                }else{
                    Object o = method.invoke(ycy, args);
                    return o;
                }
                return null;
            }
        };
        Singer singer = (Singer)Proxy.newProxyInstance(loader, interfaces, h);
        //3. 调用代理
     	singer.dance(130);
        singer.sing(80);
        String string = singer.toString();
        System.out.println(string);
    }
}
执行结果
杨超越跳舞,收钱:130.0
哼哼,钱不够不唱
YangChaoYue{}

newProxyInstance底层代码图片,分析的上面代码中的一句话
在这里插入图片描述

伪代码分析图片
这里是一段伪代码帮助我们理解
InnerProxy这个类实际上是不存在的,只在运行时产生
为了方便想象写的一段代码
在这里插入图片描述


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 伪代码
public class InnerProxy implements Singer {
    //这个h是我们写原理的代码给它传进去的
    InvocationHandler h;

    public InnerProxy(InvocationHandler h){
        this.h = h;
    }

    @Override
    public void sing(double money) {
        try {
            Class<? > clazz = this.getClass();
            Method method = clazz.getDeclaredMethod("sing",double.class);
            Object[] args = {money};
            h.invoke(this,method, args);

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    @Override
    public void dance(double money) {
        try {
            Class<? > clazz = this.getClass();
            Method method = clazz.getDeclaredMethod("dance",double.class);
            Object[] args = {money};
            h.invoke(this,method,args);

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    @Override
    public String toString() {
        try {
            Class<? > clazz = this.getClass();
            Method method = clazz.getDeclaredMethod("toString");
            Object[] args = {};
            String obj = (String) h.invoke(this, method, args);
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

动态代理随笔
方法增强: 在不惊动原始设计的基础上(不修改源代码),为其添加功能
要实现对一个类的方法增强,我们有以下三种方式: 继承,静态代理和动态代理

1. 继承

1). 实现方式
		创建一个子类继承需要增强的类,重写该方法进行增强
2). 优缺点
	a. 优点: 实现简单
	b. 缺点: 对比其他两种方式, 继承中的一个子类只能增强其父类(只能增强一个)

2. 装饰者模式(静态代理):

1). 实现方式
	a. 装饰者类和被装饰者类必须实现同一个接口或继承同一个类
    b. 在装饰者类中必须要有被装饰者类的引用
    c. 在装饰者类中对需要增强的方法进行增强
    d. 在装饰者类中对不需要增强的方法调用原来的逻辑
2). 优缺点
	a. 优点: 对比继承方式中的子类,装饰模式的代理类可以增强父接口/父类的所有子类
    b. 缺点: 对比动态代理,装饰模式的代理类需要提前定义

3. 动态代理:

1). 实现方式
	a. JDK的动态代理:
		Proxy: 基于接口的代理(Proxy来源于JDK)
	b. cglib的动态代理	
        Enhancer: 基于类的代理(第三方工具包)
2). 优缺点:
	a. 优点: 无论是JDK还是cglib的动态代理, 采用的都是动态字节码技术,也就是代理类都是运行过程中动态产生的,无需在编写代码时定义
	b. 缺点:与继承或静态代理对比, 相对不好理解

4. JDK的动态代理与cglib详解

1). 使用条件
a. JDK动态代理只能对实现了接口的类生成代理,而不能针对类
b. CGLib是针对类实现代理,主要是对指定的类生成一个子类,重写其中的方法
(就是继承,父类的方法不能用final修饰)
2). 性能对比
a. 在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点
b. 但是到JDK1.8的时候,JDK代理效率高于CGLib代理
3). spring的AOP采用的代理方案
a. 当Bean实现接口时,Spring就会用JDK的动态代理
b. 当Bean没有实现接口时,Spring使用CGLib来实现
c. 备注: 开发者可以在spring中强制使用CGLib
(在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值