为了账号安全,请及时绑定邮箱和手机立即绑定

Java 基础【19】代理

标签:
Java

 Java 代理(Proxy)模式与现实中的代理含义一致,如旅游代理、明星的经纪人。

   在目标对象实现基础上,增加额外的功能操作,由此来扩展目标对象的功能。

   JavaWeb 中最常见的过滤器、Struts 中的拦截器、Spring 中的 AOP...都有代理的应用。

   此篇博客将编写例子描述 Java 底层技术和开源类库Cglib实现代理的方法,并对比各方法的优缺性。

回到顶部

1.JDK 静态代理

   抽象接口:

复制代码

/**
 * 用户服务抽象 */public interface UserService {    /**
     * 用户登录
     *
     * @param userName 用户名
     * @param pwd      密码
     * @return 登陆结果     */
    String login(String userName, String pwd);
}

复制代码

   实现该接口:

复制代码

/**
 * 用户服务实现
 *
 * @author Rambo 2019-03-01
 **/public class UserServiceImpl implements UserService {


    @Override    public String login(String userName, String pwd) {
        Console.log("进行登陆逻辑.........");        return "登陆结果";
    }
}

复制代码

   编码代理类,实现该接口,代理目标作为私有对象:

复制代码

/**
 * 用户服务代理类
 *
 * @author Rambo 2019-03-01
 **/public class UserServiceProxy implements UserService {    private UserService userService;

    UserServiceProxy(UserService userService) {        this.userService = userService;
    }

    @Override    public String login(String userName, String pwd) {
        Console.log("登陆前扩展.....");
        userService.login(userName, pwd);
        Console.log("登陆后扩展.....");        return "登陆结果";
    }
}

复制代码

   编写测试类:

    @Test    public void testLogin() throws Exception {
        UserServiceProxy userServiceProxy = new UserServiceProxy(new UserServiceImpl());
        userServiceProxy.login("rambo","111111");
    }

   这应该是最原始实现代理的样子,缺点也很明显,需要编码代理类,势必导致代理类冗余,且当目标类增加或删除方法时,需要维护代理类。   

回到顶部

2.JDK 动态代理(接口代理)

   代理核心方法 Proxy.newProxyInstance :

复制代码

    /**
     * JDK 生成代理类
     * @param loader 当前目标对象使用类加载器
     * @param interfaces 目标对象实现的接口的类型
     * @param h 事件处理对象,通过反射执行目标对象的方法
     * @return 生成的代理类实例     */
    @CallerSensitive    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)

复制代码

   代理工厂类:

复制代码

/**
 * 代理工厂类
 *
 * @author Rambo 2019-03-01
 **/public class JdkProxyFactory {    private Object target;    public JdkProxyFactory(Object target) {        this.target = target;
    }    public Object getProxyInstance() {        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Console.log("执行目标前的扩展......");
                Object returnValue = method.invoke(target, args);
                Console.log("执行目标后的扩展......");                return returnValue;
            }
        });
    }
}

复制代码

   编写测试类:

    @Test    public void testGetProxyInstance() throws Exception {
        UserService proxyInstance = (UserService) new ProxyFactory(new UserServiceImpl()).getProxyInstance();
        proxyInstance.login("rambo", "111111");
    }

   目标对象需要实现接口,代理对象是可以不用实现接口的。

   使用目标对象接口、自定义调用处理类 InvocationHandler 实例化代理类,内部通过反射调用目标对象的方法。

回到顶部

3.Cglib 代理 (子类代理)

   当目标对象是个单独的类,没有实现任何接口,是无法使用上述两种代理方法,这时候怎么办?

   可以使用 Cglib 代理(需要单独引入 cglib 类库),自定义目标类的子类进行目标对象的扩展,且这种扩展进行在 Jvm 运行期。

   Cglib 底层是通过一个小而快的字节码处理框架 Asm 来转换字节码并生成新的类。

   不局限目标类存在方式、运行期增强目标类、底层精致的 asm 字节码框架,使 Cglib 成为许多 AOP 框架生成动态代理的首选。

   代理工厂类:

复制代码

/**
 * Cglib 动态代理工厂
 *
 * @author Rambo 2019-03-01
 **/public class CgbProxyFactory implements MethodInterceptor {    private Object target;    public CgbProxyFactory(Object target) {        this.target = target;
    }    public Object getProxyInstance() {
        Enhancer en = new Enhancer();
        en.setSuperclass(target.getClass());
        en.setCallback(this);        return en.create();

    }

    @Override    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Console.log("执行目标前的扩展......");
        Object returnValue = method.invoke(target, args);
        Console.log("执行目标后的扩展......");        return returnValue;
    }
}

复制代码

   编写测试用例:

    @Test    public void testGetProxyInstance() throws Exception {
        UserService proxyInstance = (UserService) new CgbProxyFactory(new UserServiceImpl()).getProxyInstance();
        proxyInstance.login("rambo","111111");
    }

   JDK InvocationHandler 、Cglib MethodInterceptor 具体实现的细节,如你有兴趣可翻翻源码,这里就不赘述了。

   至此代理的几种方式已经使用都已描述完毕,如有不符,还望斧正。

 

作者:Orson            
出处:http://www.cnblogs.com/java-class/


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消