代理模式(Proxy)
代理模式指的是:为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。
使用场景:
1、安全代理:屏蔽对真实角色的直接访问。
2、远程代理:通过代理类处理远程方法调用(RMI)。
3、延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
分类:
1、静态代理:静态定义代理类。
例子:静态代理设计模式
2、动态代理:动态生成代理类。
实现方式:
- JDK自带的动态代理
- javassist字节码操作库实现
- CGLIB
- ASM
JDK自带的动态代理
实现:
1、创建一个处理器,处理器继承 java.lang.reflect 包下的InvocationHandler 接口,组合真实对象,重写 invoke 方法,在invoke方法中实现对真实对象的控制访问。
2、在需要代理角色的时候,通过java.lang.reflect.Proxy 类的静态方法 newProxyInstance(类加载器,处理器的构造器参数,处理器)获取一个代理对象。
例子:
Star 接口:
/** * 歌星 */ public interface Star { void eat();//吃饭 void sing();//唱歌 void sleep();//睡觉 }
RealStar 真实对象:
/** * 歌星:佩奇 */ public class RealStar implements Star{ @Override public void eat() { System.out.println("佩奇吃饭饭"); } @Override public void sing() { System.out.println("我是一只佩奇~ 我不会唱歌~"); } @Override public void sleep() { System.out.println("佩奇睡觉觉"); } }
StarHandler 歌星处理器:实现 InvocationHandler 接口,组合 Star 类,重写 inboke 类。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 处理器 */ public class StarHandler implements InvocationHandler { private Star star;//需要代理的对象 public StarHandler(Star star) { this.star = star; }
/** * * @param o 代理对象 * @param method 获取的对象的方法 * @param objects 方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println("---------------invoke方法开始"); //调用的是吃饭,代理对象来处理 if(method.getName().equals("eat")){ System.out.println(o.getClass().getSimpleName()+":吃饭我来就行了"); } //如果是唱歌,那就让真正的歌星去唱歌。 if(method.getName().equals("sing")){ method.invoke(star,objects); } //调用的是睡觉,代理对象来处理 if(method.getName().equals("sleep")){ System.out.println(o.getClass().getSimpleName()+":睡觉我来就行了"); } System.out.println("---------------invoke方法结束"); return null; } }
测试:
import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { //创建真实对象 Star peiqi = new RealStar(); //获取处理器 StarHandler starHandler = new StarHandler(peiqi); //获取代理类对象:通过Proxy获取,需要指定类加载器、构造器参数、处理器。 Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, starHandler); //代理角色吃饭干活 proxy.eat(); proxy.sing(); proxy.sleep(); } }
运行结果:
cglib实现动态代理
使用第三方插件,基于字节码文件,生成真实对象的子类对象作为代理。
效率高于JDK自带的动态代理。
实现:
1、准备真实对象
2、准备回调对象,实现 net.sf.cglib.proxy.MethodInterceptor 接口,重写 intercept 方法,在 intercept 方法中实现对真实对象的控制访问。
3、在需要代理角色的时候,创建 net.sf.cglib.proxy.Enhancer 类。
4、通过 setSuperclass 方法设置被代理的对象它的的静态方法,通过setCallback方法设置回调对象。
5、通过create方法获取代理对象(真实对象的子类)。
需要导入jar包:
例子:
真实对象:
/** * 真实对象歌星:佩奇 */ public class RealStar{ public void eat() { System.out.println("佩奇吃饭饭"); } public void sing() { System.out.println("我是一只佩奇~ 我不会唱歌~"); } public void sleep() { System.out.println("佩奇睡觉觉"); } }
回调对象:
public class ProxyStar implements MethodInterceptor { /** * @param o 真实对象 * @param method 代理的方法对象 * @param objects 方法参数 * @param methodProxy 代理方法 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //调用的是吃饭,代理对象来处理 if(method.getName().equals("eat")){ System.out.println(methodProxy.getClass().getSimpleName()+":吃饭我来就行了"); } //如果是唱歌,那就让真正的歌星去唱歌。 if(method.getName().equals("sing")){ //invokeSuper(),调用父类的方法 methodProxy.invokeSuper(o,objects); } //调用的是睡觉,代理对象来处理 if(method.getName().equals("sleep")){ System.out.println(methodProxy.getClass().getSimpleName()+":睡觉我来就行了"); } return null; } }
测试:
public class Clients { public static void main(String[] args) { //动态代理对象 Enhancer enhancer = new Enhancer(); //设置被代理的对象 enhancer.setSuperclass(CallbackStar.class); //设置回调对象 enhancer.setCallback(new ProxyStar()); //通过回调创建一个代理对象 CallbackStar star = (CallbackStar) enhancer.create(); //执行方法 star.eat(); star.sing(); star.sleep(); } }
结果: