记录每一次进步

工作生活中的每一次坑,都是程序员的一块勋章

现在去看很多公司的招聘信息,都会发现越来越多的公司都有对SpringCloud的要求,自己目前所在公司的一部分业务也是建立在Spring Cloud上的。今天开始,将单独开设一个SpringCloud主题,讲解Spring Cloud的使用。
在使用了一段时间的SpringBoot之后,我们会想,如果说SpringBoot的思想是将大型的Web服务拆分为低耦合、高内聚的微服务的话,当拆分出来的微服务较多后,如果来(分布式环境或非分布式环境)有效地管理这些微服务,以及各微服务如何与其他微服务进行交互便成了问题。如果是最原始的方案,当某一个微服务需要调用其他微服务的时候,需要知道其他微服务的地址信息,以及接口信息。但是当系统内部的微服务关系错综复杂时,难道一个微服务的地址更改之后,需要修改并重新其他相关联的服务吗?这样的管理方式便不再合适。而Spring Cloud从技术架构上降低了对大型系统构建的要求,使我们以非常低的成本(技术或者硬件)搭建一套高效、分布式、容错的平台。

Spring Cloud

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
SpringCloud架构
当一个Web请求到来时,系统内部的调用过程如下:

  1. 外部或者内部的非Spring Cloud项目都统一通过API网关(Zuul)来访问内部服务.
  2. 网关接收到请求后,从注册中心(Eureka)获取可用服务
  3. 由Ribbon进行均衡负载后,分发到后端的具体实例
  4. 微服务之间通过Feign进行通信处理业务
  5. Hystrix负责处理服务超时熔断
  6. Turbine监控服务间的调用和熔断相关指标

在上图中可以看到,整个系统是由许多微服务组成的。微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,Spring Cloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,Spring Cloud做为大管家需要管理好这些微服务,自然需要很多小弟来帮忙。如上图中出现的Spring Cloud Config、Spring Cloud Sleuth、Spring Cloud Eureka、Spring Cloud Zuul、Spring Cloud Hystrix等等以外,还包含且不限于Spring Cloud Bus、Spring Cloud for Cloud Foundry、Spring Cloud Cluster、Spring Cloud Consul、Spring Cloud Security、Spring Cloud Data Flow、Spring Cloud Stream、Spring Cloud Task、Spring Cloud Zookeeper、Spring Cloud Connectors、Spring Cloud Starters、Spring Cloud CLI。每一个微服务都有其独一无二的作用。

Spring Cloud 与 Spring Boot

Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。

Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。

Spring Cloud的优势

微服务的框架那么多比如:dubbo、Kubernetes,为什么就要使用Spring Cloud的呢?

  • 产出于spring大家族,spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善。
  • 有Spring Boot 这个独立干将可以省很多事,大大小小的活Spring Boot都搞的挺不错。
  • 作为一个微服务治理的大家伙,考虑的很全面,几乎服务治理的方方面面都考虑到了,方便开发开箱即用。
  • Spring Cloud 活跃度很高,教程很丰富,遇到问题很容易找到解决方案
  • 轻轻松松几行代码就完成了熔断、均衡负载、服务中心的各种平台功能
    Spring Cloud对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用Spring Cloud一站式解决方案能在从容应对业务发展的同时大大减少开发成本。同时,随着近几年微服务架构和Docker容器概念的火爆,也会让Spring Cloud在未来越来越“云”化的软件开发风格中立有一席之地,我目前所在的公司很多服务就是建立在微服务及容器云上的,这极大地方便了我们开发人员的开发效率,同时也减轻了运维人员的工作。

网上关于个Spring Cloud微服务组件的文章及书籍很多,但是很多知识只是停留在如何使用的阶段。在后面的文章当中,我将在讲解Spring Cloud各微服务功能的同时,深入去解析器内部原理,以达到知其然而知其所以然的目的。作为一个爱钻牛角尖的程序员,这也是我为什么还要写这个系列的原因。

欢迎关注个人公众号:
个人公号

有朋友说,你搞了这么久Java,怎么天天写的都是Java基础,现在流行微服务啊~分布式啊~恩。。。主要还是要学习的内容太多了,感觉写个一年半载也写不完,不过为了与时俱进,后面也会偶尔写点这方面的内容,就以SpringCloud全家桶作为主要介绍对象,感兴趣的朋友们快快提前关注啊~~~好了,下面进入正题~
在《Tomcat服务器结构浅析(一)》中我们介绍到Web请求在到达Tomcat服务器后,经过一层层容器地查找以及地址的匹配,最后请求被交由Servlet进行处理:
Tomcat请求处理
在SpringMVC框架中,占据核心位置的便是DispatcherServlet。在下面的内容中,让我们来看一下,SpringMVC是如何通过DispatcherServlet来处理请求的。

Servlet

Servlet(Server Applet),全称Java Servlet。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server。此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
一个Web请求的过程如下:

  1. WEB服务器接收一个用户请求;
  2. WEB服务器将请求转交给WEB服务器关联的Servlet容器;
  3. Servlet容器找到对应的Servlet并执行这个Servlet;
  4. Servlet容器将处理结果返回给WEB服务器;
  5. WEB服务器把结果送回用户;
    而在Servlet处理请求的过程当中,又是怎样的一个流程呢?

Servlet生命周期

Servlet的生命(周期)是由容器管理的,换句话说,Servlet程序员不能用代码控制其生命。

1
2
3
4
5
6
7
8
9
10
11
12
public interface Servlet {
public void init(ServletConfig config) throws ServletException;

public ServletConfig getServletConfig();

public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;

public String getServletInfo();

public void destroy();
}
  1. 加载和实例化:
    时机取决于web.xml的定义,如果是比较原始的开发方式(通过配置文件定义Servlet),如果有x则在容器启动时,反之则在第一次针对这个Servlet的请求发生时。

  2. 初始化:
    实例化后会立马进行初始化。也就是执行init方法。

  3. 请求处理:
    初始化后,Servlet就可以接受请求了。基本方式是执行Servlet接口中的service方法。

  4. 终止服务:
    容器会在合适的时候销毁某个Servlet对象,这个策略取决于容器的开发者/商。在容器关闭的时候Servlet对象一定会被销毁。
    Servlet对象被销毁时,destroy方法会被调用。

当一个请求到达Servlet后,该Servlet的service方法将会得到调用,而具体的业务逻辑,就可以通过该方法来实现了。

DispatcherServlet

接下来,开始讲解今天的主角——DispatcherServlet。首先,还是让我们来看一看,DispatcherServlet的类继承图:
DispatcherServlet继承图

DispatcherServlet调用链

Aware类主要是提供了一个能够响应容器各阶段变化的机制,在这里不是我们关注的重点,因此,我们主要来看Servlet部分的继承树。从图中我们可以看到,DispatcherServlet的祖先之一便是Servlet接口。在Servlet生命周期部分,我们提到,请求是通过Servlet的service来进行处理的,可是在DispatcherServlet中,我们并不能找到该函数的定义。其实,service方法是被定义在其父类FrameworkServlet中的,而FrameworkServlet重写了父类HttpServlet的service方法。HttpServlet的service方法定义如下:

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
53
54
55
56
57
58
59
60
61
62
63
64
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

String method = req.getMethod();

if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}

} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);

} else if (method.equals(METHOD_POST)) {
doPost(req, resp);

} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);

} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);

} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);

} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);

} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//

String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);

resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

其主要是根据请求的不同,将请求交由不同的处理函数来处理。而FrameworkServlet重写的service方法则很简单:

1
2
3
4
5
6
7
8
9
10
11
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}

当请求是PATCH请求或者无法获取到请求方法类型时,则直接调用processRequest处理请求,否则,有父类HttpServlet的service来处理,而通过上面我们知道HttpServlet的service主要是根据方法类型,调用了不同的请求处理方法。比如,如果是一个Get请求,则调用的doGet方法,如果是Post请求,则调用的是doPost方法。而这几个方法在FrameworkServlet中被重载,以doGet为例:

1
2
3
4
5
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

可以看到,请求最终还是交由processRequest函数处理。processRequest是一个final方法,不能被子类重载,在该方法中,调用了doService方法,而DispatcherServlet实现了doService方法,到此,请求最终进入到DispatcherServlet中被消费。在doService中,除了设置请求的属性及一些简单的操作外,主要是调用了doDispatch方法来处理请求的。

DispatcherServlet几个重要成员

在介绍DispatcherServlet处理请求的流程前,让我们先来认识几个DispatcherServlet类重要的几大组件:

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
   // 文件上传组件
/** MultipartResolver used by this servlet */
private MultipartResolver multipartResolver;

// 资源定位组件
/** LocaleResolver used by this servlet */
private LocaleResolver localeResolver;

// 主题解析组件
/** ThemeResolver used by this servlet */
private ThemeResolver themeResolver;

// 处理器映射器组件集合
/** List of HandlerMappings used by this servlet */
private List<HandlerMapping> handlerMappings;

// 处理器适配器组件集合
/** List of HandlerAdapters used by this servlet */
private List<HandlerAdapter> handlerAdapters;

// 异常处理解析器集合
/** List of HandlerExceptionResolvers used by this servlet */
private List<HandlerExceptionResolver> handlerExceptionResolvers;

// 视图名解析器
/** RequestToViewNameTranslator used by this servlet */
private RequestToViewNameTranslator viewNameTranslator;

// 重定向及FlashMap存储组件
/** FlashMapManager used by this servlet */
private FlashMapManager flashMapManager;

// 视图解析组件集合
/** List of ViewResolvers used by this servlet */
private List<ViewResolver> viewResolvers;

SpringMVC定义了一套默认的组件实现类,也就是说,即使在Spring容器中没有显示定义组件,DisoatcherServlet也会装配好一套可用的默认组件,在org/springframework/web/servlet类路径下有一个DispatcherServlet.properties配置文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

如果我们希望采用非默认类型的组件,则只需要在Spring配置文件中配置自定义的组件Bean即可。SpringMVC一旦发现上下文中有用户自定义的组件,就不会使用默认的组件了。

doDispatch方法

接下来,我们来看下doDispatch方法是如何处理一个Web请求的。首先是一个处理路径图:
DispatcherServlet请求处理流程
下面,让我们以上面的图为参照,来解析一下doDispatch函数的处理逻辑

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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 {
// 检查是否为文件上传请求,如果是的话则将request包装为一个MultipartHttpServletRequest(继承自HttpServletRequest)请求
processedRequest = checkMultipart(request);

//若与原始请求不同,说明是一个文件上传请求,否则为一个普通的请求
multipartRequestParsed = (processedRequest != request);

// 根据请求,获取处理器请求执行链,否则该请求不能被应用处理
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// 获取处理器适配器
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// 如果是GET或者HEAD请求,调用HandlerAdapter.getLastModified方法看看目标Controller方法在对于该请求有没有可用的lastModified逻辑,如果有的话就使用ServletWebRequest.checkNotModified逻辑判断当前lastModfied值和http header的上次缓存值,如果还没有过期就设置304头并且返回并结束整个请求流程。否则继续。
// 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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
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;
}

// 如果模型视图对象不为null且存在视图,则设置该视图的名字
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 {
// 如果异步请求处理管理器已开始处理该请求,则调用处理器执行链的回调函数,其内部主要是调用了AsyncHandlerInterceptor类型的拦截器
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);
}
}
}
}

在多数的场景到中,我们编写控制器,使用@RestController注解对控制器进行注解,使用@GetMapping注解标注函数,dispatcherServlet的重点,就在如何将请求分发到具体的Controller中的方法中。
通过上面源码的讲解可以看出,其重点就在于通过请求获取处理器执行链HandlerExecutionChain(其内部主要是对处理该请求的对象方法的封装及处理器拦截器的包装)及处理器适配器HandlerAdapter。接下来我们看下获取处理器执行链HandlerExecutionChain的getHandler方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

其主要的逻辑是通过处理器映射器来解析请求,并返回正确的HandlerExecutionChain,HandlerMapping接口如下:
HandlerMapping继承图
上述的几个实现类通过不同的策略,将请求的路径映射到对应的处理器上。当我们访问由@RestController标注的类下的接口时(此处以ConfigController下的list接口为例),其最终便是由RequestMappingHandlerMapping解析的出了HandlerExecutionChain,通过断点可以看到:
HandlerExecutionChain结构
该处理器执行链中已经包含了处理该请求的接口的一些信息,后续的工作便是使用该接口处理请求。而这些工作都是通过处理器适配器HandlerAdapter来完成。接下来看一下HandlerAdapter的继承图:
DispatcherServlet请求处理流程
上述请求获取到的处理器对应的适配器便是RequestMappingHandlerAdapter
获取HandlerAdapter
之后,便是执行HandlerExecutionChain中拦截器的前置方法,通过适配器执行处理器对请求的处理过程并返回模型视图对象,再执行HandlerExecutionChain中拦截器的后置方法。最后,便是根据处理器对请求的处理情况,对结果进行最终的解析,方法processDispatchResult:

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
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

boolean errorView = false;

if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}

if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}

if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

从注释中可以看出,该方法便是根据处理器处理结果是抛出异常还是返回模型视图对象,对结果进一步做了处理。若处理器返回了模型视图对象,对视图的渲染,便是在该函数中通过调用render函数来完成的。由于对标注了@RestController的控制器,其返回的模型视图对象是null,故不会进行渲染,在这里也就不再将视图渲染的内容展开来讲了。

至此,DispatcherServlet主要的工作大致就分析完了。想要自己一探究竟的朋友,可以自己写一个小接口,通过断点走一边流程,相信会对DispatcherServlet有更深入的理解~~

欢迎关注个人公众号:
个人公号

概述

开发当中一直都有在使用函数式编程,尤其是在Stream类时,今天就来对jdk8中的函数式编程来做一个学习汇总。
我们最常用的面向对象编程(Java)属于命令式编程(Imperative Programming)这种编程范式。常见的编程范式还有逻辑式编程(Logic Programming),函数式编程(Functional Programming)。函数式编程作为一种编程范式,在科学领域,是一种编写计算机程序数据结构和元素的方式,它把计算过程当做是数学函数的求值,而避免更改状态和可变数据。

Lambda 表达式的形式

在介绍函数式编程之前,让我们来看一下jdk8中提供的Lambda表达式的五种形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.
Runnable noArguments = () -> System.out.println("Hello World");

2.
ActionListener oneArgument = event -> System.out.println("button clicked");

3.
Runnable multiStatement = () -> {
System.out.print("Hello");
System.out.println(" World");
};

4.
BinaryOperator<Long> add = (x, y) -> x + y;

5.
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;

1中所示的 Lambda表达式不包含参数,使用空括号 () 表示没有参数。该 Lambda 表达式 实现了 Runnable 接口,该接口也只有一个 run 方法,没有参数,且返回类型为void。2中所示的Lambda表达式包含且只包含一个参数,可省略参数的括号。Lambda表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号 ({})将代码块括起来,如3所示。该代码块和普通方法遵循的规则别无二致,可以用返回或抛出异常来退出。只有一行代码的Lambda表达式也可使用大括号,用以明确Lambda表达式从何处开始、到哪里结束。Lambda表达式也可以表示包含多个参数的方法,如4所示。这时就有必要思考怎样去阅读该Lambda 表达式。这行代码并不是将两个数字相加,而是创建了一个函数,用来计算两个数字相加的结果。变量add的类型是BinaryOperator。当需要限定使用该Lambda表达式的使用范围,比如说只能传递Long型的参数时,则可以将参数具体化为Long型,如5。
我们以Spring中创建一个自定义的事件监听器为例:

1
2
3
4
5
6
7
8
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}

ApplicationListener 只有一个抽象方法:actionPerformed,被用来表示行为:接受一个参数,返回空。当我们要自定义一个自己的ApplicationListener时,可以通过匿名内部列的形式实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootApplication
public class App {

private static final Logger log = LoggerFactory.getLogger(App.class);

public static void main(String[] args) {
SpringApplication.run(App.class, args);
}

@Bean
public ApplicationListener newApplicationListener() {
System.out.println("create a ApplicationListener");
return new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("process event");
}
};
}
}

当然,还可以使用上文提到的Lambda表达式的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SpringBootApplication
public class App {

private static final Logger log = LoggerFactory.getLogger(App.class);

public static void main(String[] args) {
SpringApplication.run(App.class, args);
}

@Bean
public ApplicationListener newApplicationListener() {
System.out.println("create a ApplicationListener");
return event -> {
System.out.println("process event");
};
}
}

一个限制是,函数接口里面只能有一个抽象方法

函数接口

其实在上一节当中,我们已经使用了函数接口。jdk中为我们提供了@FunctionalInterface注解,该注解的作用是,当我们对一个接口进行标注时,编译器会检查该接口是否只用一个抽象方法。也就是说,即使我们的接口不标注该注解,只要满足接口只有一个抽象函数的要求,就是一个函数接口。ApplicationListener就是一个函数接口。

JDK8中提供了一组常用的核心函数接口:

接口 参数 返回类型 描述
Predicate T boolean 用于判别一个对象。比如求一个人是否为男性
Consumer T void 用于接收一个对象进行处理但没有返回,比如接收一个人并打印他的名字
Function<T, R> T R 转换一个对象为不同类型的对象
Supplier None T 提供一个对象
UnaryOperator T T 接收对象并返回同类型的对象
BinaryOperator (T, T) T 接收两个同类型的对象,并返回一个原类型对象

除此之外,还有其他一些很多的函数接口,这些接口都在java.util.function包下
函数接口
任意点开一个接口,我们都可以发现,该接口只会有一个抽象函数,0个或者多个default函数。
在大多数场景中,我们使用函数接口进行编程主要是在Stream中,打开Stream的类可以发现,该类的方法参数很多都是使用到了函数接口,以Stream类foreach为例:

1
2
3
4
5
6
7
8
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("1");
stringList.add("2");
stringList.stream().forEach((value) -> {
System.out.println(value);
});
}

其实质是:

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("1");
stringList.add("2");
Consumer<String> stringConsumer = (value) -> {
System.out.println("value is : " + value);
};
stringList.stream().forEach(stringConsumer);
}

方法引用

在使用Stream的过程中,我们也许还会见到下面这种情况:

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
53
54
55
56
public class App {

private static final Logger log = LoggerFactory.getLogger(App.class);

public static void main(String[] args) {
List<Integer> list = Lists.newArrayList(3, 5, 2, 9, 1);
int maxInt = list.stream()
.max(Integer::compareTo)
.get();
assertEquals(maxInt, 9);

List<Integer> list2 = Lists.newArrayList(-3, 5, -2, 9, -1);
assertEquals(list2.stream().map(Math::abs).allMatch(e -> e > 0), true);

Comparator<Person> comparator = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge().compareTo(o2.getAge());
}
};
List<Person> PersonList = Lists.newArrayList(new Person("teny",23), new Person("tome",55));
Person minAgePerson = PersonList.stream()
.min(comparator::compare)
.get();
assertEquals(minAgePerson.getAge(), 23);

Person newPerson = Person.createPerson(Person::new);
}

public static class Person {
private String name;
private Integer age;

public static Person createPerson(Supplier<Person> supplier) {
return supplier.get();
}

public Person() {
this.name = "";
this.age = -1;
}

public Person(String name, Integer age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public Integer getAge() {
return age;
}
}
}

Integer::compareTo也是属于Java8引入的新特性,叫做方法引用(Method References),其实就是 (int1, int2) -> int1.compareTo(int2) 的简写。
引用方法有下面几种方式

对象引用::实例方法名

1
2
3
4
5
6
7
8
9
10
11
Comparator<Person> comparator = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge().compareTo(o2.getAge());
}
};
List<Person> PersonList = Lists.newArrayList(new Person("teny",23), new Person("tome",55));
Person minAgePerson = PersonList.stream()
.min(comparator::compare)
.get();
assertEquals(minAgePerson.getAge(), 23);

类名::静态方法名

1
2
List<Integer> list2 = Lists.newArrayList(-3, 5, -2, 9, -1);
assertEquals(list2.stream().map(Math::abs).allMatch(e -> e > 0), true);

类名::实例方法名

1
2
3
4
5
List<Integer> list = Lists.newArrayList(3, 5, 2, 9, 1);
int maxInt = list.stream()
.max(Integer::compareTo)
.get();
assertEquals(maxInt, 9);

类名::new

1
Person newPerson = Person.createPerson(Person::new);

欢迎关注个人公众号:
个人公号

最近有点懒,博客更新少,补上一篇之前的欠债。。。这篇博客内容基于《Tomcat架构解析》,这里把Tomcat主要的结构介绍了下,更详细的内容大家可以参考该书。

Tomcat是全世界最著名的基于Java语言的轻量级应用服务器,是一款完全开源免费的Servlet容器实现。同时,它支持HTML,JS等静态资源的处理,因此也可作为轻量级的WEB服务器来使用。在以前的WEB开发当中,我们主要通过将程序打包,将打包文件放到webapps下来进行访问,而在使用SpringBoot作为开发框架的情况下,由于SpringBoot已内嵌Tomcat,不需要将打包文件放在特定的文件夹下,而是直接运行程序即可。这篇博客第一部分内容将介绍Tomcat的整体架构,第二部分介绍SpringBoot内嵌Tomcat的实现。

Tomcat总体架构

首先,让我们来看一张图:
Tomcat应用服务器
Tomcat总体的设计便是围绕着这张图来的。下面我们依次对上图中的各个部分做一下介绍。

Lifecycle 接口

该接口主要定义了容器整个生命周期过程中的各个阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Lifecycle {
public void init() throws LifecycleException;

public void start() throws LifecycleException;

public void stop() throws LifecycleException;

public void destroy() throws LifecycleException;

public void addLifecycleListener(LifecycleListener listener);

...
}

该接口包含了容器初始化,开始,停止,销毁等过程。其实现类的各个组件在容器的工作过程当中需要做的工作,即在这几个函数中来完成。

Server

表示整个Servlet容器,因此Tomcat运行环境中只有唯一一个Server实例。在该接口的唯一实现类StandardServer中,除了表示Service的一个对象数组外,主要是一些关于Tomcat的属性,比如port,address等。该容器的这些属性,可以通过properties文件或者yaml文件进行配置(比如端口通过server.port=8080进行配置),或者是原来的开发方式,通过Server.xml进行配置。

Service

Service表示一个或者多个Connector的集合,这些Connector共享同一个Container(即Engine)来处理其请求。在同一个Tomcat实例内可以包含任意多个Service实例,它们彼此独立。Service其实是作为Tomcat中接收请求,以及处理请求的容器的纽带存在的。tomcat中的实现类StandardService有以下几个重要的属性:

1
2
3
4
5
6
public class StandardService extends LifecycleMBeanBase implements Service {
private Server server = null;
protected Connector connectors[] = new Connector[0];
private Engine engine = null;
protected final Mapper mapper = new Mapper();
}

server表示其所属Server,Engine作为处理该service中Connector的容器。Mapper可以看作是映射器,要来处理请求地址到处理该请求的容器及Servlet的映射。

Engine

在Tomcat中,Engine为最高层级的容器对象。尽管Engine不是直接处理请求的容器,却是获取目标容器的入口。

Host

Host 作为一类容器,表示Servlet引擎(Engine) 中的虚拟机, 与一个服务器的网络名有关,如域名等。客户端可以使用这个网络名连接服务器,这个名称必须要在DNS服务器上注册

Context

Context作为一类容器,用于表示ServetContext ,在Servlet规范中, 一个ServletContext表示一个独立的Web应用

Wrapper

Wrapper作为一类容器, 用于表示Web应用中定义的Servlet(其实是对Servlet进行了一层封装)。

Connector

表示Tomcat中的链接器,其主要作用是监听并转化Socket请求,并交由Container处理。其实就是对不同协议及协议处理器进行的封装。下面是我们需要关注的几个属性域:

1
2
3
4
public class Connector extends LifecycleMBeanBase {
protected Service service = null;
protected final ProtocolHandler protocolHandler;
}

Service是其父容器,ProtocolHandler表示协议处理器

ProtocolHandler

ProtocolHandler表示协议处理器,是一个接口,其实现类有以下几种:
ProtocolHandler继承层次
从图中我们大概能够猜到,其中的每一个实现类,其实都代表着一种I/O协议的处理过程,我们以同步非阻塞I/O的处理器Http11NioProtocol为例,其最初继承于抽象类AbstractProtocol,它的定义如下:

1
2
3
4
5
6
public abstract class AbstractProtocol<S> implements ProtocolHandler,
MBeanRegistration {
private final AbstractEndpoint<S> endpoint;
private Handler<S> handler;
private final Set<Processor> waitingProcessors = Collections.newSetFromMap(new ConcurrentHashMap<Processor, Boolean>());
}

AbstractEndpoint代表的是协议端点,比如,Nio使用的是NioEndpoint类,即为nio的实现逻辑,对nio类型的Socket进行监听, Handler作为AbstractEndpoint接收到I/O后,用来处理I/O的处理器。

请求处理过程

当我们的浏览器或者是其他工具发起一个Http请求时候,Tomcat的整个处理过程如下:
Tomcat请求处理
从一开始的Endpoint监听到Http请求后,调用Processor进行处理,Process调用CoyoteAdapter进行处理,CoyoteAdapter通过Mapper获取到处理该请求的顶级容器Engine,通过一层层的查找,最终获取到处理该请求的Wrapper,经过Tomcat中定义的一系列过滤器(Filter)后,最终由Servlet(在SpringMVC中,便是被DispatcherServlet)进行了消费。Tomcat整个处理的流程便是这样的。

SpringBoot内嵌Tomcat

再完成了Tomcat简单的解析之后,我们还要问,在启动SpringBoot应用的过程当中,是如何启动Tomcat的呢?在Tomcat中,其已经为我们提供了一个表示其实例的Tomcat类,通过查找,我们知道,该类的实例是在TomcatEmbeddedServletContainerFactory类的getEmbeddedServletContainer函数中被创建的。启动一个简单的SpringBoot应用,通过断点,我们能够看到它被调用的路径:
Tomcat启动
从上图可知,到Springboot应用刷新容器的时候,会在该过程当中创建Tomcat的实例,我们来下看下函数的实现过程:

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
public class TomcatEmbeddedServletContainerFactory
extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
// 创建Tomcat的实例
Tomcat tomcat = new Tomcat();

// 为Tomcat设置应用的根目录
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());

// 根据Springboot使用的I/O协议,创建Connector,默认的协议是`String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol`,及NIO协议(同步非常阻塞)
Connector connector = new Connector(this.protocol);
// 为Service添加Connector,若Tomcat还没有Service,则getService函数中会创建
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);

// 通过配置autoDeploy禁止虚拟主机自动部署Web应用
tomcat.getHost().setAutoDeploy(false);

// 配置Tomcat的顶级容器Engine
configureEngine(tomcat.getEngine());

// 添加额外的自定义协议的Connector
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}

// 配置虚拟主机Host,其内会进一步初始化Host的字容器
prepareContext(tomcat.getHost(), initializers);

// 对Tomcat进行包装,返回TomcatEmbeddedServletContainer的实例
return getTomcatEmbeddedServletContainer(tomcat);
}
}

关于Tomcat的内容其实还有很多,只是由于时间限制,不能在这里更深入地讲解,若后期有机会,会继续更加深入地介绍Tomcat的各个组件,这篇内容就做为Tomcat容器系列的第一篇吧~

欢迎关注个人公众号:
个人公号

AOP最为Spring的一大核心卖点,在开发当中经常使用到。对于切面、切点的定义我们可能很熟悉,但是对于实现该技术的底层技术细节,却知之甚少。利用周末的时间,博主将AOP的底层实现细节研究了一下,在此做为记录。

AOP

AOP是Aspect Oriented Programing的简称,可译为“面向切面编程”。在业务实现过程当中,有很多横切面上的业务是存在共通之处的,比如,在每一个业务模块的入口,需要打印业务开始的日志信息,在完成业务后需要打印业务调用耗时,对于这些共通的业务,由于不能通过多态继承的方式来实现,会导致很多相似代码出现在不同的业务逻辑当中,AOP就是为了解决这些问题而存在的。下面让我们来一个例子:

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
public interface ForumService {
public void removeTopic(int topidId);
public void createForum(Forum forum);
}

public class ForumServiceImpl {
private TransactionManager transManager;
private PerformanceMotitor pmonitor;
private TopicDao topicDao;
private ForumDao forumDao;

public void removeTopic(int topidId) {
pmomitor.start();
transManager.beginTransaction();
topicDao.remove(topidId);
transManager.commit();
pmonitor.end();
}

public void createForum(Forum forum) {
pmomitor.start();
transManager.beginTransaction();
forumDao.create(forum);
pmonitor.end();
}
}

在该类的两个方法中,存在共通的逻辑

1
2
pmomitor.start();
transManager.beginTransaction();
1
pmonitor.end();

若没有办法将这些共通的代码提炼出来,则会在业务逻辑中产生大量重新性的代码(该例子虽然可以提取公共逻辑到一个辅助函数中来解决,但是很多公共领域内如事务的开启与关闭并不是要业务逻辑所关心的,而是应该有基础的框架来为客户提供)。那么如何解决这种问题呢?其实最简单的方法便是在调用这些业务方法之前,对调用进行拦截,并执行公共的与业务无关的代码,然后再调用实际的业务逻辑。AOP就是基于这种思想来实现的。在设计模式当中,存在代理模式,而Spring中的AOP实现,便是基于这一设计模式来实现的。接下来让我们看一下,有关Spring中动态代理的一些相关介绍。

Spring AOP的核心基础——动态代理

JDK动态代理

从JDK1.3开始,Java为我们提供了动态代理技术,允许开发者在运行期间创建接口的动态代理实例。JDK的动态代理主要涉及到Java.lang.reflect中的两个类,一个是Proxy,一个是InvocationHander,InvocationHander是一个接口,可以通过该接口定义横切的公共逻辑,并通过反射机制来调用目标类的代码,动态地将横切逻辑与业务逻辑编织在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class PerformanceHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
pmomitor.start();
transManager.beginTransaction();
Object obj = method.invoke(target, args);
pmonitor.end();
}
}

public class ForumServiceTest {
@Test
public void proxy() {
ForumService target = new ForumServiceImpl();
PerformanceHandler handler = new PerformanceHandler(target);
ForumService proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
proxy.createForum(10);
proxy.removeTopic(1012);
}
}

在上面代码中,我们定义了一个横向的切面逻辑PerformanceHandler,其实现了InvocationHandler。利用Proxy.newProxyInstance函数创建出一个目标对象的代理对象proxy,当调用代理对象的createForum及removeTopic函数时,便会先执行pmomitor.start()和transManager.beginTransaction(),之后才执行目标对象真是的业务逻辑,最后执行pmonitor.end()。如此,也就完成了公共逻辑的提取。让我们看一下InvocationHandler接口

1
2
3
4
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

该接口只有一个接口方法,参数分别为proxy——需要代理的目标对象,method——被拦截的目标对象的方法,args——方法参数。以及Proxy.newProxyInstance:

1
2
3
4
5
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
...
}

对于函数的逻辑暂时无需关心,重点是该函数的入参,loader——目标类的类加载器,interfaces——目标类实现的接口列表,h——调用处理器的实例。

CGLib 动态代理

使用JDK的动态代理有一个限制,即它只能为接口创建代理实例,从Proxy的newProxyInstance方法入参就可以看出,第二个入参interfaces就是需要代理实例实现的接口列表。那么,对于不是interfaces的下接口的实例函数,是否就没有办法实现动态代理呢?CGLib作为一个替代者,填补了这项空缺。
CGLib采用底层的字节码技术,可以为一个类生成子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑,下面是采用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
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();

public Object getProxy(Class clazz) {
//通过字节码技术动态创建子类实例
enhancer.setSuperClass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
pmomitor.start();
transManager.beginTransaction();
Object result = proxy.invokeSuper(obj, args);
pmonitor.end();
return result;
}
}

public class ForumServiceTest {
@Test
public void proxy() {
CglibProxy proxy = new CglibProxy();
ForumService forumServiceProxy = (ForumService) proxy.getProxy(ForumServiceImpl.class)
forumServiceProxy.createForum(10);
forumServiceProxy.removeTopic(1012);
}
}

在上面的代码中,用户可以通过getProxy方法为一个类创建动态代理对象,无论这个类中的函数是否是从接口实现而来还是类本身的函数,当调用getProxy返回的对象时,横切逻辑都能得到执行。
基于以上两个简单的例子,让我们来看一下Spring中实现动态代理的逻辑。

Spring中代理的实现逻辑

在Spring中,动态代理的创建是在Bean实例化并初始化之后,调用的几个BeanPostProcessor中的进行的,这几个BeanPostProcessor后处理器分别是BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator以及AnnotationAwareAspectJAutoProxyCreator。这三个类都是AbstractAutoProxyCreator类的子类,在该抽象类中有一个保护方法:

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
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}

return proxyFactory.getProxy(getProxyClassLoader());
}

我们重点看ProxyFactory proxyFactory = new ProxyFactory();该函数使用了一个代理工厂生成目标类的代理,而ProxyFactory类的getProxy方法:

1
2
3
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

中我们看到其调用了ProxyFacory父类ProxyCreatorSupport中的createAopProxy方法:

1
2
3
4
5
6
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}

getAopProxyFactory放回的是一个AopProxyFactory接口的实例,而AopProxyFactory唯一的一个实例便是DefaultAopProxyFactory,其createAopProxy逻辑为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

从这个函数中我们可以看出,Spring使用了之前介绍的两种代理技术——JDK与CGLib,根据不同的条件,则会使用不同的代理技术来创建代理。其中,ObjenesisCglibAopProxy(继承自CglibAopProxy)使用类CgLib动态代理技术创建代理,而JdkDynamicAopProxy使用JDK动态代理技术创建代理。如果通过ProxyFactory的setInterfaces方法指定了目标接口进行代理,则ProxyFactory使用JdkDynamicAopProxy;如果是针对类的代理,则使用CglibAopProxy。此外,还可以通过ProxyFacory的setOptimize(true)方法让ProxyFactory启动优化代理方式,这样,针对接口的代理也会使用CglibAopProxy。

至此,我们介绍了Spring中AOP实现的基础——动态代理的原理以及Spring对两种代理技术的集成。可以看出,JDK的动态代理是基于组合模式的,其代理对象持有一个目标类的实例。而CGLib则是利用字节码技术,基于继承实现的动态代理子类。本文没有介绍具体的AOP的使用方式,重点是介绍AOP的底层实现细节。由于AOP的使用方式介绍起来篇幅过大,有机会的话一定会好好总结下~

欢迎关注个人公众号:
个人公号

0%