设计模式之代理模式

代理模式

代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能

代理模式UML类图

一个典型的代理模式通常有三个角色,这里称之为代理三要素:共同接口、真实对象、代理对象

代理模式分为静态代理和动态代理两种实现方式,其中动态代理有JDK动态代理和CGLIB动态代理两种实现方式。

静态代理

例子

接口:IUserDAO

package cn.chengh.proxy;

public interface IUserDAO {
    
    void doAction();
}

实现类:UserDAOImpl

package cn.chengh.proxy;

public class UserDAOImpl implements IUserDAO {

    @Override
    public void doAction() {
        System.out.println("搞事情...");
    }

}

代理类:UserDAOProxy

package cn.chengh.proxy;

public class UserDAOProxy implements IUserDAO {

    private IUserDAO target;
    
    public UserDAOProxy(IUserDAO target) {
        this.target = target;
    }
    
    @Override
    public void doAction() {
        System.out.println("搞事情前...");
        target.doAction();
        System.out.println("搞完事情后...");
    }

}

测试

package cn.chengh.proxy;

public class MyTest {
    public static void main(String[] args) {
        // 目标对象
        IUserDAO target = new UserDAOImpl();
        // 代理对象
        UserDAOProxy proxy = new UserDAOProxy(target);
        // 代理对象执行代理操作
        proxy.doAction();
    }
}

上述静态代理的实现方式可以很容易看出静态代理的优缺点

优点:1、在不修改目标对象的前提下扩展目标对象的功能。2、实现起来简单,容易理解。

缺点:一个代理对象代理一个真实对象,当多个真实对象需要被代理时需要创建多个代理对象,这显然会产生过多的代理类。

JDK动态代理

 

动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。

注:JDK动态代理要求目标对象必须实现接口,否则不能使用动态代理。

JDK中生成代理对象主要涉及的类和方法:

java.lang.reflect Proxy类,使用的方法:

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

这三个参数的含义:

ClassLoader loader:目标对象的类加载器

Class<?>[] interfaces:目标对象实现的接口

InvocationHandler h:事件处理器,代理对象的具体代理操作

java.lang.reflect InvocationHandler接口,使用的方法:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

在invoke实现代理类的具体代理操作。

例子

接口:IUserDAO

package cn.chengh.jdkproxy;

public interface IUserDAO {
    void doAction();
}

实现类:UserDAOImpl

package cn.chengh.jdkproxy;

public class UserDAOImpl implements IUserDAO {

    @Override
    public void doAction() {
        System.out.println("搞事情...");        
    }

}

代理工厂类:ProxyFactory,用来生成代理对象

package cn.chengh.jdkproxy;

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

public class ProxyFactory {
    
    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 {
                System.out.println("搞事情前...");
                // 执行目标对象方法
                method.invoke(target, args);
                System.out.println("搞事情后...");
                return null;
            }
        });
    }
}

测试

package cn.chengh.jdkproxy;

public class MyTest {

    public static void main(String[] args) {
        // 目标对象
        IUserDAO target = new UserDAOImpl();  
        // 得到代理对象
        IUserDAO proxy = (IUserDAO) new ProxyFactory(target).getProxyInstance();  
        // 代理对象执行代理操作
        proxy.doAction(); 
    }
}

JDK动态代理的优缺点

优点:动态代理对象是在运行时动态生成的,相比静态代理,不需要编写大量的代理类。

缺点:JDK动态代理要求目标对象必须实现接口,否则不能使用JDK动态代理。

CGLIB动态代理

CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的

cglib特点

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
  • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。
  • 它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
  • CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

例子

cglib动态代理需要引入cglib-full-2.0.2.jar包

目标类:UserDAO

package cn.chengh.cglibproxy;

public class UserDAO {
    
    public void doAction() {
        System.out.println("搞事情...");
    }
}

代理工厂类:ProxyFactory,用来生成代理对象

package cn.chengh.cglibproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class ProxyFactory implements MethodInterceptor {
    
    private Object target;
    
    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxyInstance() {
        // 工具类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(target.getClass());
        // 设置回调函数
        enhancer.setCallback(this);
        // 创建子类对象代理
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("搞事情前...");
        method.invoke(target, args);
        System.out.println("搞事情后...");
        return null;
    }
}

测试

package cn.chengh.cglibproxy;

public class MyTest {
    public static void main(String[] args) {
        UserDAO target = new UserDAO();
        UserDAO proxy = (UserDAO) new ProxyFactory(target).getProxyInstance();
        proxy.doAction();
    }
}

动态代理的应用

Spring框架中aop就是使用了jdk动态代理和cglib动态代理。如果目标对象没有实现接口,则默认会采用CGLIB代理。如果目标对象实现了接口,可以强制使用CGLIB实现代理。

posted @ 2018-10-08 15:41  chenghaow  阅读(415)  评论(0编辑  收藏  举报