今天要说的是委派模式。
使用起来 和代理模式有点像,在《设计模式之禅》中讲到代理模式就是委派模式,对于这个观点我不敢苟同。看了《Spring5核心原理与30个类手写实战》以及网上查阅资料,我总结了以下几点:
- 代理模式注重过程,而委派模式更加看重结果。生活中常有委派发生,班级大扫除的时候,班主任安排任务给班长,班长把分配给同学们,班主任呢,他只关注打扫的干不干净,至于怎么打扫的,他不太关心。
- 代理模式的对象自始至终都没有发生改变,且在代理模式中代理类不能改变业务逻辑。委托恰恰相反,不仅可以实现都有的逻辑,也可以自由的更换委托对象,就像上面的例子,老师也可以把任务委派给卫生委员。
- 在编程方面,代理模式更像是上下级的关系,委托则像平级关系。
下面分析源码加深一下理解。
源码分析
jdk中有一个典型的委托,众所周知jvm在加载类是用的双亲委托模型,这又是什么呢?一个类加载器在加载类时,先把这个请求委托给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委托,直到顶层的启动类加载器。如果父类加载器能够完成类加载,就成功返回,如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载。从定义中可以看到双亲加载模型一个类加载器加载类时,首先不是自己加载,而是委托给父加载器。这就和上面打扫卫生的例子很像。父加载器肯定有自己的业务逻辑 ,对比下代理模式,正好印证上面的第二条。下面我们来看看loadClass
方法的源码,此方法位于ClassLoader
类里。
在此类里定义了一个双亲,用于下面的加载。
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
同样在Method
l类里我们常用代理执行方法invoke()
也存在类似的机制。
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
看完代码,相信童鞋们对委托和代理区别搞清楚了吧。在spring的DispacherServlet
也实现了委托,让我们一起来看下吧。
委托在Spring中应用
学过spring都知道DispacherServlet
是提供web的集中访问点,分配任务去给处理器执行
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
DispacherServlet
将任务分配给处理器,主要由
-
HandlerAdapter
:确定当前请求的处理程序适配器 -
HandlerExecutionChain
:确定当前请求的处理程序。 -
cleanupMultipart()
:清理multipart多余的资源。 -
HandlerMapping
:将请求映射到处理器 -
ViewReslover
: 解析视图名到具体试图实现。
从以上我们可以看出DispatcherServlet主要负责流程的控制。当然我分析的可能有出入,小伙伴们可以指出来,一起讨论。
好了,委托模式就到这了,童鞋们只有深入理解委托和代理的异同点,才能在以后的学习生活中更好的使用,至少还可以在小伙伴面前装个逼。哈哈哈哈哈哈哈哈哈
看过我的博文都知道,我的文章会有大幅度代码,有些人可能会质疑我。我个人认为,学习东西需要思考,思考有个方向,文字解释只是引导方向,阅读了相关的代码,并思考代码中应用,才能有自己的体会 ;光靠别人解释,吃别人咀嚼过东西没有味道,体会不到真正的意义。仅代表个人观点,有不同意见可以提出,一起讨论。
声明
本文章为作者读书笔记及感悟,其中参考了《spring5核心原理与30个类手写实战》以及互联网上的内容。如有错误,请评论或者私聊我,欢迎探讨技术问题 。即将毕业,在准备找工作,有朋友想给我介绍的,欢迎添加微信:sllbiao。