Java学习笔记----静态代理和动态代理

什么是代理?

本质上说,相当于我们平时租房子的中介,屏蔽掉了你与房主之间的联系,直接给你提供租房服务。

有的人有好多套房子,这个人就相当于是一个目标类,这个中介就是代理类,我们使用代理类就可以调用目标类的方法。代理类封装了目标类,相当于中介有房主的这些房子的信息,我们直接找中介就可以租到房子。我们通过中介,甚至还可以看其他房主的房子,也就是通过代理类调用其他类的方法,相当于实现了给目标类扩展新功能而不需要修改目标类的代码,这样一定程度上可以实现解耦。

Java中的代理有两种:

静态代理:手写代理类将目标类封装起来,缺点是需要自己写代码

动态代理:运行阶段由JVM自动生成代理类,可以直接使用

静态代理实现:

静态代理需要我们自己写,如果方法少的话感觉还行,方法多的话感觉十分崩溃,操作性太差,以下是静态代理的简单实现

首先需要包装目标类,我们先写一个接口,让目标类和代理类都实现这个接口,那么两个类的方法名就会一样,然后将目标类传入代理类中即可

//定义接口
interface animal{
    void eat();
    void run();
}


public class agentTest {
    //目标类
    public static class Cat implements animal{
        
        //目标类实现接口中的方法
        @Override
        public void eat() {
            System.out.println("狗吃饭");
        }

        @Override
        public void run() {
            System.out.println("狗奔跑");
        }
    }
    //代理类
    public static class Catagent implements animal{
        private Cat cat;

        public Catagent(Cat cat){
            this.cat = cat;
        }
        //代理类在目标类的基础上扩展方法
        @Override
        public void run() {
            System.out.println("代理类---跑");
            cat.run();
        }

        @Override
        public void eat() {
            System.out.println("代理类---吃");
            cat.eat();
        }
    }

    public static void main(String[] args) {
       Cat cat = new Cat();
       Catagent catagent =  new Catagent(cat);
       catagent.run();
       catagent.eat();

    }
}

动态代理实现:

前面说了,静态代理要自己写,方法多了十分崩溃,因此我们通常使用动态代理,实现动态代理主要有两种方式,一种是基于JDK的动态代理,一种是基于CGLib的动态代理

动态代理除了目标类和代理类之外,还有中间类

基于JDK的动态代理

Java中为我们提供Proxy类的代理类,这个类中的有参构造是传递一个InvocationHandler类型的参数然后复制给属性h ,然后有一个newProxyInstance()这个方法,这个方法的参数是类加载器(通常用目标类加载器即可)、目标类的实现接口(为了让代理类和目标类的方法名保持一致)和InvocationHandler类型的参数(这个参数自己实现)

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

//定义接口
interface animal{
    void eat();
    void run();
}


public class agentTest {
    //目标类
    public static class Cat implements animal{

        //目标类实现接口中的方法
        @Override
        public void eat() {
            System.out.println("狗吃饭");
        }

        @Override
        public void run() {
            System.out.println("狗奔跑");
        }
    }

    public static void main(String[] args) {
        //代理类的字节码文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        animal target = new Cat();
        animal proxycat = (animal) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("现在是动态代理");
                method.invoke(target,args);
                return null;
            }
        });
        proxycat.run();
        proxycat.eat();
    }
}

代理的作用?

主要是可以实现解耦,可以避免我们直接与目标类接触,有利于目标类的扩展。很多类中都使用了代理,用户一般不会感知到。就好像一般取餐厅吃饭都会告诉服务员不要辣椒,而不会直接去找厨师说。

基于JDK的动态代理的缺点?

基于JDK的动态代理要目标类必须实现一个或几个接口,那么如果目标类没有实现接口咋办,就。。有点尴尬。所以就有了基于CGLib的动态代理,可以弥补基于JDK的动态代理目标类必须要实现一个接口的缺陷。

基于CGLib的动态代理实现:

CGLib动态代理的原理是,代理类会继承目标类,成为目标类的子类。每次去调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑。

来写个栗子。

首先,建一个maven项目,导入CGLib依赖

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

先写一个目标类:

//目标类
public class Dog {
        public void eat(){
            System.out.println("狗-----eat");
        }
        public void run(){
            System.out.println("狗-----run");
        }
}

再让代理类实现方法拦截器接口,实现代理的类:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//实现代理的类
//这个类要实现方法拦截器接口MethodInterceptor
//此接口中只有一个方法intercept,这个方法有四个参数,分别是
// 1 实现这个接口类的对象 2 被拦截的方法 3 被拦截方法的参数 4 要触发父类的方法对象


public class dogProxy implements MethodInterceptor {
    private Object target;
    public Object getInstance(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);

        return enhancer.create();
    }

   // @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("事务开始----");
        methodProxy.invokeSuper(o,objects);
        System.out.println("事务结束-----");
        return null;
    }
}

写个主函数测试一下:

//本质上,基于CGLib的动态代理的核心是enhancer类,这个类根据传递进去的参数来生成新的类的对象
public class cglibTest {
    public static void main(String[] args) {
        dogProxy cglib = new dogProxy();
        //getInstance()方法的作用是 1 设置enhancer对象的父类  2 设置enhancer的回调对象 3 创建代理对象
        Dog dog = (Dog)cglib.getInstance(new Dog());
        System.out.println(dog.getClass().getName());
        System.out.println(dog.getClass().getSuperclass().getName());
        dog.eat();
        dog.run();

    }
}

运行结果:
在这里插入图片描述

原理:当我们调用方法时,会先在代理类中判断是否实现了方法拦截器接口,如果没实现就直接调用目标类的方法,如果实现了,就会被方法拦截器拦截,在方法拦截器里面会对类的所有方法建立索引,相当于将每个方法的引用保存在数组中,这样我们就可以根据索引直接调用方法,而不是用反射。我们要添加的逻辑就添加在方法拦截器里面。

基于CGLib的动态代理缺点:

CGLib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明称final类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值