使用JDK与Cglib动态代理技术统一管理日志记录

Java中动态代理主要有JDK和CGLIB两种方式。

区别主要是jdk是代理接口,而cglib是代理类。

  • 优点:这种方式已经解决我们前面所有日记需要的问题。非常的灵活。而且可以方便的在后期进行维护和升级。
  • 缺点:当然使用jdk动态代理,必需要有接口。如果没有接口。就无法使用jdk动态代理技术。

计算接口 Calculate.java

public interface Calculate {
    /**
     * 加法运算
     * @param num1  参数 1
     * @param num2  参数 2
     * @return
     */
    public int add(int num1, int num2);

    /**
     * 加法运算
     * @param num1  参数 1
     * @param num2  参数 2
     * @param num3  参数 3
     * @return
     */
    public int add(int num1, int num2, int num3);

    /**
     * 除法运算
     * @param num1  参数 1
     * @param num2  参数 2
     * @return
     */
    public int div(int num1, int num2);
}

注意:光理论是不够的,在此送大家十套2020最新Java架构实战教程 + 大厂面试题库,进裙: 783802103 裙文件自行获取一起交流进步哦!
实现计算接口中的方法 CalculateImpl.java

/**
 * 实现计算接口中的方法
 * Created by YongXin Xue on 2020/05/05 11:29
 */
public class CalculateImpl implements Calculate {
    @Override
    public int add(int num1, int num2) {
        // 记录当前操作,及运算参数
        LogUtils.logBefore("add", num1, num2);
        int result = num1 + num2;
        return result;
    }

    @Override
    public int add(int num1, int num2, int num3) {
        // 记录当前操作,及运算参数
        LogUtils.logBefore("add", num1, num2, num3);
        int result = num1 + num2 + num3;
        return result;
    }

    @Override
    public int div(int num1, int num2) {
        // 记录当前操作,及运算参数
        LogUtils.logBefore("div", num1, num2);
        int result = 0;
        try {
            result = num1 / num2;
            // 记录运算结果
            LogUtils.logAfterReturning("div", result);
        }catch (Exception e){
            // 记录异常信息
            LogUtils.logAfterThrowing("div", e);
        }
        return result;
    }
}

记录日志工具类 LogUtils.java

/**
 * 记录日志工具类
 * Created by YongXin Xue on 2020/05/05 11:38
 */
public class LogUtils {
    /**
     * 记录前置的日志操作
     * @param method  当前运算操作
     * @param args  当前运算参数
     */
    public static void logBefore(String method, Object ... args){
        System.out.println("操作运算是 : " + method + " 参数是 : " + Arrays.asList(args));
    }

    /**
     * 返回日志操作
     * @param method    当前方法
     * @param result    当前操作返回值
     */
    public static void logAfterReturning(String method, Object result){
        System.out.println("当前操作运算时 : " + method  + " 返回值是 : " + result);
    }

    /**
     * 当前操作产生的异常
     * @param method    当前操作
     * @param e  发生的异常
     */
    public static void logAfterThrowing(String method, Exception e){
        System.out.println("当前运算时 : " + method + " 发生的异常是 : " + e);
    }
}

JDK 动态代理的工厂类 JDKProxyFactory.java


/**
 * JDK 动态代理的工厂
 * Created by YongXin Xue on 2020/05/05 13:02
 */
public class JDKProxyFactory {

    /**
     * 通过 JDK 底层自带的 JDK 动态代理技术解决日志需求问题
     * @param target
     * @return
     */
    public static Object createJDKProxy(Object target){
        /**
         * Proxy 是Jdk中自带的一个工具类(反射包下,属于反射的功能).
         * Proxy类的作用: 它可以帮我们创建代理类或实例
         * 方法newProxyInstance()说明: 创建代理对象实例
         * 第一个参数是: 目标对象的类加载器
         * 第二个参数是: 目标对象实现的所有接口
         * 第三个参数是: InvocationHandler  接口的实例
         * InvocationHandler 接口的实现类可以对代理的目标对象方法进行增强操作.
         * 代理的目标对象 ===>>>  需要额外增加功能的类(对象实例)
         * 增强操作    ===>>> 给原来功能添加的额外功能叫增强操作 ( 日记就是增强操作 )
         */
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {   // 匿名内部类
                    /**
                     * invoke 方法是 InvocationHandler 接口中唯一的方法
                     * 代理对象每次调用方法时,都会执行 invoke() 方法 , 所有的增强操作都需要在invoke()方法中完成
                     * @param proxy     代理对象实例
                     * @param method    代理调用的方法的反射 Method 对象实例
                     * @param args      调用代理方法时传递进来的参数
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("代理调用了 invoke 方法 ");
                        System.out.println(method);     //打印方法信息
                        System.out.println(Arrays.asList(args));    //打印参数信息
                        // invoke() 方法执行代理对象的(加法 / 除法 / 增强日志)操作
                        Object result = null;
                        LogUtils.logBefore(method.getName(), args);
                        try {
                            // 1. 返回值是 method 方法调用时的返回值
                            result = method.invoke(target, args);
                            // 2. 增强操作
                            LogUtils.logAfterReturning(method.getName(), result);
                        }catch (Exception e){
                            LogUtils.logAfterThrowing(method.getName(), e);
                        }
                        // invoke() 返回代理方法的返回值
                        return result;
                    }
                });
    }

	// 测试代码
    public static void main(String[] args) {
        // 目标对象
        Calculate target  = new CalculateImpl();
        // 创建 Calculate 的代理对象实例
        Calculate calculateProxy  = (Calculate) createJDKProxy(target );
        // jdk动态代理对象实例和目标对象实例 同宗同族 ( 他们都实现了相同的接口 )
        System.out.println(calculateProxy  instanceof Calculate);
        System.out.println(target  instanceof Calculate);

        System.out.println( "代理方法的结果是 : " +  calculateProxy.div(100,20) );

        // jdk动态代理创建出来的代理对象实例 是 目标对象 接口的一个实现类
        // 这个代理对象 和 目标对象类没有父子关系 ( 只能用接口接收代理对象 )
    }
}

使用 Cglib 代理

  1. Jdk动态代理是通过实现目标对象所有接口产生一个代理对象实例从而解决问题.
  2. 如果目标对象没有接口.则可以使用Cglib动态代理技术.
  3. Cglib动态代理技术对目标对象有没有实现接口,没有要求.
  4. Cglib动态代理技术,是通过拷贝然后修改目标对象的类的字节码来产生一个代理对象
  5. 而且这个Cglib产生的代理对象实例 是 目标对象的一个子类.

IA 接口 IA.java

public interface IA {
    public String show(String start);
}

IA 实现类 IAImpl.java

public class IAImpl implements IA {
    @Override
    public String show(String start) {
        System.out.println(start + "开始表演!");
        return start + "表演的不错!!";
    }
}

使用 Cglib 代理 CglibProxyFactory.java


/**
 * 使用 Cglib 代理
 * Created by YongXin Xue on 2020/05/05 15:03
 */
public class CglibProxyFactory {

    public static Object createCglibProxy(Object target){
        // 是 Cglib 用于创建代理对象的增强工具类
        Enhancer enhancer = new Enhancer();
        // Cglib需要对目标对象的Class字节码进行修改.
        // Cglib产生的代理对象实例.是目标对象的子类
        enhancer.setSuperclass(target.getClass());
        // 只要是代理都会对原来的内容进行增强操作 ( 增强就是在原有功能上 额外添加的功能 )
        // setCallback() 设置用于增强 操作的实现类( MethodInterceptor对代理方法进行拦截 )
        // 每次只要调用Cglib代理的方法,都会执行 MethodInterceptor 接口中 intercept() 方法
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * intercept() 方法 跟 JDK 代理中的 InvocationHandler接口中 invoke() 功能完全一样
             * @param proxy     Cglib代理对象实例
             * @param method    调用方法的反射对象实例
             * @param args   调用方法时传递的参数
             * @param methodProxy   代理方法的method代理对象
             * @return      是代理对象方法的返回值.
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                try {
                    LogUtils.logBefore(method.getName(), args);
                    // 调用目标方法 [加 / 减 / 乘 / 除 / 或具体方法]
                    result = method.invoke(target, args);
                    // 执行增强代码
                    LogUtils.logAfterReturning(method.getName(), args);
                }catch (Exception e){
                    e.printStackTrace();
                    LogUtils.logAfterThrowing(method.getName(), e);
                }

                return result;
            }
        });
        // 创建 Cglib 代理对象实例
        return enhancer.create();
    }
	//测试
    public static void main(String[] args) {
        // 目标对象
        Calculate calculate = new CalculateImpl();
        // 创建代理对象实例
        Calculate cglibProxy = (Calculate) createCglibProxy(calculate);
        // 调用代理方法式会执行 MethodInterceptor 接口中 intercept() 方法
        int result = cglibProxy.div(120, 0);
        // Cglib 代理 是目标子类执行 MethodInterceptor 接口中 intercept() 方法
        System.out.println(cglibProxy instanceof Calculate);
    }
}

  • 优点:在没有接口的情况下,同样可以实现代理的效果。
  • 缺点:同样需要自己编码实现代理全部过程。

注意:最后送大家是十套2020最新Java架构实战教程 + 大厂面试题库,进裙: 783802103 裙文件自行获取,一起交流进步哦!

原创文章 42 获赞 280 访问量 5万+

猜你喜欢

转载自blog.csdn.net/Lance_welcome/article/details/105933381
今日推荐