java 代理

java 代理

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象

  • java 代理
    • JDK中生成代理对象的API
    • 静态代理
    • 动态代理

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.

下面举个案例来解释:

在项目中活着网上例子最多的一种例子就是日志操作。日志并不属于业务逻辑中,但日志记录能帮我们确定整个系统的运行状态以及运行过程中的一些细节。

下面以一个用户的登录和登出坐例子(这只是一个例子)

代码示例:

接口:ILogin.java

public interface ILogin {

    void login(String userName, String password);

    void logout(String userName);
}

目标对象:LoginController.java

public class LoginController implements ILogin {

    public void login(String userName, String password) {
        // 用户验证登录操作
    }

    public void logout(String userName) {
        // 用户退出操作
    }
}

代理对象:LoginControllerProxy.java

/**
 * 代理对象,静态代理
 */
public class LoginControllerProxy implements ILogin{
    // 日志
    private static Logger logger = Logger.getLogger(LoginControllerProxy.Class);

    //接收保存目标对象
    private ILogin target;
    public LoginControllerProxy(ILogin target){
        this.target=target;
    }

    public void login(String userName, String password) {
        // 添加验证
        logger.info("用户登录:用户名:" + userName + "\t密码:" + password + "\tdate:" + new Date());
        target.login(userName, password);
    }

    public void logout(String userName) {
        // 用户退出
        logger.info("用户退出:用户名:" + userName + "\tdate:" + new Date());
        target.logout(userName);
    }
}

测试类:App.java

public class App {
    public static void main(String[] args) {
        //目标对象
        LoginController target = new LoginController();

        //代理对象,把目标对象传给代理对象,建立代理关系
        LoginControllerProxy proxy = new LoginControllerProxy(target);

        proxy.login("张瑀楠", "zyndev@gmail.com");
        proxy.logout("张瑀楠");
    }
}

静态代理总结:

1.优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.

2.缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式

动态代理

动态代理有以下特点:

  1. 代理对象,不需要实现接口
  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
  3. 动态代理也叫做:JDK代理,接口代理

JDK中生成代理对象的API

代理类所在包:java.lang.reflect.Proxy

JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  1. ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
  2. Class[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
  3. InvocationHandler
  4. h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

代码示例: 接口类 ILogin.java以及接口实现类,目标对象 LoginController.java是一样的,没有做修改.在这个基础上,增加一个代理工厂类(ProxyFactory.java),将代理类写在这个地方,然后在测试类(需要使用到代理的代码)中先建立目标对象和代理对象的联系,然后代用代理对象的中同名方法

代理工厂类:ProxyFactory.java

/**
 * 创建动态代理对象
 * 动态代理不需要实现接口,但是需要指定接口类型
 */
public class ProxyFactory{

    private static Logger logger = Logger.getLogger(ProxyFactory.Class);

    //维护一个目标对象
    private Object target;

    public ProxyFactory(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 {
                        logger.info("调用方法前:" + method.getName + "\targs:" + args);
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        logger.info("调用方法后:" + method.getName + "\treturnValue:" + returnValue);
                        return returnValue;
                    }
                }
        );
    }

}

测试类:App.java

public class App {
    public static void main(String[] args) {
        //目标对象
        LoginController target = new LoginController();
        // 给目标对象,创建代理对象
        ILogin proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        proxy.login("张瑀楠", "zyndev@gmail.com");
        proxy.logout("张瑀楠");
    }
}

相比静态代理:

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

使用cglib实现代理


原文发布于微信公众号 - 全栈布道士(gh_773193545262)

原文发表时间:2017-09-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LIN_ZONE

PHP 常用设计模式 (转载)

单例模式顾名思义,就是只有一个实例。作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

1642
来自专栏java系列博客

深入理解Java内存模型(二)——重排序

2194
来自专栏码字搬砖

JVM内存模型之栈

内容 HotSpot将java虚拟机栈与本地方法栈合并成一个了(操作系统中的栈是通过硬件ESP、EBP寄存器来实现的)。虚拟机的栈在细分,分为: 当前栈...

1223
来自专栏Java学习网

JavaScript依赖注入的实现思路

JavaScript依赖注入的实现思路 如今各个框架都在模块化,连前端的javascript也不例外。每个模块负责一定的功能,模块与模块之间又有相互依赖,那么问...

2216
来自专栏用户2442861的专栏

【深入Java虚拟机】之四:类加载机制

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17881581

651
来自专栏java思维导图

Java中高级面试题部分答案解析(2)

这里是一些题型解析,还是这些话:不一定全部正确,有一些是没有固定答案的,如果发现有错的或者更适合的答案欢迎留言矫正。

951
来自专栏Java开发者杂谈

Python(1):入门

安装:    在linux中一般都自带有python2.7的版本,如果想升级python到最新的版本可以参考其他博客(http://www.cnblogs.c...

3828
来自专栏python3

python类(class)

类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例,把所有相似的功能都封装到一个类里,最理...

1163
来自专栏salesforce零基础学习

salesforce 零基础学习(二十四)解析csv格式内容

salesforce中支持对csv格式的内容批量导入,可以使用dataloader,然而有些情况下,当用户没有相关权限使用dataloader导入情况下,就的需...

25510
来自专栏GreenLeaves

C#核编之一个简单的C#程序

构建一个简单的C#应用程序需要注意一下几点: 1、C#要求所有的程序逻辑都包含在一个类型定义中   --->这里的类型指的是(类,接口,结构,枚举,委托中的一个...

20310

扫码关注云+社区

领取腾讯云代金券