设计模式系列三-代理模式
代理模式主要有两个目的:
1、保护被代理对象
2、增加被代理对象
静态代理:
静态代理就是利用代理对象持有被代理对象
举个栗子:
public interface Animal { void dosomething(); }
public class Bird implements Animal { @Override public void dosomething(){ System.out.println("bird fly"); } }
public class StaticProxy { private Animal animal; public StaticProxy(Animal animal){ this.animal = animal; } public void dosomething(){ System.out.println("静态代理"); animal.dosomething(); } }
public class Test2 { public static void main(String[] args) { Bird bird = new Bird(); // 静态代理 StaticProxy staticProxy = new StaticProxy(bird); staticProxy.dosomething(); } }
动态代理
jdk动态代理
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxy implements InvocationHandler { private Bird bird; public Object getInstance(Bird bird){ //生成$Proxy0的class文件,也就是代理类的字节码文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); this.bird = bird; Class<? extends Bird> clazz = bird.getClass(); // 这里生成代理对象 return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("jdk动态代理"); before(); Object invoke = method.invoke(bird, args); after(); return invoke; } private void before(){ System.out.println("jdk before"); } private void after(){ System.out.println("jdk after"); } }
public class Test2 { public static void main(String[] args) { Bird bird = new Bird(); // jdk动态代理 Animal proxy = (Animal) new JDKProxy().getInstance(bird); proxy.dosomething(); } }
代理方法:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
newProxyInstance,方法有三个参数:
loader: 用哪个类加载器去加载代理对象,就是产生的代理类会是这个类的实现类
interfaces:动态代理类需要实现的接口,会把这里传入的所有方法都增强,都会调用invoke方法
h:动态代理方法在执行时,会调用h里面的invoke方法去执行,也就是InvocationHandler的实现类,用内部类的方式也行
Animal proxyDog = (Animal)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("jdk动态代理...."); method.invoke(target, args); return null; } });
cglib动态代理
需要引入cglib包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { public Animal getInstance(Class<?> clazz){ // 输出生成的代理类 方便理解原理 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "c:/"); // 创建 增强器 类似JDK的proxy类 Enhancer enhancer = new Enhancer(); // 设置目标类 enhancer.setSuperclass(clazz); // 设置回调函数 也就是实现了MethodInterceptor接口的类 enhancer.setCallback(this); // 创建代理类 Animal o = (Animal) enhancer.create(); return o; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib动态代理"); before(); Object o1 = methodProxy.invokeSuper(o, objects); after(); return o1; } private void before(){ System.out.println("cglib Proxy before method."); } private void after(){ System.out.println("cglib Proxy after method."); } }
public class Test2 { public static void main(String[] args) { // cglib动态代理 Animal instance = new CglibProxy().getInstance(Bird.class); instance.dosomething(); } }
也可以仿造jdk、cglib的方式自己实现动态代理模式,思路都是动态生成java代理类然后动态加载
Cglib 采用了 FastClass 机制,它的原理简单来说就是:为代理类和被代理类各生成一个 Class,这个 Class 会为代理类或被代理类的方法分配一个 index(int 类型)。这个 index 当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK动态代理通过反射调用高
CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高。
静态代理和动态的本质区别
1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
代理模式的优缺点
使用代理模式具有以下几个优点:
1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。
当然,代理模式也是有缺点的:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度
Spring中的代理模式 Proxy开头的类,AOP实现