动态代理

Posted liqiliang1437

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理相关的知识,希望对你有一定的参考价值。

动态代理

一 , 动态代理概述

? 代理类在程序运行时创建的方式被成为动态代理。也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

二 , 动态代理分类

jdk动态代理 和 cglib动态代理

三 , jdk动态代理

jdk动态代理:必须基于接口

java.lang.reflect.Proxy:

? Java动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其实例。

//方法1: 该方法用于获取指定动态代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
 
//方法2:该方法用于获取关联于指定类装载器和一组接口的动态代理对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
 
//方法3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
 
//方法4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理对象
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

java.lang.reflect.InvocationHandler:

调用处理器接口,它自定义了一个invoke方法,用于集中处理在动态代理对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个实现了该接口的调用处理器对象。

InvocationHandler的核心方法:

//该方法负责集中处理动态代理类上的所有方法调用。
//第一个参数是代理对象,第二个参数是被调用的方法对象,第三个方法是调用参数。
//调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行。
Object invoke(Object proxy, Method method, Object[] args)

1.代码演示

User接口

package com.liqiliang.Proxy;

public interface User {
    void add();
    void delete();
    void update();
    void select();
}

UserImpl实现类

package com.liqiliang.Proxy;

public class UserImpl implements User {
    @Override
    public void add() {
        System.out.println("增");
    }

    @Override
    public void delete() {
        System.out.println("删");
    }

    @Override
    public void update() {
        System.out.println("改");
    }

    @Override
    public void select() {
        System.out.println("查");
    }
}

MyProxyTest测试类

package com.liqiliang.Proxy;

import org.junit.Test;

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

public class MyProxyTest {
        private User user = new UserImpl();
    @Test
    public void test01(){
        user.add();
        user.delete();
        user.update();
        user.select();
        System.out.println("================");

        User proxy = (User) Proxy.newProxyInstance(user.getClass().getClassLoader(),
                user.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("A");
                        Object invoke = method.invoke(user, args);
                        System.out.println("B");
                        return invoke;
                    }
                });
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.select();
    }
}

2.小结

1、JDK动态代理:基于接口的;
2、JDK动态代理实现要点:
		Proxy类
		newProxyInstance静态方法
		InvocationHandler增强方法

四 , cglib动态代理

cglib动态代理 : 不基于接口

1.思考

如果目标类没有实现接口呢?
	那么就无法使用JDK的动态代理,因此这种方式有其局限性,必须实现一个接口。
可以使用的方案:
	使用CGLIB动态代理

net.sf.cglib.proxy.Enhancer

Enhancer类是CGLib中的一个字节码增强器,作用用于生成代理对象,跟上一章所学的Proxy类相似,常用方式为:

//方法1:该方法用于为指定目标类、回调对象
 public static Object create(Class type, Callback callback)

net.sf.cglib.proxy.MethodInterceptor

//方法1:
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;

2.代码演示

导入gclib依赖

<!--cglib依赖-->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

UserImpl

package com.liqiliang.Proxy;

public class UserImpl{

    public void add() {
        System.out.println("增");
    }


    public void delete() {
        System.out.println("删");
    }


    public void update() {
        System.out.println("改");
    }


    public void select() {
        System.out.println("查");
    }
}

MyProxyTest测试类

package com.liqiliang.Proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

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

public class MyProxyTest {
        private UserImpl user = new UserImpl();
    @Test
    public void test01(){
        user.add();
        user.delete();
        user.update();
        user.select();
        System.out.println("================");

        /*
        create(Class type, Callback callback)
        第一个参数:Class type,代理类的类型 --> user.getClass(),不需要子类继承代理类
                  或者 代理类的子类 --> UserProxy.class,需要一个子类来继承代理类
        第二个参数:构建方法拦截器,拦截
         */
        UserImpl proxy = (UserImpl) new Enhancer().create(
//              user.getClass(),
                UserProxy.class,
                new MethodInterceptor() {
            @Override
            public Object intercept(
                    Object o,
                    Method method,
                    Object[] objects,
                    MethodProxy methodProxy) throws Throwable {
                System.out.println("前增强!");
                Object invoke = method.invoke(user, objects);
                System.out.println("后增强!");
                return invoke;
            }
        });
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.select();
    }
}

UserProxy 继承代理类的子类(可以不写)

package com.liqiliang.Proxy;

public class UserProxy extends UserImpl {
}

3.小结

1、Cglib动态代理:基于类,无需实现接口;
2、被代理的目标类不能被final修饰

五 , 动态代理小结

通过动态代理可以完成对已有方法的功能的增强:
1、JDK动态代理
	要求:
		被代理对象至少实现一个接口
	应用场景:
		被代理对象有接口

2、CGLIB动态代理
	要求:
		被代理类上不能用static、final修饰
	应用场景:
		被代理对象没有实现接口

以上是关于动态代理的主要内容,如果未能解决你的问题,请参考以下文章

动态 Rstudio 代码片段

是否可以动态编译和执行 C# 代码片段?

支持动态或静态片段的不同屏幕尺寸?

Forge Viewer - 如何在场景中访问(或获取渲染/片段代理)克隆的网格?

在ansible模板中使用动态组名称

代理模式(动态)