设计模式之代理模式
代理模式
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式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实现代理。