【spring】spring是如何避免请求的?源码分析
【spring】spring是如何处理请求的?源码分析
最近在处理一些问题的时候,突然想到spring的两个问题。
1.spring是如何回调我们的controller中定义的方法的,request response model 是怎么来的?为什么先后顺序可以颠倒?
2.spring是如何帮我们注入基本类型的参数的,比如我参数中有两个String,但是参数名字不一样,一个是id,一个是type,但是spring可以直接帮我们注入进来,不需要添加任何注解,他是怎么实现的?
抱着这两个问题,我开始看spring是如何处理请求的。
首先,spring是为我们封装了servlet,所以肯定会有一个类继承HttpServlet,从这个入口开始寻找,我们就找到了DispatcherServlet ,他是HttpServlet的一个实现类。其中重写了父类的doService方法。看到方法最后有调用doDispatch(request, response);方法,到了spring的处理器。
在这个方法中做了如下几件事情
1.寻找URL所对应的mappedHandler处理器,如果找不到的话,执行noHandlerFound方法,其中就是会返回404。
2.如果是get请求的话,并且HTTP请求头标签中包含If-Modified-Since,在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端接到之后,就直接把本地缓存文件显示到浏览器中。
3.获取到自己定义的拦截器,循环invoke拦截器。
4.回调我们所定义的mapperHandler 也就是我们再controller中所定义的处理这个请求的方法。
5.获取到返回的 MV 通过response写出。
下面主要来看下第四部,spring是怎么回调我们自己定义的controller的。
这里ha是HandlerAdapter 接口,根据spring的配置文件,走不同的处理器。
如上配置可以看到,配置的处理器为RequestMappingHandlerAdapter。他实现了父类的handler方法,然后调用子类的handleInternal方法处理请求。
RequestMappingHandlerAdapter中实现了父类的handleInternal,在这个类里面主要做了一些限制判断。
1.如HTTP请求方式和方法上限制了HTTP请求不同的话,则会抛出HttpRequestMethodNotSupportedException。
2.如果在配置文件中配置了requireSession=true的话,此处会抛出 HttpSessionRequiredException("Pre-existing session required but none found");
现在回到我们handleInternal方法中,接下去如果我们配置了synchronizeOnSession配置,表示该控制器是否在执行时同步session,从而保证该会话的用户串行访问该控制器。接下去执行invokeHandlerMethod方法。
这里主要看一下 requestMappingMethod.invokeAndHandle(webRequest, mavContainer); 中的invokeForRequest方法。
这个方法中,主要做两件事情,
1.获取到了本次请求所对应执行方法的参数。
2.通过反射invoke 调用方法。注入参数
这里也是让我一开始很困惑的地方,因为反射只能获取到方法参数的类型,无法获取到方法参数的变量名,那spring是如何做到获取到方法参数变量名呢?我们继续往下面看。
看到spring通过getMethodArgumentValues方法获取了所有的参数列表。
其中resolveArgument的方法中HandlerMethodArgumentResolver是一个接口,通过 getArgumentResolver(parameter); 方法获取到不同的解析器。来解析参数值。
HandlerMethodArgumentResolver接口中定义了resolveArgument让子类来实现方法,获取到具体的参数,在@RequestParam注解的参数的解析器是使用的AbstractNamedValueMethodArgumentResolver类来实现的。我们具体来看一下AbstractNamedValueMethodArgumentResolver类中的实现。
看到如上代码中,首先获取到了某个参数的类型,再通过getNamedValueInfo(parameter);方法返回了NamedValueInfo对象,在NamedValueInfo对象中,就已经包含了当前参数的名字是什么,那就继续看getNamedValueInfo方法。
spring代码中写了一个cache用于保存已经解析过的nameValueInfo,因为在程序运行期间,这个值肯定是固定的。
如果cache中返回空的话,则会通过createNamedValueInfo(parameter);方法创建一个nameValueInfo,再通过updateNamedValueInfo(parameter, namedValueInfo);更新内部的name,这里createNamedValueInfo是一个抽象方法,通过子类去实现,如PathVariableMethodArgumentResolver,RequestParamMethodArgumentResolver等等。这里我们主要看一下RequestParamMethodArgumentResolver中的createNamedValueInfo方法。和
首先,spring先获取了我们家在参数上的RequestParam的注解,如果为空的话,就调用RequestParamNamedValueInfo的无参构造,否则通过annotation构建RequestParamNamedValueInfo。默认的值为value="" , require = false , DEFAULT_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n"。
下面来看一下updateNamedValueInfo的方法,
首先先获取了info里面的name,如果name非空的话,name=parameter.getParameterName(); , 在getParameterName方法中获取到了参数的变量名。
可以看到根据当前的method是否为空来判断是根据method获取参数名还是根据构造器获取参数名。我们现在这里是通过LocalVariableTableParameterNameDiscoverer中的getParameterNames方法来获取参数名字,返回的是一个参数名字的数组,然后根据当前的索引值parameterIndex来确定返回的参数是哪一个,下面来看下一内部的具体实现。
首先先获取当前method的作用的class,先在缓存中看看当前class有没有,(注意缓存的结构,Map<Class<?>, Map<Member, String[]>> key是class , value是Method 和 方法参数的对应关系)。如果有的话直接返回,否则把当前class传入inspectClass方法获取方法的参数值。
这里spring通过类加载器获取到这个类的inputStream,最后通过了ClassReader获取到了类中的所有方法和方法参数,依赖于asm的jar包。ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。具体请看
http://www.cnblogs.com/liuling/archive/2013/05/25/asm.html
看到这里才知道,原来spring不是通过反射来获取到的,而是通过asm jar包来解析class的字节获取到了方法的参数名字,用来注入到方法中。
下面是本人看完spring源码的一点心得
1.spring 在很多地方使用了缓存Map,用于提高性能,我们再编写代码的时候是否也可以做到呢?
2.java本来是反射是不支持获取到方法参数的变量名的,spring为了提供更好的服务,通过解析class的字节码,来完成这项功能,那我们在编写代码的时候是否可以做到呢?
最近在处理一些问题的时候,突然想到spring的两个问题。
1.spring是如何回调我们的controller中定义的方法的,request response model 是怎么来的?为什么先后顺序可以颠倒?
2.spring是如何帮我们注入基本类型的参数的,比如我参数中有两个String,但是参数名字不一样,一个是id,一个是type,但是spring可以直接帮我们注入进来,不需要添加任何注解,他是怎么实现的?
抱着这两个问题,我开始看spring是如何处理请求的。
首先,spring是为我们封装了servlet,所以肯定会有一个类继承HttpServlet,从这个入口开始寻找,我们就找到了DispatcherServlet ,他是HttpServlet的一个实现类。其中重写了父类的doService方法。看到方法最后有调用doDispatch(request, response);方法,到了spring的处理器。
/** * Process the actual dispatching to the handler. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; try { ModelAndView mv; boolean errorView = false; try { processedRequest = checkMultipart(request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == 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 (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // Apply preHandle methods of registered interceptors. HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // Do we need view name translation? if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } // Apply postHandle methods of registered interceptors. if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } }catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); }catch (Exception ex) { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv != null); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, 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"); } } // Trigger after-completion for successful outcome. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); }catch (Exception ex) { // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; }catch (Error err) { ServletException ex = new NestedServletException("Handler processing failed", err); // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; }finally { // Clean up any resources used by a multipart request. if (processedRequest != request) { cleanupMultipart(processedRequest); } } }
在这个方法中做了如下几件事情
1.寻找URL所对应的mappedHandler处理器,如果找不到的话,执行noHandlerFound方法,其中就是会返回404。
2.如果是get请求的话,并且HTTP请求头标签中包含If-Modified-Since,在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端接到之后,就直接把本地缓存文件显示到浏览器中。
3.获取到自己定义的拦截器,循环invoke拦截器。
4.回调我们所定义的mapperHandler 也就是我们再controller中所定义的处理这个请求的方法。
5.获取到返回的 MV 通过response写出。
下面主要来看下第四部,spring是怎么回调我们自己定义的controller的。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这里ha是HandlerAdapter 接口,根据spring的配置文件,走不同的处理器。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="webBindingInitializer" ref="webBindingInitializer"/> <property name="requireSession" value="false"/> <property name="order" value="0"/> </bean>
如上配置可以看到,配置的处理器为RequestMappingHandlerAdapter。他实现了父类的handler方法,然后调用子类的handleInternal方法处理请求。
/** * {@inheritDoc} <p>This implementation expects the handler to be an {@link HandlerMethod}. */ public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) hrows Exception { return handleInternal(request, response, (HandlerMethod) handler); }
@Override protected final ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response,HandlerMethod handlerMethod) throws Exception { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()){ // Always prevent caching in case of session attribute management. checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true); } else { // Uses configured default cacheSeconds setting. checkAndPrepare(request, response, true); } // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return invokeHandlerMethod(request, response, handlerMethod); } } } return invokeHandlerMethod(request, response, handlerMethod); }
RequestMappingHandlerAdapter中实现了父类的handleInternal,在这个类里面主要做了一些限制判断。
1.如HTTP请求方式和方法上限制了HTTP请求不同的话,则会抛出HttpRequestMethodNotSupportedException。
2.如果在配置文件中配置了requireSession=true的话,此处会抛出 HttpSessionRequiredException("Pre-existing session required but none found");
现在回到我们handleInternal方法中,接下去如果我们配置了synchronizeOnSession配置,表示该控制器是否在执行时同步session,从而保证该会话的用户串行访问该控制器。接下去执行invokeHandlerMethod方法。
/** * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} if view resolution is required. */ private ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, requestMappingMethod); mavContainer.setIgnoreDefaultModelOnRedirect (this.ignoreDefaultModelOnRedirect); requestMappingMethod.invokeAndHandle(webRequest, mavContainer); modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; }else { ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); RequestContextUtils.getOutputFlashMap(request). putAll(flashAttributes); } return mav; } }
这里主要看一下 requestMappingMethod.invokeAndHandle(webRequest, mavContainer); 中的invokeForRequest方法。
public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { StringBuilder builder = new StringBuilder("Invoking ["); builder.append(this.getMethod().getName()).append("] method with arguments "); builder.append(Arrays.asList(args)); logger.trace(builder.toString()); } Object returnValue = invoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]"); } return returnValue; }
这个方法中,主要做两件事情,
1.获取到了本次请求所对应执行方法的参数。
2.通过反射invoke 调用方法。注入参数
这里也是让我一开始很困惑的地方,因为反射只能获取到方法参数的类型,无法获取到方法参数的变量名,那spring是如何做到获取到方法参数变量名呢?我们继续往下面看。
看到spring通过getMethodArgumentValues方法获取了所有的参数列表。
/** * Get the method argument values for the current request. */ private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(parameterNameDiscoverer); GenericTypeResolver.resolveParameterType(parameter, getBean().getClass()); args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (argumentResolvers.supportsParameter(parameter)) { try { args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory); continue; } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex); } throw ex; } } if (args[i] == null) { String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); throw new IllegalStateException(msg); } } return args; }
其中resolveArgument的方法中HandlerMethodArgumentResolver是一个接口,通过 getArgumentResolver(parameter); 方法获取到不同的解析器。来解析参数值。
HandlerMethodArgumentResolver接口中定义了resolveArgument让子类来实现方法,获取到具体的参数,在@RequestParam注解的参数的解析器是使用的AbstractNamedValueMethodArgumentResolver类来实现的。我们具体来看一下AbstractNamedValueMethodArgumentResolver类中的实现。
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)throws Exception { Class<?> paramType = parameter.getParameterType(); NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); Object arg = resolveName(namedValueInfo.name, parameter, webRequest); if (arg == null) { if (namedValueInfo.defaultValue != null) { arg = resolveDefaultValue(namedValueInfo.defaultValue); } else if (namedValueInfo.required) { handleMissingValue(namedValueInfo.name, parameter); } arg = handleNullValue(namedValueInfo.name, arg, paramType); } if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); arg = binder.convertIfNecessary(arg, paramType, parameter); } handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; }
看到如上代码中,首先获取到了某个参数的类型,再通过getNamedValueInfo(parameter);方法返回了NamedValueInfo对象,在NamedValueInfo对象中,就已经包含了当前参数的名字是什么,那就继续看getNamedValueInfo方法。
/** * Obtain the named value for the given method parameter. */ private NamedValueInfo getNamedValueInfo(MethodParameter parameter) { NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter); if (namedValueInfo == null) { namedValueInfo = createNamedValueInfo(parameter); namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo); this.namedValueInfoCache.put(parameter, namedValueInfo); } return namedValueInfo; }
spring代码中写了一个cache用于保存已经解析过的nameValueInfo,因为在程序运行期间,这个值肯定是固定的。
如果cache中返回空的话,则会通过createNamedValueInfo(parameter);方法创建一个nameValueInfo,再通过updateNamedValueInfo(parameter, namedValueInfo);更新内部的name,这里createNamedValueInfo是一个抽象方法,通过子类去实现,如PathVariableMethodArgumentResolver,RequestParamMethodArgumentResolver等等。这里我们主要看一下RequestParamMethodArgumentResolver中的createNamedValueInfo方法。和
@Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { RequestParam annotation = parameter.getParameterAnnotation(RequestParam.class); return (annotation != null) ? new RequestParamNamedValueInfo(annotation) : new RequestParamNamedValueInfo(); }
首先,spring先获取了我们家在参数上的RequestParam的注解,如果为空的话,就调用RequestParamNamedValueInfo的无参构造,否则通过annotation构建RequestParamNamedValueInfo。默认的值为value="" , require = false , DEFAULT_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n"。
下面来看一下updateNamedValueInfo的方法,
/** * Create a new NamedValueInfo based on the given NamedValueInfo with sanitized values. */ private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) { String name = info.name; if (info.name.length() == 0) { name = parameter.getParameterName(); Assert.notNull(name, "Name for argument type [" + parameter.getParameterType().getName() + "] not available, and parameter name information not found in class file either."); } String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue); return new NamedValueInfo(name, info.required, defaultValue); }
首先先获取了info里面的name,如果name非空的话,name=parameter.getParameterName(); , 在getParameterName方法中获取到了参数的变量名。
public String getParameterName() { if (this.parameterNameDiscoverer != null) { String[] parameterNames = (this.method != null ? this.parameterNameDiscoverer.getParameterNames(this.method) : this.parameterNameDiscoverer.getParameterNames(this.constructor)); if (parameterNames != null) { this.parameterName = parameterNames[this.parameterIndex]; } this.parameterNameDiscoverer = null; } return this.parameterName; }
可以看到根据当前的method是否为空来判断是根据method获取参数名还是根据构造器获取参数名。我们现在这里是通过LocalVariableTableParameterNameDiscoverer中的getParameterNames方法来获取参数名字,返回的是一个参数名字的数组,然后根据当前的索引值parameterIndex来确定返回的参数是哪一个,下面来看下一内部的具体实现。
public String[] getParameterNames(Method method) { Class<?> declaringClass = method.getDeclaringClass(); Map<Member, String[]> map = this.parameterNamesCache.get(declaringClass); if (map == null) { // initialize cache map = inspectClass(declaringClass); this.parameterNamesCache.put(declaringClass, map); } if (map != NO_DEBUG_INFO_MAP) { return map.get(method); } return null; }
首先先获取当前method的作用的class,先在缓存中看看当前class有没有,(注意缓存的结构,Map<Class<?>, Map<Member, String[]>> key是class , value是Method 和 方法参数的对应关系)。如果有的话直接返回,否则把当前class传入inspectClass方法获取方法的参数值。
/** * Inspects the target class. Exceptions will be logged and a maker map returned * to indicate the lack of debug information. */ private Map<Member, String[]> inspectClass(Class<?> clazz) { InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz)); if (is == null) { // We couldn't load the class file, which is not fatal as it // simply means this method of discovering parameter names won't work. if (logger.isDebugEnabled()) { logger.debug("Cannot find '.class' file for class [" + clazz + "] - unable to determine constructors/methods parameter names"); } return NO_DEBUG_INFO_MAP; } try { ClassReader classReader = new ClassReader(is); Map<Member, String[]> map = new ConcurrentHashMap<Member, String[]>(); classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), false); return map; } catch (IOException ex) { if (logger.isDebugEnabled()) { logger.debug("Exception thrown while reading '.class' file for class [" + clazz + "] - unable to determine constructors/methods parameter names", ex); } } finally { try { is.close(); } catch (IOException ex) { // ignore } } return NO_DEBUG_INFO_MAP; }
这里spring通过类加载器获取到这个类的inputStream,最后通过了ClassReader获取到了类中的所有方法和方法参数,依赖于asm的jar包。ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。具体请看
http://www.cnblogs.com/liuling/archive/2013/05/25/asm.html
看到这里才知道,原来spring不是通过反射来获取到的,而是通过asm jar包来解析class的字节获取到了方法的参数名字,用来注入到方法中。
下面是本人看完spring源码的一点心得
1.spring 在很多地方使用了缓存Map,用于提高性能,我们再编写代码的时候是否也可以做到呢?
2.java本来是反射是不支持获取到方法参数的变量名的,spring为了提供更好的服务,通过解析class的字节码,来完成这项功能,那我们在编写代码的时候是否可以做到呢?