JDK动态代理 cgLib动态代理 java agent

本文详细介绍了Java动态代理的实现,包括JDK动态代理和CGLIB动态代理。JDK动态代理基于接口生成代理对象,而CGLIB则可以直接代理无参构造或有参构造的类。此外,还简要提及了JAVAAGENT,这是一种在类加载前进行拦截的技术,涉及字节码修改。
摘要由CSDN通过智能技术生成

一丶JDK动态代理

/**
 * 被代理接口(JDK动态代理必须有接口)
 *
 * @author hujun
 * @date 2021/09/11
 */
public interface ProxiedInterface {

    void sayHello();

    void sayBye();
}

/**
 * 需要代理的实现类
 *
 * @author hujun
 * @date 2021/09/11
 */
public class ProxyInterfaceRealize implements ProxiedInterface {
    @Override
    public void sayHello() {
        System.out.println("hello word");
    }

    @Override
    public void sayBye() {
        System.out.println("bye");
    }
}

/**
 * 获取代理对象
 *
 * @author hujun
 * @date 2021/09/11
 */
public class JDKProxyFactory {

    /**
     * 根据接口生成代理对象
     */
    public static <T> T getProxyInterfaceObject(Class<T> proxyInterface) {
        if (!proxyInterface.isInterface()) {
            throw new RuntimeException("no interface");
        }
        // 创建代理对象 类加载器 需要代理的接口的class InvocationHandler对象(具体的代理逻辑)
        Object proxyInstance = Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[]{proxyInterface}, (proxy, method, args) -> {
            // proxy:代理对象 method:执行的方法对象 args:方法参数
            if (method.getDeclaringClass() != proxyInterface) {
                throw new NoSuchMethodException();
            }
            // 代理的只是接口 没有具体的实现类 根据method自定义实现
            if (method.getName().equals("sayHello")) {
                System.out.println("hello word");
            } else {
                System.out.println("bye");
            }
            return null;
        });
        return (T) proxyInstance;
    }

    /**
     * 代理接口的具体实现类的实例对象
     */
    public static <T> T getProxyObject(Object proxyObject) {
        Class<?> proxyClass = proxyObject.getClass();
        Class<?>[] interfaces = proxyClass.getInterfaces();
        if (interfaces.length < 1) {
            throw new RuntimeException("no interface");
        }
        Object proxyInstance = Proxy.newProxyInstance(proxyClass.getClassLoader(), interfaces, new ProxyInvocationHandler(proxyObject));
        return (T) proxyInstance;
    }

    /**
     * InvocationHandler的实现类
     */
    @RequiredArgsConstructor
    static class ProxyInvocationHandler implements InvocationHandler {

        /**
         * 被代理的对象
         */
        private final Object target;

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getDeclaringClass().equals(Object.class)) {
                return method.invoke(target, args);
            }
            long now = System.currentTimeMillis();
            // 执行被代理对象的方法
            Object result = method.invoke(target, args);
            System.out.println("消耗时间:" + (System.currentTimeMillis() - now));
            return result;
        }
    }
}

public static void main(String[] args) {
        // 根据接口生成代理对象
        ProxiedInterface proxiedInterface = JDKProxyFactory.getProxyInterfaceObject(ProxiedInterface.class);
        proxiedInterface.sayHello();
        proxiedInterface.sayBye();

        // 根据接口的实现类生成代理对象
        ProxiedInterface proxyObject = JDKProxyFactory.getProxyObject(new ProxyInterfaceRealize());
        proxyObject.sayHello();
        proxyObject.sayBye();
    }

二丶CGLIB动态代理

		<!-- cglib依赖 -->
		<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
        <!-- 不需要构成方法生成对象 -->
        <dependency>
            <groupId>org.objenesis</groupId>
            <artifactId>objenesis</artifactId>
            <version>3.2</version>
        </dependency>
/**
 * 无参构造需要代理的对象
 *
 * @author hujun
 * @date 2021/09/11
 */
public class ByProxyClass {

    public void sayHello() {
        System.out.println("hello word");
    }

}

/**
 * 有参构造需要的代理对象
 *
 * @author hujun
 * @date 2021/09/11
 */
@RequiredArgsConstructor
public class ProxyNoDefaultConstruction {

    private final String message;

    public void sayMessage() {
        System.out.println(message);
    }
}

/**
 * 获取代理对象
 *
 * @author hujun
 * @date 2021/09/11
 */
public class CglibProxyFactory {

    /**
     * 通过构造方法创建代理对象
     */
    public static <T> T getProxyObjectByParams(Class<T> byProxyClass, Object... params) {
        // cglib用来增强被代理对象的类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(byProxyClass);
        // 设置代理拦截
        enhancer.setCallback(new ProxyMethodHandle());
        Class<?>[] paramsType = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            paramsType[i] = params[i].getClass();
        }
        // 创建代理对象
        return (T) enhancer.create(paramsType, params);
    }

    /**
     * 也可以直接代理接口
     */
    public static <T> T getProxyObjectByInterface(Class<T> byProxyInterface) {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(new InterfaceHandle());
        enhancer.setInterfaces(new Class[]{byProxyInterface});
        return (T) enhancer.create();
    }

    /**
     * 当你拥有一个实例对象后,对实例对象的代理一般都会使用组合的方式,增强方法的行为写在代理拦截中,实际执行的方法是该实例对象的方法
     * 这在实例对象拥有无参构造的时候,可以直接创建一个代理对象,而实例对象只有有参构造的情况下,使用cglib创建代理对象也需要执行这个有参
     * 构造,这在已经拥有实例对象的情况下,还执行有参构造没有多大意义,代理对象通常是不需要父类的属性的
     * 所以代理实例对象时,采用Objenesis来生成代理对象,这个可以在没有无参构造的情况下创建对象
     */
    public static <T> T getProxyObjectNoDefaultConstructor(T obj) {
        Enhancer enhancer = new Enhancer();
        // 是否使用factory factory是cglib的一个接口,会默认实现这个接口的方法
        enhancer.setUseFactory(false);
        // 设置父类
        enhancer.setSuperclass(obj.getClass());
        // 设置代理逻辑的类型(因为是通过cglib创建Class对象,创建Class对象不能直接设置代理拦截的逻辑)
        enhancer.setCallbackType(InvocationHandler.class);
        // 创建一个代理Class对象
        Class proxyClass = enhancer.createClass();
        // 注册这个代理对象的拦截
        Enhancer.registerStaticCallbacks(proxyClass, new ProxyTargetHandle[]{new ProxyTargetHandle(obj)});
        // 可以根据Class对象创建对象实例 而不通过构造方法
        Objenesis objenesis = new ObjenesisStd();
        return (T) objenesis.newInstance(proxyClass);
    }

    /**
     * 直接代理接口的InvocationHandler
     */
    static class InterfaceHandle implements InvocationHandler {

        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            // 因为只代理了接口,没有具体实现,只能有invoke实现逻辑
            if (method.getName().equals("sayHello")) {
                System.out.println("hello word");
            } else {
                System.out.println("bye");
            }
            return null;
        }
    }

    /**
     * 代理方法的实现
     */
    static class ProxyMethodHandle implements MethodInterceptor {

        /**
         * @param o           代理对象
         * @param method      执行的方法
         * @param objects     方法参数
         * @param methodProxy 方法代理
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            // 使用代理对象直接执行method时,会重新进入代理逻辑,造成递归
            long now = System.currentTimeMillis();
            // methodProxy可以直接调用父类方法
            Object res = methodProxy.invokeSuper(o, objects);
            System.out.println("消耗时间:" + (System.currentTimeMillis() - now));
            return res;
        }
    }

    /**
     * 代理实例对象的拦截
     */
    @RequiredArgsConstructor
    static class ProxyTargetHandle implements InvocationHandler {

        /**
         * 代理的实例
         */
        private final Object target;

        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            long now = System.currentTimeMillis();
            // 调用代理实例的具体方法,这个方法前后就可以实现对应的代理逻辑
            Object result = method.invoke(target, objects);
            System.out.println("消耗时间:" + (System.currentTimeMillis() - now));
            return result;
        }
    }
}

public static void main(String[] args) {
        // cglib代理接口
        ProxiedInterface object = CglibProxyFactory.getProxyObjectByInterface(ProxiedInterface.class);
        object.sayBye();
        object.sayHello();

        // 代理无参构造类
        ByProxyClass proxyObject = CglibProxyFactory.getProxyObjectByParams(ByProxyClass.class);
        proxyObject.sayHello();

        // 代理有参构造类
        ProxyNoDefaultConstruction proxyObjectByParams = CglibProxyFactory.getProxyObjectByParams(
                ProxyNoDefaultConstruction.class, "hello message");
        proxyObjectByParams.sayMessage();

        // 代理有参构造 代理类不进行构造函数初始化 使用对象组合的形式
        ProxyNoDefaultConstruction helloProxy = CglibProxyFactory.getProxyObjectNoDefaultConstructor(
                new ProxyNoDefaultConstruction("hello proxy"));
        helloProxy.sayMessage();
    }

三丶JAVA agent

java提供的通过指定jar包的形式在类加载之前进行拦截,需要加拦截的话,会设计字节码的修改(以后在学习这方面的东西,现在先简单了解)

	首先需要有一个拦截的jar包
	/**
     * 必须是静态方法,且方法名必须为premain
     * agentOps:命令行传入的参数
     * instrumentation:对类可以进行操作的类,这个方法可以没有这个参数,也就是可以有premain(String) premain(String,Instrumentation)
     * 这两种方法,两个同时存在的时候,后者优先
     * 这个是在应用的启动前执行
     */
    public static void premain(String agentOps, Instrumentation instrumentation) {
        System.out.println("开始agent" + agentOps);
        // 获取所有加载的class
        Class<?>[] classes = instrumentation.getAllLoadedClasses();
        for (Class<?> cls : classes) {
            System.out.println("agent main loaded class: " + cls.getName());
        }
        // 自定义的转换 实现ClassFileTransformer接口
        instrumentation.addTransformer(new MyClassTransformer());
    }
	/**
     * 基本同premain方法 这个可以在应用运行的时候使用,可以做到修改类
     */
    public static void agentmain(String agentArgs, Instrumentation instrumentation) {
        System.out.println("agent main start: " + agentArgs);
        Class<?>[] classes = instrumentation.getAllLoadedClasses();
        for (Class<?> cls : classes) {
            System.out.println("agent main loaded class: " + cls.getName());
        }
        instrumentation.addTransformer(new MyClassTransformer());
    }

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <!-- 使用maven指定premain-class和agent-class 然后可以将这个打成jar包 这两个只需要有其中一个即可 -->
                            <premain-class>com.demo.AgentDemo</premain-class>
                            <agent-class>com.demo.AgentMainDemo</agent-class>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

在这里插入图片描述
运行的main方法增加-javaagent:(对应jar文件的路径)\demo.jar=arg,=后面跟的是premain或agentMain第一个string参数

public static void main(String[] args) throws Exception {
        // 列出当前运行的虚拟机 tools.jar包中的VirtualMachine
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        String mainClass = "proxy.agent.JavaAgentTest";
        String agentPath = "D:\\..\\demo.jar";
        for (VirtualMachineDescriptor vmd : list) {
            // 虚拟机显示的名字是指定mainClass的时候
            if (vmd.displayName().equals(mainClass)) {
                // attach对应的虚拟机,然后指定对应的agent jar包就会执行里面的agentmain方法
                VirtualMachine attach = VirtualMachine.attach(vmd.id());
                attach.loadAgent(agentPath);
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dichotomy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值