Spring动态代理原理是什么?

动态代理的两种模式

  1.        jdk动态代理    //基于实现接口
  2.        Cglib动态代理   //基于方法的继承

????????啥东西

这就不得不提到 静态代理了: 说得高大上,其实就是写一个增强类继承被增强类,

实现被增强类的同时,也可以在其中添加一些代码,增强了被增强类

但问题是:静态代理想要再增强,无非就是修改代码;或者再写一个类去增强那个增强类。

在实际开发中,这是一种很蠢的行为;跟套娃一样不说,代码冗余还越来越多。

动态代理

Spring提供了完美的动态代理,我们来看看其原理:

代码:jdk动态代理 :针对实现接口的类进行增强。通过被代理对象实现的接口产生其代理对象的

public interface Subject { //公共接口
    void buyHouse();
}
public class RealSubject implements Subject{
    @Override
    public void buyHouse() {
        System.out.println("买房子");
    }
}

买房接口和买家买房实现类

jdk动态代理类:

public class JDKProxyFactory { //jdk动态代理工厂

    public static Object getProxyObj(Object obj){ //目标对象


        /**
         * ClassLoader loader,       类加载
         * Class<?>[] interfaces,    目标对象实现接口
         * InvocationHandler h       调用拦截器
         */

        //获取类加载器
        ClassLoader loader = obj.getClass().getClassLoader();
        //获取目标对象实现的接口
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        //定义调用拦截器
        InvocationHandler handler = new InvocationHandler() {
            //当目标对象的方法调用的时候会回调这个方法

            /**
             *
             * @param proxy     代理对象
             * @param method    目标对象的方法
             * @param args      目标对象的参数
             * @return
             * @throws Throwable
             */
            @Override
public Object invoke(Object proxy, Method method, Object[] args)
 throws Throwable {
                System.out.println("找房源....");
                //调用目标对象的方法!!!!
                //参数1:类的对象  参数2:方法的参数
                Object invoke = method.invoke(obj, args);
                System.out.println("办理手续....");
                return invoke;  //这里的返回值就是目标对象的返回值
            }
        };
Object proxyObj = Proxy.newProxyInstance(loader, interfaces, handler);
        return proxyObj;
    }

代码解析:编写一个getProxyObj供外界调用 

1.通过反射获取类加载器   //对于java反射方面的知识必须了解不然无法学习动态代理

2.获取目标对象实现的接口

3.定义拦截器,当调用买房子方法时就会拦截下来才能进行增强

4.在拦截器重写invoke方法。核心就是一个 invoke(proxy,method,args)方法

//代理的对象  目标对象的方法  目标对象的参数

5.在代理方法里进行原方法的增强。

System.out.println("找房源....");
//调用目标对象的方法
//参数1:类的对象  参数2:方法的参数
Object invoke = method.invoke(obj, args);
System.out.println("办理手续....");
return invoke;  //这里的返回值就是目标对象的返回值

解析:为什么method就是被增强方法呢?为啥调用invoke就进行了增强呢?

测试类: 

public class JDKProxyTest {
    @Test
    public void test01(){
        //创建目标对象
        Subject realSubject = new RealSubject();
        //realSubject.buyHouse();

        //通过代理工厂创建代理对象
        Subject proxyObj = (Subject) JDKProxyFactory.getProxyObj(realSubject);
        //把方法传给代理类
        proxyObj.buyHouse();
        //通过代理类调用实际上调用的是invoke()

        //代理对象和目标对象的关系?    共同实现了同一个接口(兄弟)
        System.out.println(proxyObj instanceof  Subject);//true
        System.out.println(proxyObj instanceof  RealSubject);//false


    }

关键问题:为什么method就是被增强方法呢?为啥调用invoke就进行了增强呢?为什么通过代理类调用方法时会被拦截并且进入invoke()方法?

1:因为将对象交由jdk动态代理了,其通过反射可以获取到类的所有信息;通过增强的对象调用原有的方法,不会执行原有的方法,而是执行代理对象的方法。

2:而代理类调用拦截器拦截方法 InvocationHandler,将增强方法对象调用的原有方法的操作拦截下来,并进行增强。InvocationHandler 重写了invoke()所以被拦截后一定会执行invoke()方法

这是InvocationHandler方法说明

处理代理实例上的方法调用并返回结果。

当在与该方法关联的代理实例上调用该方法时,将在调用处理程序上调用该方法。

参数:proxy——在方法上调用方法的代理实例——在代理实例上调用的接口方法对应的方法实例。

方法对象的声明类将是在其中声明方法的接口,它可能是代理类继承该方法的代理接口的超接口。

Args—包含代理实例的方法调用中传递的参数值的对象数组,如果接口方法不接受参数,则为null。基元类型的参数被包装在适当的基元包装类的实例中,比如java.Lang。整数或. lang。布尔。

返回:从代理实例的方法调用中返回的值。如果接口方法声明的返回类型是基元类型,则该方法返回的值必须是相应基元包装类的一个实例;否则,它必须是可分配给声明的返回类型的类型。如果该方法返回的值为null,且接口方法的返回类型为原语,则代理实例上的方法调用将抛出NullPointerException。如果这个方法返回的值与上面描述的接口方法声明的返回类型不兼容,那么代理实例上的方法调用将抛出一个ClassCastException。

invoke(Object proxy, Method method, Object[] args) //这三个参数也是通过反射获取到的。

(通过反射获取的方法实例,方法接口对应的方法实例,对象方法的参数数组)

3.Object invoke = method.invoke(obj, args) (类对象的实例,方法的参数数组)

底层调用了native方法:可以看出:返回方法有三个参数

 public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        if (++this.numInvocations > ReflectionFactory.inflationThreshold()
 && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) 
{
            MethodAccessorImpl var3 = (MethodAccessorImpl)(
new MethodAccessorGenerator()).generateMethod
(this.method.getDeclaringClass(), this.method.getName(),
 this.method.getParameterTypes(), this.method.getReturnType(), 
this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }

        return invoke0(this.method, var1, var2);
    }
Object proxyObj = Proxy.newProxyInstance(loader, interfaces, handler)将类加载器,接口参数,拦截器(方法增强后)  一并返回给调用增强方法的类
System.out.println(proxyObj instanceof  Subject);//true
System.out.println(proxyObj instanceof  RealSubject);//false

Cglib动态代理:

流程与JDK动态代理流程相似

不同点:cglib是继承方法对其进行增强

需要导入jar包 在spring-context 里包含了

public class CglibProxyFactory {
    public static Object getProxyObj(Object obj){
        //增强对象
        Enhancer enhancer = new Enhancer();
        //设置目标对象   (代理对象)
        enhancer.setSuperclass(obj.getClass());
        //代理的时候对方法进行拦截
        enhancer.setCallback(new MethodInterceptor() {
            /**
             *
             * @param o            代理对象
             * @param method       目标对象的方法
             * @param objects      目标对象的方法的参数
             * @param methodProxy  目标对象的方法的代理对象(父类是接口)
             * @return
             * @throws Throwable
             */
            @Override
public Object intercept(Object o, Method method, Object[] objects,
 MethodProxy methodProxy) throws Throwable {
                System.out.println("贴广告");
                Object invoke = method.invoke(obj, objects);
                System.out.println("签合同");
                return invoke;
            }
        });
        Object proxyObj = enhancer.create();
        return proxyObj;
    }

@param methodProxy:针对父类是接口的情况,因为不能getsuperclass呀

测试类:

    @Test
    public void test01(){
        //创建目标对象
        RealSubject realSubject = new RealSubject();

        //通过代理工厂创建代理对象
RealSubject proxyObj = (RealSubject) CglibProxyFactory.getProxyObj
(realSubject);
 proxyObj.zuHouse();


        //目标对象和代理对象的关系    继承(子类)
        System.out.println(proxyObj.getClass());
        System.out.println(proxyObj instanceof RealSubject);

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值