Spring源码探索-核心原理下(AOP、MVC)
AOP
AOP是在Bean的后置处理器中设置的也就是在初始化Bean的时候(initializeBean
AOP源码的入口函数)。
这里重点主要分为两个部分一个是代码织入的部分也就是编译阶段,还有一个就是代码运行阶段。
时序图
核心代码
initializeBean
applyBeanPostProcessorsAfterInitialization
这个就是遍历所有的后置处理器,进行一些代码的织入,比如AOP的代码织入
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
在
Spring
中,BeanPostProcessor
的实现子类非常的多,分别完成不同的操作,如:AOP面向切
面编程的注册通知适配器、Bean
对象的数据校验、Bean
继承属性、方法的合并等等,我们以最简单的
AOP 切面织入来简单了解其主要的功能。下面我们来分析其中一个创建 AOP 代理对象的子类
AbstractAutoProxyCreator
类。该类重写了 postProcessAfterlnitialization()
方法。
wrapIfNecessary
这个方法主要是做一些判断,判断这个类是否需要创建代理类
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
createProxy
这就是个比较核心的方法了在这里进行代理类的创建以及代码的织入
private Object buildProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
BeanFactory var7 = this.beanFactory;
if (var7 instanceof ConfigurableListableBeanFactory clbf) {
AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (proxyFactory.isProxyTargetClass()) {
if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
Class[] var12 = beanClass.getInterfaces();
int var8 = var12.length;
for(int var9 = 0; var9 < var8; ++var9) {
Class<?> ifc = var12[var9];
proxyFactory.addInterface(ifc);
}
}
} else if (this.shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
} else {
this.evaluateProxyInterfaces(beanClass, proxyFactory);
}
Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
this.customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (this.advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
ClassLoader classLoader = this.getProxyClassLoader();
if (classLoader instanceof SmartClassLoader smartClassLoader) {
if (classLoader != beanClass.getClassLoader()) {
classLoader = smartClassLoader.getOriginalClassLoader();
}
}
return classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader);
}
可以看到最后是通过一个代理工厂生成代理类,这里用了一个策略模式和简单工厂模式结合的设计模式,具体策略其实就是JDK
方式生成代理类,还一种策略是CgLib
方式生成代理类
这里我们就拿
JDK
创建代理类的方式进行接下来的源码解读
至此代理类的创建也就算完成了。
代理类完成了创建接下来就需要看
Spring
如何将对应的代码织入进来拿JDK
距距离我们可以找JdkDynamicAopProxy
这个类的invoke
方法
JdkDynamicAopProxy
-invoke
方法
在这里进行了代码的织入
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
Object var12;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
Boolean var18 = this.equals(args[0]);
return var18;
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
Integer var17 = this.hashCode();
return var17;
}
if (method.getDeclaringClass() == DecoratingProxy.class) {
Class var16 = AopProxyUtils.ultimateTargetClass(this.advised);
return var16;
}
Object retVal;
if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
return retVal;
}
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = target != null ? target.getClass() : null;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
}
var12 = retVal;
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
return var12;
}
这里有个很重要的方法就是getInterceptorsAndDynamicInterceptionAdvice
这个方法返回了所有Interceptor。具体怎么找到的其实也比较简单:
MethodCacheKey
你可以简单理解为方法作为key(主要作用就是获取到结果之后缓存下来)
也就是从提供的配置实例
config
中获取 advisor
列表,遍历处理这些 advisor
. 如果是 IntroductionAdvisor
,则判断此 Advisor
能否应用到目标类 targetClass
上.如果是 PointcutAdvisor
,则判断此 Advisor
能否应用到目标方法 Method
上.将满足条件的 Advisor
通过 AdvisorAdaptor
转化成 Interceptor
列表返回
拿到这个列表之后就开始对代码进行织入:
ReflectiveMethodInvocation
-proceed
方法
到这里代码的织入就完成了,最后就是看一下最后的运行是如何实现的,这里我们可以找一下
MethodInterceptor
的实现类,我们可以哪几个典型的Interceptor
拿出来说明,也就是方法执行前的执行后的抛异常的
- 方法执行前的
Interceptor
-MethodBeforeAdviceInterceptor
- 方法执行后的
Interceptor
-AspectJAfterAdvice
- 方法异常的
Interceptor
-AspectJAfterThrowingAdvice
核心类
ProxyFactory
:代理生成工厂根据不同的策略生成对应的代理类MethodInterceptor
:是AOP项目中的拦截器(注:不是动态代理拦截器),区别于HandlerInterceptor
拦截目标时请求,它拦截的目标是方法。Advice
:’切面〞对于某个“连接点〞所产生的动作。其中,一个“切面”可以包含多个 “Advice
”Joinpoint
:是AOP的连接点。一个连接点代表一个被代理的方法。我们从源码角度看连接点有哪些属性和功能。
MVC
容器初始化时会建立所有 url
和 Controller
中的Method
的对应关系,保存到 HandlerMapping
中,用户请求是根据 Request
请求的url
快速定位到 Controller
中的某个方法。在Spring
中先将 url
和 Controller
的对应关包系,保存到 Map<url,Controller>
中。Web 容器启动时会通知 Spring
初始化容器(加载Bean
的定义信息和初始化所有单例 Bean
),然后 SpringMVC
会遍历容器中的 Bean
,获
取每一个 Controller
中的所有方法访问的 url
然后将 url
和 Controller
保存到一个 Map
中;这样就可以根据 Request
快速定位到 Controller
,因为最终处理 Request
的是
Controller
中的方法,Map
中只保留了 url
和 Controller
中的对应关系,所以要根据
Request
的url
进一步确认 Controller
中的 Method
,这一步工作的原理就是拼接
Controller
的 url(Controller 上 @RequestMapping 的值)和方法的 url(Method 上
@RequestMapping 的值),与 request
的 url
进行匹配,找到匹配的那个方法;确定处
理请求的 Method
后,接下来的任务就是参数绑定,把 Request
中参数绑定到方法的形
式参数上,这一步是整个请求处理过程中最复杂的一个步骤。最后就是调用返回
核心类
DispatcherServlet
DispatcherServlet
是 SpringMVC中的前端控制器(Front Controller),
负责接收 Request
并将 Request
转发给对应的处理组件。
HanlerMapping
HanlerMapping
是 SpringMVC 中完成 url
到 Controller
映射的组件。DispatcherServlet
接收 Request
,然后 从 HandlerMapping
查找处理 Request
的 Controller
。
HandlerAdapters
因为 Spring MVC 中 Handler
可以是任意形式的,只要
能够处理请求便行,但是把请求交给 Servlet
的时候,由于 Servlet
的方法结构都是如
doService(HttpServletRequest req, HttpServletResponse resp)
这样的形式,让固定的 Servlet
处理方法调用 Handler
来进行处理,这一步工作便是 HandlerAdapter
要做
的事。对应的有HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter 和 AnnotationMethodHandlerAdapter
AnnotationMethodHandlerAdapter
主要是适配注解类处理器,注解类处理器就是我们经常使用的 @Controller 的这类处理器HttpRequestHandlerAdapter
主要是适配静态资源处理器,静态资源处理器就是实现了 HttpRequestHandler 接口的处理器,这类处理器的作用是处理通过 SpringMVC 来访问的静态资源的请求SimpleControllerHandlerAdapter
是 Controller 处理适配器,适配实现了 Controller 接口或 Controller 接口子类的处理器,比如我们经常自己写的 Controller 来继承 MultiActionController.SimpleServletHandlerAdapter
是 Servlet 处理适配器, 适配实现了 Servlet 接口或 Servlet 的子类的处理器,我们不仅可以在 web.xml 里面配置 Servlet,其实也可以用 SpringMVC 来配置 Servlet,不过这个适配器很少用到,而且 SpringMVC 默认的适配器没有他,默认的是前面的三种。
ModelAndView
是Web MVC 框架中 Model 和 View 的持有人。 这两类是截然不同的。 ModelAndView 仅保留两者,以使控制器有可能在单个返回值中返回模型和视图。 该视图由 ViewResolver 对象解析; 该模型是存储在 Map 中的数据。
ViewResolvers
视图解析器。这个组件的主要作用,便是将 String
类型的视图名和Locale
解析为 View
类型的视图。这个接口只有一个 resolveViewName
方法。从方法的定义就可以看出,Controller
层返回的 String
类型的视图名 viewName
最终会在这里被解析成为 View
. View
是用来渲染页面的,也就是说,它会将程序返回的
参数和数据填入模板中,最终生成 html
文件。ViewResolver
在这个过程中,主要做两
件大事,即,ViewResolver
会找到渲染所用的模板(使用什么模板来渲染?)和所用的
技术(其实也就是视图的类型,如JSP
啊还是其他什么 Blabla
的)填入参数。默认情况
下,Spring MVC 会为我们自动配置一个 InternalResourceViewResolver
,这个是针对
JSP
类型视图的。
时序图
核心代码
initServletBean
protected final void initServletBean() throws ServletException {
ServletContext var10000 = this.getServletContext();
String var10001 = this.getClass().getSimpleName();
var10000.log("Initializing Spring " + var10001 + " '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("Initializing Servlet '" + this.getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (RuntimeException | ServletException var4) {
this.logger.error("Context initialization failed", var4);
throw var4;
}
if (this.logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data";
this.logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value);
}
if (this.logger.isInfoEnabled()) {
this.logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
这段代码中最主要的逻辑就是初始化 IOC
容器,最终会调用 refresh
方法,IoC容器初始化之后,最后有调用了 onRefresh
方法。这个方法最终是在
DisptcherServlet
中实现
在这里就开始初始化九大组件了
doDispatch
这个方法就到了运行阶段了
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//取得处理当前请求的 Controller,这里也称为 hanlder,处理器,
//这里并不是直接返回 Controller,而是返回的 HandlerExecutionChain
//请求处理器链对象,该对象封装了 handler 和 interceptors.
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//获取处理 request 的处理器适配器 handler adapter
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//实际的处理器处理请求,返回结果视图对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//结果视图对象的处理
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new ServletException("Handler dispatch failed: " + var21, var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new ServletException("Handler processing failed: " + var23, var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
getHandler(processedRequest)
方法实际上就是从 HandlerMapping
中找到 url
和
Controller
的对应关系。也就是 Map<url,Controller>
。我们知道,最终处理 Request
的是 Controller
中的方法,我们现在只是知道了 Controller
,接下来就需要确定具体是哪个方法处理来处理 Request
,这个是在获取HandlerExecutionChain
的时候获取的具体源码如下:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
//遍历 Controller 上的所有方法,获取 url 匹配的方法
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
整个处理过程中最核心的逻辑其实就是拼接 Controller
的 url
和方法的 url
与 Request
的 url
进行匹配,找到匹配的方法。
getMethodArgumentValues
获取方法参数的方法
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
MethodParameter[] parameters = this.getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
} else {
Object[] args = new Object[parameters.length];
for(int i = 0; i < parameters.length; ++i) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] == null) {
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
} catch (Exception var10) {
if (logger.isDebugEnabled()) {
String exMsg = var10.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw var10;
}
}
}
return args;
}
}
上文中invocableMethod.invokeAndHandle
(最终要实现的目的就是:完成 Request
中的参数和方法参数上数据的绑定。Spring MVC 中提供两种 Request
参数到方法中参数的绑定方式:
- 通过注解进行绑定,@RequestParam。
- 通过参数名称进行绑定。
使用注解进行绑定,我们只要在方法参数前面声明
@RequestParam("name")
,就可以 将request
中参数name
的值绑定到方法的该参数上。使用参数名称进行绑定的前提是 必须要获取方法中参数的名称,Java 反射只提供了获取方法的参数的类型,并没有提供 获取参数名称的方法。SpringMMC解决这个问题的方法是用asm
框架读取字节码文件, 来获取方法的参数名称。asm 框架是一个字节码操作框架。所以建议,使用注解来完成参数绑定,这样就可以省去asm
框架的读取字节码
转载自:https://juejin.cn/post/7221202130340708407