Java反射(三)反射与代理设计模式
代理设计模式的思想,即用一个代理类实现为被代理类添加额外的工作,在实际开发中可能是日志记录、耗时计算等。
1.代理模式必不可少的元素:被代理接口、被代理类、代理类(组合被代理类);
2.一般可以使用两种方式实现代理:(1)静态代理;(2)动态代理。
3.还有一个可以直接对类进行代理,不需要被代理接口,即为CGLib。
1.静态代理
静态代理很容易理解,代理类与被代理类都实现同一个接口,代理类将被代理类组合为自己的属性,在调用接口方法时,添加额外的功能。举例如下:
- 被代理接口:IMessage
- 被代理类:MessageReal
- 代理类:MessageProxy,其中包含对代理对象的额外处理。
interface IMessage{ void send(String msg); } /** * 被代理类 */ class MessageReal implements IMessage{ public void send(String msg){ System.out.println("发送消息"); } } /** * 代理类 */ class MessageProxy implements IMessage{ private IMessage message; public MessageProxy(IMessage message){ this.message = message; } /** * 代理后的处理方法 * @param msg */ public void send(String msg){ if(connect()){ message.send(msg); close(); } } private boolean connect(){ System.out.println("创建连接"); return true; } private void close(){ System.out.println("关闭连接"); } }
静态代理特点:每个代理类只能代理一个被代理类。如果开发中存在上千个类需要代理,这种方式就不适合了,可以采用动态代理的方式。
2.动态代理
上文中说代理必备的元素:被代理接口、被代理类、代理类,在动态代理中也是必不可少的。关键元素:
- java.lang.reflect.Proxy,代理类,其通过生成代理字节码实现代理,具体底层实现请关注后文。
- java.lang.reflect.InvocationHandler ,辅助代理类添加额外的功能,由用户自定义实现,执行具体方法时,进行回调。
(1)Proxy
重要的静态方法是newProxyInstance方法,该方法返回的即为代理类,定义如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader
- 定义代理类的ClassLoader(the class loader to define the proxy class),实现与被代理类使用相同的类加载器
interfaces
- 被代理接口(the list of interfaces for the proxy class to implement)
h
- 代理回调处理类(the invocation handler to dispatch method invocations to),自定义的InvocationHandler类,一般会包含被代理类
(2)InvocationHandler
重要方法invoke(),处理代理类方法调用并返回结果,该方法在关联的代理类调用方法时会被调用。
Object invoke(Object proxy,Method method,Object[] args)throws Throwable
proxy - 调用该方法的代理类,一般程序中不使用
method - 被调方法的Method对象
args - 被调方法的参数。
(3)动态代理实例
设计个网络代理NetProxy,实现InvocationHandler接口,添加代理类处理逻辑。
- 被代理接口:target.getClass().getInterfaces()
- 被代理类:Object target
- 代理类:Proxy.newProxyInstance
class NetProxy implements InvocationHandler{ private Object target;//被代理类,Object可以代理任何类型的类 public <T> T proxy(Object target, Class<T> clazz){ this.target = target; return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } /** * 代理对象方法调用的回调处理方法 * @param proxy * @param method * @param args * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ Object result = null; if(this.connect()){ method.invoke(target, args); this.close(); } return result; } private boolean connect(){ System.out.println("创建连接"); return true; } private void close(){ System.out.println("关闭连接"); } }
测试:
public static void main(String[] args){ IMessage message = new NetProxy().proxy(new MessageReal(), IMessage.class); message.send("hello"); }
输出:
创建连接
发送消息
关闭连接
3.CGLib代理
上文描述的静态代理和动态代理都基于接口实现,即需要有明确的被代理接口。CGLib代理,直接对类进行代理,不需要接口。其底层是通过生成代理类字节码实现的。
- 被代理接口:无
- 被代理类:Object target
- 代理类:Enhancer.create()
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; class NetProxy implements MethodInterceptor{ private Object target;//被代理类,Object可以代理任何类型的类 public <T> T proxy(Object target, Class<T> clazz){ this.target = target; Enhancer enhancer = new Enhancer();//创建代理的程序类 enhancer.setSuperclass(target.getClass());//假定一个父类 enhancer.setCallback(this);//设置代理类回调处理方法 return (T)enhancer.create();//创建代理类 } /** * 拦截器,代理执行会被拦截 * @param proxy * @param method * @param objects * @param methodProxy * @return * @throws Throwable */ public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if(this.connect()){ method.invoke(target, objects); this.close(); } return null; } private boolean connect(){ System.out.println("创建连接"); return true; } private void close(){ System.out.println("关闭连接"); } }
4.总结
1.常用的代理模式三要素:被代理接口、被代理类、代理类,对面向接口的编程,该方式比较适合
2.静态代理和动态代理都是基于三要素实现的,区别在于静态代理可实现具体代理类代理某个类,不能扩展;动态代理可以代理任一类型的类
3.动态代理核心是Proxy和InvocationHandler类,底层通过生成代理字节码文件实现代理
4.CGLib代理不需要接口,可直接代理类,在Spring中同时支持Java动态代理和CGlib代理。