专栏名称: glmapper_2018
全栈工程师
目录
相关文章推荐
丁香医生  ·  涂了防晒到底要不要卸妆?一种类型必须卸 ·  21 小时前  
丁香医生  ·  减肥的人请注意,小心减出脂肪肝 ·  昨天  
丁香生活研究  ·  10 个伤牙行为,很多人天天做,测测你做过几个? ·  3 天前  
丁香生活研究  ·  低至 17.4 ... ·  4 天前  
51好读  ›  专栏  ›  glmapper_2018

《猪弟拱Java》连载番外篇:Java代理(中)

glmapper_2018  · 掘金  ·  · 2018-01-16 02:03

正文

请到「今天看啥」查看全文


Void run () { cons.setAccessible( true ); return null ; } }); } //调用构造器对象的方法生成代理类实例 return cons.newInstance( new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }

④ 最后就是实现我们的 invoke 方法了,先看一下方法的结构和我们的实现:

    /**
     * @param proxy  代理对象
     * @param method 执行的目标方法
     * @param args   方法参数
     * @return Object 目标方法执行的结果
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //目标方法的方法名
        String methodName = method.getName();
        //打印入参(前置通知)
        LOGGER.info("{} 方法的入参:{}", methodName, args);
        try {
            //反射Reflect执行核心方法
            Object result = method.invoke(realSubject, args);
            //打印执行结果(返回后通知)
            LOGGER.info("{}方法的返回结果为:{}", methodName,result);
            return result;
        } catch (Throwable e) {
            //打印异常日志(异常通知)
            LOGGER.error("执行目标方法发生异常,异常:", e);
            return Boolean.FALSE;
        } finally {
            //方法执行完打印(后置通知)
            LOGGER.info("方法执行完成");
        }
    }

是不是很简单呢,下面是入口程序:

package proxy;

/**
 * 程序入口
 *
 * @author 猪弟
 * @since v1.0.0
 */
public class Bootstrap {

    public static void main(String[] args){
        demo1();
    }

    public static void demo1(){
        ISmsSupport smsSupport = new SmsSupportImpl();
        ISmsSupport proxy = (ISmsSupport) new SmsProxy(smsSupport).getProxy();
        proxy.sendMsg("hello world", "110");
    }

}

我们先看一下运行结果 先把 SmsSupportImpl 中的 int i = 1/ 0; 注释掉正常运行:

17:12:26.976 [main] INFO proxy.SmsProxy - sendMsg 方法的入参:[hello world, 110]
hello world,110
17:12:26.980 [main] INFO proxy.SmsProxy - sendMsg方法的返回结果为:true
17:12:26.980 [main] INFO proxy.SmsProxy - 方法执行完成

再取消注释,发生异常:

17:13:31.169 [main] INFO proxy.SmsProxy - sendMsg 方法的入参:[hello world, 110]
17:13:31.175 [main] ERROR proxy.SmsProxy - 执行目标方法发生异常,异常:
java.lang.reflect.InvocationTargetException: null
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at proxy.SmsProxy.invoke(SmsProxy.java:63)
	at com.sun.proxy.$Proxy0.sendMsg(Unknown Source)
	at proxy.Bootstrap.demo1(Bootstrap.java:19)
	at proxy.Bootstrap.main(Bootstrap.java:12)
Caused by: java.lang.ArithmeticException: / by zero
	at proxy.SmsSupportImpl.sendMsg(SmsSupportImpl.java:13)
	... 8 common frames omitted
17:13:31.175 [main] INFO proxy.SmsProxy - 方法执行完成

从上面的运行结果可以看出,打印了入参和返回结果,正确拦截了异常并打印了异常信息,还有方法调用完成的日志。好啦!到这里动态代理的实现就结束了,接下来看看为什么要使用动态代理。

在这个案例中其实体现不出来动态代理的优势,为什么呢,因为要打印日志的类太少了,完全可以采用硬编码的方式去实现,那么想象一下有一万个类,每个类中有10个方法,每个方法之前都没有打日志,现在要你去实现为每个方法都实现入参和返回结果打印,你还会采用硬编码的方式吗?此时动态代理就发挥了作用,你只需要写一个日志代理类,专门用来完成这个打印功能,上代码:

package proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 日志代理打印类
 *
 * @author 猪弟
 * @since v1.0.0
 */
public class LogProxy<T> implements InvocationHandler {

    /**
     * 日志实例
     */
    private final static Logger LOGGER = LoggerFactory.getLogger(LogProxy.class);

    /**
     * 被代理对象
     */
    private T realSubject;

    /**
     * 构造器
     *
     * @param realSubject
     */
    public LogProxy(T realSubject) {
        this.realSubject = realSubject;
    }

    /**
     * 获取代理对象
     *
     * @return 代理
     */
    public T getProxy() {
        Class<?> subjectClass = realSubject.getClass();
        return (T) Proxy.newProxyInstance(subjectClass.getClassLoader(), subjectClass.getInterfaces(), this);
    }

    /**
     * @param proxy  代理对象
     * @param method 执行的目标方法
     * @param args   方法参数
     * @return Object 目标方法执行结果
     * @throws Throwable
     */
    @Override






请到「今天看啥」查看全文