Go-net源码解析
Posted Dav-ove3
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go-net源码解析相关的知识,希望对你有一定的参考价值。
学习一门语言,那么我们必然要涉及到网络通信,而谈到网络通信却又离不开tcp,这里我们利用go标准库net来模拟一个服务端、客户端的流程,从而深入学习其中的代码流程(深入其中解析本质)
func main()
server, err := net.Listen("tcp", "127.0.0.1:8080") // 开启服务端
if err != nil
log.Fatal(err)
defer server.Close()
go func() // 模拟客户端往服务端发送数据
coon, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil
log.Fatal(err)
defer coon.Close()
coon.Write([]byte("hello world")) // 客户端发送数据
()
client, _ := server.Accept() // 接受客户端
defer client.Close()
fmt.Println("client addr: ", client.LocalAddr().String()) // 打印服务端地址
var recv []byte = make([]byte, 100)
client.Read(recv)
//recv, _ := io.ReadAll(client)
fmt.Println("server recv from client: ", string(recv)) // 打印客户端发送的数据
以上代码完成了一个简单的网络通信过程,那么我们就先从服务端入手吧,这里显而易见的是net.Listen("tcp", "127.0.0.1:8080")
这个函数,我们可以猜想这个函数的处理流程应该是极其重要的,跟进看看:
func Listen(network, address string) (Listener, error)
var lc ListenConfig
return lc.Listen(context.Background(), network, address) // 返回一个Listener接口
Listener接口必然映射这一个底层的结构体,那么我们就找到这个结构体分析一下:(跟入lc.Listen(context.Background(), network, address)
函数
func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error)
addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
if err != nil
return nil, &OpErrorOp: "listen", Net: network, Source: nil, Addr: nil, Err: err
sl := &sysListener // 实例化一个sysListener结构体
ListenConfig: *lc,
network: network,
address: address,
var l Listener
la := addrs.first(isIPv4)
switch la := la.(type) // 检查监听的协议
case *TCPAddr:
l, err = sl.listenTCP(ctx, la) // 监听tcp,并设置l为TCPListener结构体
case *UnixAddr:
l, err = sl.listenUnix(ctx, la) // 设置l为UnixListener结构体
default:
return nil, &OpErrorOp: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrErrorErr: "unexpected address type", Addr: address
if err != nil
return nil, &OpErrorOp: "listen", Net: sl.network, Source: nil, Addr: la, Err: err // l is non-nil interface containing nil pointer
return l, nil
而最终返回的结构体则是从sl.listenTCP(ctx, la)
或者sl.listenUnix(ctx, la)
得到的,那我们就由sl.listenTCP(ctx, la)
来分析底层的结构体:
func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error)
fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control) // 相当于C语言之中的socket函数
if err != nil
return nil, err
return &TCPListenerfd: fd, lc: sl.ListenConfig, nil // 返回的是TCPListener结构体
其实际流程为初始化一个socket套接字,并封装于TCPListener结构体之中
type TCPListener struct
fd *netFD // 套接字
lc ListenConfig // TCP配置
type ListenConfig struct
Control func(network, address string, c syscall.RawConn) error
KeepAlive time.Duration
那么上层使用的Accpet函数实际为:
func (l *TCPListener) Accept() (Conn, error)
if !l.ok()
return nil, syscall.EINVAL
c, err := l.accept() // 开始监听,调用内部函数
if err != nil
return nil, &OpErrorOp: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err
return c, nil
func (ln *TCPListener) accept() (*TCPConn, error)
fd, err := ln.fd.accept() // 开始监听,对于netFD结构体的一层封装
if err != nil
return nil, err
tc := newTCPConn(fd) // 返回tcp客户端,设置tc为TCPConn结构体
if ln.lc.KeepAlive >= 0 // 如果持续存活
setKeepAlive(fd, true)
ka := ln.lc.KeepAlive
if ln.lc.KeepAlive == 0
ka = defaultTCPKeepAlive
setKeepAlivePeriod(fd, ka)
return tc, nil
而返回结果则是tcp对于的一个客户端会话:
type TCPConn struct // 实际上就是一个套接字,经过了层层(二层)封装
conn
// TCPConn 继承于 conn,而conn是套接字的封装
type conn struct // 对于套接字的一层封装,具有有读写等功能
fd *netFD
同理,客户端对于服务端的连接流程大致一样,同样从net.Dial("tcp", "127.0.0.1:8080")
开始分析:
func Dial(network, address string) (Conn, error)
var d Dialer // 拨号器
return d.Dial(network, address) // connect封装
func (d *Dialer) Dial(network, address string) (Conn, error)
return d.DialContext(context.Background(), network, address)
// 关键函数如下
func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error)
...
c, err := sd.dialParallel(ctx, primaries, fallbacks) // 解析 `整个连接`
...
关键函数sd.dialParallel(ctx, primaries, fallbacks)
,如下:
func (sd *sysDialer) dialParallel(ctx context.Context, primaries, fallbacks addrList) (Conn, error)
if len(fallbacks) == 0
return sd.dialSerial(ctx, primaries)
returned := make(chan struct)
defer close(returned)
type dialResult struct
Conn
error
primary bool
done bool
results := make(chan dialResult) // unbuffered
startRacer := func(ctx context.Context, primary bool)
ras := primaries
if !primary
ras = fallbacks
c, err := sd.dialSerial(ctx, ras) // 开始尝试连接
select
case results <- dialResultConn: c, error: err, primary: primary, done: true: // 传输连接成功返回的结果
case <-returned: //如果该函数应该返回,则释放资源
if c != nil
c.Close()
var primary, fallback dialResult
// Start the main racer.
primaryCtx, primaryCancel := context.WithCancel(ctx)
defer primaryCancel()
go startRacer(primaryCtx, true)
// Start the timer for the fallback racer.
fallbackTimer := time.NewTimer(sd.fallbackDelay())
defer fallbackTimer.Stop()
for // 循环等待
select
case <-fallbackTimer.C:
fallbackCtx, fallbackCancel := context.WithCancel(ctx)
defer fallbackCancel()
go startRacer(fallbackCtx, false)
case res := <-results: // 等待结果返回,实际为dialResult结构体
if res.error == nil
return res.Conn, nil
if res.primary
primary = res
else
fallback = res
if primary.done && fallback.done
return nil, primary.error
if res.primary && fallbackTimer.Stop()
// If we were able to stop the timer, that means it
// was running (hadn\'t yet started the fallback), but
// we just got an error on the primary path, so start
// the fallback immediately (in 0 nanoseconds).
fallbackTimer.Reset(0)
好了,现在我们可以看出来,net
实际上就是对于socket
的层层封装罢了
Spring 源码解析之HandlerAdapter源码解析
Spring 源码解析之HandlerAdapter源码解析(二)
前言
看这篇之前需要有
Spring 源码解析之HandlerMapping源码解析(一)
这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了
解决上篇文章遗留的问题
getHandler(processedRequest)
这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMappinggetHandlerAdapter(mappedHandler.getHandler());
如果取到对应的HandlerAdapter
问题一
先来看DispatcherServlet中HandlerMapping初始化,从下属代码看来,其实HandlerMapping的初始化工作并不在DispatcherServlet,而是在Spring初始化的地方,这里只不过是把所有的HandlerMapping加载到DispatcherServlet中,并且排序生成集合
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//这里从BeanFactory中获取已经初始化好的HandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
//对handlerMappings进行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
接下来是查找适合的HandlerExecutionChain,从代码来看,上面的排序代码
AnnotationAwareOrderComparator.sort(this.handlerMappings);
是有很大用处的,从这段代码逻辑来看,其实是通过for循环优先找到排在最前面并且适合的HandlerExecutionChain,从上一篇《Spring 源码解析之HandlerMapping源码解析(一)》文章可知,我这里大概初始化了十种处理请求的handlerMappings,普通的页面请求和json请求都是通过RequestMappingHandlerMapping进行处理的
//DispatcherServlet中的getHandler
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;
}
//RequestMappingHandlerMapping的父类AbstractHandlerMapping中实现了getHandler方法
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//调用子类AbstractHandlerMethodMapping getHandlerInternal()
Object handler = getHandlerInternal(request);
//如果没有获取到,就使用默认的
if (handler == null) {
handler = getDefaultHandler();
}
//如果默认的没有定义就返回null
if (handler == null) {
return null;
}
//说实话当时我看到源码里面的注释,当时我内心是崩溃的,Spring开发人员自己都不知道这个代码是做什么的,你难道是问我 - -
//你tm一定是在逗我
// Bean name or resolved handler?(这个英文注释是开发人员自己写的,不是我写上去的)
//反正大概意思就是handler如果定义了一个字符串,那么根据这个name去查找Handler
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//根据handlermethod获取相应的拦截器处理调用链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
//AbstractHandlerMethodMapping 中实现AbstractHandlerMapping getHandlerInternal方法,去获取HandlerMethod
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取访问路径比如定义了路径/admin/login
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
//获取可读锁,暂时不知道用意在哪里
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//解锁
this.mappingRegistry.releaseReadLock();
}
}
//AbstractHandlerMethodMapping 中根据路径查询到相应的Controller方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//从注册的mappingRegistry中获取直接符合的
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
//如果不等于null,添加符合的进去
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
//排序后取第一个符合的
Match bestMatch = matches.get(0);
//如果符合处理请求HandlerMethod的数量大于1
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
//判断第一个和第二个符合的,如果都一样 那么就抛出异常
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
// 存入request的attribute中,具体有什么用不太了解request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
handleMatch(bestMatch.mapping, lookupPath, request);
//获取handlerMethod
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
//AbstractHandlerMapping中获取HandlerExecutionChain调用链
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
大致流程如上面代码,所示其实核心无非就是把url和controller里面的method关联起来,通过查找去找到合适的
问题二
如何根据合适的找到合适的
HandlerAdapter
,看看下面代码
//初始化就不继续讲拉,跟handpermapping一模一样,也是经过排序后的
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//遍历所有的handlerAdapters
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
//调用相应实现的support方法,排在最前面的优先调用
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
//比如说`RequestMappingHandlerAdapter`的实现如下 ,用instanceof 来判断是否是HandlerMethod即可
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
从上面看来,其实想定义自己的HandlerAdapter也是可以的,只要把排序的Order设置大一些,优先找到自定义的HandlerAdapter即可
HandlerAdapter功能介绍
这里再次详细介绍一下HandlerAdapter,HandlerAdapter实现大概分为以下几种
1.RequestMappingHandlerAdapter(3.1之前是AnnotationMethodHandlerAdapter)
这里我就直接先分析
RequestMappingHandlerAdapter
,大致类图如上所示,RequestMappingHandlerAdapter主要作用是调用相应的HandlerMethod,并把参数封装成具体method需要的格式,初始化的时候RequestMappingHandlerAdapter默认加载了如下的参数解析方式
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
//添加RequestParam 普通的参数解析@RequestParam 注解参数,但不处理参数类型为Map,且不包含value值
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
//Resolves {@link Map} method arguments annotated with an @{@link RequestParam}
//上述英文描述,大致就是说把参数解析成map的Resolver,解决了RequestParamMethodArgumentResolver不能解析成map的缺点
resolvers.add(new RequestParamMapMethodArgumentResolver());
//PathVariableMethod这个经常用spring的都知道,其实就是路径里面的参数,restful风格的参数
resolvers.add(new PathVariableMethodArgumentResolver());
//处理是参数类型是map的情况
/** @RequestMapping(value = "/index/article/{id:\\\\d+}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String getArticleDetail(@PathVariable Map map) {
int id=0;
*/
//比如上面的id 转换到map中,就是key为 id value为具体值
resolvers.add(new PathVariableMapMethodArgumentResolver());
//spring 3.2后增加的注解@MatrixVariable的解析,是 @PathVariable的一种辅助注解,具体可以百度下看看使用方式
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
//Also adds a fall-back strategy to instantiate the model attribute from a URI template variable or from a request parameter if the name matches the model attribute name and there is an appropriate type conversion strategy.
//上述英文是Spring文档里面对这个类的描述,大概意思就是request的attribute name名跟model的属性名相同的时候
resolvers.add(new ServletModelAttributeMethodProcessor(false));
//@RequestBody和@ResponseBody 注解的解析
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
//Supported method argument types include MultipartFile
//这个主要是文件上传的解析,文件上传的时候可以指定名字,支持@RequestPart
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
//支持@RequestHeader
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
//支持@RequestHeader map参数类型
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
//支持Cookie参数
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
//支持注解@Value
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
//上面都是基于注解的解析,下面都是基于参数名称的解析
// Type-based argument resolution
/**
Resolves request-related method argument values of the following types:
WebRequest
ServletRequest
MultipartRequest
HttpSession
Principal
Locale
TimeZone (as of Spring 4.0)
ZoneId (as of Spring 4.0 and Java 8)
InputStream
Reader
HttpMethod (as of Spring 4.0)
**/
//这个主要是解析上面的参数
resolvers.add(new ServletRequestMethodArgumentResolver());
//处理返回类型
/**
* ServletResponse
* OutputStream
* Writer
**/
resolvers.add(new ServletResponseMethodArgumentResolver());
//解析 HttpEntity RequestEntity
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
//Resolves RedirectAttributes
resolvers.add(new RedirectAttributesMethodArgumentResolver());
//Resolves Model arguments and handles Model return values
//解析Model参数
resolvers.add(new ModelMethodProcessor());
//Resolves Map method arguments and handles Map return values.
resolvers.add(new MapMethodProcessor());
// Resolves Errors method arguments
resolvers.add(new ErrorsMethodArgumentResolver());
//Resolves a SessionStatus argument
resolvers.add(new SessionStatusMethodArgumentResolver());
// Resolvers argument values of type UriComponentsBuilder
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
//这里可以自己实现getCustomArgumentResolvers() 去定义自己的参数实现
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
//Resolves method arguments annotated with @RequestParam
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
//Resolves 参数包含注解ModelAttribute
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
通过上述的代码表明,
RequestMappingHandlerAdapter
封装了所有的参数解析,继续看核心内容,handlerAdapter对方法的调用逻辑如下
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav = null;
//校验该请求的method是否支持, 也就是这上面支持的方法@RequestMapping(value = "edit/save", method = RequestMethod.POST)
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
//是否采用session 锁,这块暂时没有找到在哪用,倒是可以通过配置bean的方式来实现,下面是 英文的解释
/**
Set if controller execution should be synchronized on the session, to serialize parallel invocations from the same client.
More specifically, the execution of the handleRequestInternal method will get synchronized if this flag is "true". The best available session mutex will be used for the synchronization; ideally, this will be a mutex exposed by HttpSessionMutexListener.
The session mutex is guaranteed to be the same object during the entire lifetime of the session, available under the key defined by the SESSION_MUTEX_ATTRIBUTE constant. It serves as a safe reference to synchronize on for locking on the current session.
In many cases, the HttpSession reference itself is a safe mutex as well, since it will always be the same object reference for the same active logical session. However, this is not guaranteed across different servlet containers; the only 100% safe way is a session mutex.
*/
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
//具体的方法调用处
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
}
//这块感觉跟上面的逻辑有点重复,如果加了锁并且锁住了上面执行一遍invokeHandlerMethod 这里还会执行一遍invokeHandlerMethod,无论做了什么操作,性能上会有一些下降吧,个人看法
mav = invokeHandlerMethod(request, response, handlerMethod);
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
return mav;
}
invokeHandlerMethod(request, response, handlerMethod)
是具体调用Controller的部分
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
///包装一下request, response 提供一些操作方法
ServletWebRequest webRequest = new ServletWebRequest(request, response);
//获取到DataBinderFactory(这里创建的是ServletRequestDataBinderFactory) 也就是经常使用的@InitBinder,这里以后会单独留一个模块出来进行讲解
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//Provides methods to initialize the Model before controller method invocation and to update it afterwards
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//这里是异步请求的管理,这里暂时不做介绍,对正常的同步请求没有影响
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//这里调用具体的方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//返回响应的ModelAndView对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
上面调用了
invocableMethod.invokeAndHandle(webRequest, mavContainer)
,这个方法在ServletInvocableHandlerMethod中具体实现如下
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//调用invokeForRequest实现去具体调用controller方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//设置response状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
//主要解析方法参数,然后调用真正的反射方法
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//通过之前的参数解析器把参数解析成相应的各个对象
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
//进行方法的调用
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}
//doInvoke才是真正调用方法的地方
protected Object doInvoke(Object... args) throws Exception {
//先通过设置方法的访问权限,用过反射的都知道先setAccessible为true
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
//执行调用
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(getInvocationErrorMessage(message, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
throw new IllegalStateException(msg, targetException);
}
}
}
2.SimpleServletHandlerAdapter
看名字就能够大致猜到这个类的作用,
SimpleServletHandlerAdapter
从翻译上来说叫做简单的Servlet处理适配器,源码如下所示,看起来确实没有几句代码,大致英文解释(Adapter to use the Servlet interface with the generic DispatcherServlet.Calls the Servlet’s {@code service} method to handle a request)。大概就是说可以用Spring去管理Servlet,这样就可以在Spring中使用Servlet,这就是说Servlet可以使用ioc和aop的一些功能了,但是感觉这样的使用场景并不多,所以也很少使用,其次还有另外一种使用Spring管理Servlet的方式,就是采用全注解配置DispatcherServlet的方式,所以感觉这个adapter没有太大用处
public class SimpleServletHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Servlet);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((Servlet) handler).service(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
}
参照网上的实习代码如下所示,servlet的bean name代表了请求路径:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd"
default-lazy-init="true">
<!--启用注解 定义组件查找规则 -->
<context:component-scan base-package="com.demo">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service" />
</context:component-scan>
<!-- servlet适配器,这里必须明确声明,因为spring默认没有初始化该适配器 -->
<bean id="servletHandlerAdapter" class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
<!-- demo servlet -->
<bean name="/demo.do" class="com.demo.DemoServlet"/>
</beans>
3.SimpleControllerHandlerAdapter
The Controller interface is explicitly designed to operate on HttpServletRequest and HttpServletResponse objects, just like an HttpServlet. It does not aim to decouple itself from the Servlet API
上面英文大概意思就是,让Controller的用法像使用servlet一样,需要Controller实现Controller接口,实现代码如下,无非就是直接调用Controller方法
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
总结
本文在这次介绍中总共留下几个流程没有讲述,在接下来的里面会更详细的介绍
1. WebDataBinderFactory 也就是@InitBinder实现的流程?
2. WebAsyncManager 和AsyncWebRequest 这些都是异步请求的管理?
3. Spring是如何知道请求对应Controller的方法的?
4. Spring模板的渲染机制?
其他地方如果读者有问题可以随时联系,你的问题也许是我没有考虑到的地方。
欢迎查看下篇文章Spring 源码解析之HandlerAdapter源码解析(三)
以上是关于Go-net源码解析的主要内容,如果未能解决你的问题,请参考以下文章
Spring 源码解析之HandlerAdapter源码解析
spring系统架构源码解析AutowireCandidateResolver