代理模式
为其他对象提供一种代理以控制对这个对象的访问
快捷方式
核心概念
- 抽象角色 声明真实对象和代理对象的共同接口
- 代理角色 代理角色内部含有真实对象的引用,从而操作真实对象
- 真实角色
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象和代理对象一起实现相同的接口或者继承相同的父类
静态代理使用的是组合模式,在代理类中包含有被代理类的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class TestStaticProxy { public static void main(String[] args) { NikeClothFactory nikeClothFactory = new NikeClothFactory(); ProxyFactory proxyFactory = new ProxyFactory(nikeClothFactory); proxyFactory.productCloth(); } }
interface ClothFactory{ void productCloth(); }
class NikeClothFactory implements ClothFactory{
@Override public void productCloth() { System.out.println("Nike工厂生产了一件Nike"); } }
class ProxyFactory implements ClothFactory{
private ClothFactory clothFactory;
public ProxyFactory(ClothFactory clothFactory){ this.clothFactory = clothFactory; } @Override public void productCloth() { System.out.println("代理类开始执行,准备调用被代理类"); clothFactory.productCloth(); } }
|
静态代理虽然可以在不修改目标对象功能的前提下对目标功能进行扩展,但是一旦接口增加方法,目标对象和代理类都要同时修改,而且代理对象和被代理对象要实现一样的接口,导致有很多的代理类,不便于维护
动态代理
动态代理有很多种方式,如jdk代理,cglib,ASM等
jdk动态代理
jdk动态代理的底层是用的是java的反射,但是jdk代理的前提是目标类必须实现接口
Proxy
Proxy类是所有代理类的父类,用来生成代理类和代理对象,包含有两个重要方法
getProxyClass方法用来生成代理类的Class
1 2
| public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
|
newProxyInstance方法是用来生成代理类的对象
1 2 3 4 5 6
|
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
|
使用步骤
- 首先实现一个InvocationHandler接口,重写invoke方法,方法调用会被转发到该类的invoke()方法
- 通过为Proxy类指向classLoader对象和一组interface来创建代理类
- 使用代理对象调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public class TestDynamicProxy { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); MyInvationHandler myInvationHandler = new MyInvationHandler(); Object obj = myInvationHandler.bind(realSubject); Subject sub = (Subject) obj; sub.action(); } }
interface Subject{ void action(); }
class RealSubject implements Subject{
@Override public void action() { System.out.println("被代理类开始执行"); } }
class MyInvationHandler implements InvocationHandler{
Object obj; public Object bind(Object obj){ this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this); }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法执行前"); Object result = method.invoke(obj,args); System.out.println("方法执行后"); return result; } }
|
可以在单测方法或者main方法中添加System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”);来看到代理类的class文件,文件会被生成到com/sun/proxy目录下
cglib代理
静态代理和jdk动态代理都要求目标对象一定要实现接口,但是有时候目标对象只是一个单独的对象,并没有实现任何接口,这个时候采用以目标对象子类的方式实现代理,该方法称为cglib代理
Cglib包的底层是通过使用一个字节码处理框架ASM来转换字节码并生成新的类,由于要生成子类,所以要被代理的类不可以被final修饰
需要引入cglib的包
1 2 3 4 5
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
|
使用步骤
- 首先实现MethodIntercepor,方法调用会被转发到intercept()方法
- 使用CGLIB来获取代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class CglibProxy implements MethodInterceptor {
public Object getProxy(Class clazz){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("前置处理"); Object result = methodProxy.invokeSuper(o,objects); System.out.println("后置处理"); return result; } }
public class Test {
public void print(){ System.out.println("方法执行"); }
public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); Test proxyImp = (Test) proxy.getProxy(Test.class); proxyImp.print();
} }
|
jdk动态代理和cglib的区别
java动态代理利用反射生成实现代理接口的匿名类,调用方法时调用InvokeHandler处理;cglib动态代理利用asm,将代理对象类的class文件加载进来,通过修改其字节码文件生成子类来处理
优缺点
优点
缺点
- 由于调用方和被调用方之间增加了代理,会造成请求的处理速度变慢