java代理设计模式讲解,cglib jdk动态代理 的使用初探与错误分析

代理模式原理

为了更好满足单一职责的原则,而又非入侵式的,还可以保证可以完整的实现这一整个过程,我们使用代理模式
  1. 例子(买火车票
    我要去湖北了,需要买一张火车票

火车票流程图 去火车站——> 买票——>回来

1.去火车站
2.买票
3.回来
现在可以在手机买票软件上购票 ,那么只需要步骤二就可以 步骤一和步骤三被代理掉了
事实上我只需要而且只想自己买票,这就是我的职责(买票就是核心代码)
现在买票我一个人全包,去买票和回来我要就给别人做(非核心代码啊)

购票接口

//这里是购票接口(核心代码)
interface  Buy{
    void buy();
}
核心逻辑实现
//这是我,只需要专心于买票
class I implements Buy{

    public void buy() {
        System.out.println(" 买票" );
    }
    
}

实现代理和main 方法

//这里是代理类 类似手机购票软件
//我的实现也写在里面
public class BuyProxy implements Buy{
    public static void main(String[] args) {
        //第一步先实现核心逻辑
        I i = new I();
        //第二步声明代理
        BuyProxy proxy = new BuyProxy();
        //第三步选择代理对象
        proxy.getByAgent(i);
        //执行代理方法
        proxy.buy();
    }

    //这里使用多态 ,, 实现了可插拔功能(低耦合)
    //这个就是被代理对象
    private Buy buy;
    //如果是动态代理这里入参是Object 静态无所谓
    public void getByAgent(Buy buy){
        this.buy = buy;
    }
    /**
     * 这里是代理类实现购票接口的功能
     */
    public void buy() {
        System.out.println("前置增强-》去火车站");
        buy.buy(); //实现购票功能
        System.out.println("后置增强-》回家");
    }
}

在这里插入图片描述

这样就实现了了一个静态代理 这就是 aop(切面编程) 的原理

使用代理模式的好处

1.在程序中通过代理,可以详细控制访问某个或者某类对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。
譬如: 记录方法执行耗时 判断是否满足进入方法的条件 ,开启/关闭一些资源 ,日志,
对核心业务和非核心业务解耦

动态代理

java 现在有两种动态代理解决方案
1.cglib 需要导入cglib 的jar 包
2. jdk动态代理 使用java 原生的动态代理
Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

cglib动态代理:总结就是通过加载字节码,然后修改他的字节码,然后生成子类(代理类)

JDK动态代理和cglib字节码生成的区别?
1、jdk动态代理只能对实现了接口的类生成代理,而不能针对类

2、cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,可以对接口和类生成代理,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的

Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现
3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
两种动态代理的对比参考出处:https://blog.csdn.net/qq_43842093/article/details/121709970

cglib动态代理

上面实现了简单的静态代理,还讲了cglib 的原理,现在我们在实现 cglib 的动态代理
spring-core jar包默认导入了cglib 包 如果有spring-core jar包 就不需要重新导入cglib jar包

	<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>

代理类 继承MethodInterceptor 实现 interceptor方法

public class MyProxy implements MethodInterceptor {

private Object target ;

    public Object  getProxy(Object clazz){
        try {
        //生成一个实例对象
            this.target = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //声明一个类增强器
        Enhancer en = new Enhancer();
        //设置他要增强的类
        en.setSuperclass(clazz);
        //设置拦截类(实现了MethodInterceptor的类,当前类就实现了)
        en.setCallback(this);
        //返回动态生成的代理类
        return  en.create();
    }

    /**
     *对方法的增强或重写
     * 对接口的实现()
     *
     * @param o 动态生成的代理类对象
     * @param method 调用的方法
     * @param objects 参数集合
     * @param methodProxy 通过调用的方法生成的代理方法
     * @return 执行 method.invoke()的返回值
     * @throws Throwable
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println( o.getClass().getName());
        System.out.println("method = "+method);
        System.out.println("objects = " + Arrays.toString(objects));
        System.out.println("methodProxy = " + methodProxy.getClass().getName());
        Object invoke = methodProxy.invokeSuper(o, objects);
        Object invoke1 = methodProxy.invoke(this.target, objects);

        return invoke;
    }
}


被代理的对象

public abstract class PieMapper {

    public List<Pie> getList(){

        System.out.println("1314 = " + 1314);
        return null;
    };
    public abstract void del();
}

测试代码

public class MyProxyMain  {
    public static void main(String[] args) {
	    //代理类实现
        MyProxy myProxy = new MyProxy();
    	//真实的核心逻辑实现被代理 PieMapper
        PieMapper proxy = (PieMapper) myProxy.getProxy(PieMapper.class);
        //执行代理方法
        proxy.del();
    }
}

代码执行结果可以看他打印的地址
在这里插入图片描述

注意:代理生成的是 PieMapper的子类,类似多态,可以看他打印的地址

在这里插入图片描述

出现内存溢出异常

** 注意对于methodProxy.invoke(),如果第一个参数是o,也就是代理对像,那么就会出现OOM内存溢出现象,这是由于当在程序中,当生成了代理类去调用具体类(被代理类)的方法时,会先去走拦截器,然后跑代理类增强后的方法,此时,在增强方法中,利用代理对象又去调用具体类的方法,又会再次去走拦截器跑代理类增强后的方法,这样就会出现无限循环的情况导致OOM;**
**对于对于methodProxy.invoke(),如果第一个参数是this.object,那么当程序中生成代理类时,去调用方法时,依旧先去走增强逻辑,但是此时,方法内部调用被代理对象的方法,内部方法就不会执行增强逻辑(这和AOP的行为相似,只增强直接调用的方法,不会增强调用方法内部继续调用的其他方法)**
错误的例子

在这里插入图片描述

总结
invokeSuper调用的是被代理类的方法, 但只有代理类才存在基类, 必须使用代理类作为obj参数调用
invoke调用的是增强方法, 必须使用被代理类的对象调用, 使用代理类会造成OOM

jdk动态代理

直接上例子

代理类 继承 InvocatinoHandler 实现代理方法invoke


/**
 * 实现InvocationHandler   用到的都在java.lang.reflect包下
 */
public class MyProxy implements InvocationHandler {

    private Object target;
    public MyProxy(Class clazz){
        try {
            this.target = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置增强");
        //使用反射机制执行方法
        Object invoke = method.invoke(target,args);
        System.out.println("执行方法int = "+(Integer)invoke);
        //实现功能增强
        System.out.println("后增强");
        return invoke;
    }
}

被代理类
因为和cglib 差不太多 就写在一个类里

public class MyproxyMain {
    public static void main(String[] args) {
        //这是接口的匿名内部类 实现了核心代码
        DepartmentMapper mapper = new DepartmentMapper() {
            @Override
            public Integer getList() {
                return 4;
            }
        };
        //把匿名内部类放入代理中
        MyProxy myProxy = new MyProxy(mapper.getClass());
        //创建目标类对象的代理类对象
        //newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) 
        //loader - 类加载器来定义代理类 
		//interfaces - 代理类实现的接口列表 
		//h - 实现了InvocationHandler 的代理类 
        Object o = Proxy.newProxyInstance(mapper.getClass().getClassLoader(), mapper.getClass().getInterfaces(), myProxy);
        DepartmentMapper o1 = (DepartmentMapper) o;
        //输出
        o1.getList();

    }
}

输出
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值