架构设计
请列举出在JDK中几个常用的设计模式?
单例模式(Singleton pattern)用于Runtime,Calendar和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像Boolean.valueOf,观察者模式(Observer pattern)被用于 Swing 和很多的事件监听中。装饰器设计模式(Decorator design pattern)被用于多个 Java IO 类中。
什么是设计模式?你是否在你的代码里面使用过任何设计模式?
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。设计模式是代码可用性的延伸
设计模式分类:创建型模式,结构型模式,行为型模式
-
创建型模式,是对对象创建过程的各种问题和解决方案的总结,常见的创建型模式有工厂模式(Factor)、单例模式(Singleton)等;
-
结构型模式,关注于类和对象的继承、组合方式的实践经验。常见的结构性模式有装饰者模式(Decorator)、代理模式(Proxy)等。
-
行为型模式,是从类或对象之间交互、职责划分等角度总结的模式。常见的行为型模式有观察者模式(Observer)、模板方法模式(Template Method)等。
@$静态代理、JDK动态代理以及CGLIB动态代理
代理模式是java中最常用的设计模式之一,通过代理可以让调用者与实现者之间解耦,在spring框架中广泛应用。对于java的代理模式,一般可分为:静态代理、jdk动态代理、以及CGLIB动态代理。
对于上述三种代理模式,分别进行说明。
静态代理
静态代理其实就是在程序运行之前,提前写好被代理方法的代理类,编译后运行。在程序运行之前,代理类的.class文件就已经生成。下面我们实现一个静态代理demo:
定义一个接口Target
package com.test.proxy; public interface Target { public String execute(); }
TargetImpl 实现接口Target
package com.test.proxy; public class TargetImpl implements Target { @Override public String execute() { System.out.println("TargetImpl execute!"); return "execute"; } }
代理类
package com.test.proxy; public class Proxy implements Target{ private Target target; public Proxy(Target target) { this.target = target; } @Override public String execute() { System.out.println("perProcess"); String result = this.target.execute(); System.out.println("postProcess"); return result; } }
测试类:
package com.test.proxy; public class ProxyTest { public static void main(String[] args) { Target target = new TargetImpl(); Proxy p = new Proxy(target); String result = p.execute(); System.out.println(result); } }
运行结果:
perProcess TargetImpl execute! postProcess execute
静态代理需要针对被代理的方法提前写好代理类,如果被代理的方法非常多则需要编写很多代码,因此,对于上述缺点,通过动态代理的方式进行了弥补。
jdk动态代理
jdk动态代理主要是通过反射机制,在运行时动态生成代理类的.class文件
接口
package com.test.dynamic; public interface Target { public String execute(); }
实现类
package com.test.dynamic; public class TargetImpl implements Target { @Override public String execute() { System.out.println("TargetImpl execute!"); return "execute"; } }
代理类
package com.test.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicProxyHandler implements InvocationHandler{ private Target target; public DynamicProxyHandler(Target target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("========before=========="); Object result = method.invoke(target,args); System.out.println("========after==========="); return result; } }
测试类
package com.test.dynamic; import java.lang.reflect.Proxy; public class DynamicProxyTest { public static void main(String[] args) { Target target = new TargetImpl(); DynamicProxyHandler handler = new DynamicProxyHandler(target); Target proxySubject = (Target) Proxy.newProxyInstance(TargetImpl.class.getClassLoader(),TargetImpl.class.getInterfaces(),handler); String result = proxySubject.execute(); System.out.println(result); } }
运行结果:
========before========== TargetImpl execute! ========after=========== execute
无论是jdk动态代理还是静态代理,都需要定义接口,然后才能实现代理功能。这同样存在局限性,因此,为了解决这个问题,出现了第三种代理方式:cglib动态代理。
cglib动态代理
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
目标类
package com.test.cglib; public class Target { public String execute() { String message = "-----------test------------"; System.out.println(message); return message; } }
通用代理类
package com.test.cglib; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MyMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println(">>>>MethodInterceptor start..."); Object result = proxy.invokeSuper(obj,args); System.out.println(">>>>MethodInterceptor ending..."); return "result"; } }
测试类
package com.test.cglib; import net.sf.cglib.proxy.Enhancer; public class CglibTest { public static void main(String[] args) { System.out.println("***************"); Target target = new Target(); CglibTest test = new CglibTest(); Target proxyTarget = (Target) test.createProxy(Target.class); String res = proxyTarget.execute(); System.out.println(res); } public Object createProxy(Class targetClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setCallback(new MyMethodInterceptor()); return enhancer.create(); } }
执行结果:
*************** >>>>MethodInterceptor start... -----------test------------ >>>>MethodInterceptor ending... result
代理对象的生成过程由Enhancer类实现,大概步骤如下:
-
生成代理类Class的二进制字节码;
-
通过Class.forName加载二进制字节码,生成Class对象;
-
通过反射机制获取实例构造,并初始化代理类对象。
JDK Proxy 的优势:
-
最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。
-
平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。
-
代码实现简单。
基于类似 cglib 框架的优势:
-
有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似 cglib 动态代理就没有这种限制。