Spring 面试问题

2020/01/26 Knowledge

参考来源

目录

什么是 Spring

Spring 是个包含一系列功能的合集,如快速开发的 Spring Boot(相当于脚手架,快速搭建),支持微服务的 Spring Cloud(用 Spring Boot 的方式完成了很多为服务所应具有功能的封装),支持认证与鉴权的 Spring Security,Web 框架 Spring MVC。IOC 与 AOP 依然是核心。

Spring 的重要模块

Spring模块

  • Spring Core:基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。
    • Beans 管理 Bean
    • Core 核心
    • Context 上下文(配置文件)
    • SpEL 表达式
  • Spring AOP:实现切面编程
  • Spring Aspects:实现 AOP 的框架
  • Spring JDBC:JAVA 数据库连接
  • Spring JMS:JAVA 消息服务
  • Spring ORM: 用于支持 Hibernate 等 ORM 工具。
  • Spring Web: 为创建 Web 应用程序提供支持。
  • Spring Test: 提供了对 JUnit 和 TestNG 测试的支持。
  • Spring Transactions:事务管理

Spring Bean 的创建时机

  1. 由 spring 产生的 bean 默认是单例的;
  2. 默认的情况下,启动 spring 容器时就创建对象了;
  3. spring的配置文件中的一个属性 lazy-init=”default/true/false”。默认为 false: 在启动 spring 容器的时候创建对象;如果为 true:在 context.getBean 时创建对象;
  4. 如果 spring 的配置文件的 scope 为 prototype,则在得到该 bean 时(context.getBean)才创建对象;

Spring MVC

SpringMVC、Tomcat 怎样完成一次Http请求的?

Tomcat 如何调用 servlet

Tomcat+servlet

  1. Web客户向 Servlet 容器(Tomcat)发出 Http 请求
  2. Servlet 容器分析客户的请求信息
  3. Servlet 容器创建一个 HttpRequest 对象,将客户请求的信息封装到这个对象中
  4. Servlet 容器创建一个 HttpResponse 对象
  5. Servlet 容器调用 HttpServlet 对象的 service 方法,把 HttpRequest 对象与 HttpResponse 对象作为参数传给 HttpServlet 对象
  6. HttpServlet 调用 HttpRequest 对象的有关方法,获取 Http 请求信息
  7. HttpServlet 调用 HttpResponse 对象的有关方法,生成响应数据
  8. Servlet 容器把 HttpServlet 的响应结果传给 Web 客户

Tomcat 和 servlet 的映射关系如何定

web.xml 的作用是配置 Http 和 Servlet 之间的映射关系、filter、context 参数等。这样通过这份约定的配置文件,Tomcat 可以把 Http 请求映射到不同的 Servlet 实例上。所以,在 Servlet 时代的 web.xml 中,会有很多的项配置。

SpringMVC 的转变

我们上面说过,SpringMVC 也是 Servlet 的实现,只不过 SpringMVC 增加了一个 DispatcherServlet(就是后面说的前端控制器),所有的 http 请求都是映射到这个 Servlet 上,请求进入到这个 Servlet 中之后,就算进入到了框架之中了,由这个 Servlet 来统一的分配 http 请求到各个 Controller(通过 HandlerMapping)

前段控制器模式

前端控制器模式(Front Controller Pattern)是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。以下是这种设计模式的实体。

Spring MVC 框架,与其他很多 web 的 MVC 框架一样:请求驱动;所有设计都围绕着一个中央 Servlet 来展开,它负责把所有请求分发到控制器;同时提供其他 web 应用开发所需要的功能。不过 Spring 的中央处理器,DispatcherServlet,能做的比这更多。它与 Spring IoC 容器做到了无缝集成,这意味着,Spring 提供的任何特性,在 Spring MVC中 你都可以使用。

下图展示了 Spring Web MVC 的 DispatcherServlet 处理请求的工作流。熟悉设计模式的朋友会发现,DispatcherServlet 应用的其实就是一个“前端控制器”的设计模式(其他很多优秀的web框架也都使用了这个设计模式)。

前段控制器模式

上图也可以看作是最简单的 Spirng MVC 架构,详细的后面会讲

组件说明

  1. DispatcherServlet:前端控制器。用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性,系统扩展性提高,由框架实现。
  2. HandlerMapping:处理器映射器。HandlerMapping 负责根据用户请求的 url 找到 Handler 即处理器,springmvc 提供了不同的映射器实现不同的映射方式,根据一定的规则去查找,例如:xml 配置方式,实现接口方式,注解方式等,由框架实现。
  3. HandlAdapter:处理器适配器。通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行,由框架实现。
  4. Handler:处理器,后端控制器。Handler 是继 DispatcherServlet 前端控制器的后端控制器,在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理。由于 Handler 涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发 Handler。
  5. ModelAndView 是 springmvc 的封装对象,将 model 和 view 封装在一起。
  6. ViewResolver:视图解析器。ViewResolver 负责将处理结果生成 View 视图,ViewResolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
  7. View:是 springmvc 的封装对象,是一个接口, springmvc 框架提供了很多的 View 视图类型,包括:jspview、pdfview、jstlView、freemarkerView、pdfView 等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

流程

SpringMVC流程

SpringMVC 执行流程:

  1. 用户发送请求至前端控制器 DispatcherServlet
  2. DispatcherServlet 收到请求调用处理器映射器HandlerMapping。
  3. 处理器映射器根据请求 url 找到具体的处理器(根据 xml、注解等),生成处理器执行链 HandlerExecutionChain(包括处理器对象和处理器拦截器的全部处理的 handler(controller,也叫页面控制器)和拦截器)一并返回给 DispatcherServlet。
  4. DispatcherServlet 根据处理器 Handler 获取处理器适配器 HandlerAdapter 执行 HandlerAdapter 处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
  5. 执行处理器 Handler。
  6. Handler 执行完成返回 ModelAndView
  7. HandlerAdapter 将 Handler 执行结果 ModelAndView 返回到 DispatcherServlet
  8. DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器
  9. ViewReslover 解析后返回具体 View
  10. DispatcherServlet 对 View 进行渲染视图(即将模型数据 model 填充至视图中)。
  11. DispatcherServlet 响应用户。

上面 11 步比较复杂,简化一下流程

DispatcherServlet 收到请求 -> HandlerMapping 查找对应 handler(controller)-> DispatcherServlet 找到 Handler 处理 -> handler 返回 ModelAndView 给 DispatcherServlet -> DispatcherServlet 找 View Resolver 解析视图 -> 返回给用户。

收到请求 -> 找 Controller 处理 -> 获得 ModelAndView -> 解析 ModelAndView -> 返回

执行流程对应的代码

所有的序号跟代码中的注释所对应。

  1. 请求到达前端控制器的第一站,先做些准备工作。该代码在 DispatcherServlet 中,servlet 会先调用 service 方法, service 方法中调用 processRequest,在 processRequest 方法中会执行 doService 做准备工作。

     /**
     * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
     * for the actual dispatching.
     */
     @Override
     protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
     if (logger.isDebugEnabled()) {
         String requestUri = urlPathHelper.getRequestUri(request);
         logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
                 " request for [" + requestUri + "]");
     }
    
         //保护现场,存储所有的请求参数
     // Keep a snapshot of the request attributes in case of an include,
     // to be able to restore the original attributes after the include.
     Map<String, Object> attributesSnapshot = null;
     if (WebUtils.isIncludeRequest(request)) {
         logger.debug("Taking snapshot of request attributes before include");
         attributesSnapshot = new HashMap<String, Object>();
         Enumeration<?> attrNames = request.getAttributeNames();
         while (attrNames.hasMoreElements()) {
             String attrName = (String) attrNames.nextElement();
             if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                 attributesSnapshot.put(attrName, request.getAttribute(attrName));
             }
         }
     }
    
         //将框架相关信息存储至request,方便后面的处理器和视图用到
     // Make framework objects available to handlers and view objects.
     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
     request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
     request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
     request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
    
     FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
     if (inputFlashMap != null) {
         request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
     }
     request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
     request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    
         //请求分发
     try {
         doDispatch(request, response);
     }
     finally {
         // Restore the original attribute snapshot, in case of an include.
         if (attributesSnapshot != null) {
             restoreAttributesAfterInclude(request, attributesSnapshot);
         }
     }
     }
    
  2. 处理请求。通过 url 查找 HandlerMap 中最相近的 key(url),然后由 key 获取 HandlerMapping 对象。通过处理器映射器获取处理器。通过查询处理器适配器获得 Controller 处理器。

     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
         HttpServletRequest processedRequest = request;
         HandlerExecutionChain mappedHandler = null;
         int interceptorIndex = -1;
    
         try {
             ModelAndView mv; // controller 返回的结果
             boolean errorView = false;
    
             try {
                 processedRequest = checkMultipart(request);
    
                 // Determine handler for the current request
                 //步骤3.1~3.4用于获取包含处理器Handler和拦截器AdapterIntercepters的处理器执行链HandlerExecutionChain
                 mappedHandler = getHandler(processedRequest, false);
                 if (mappedHandler == null || mappedHandler.getHandler() == null) {
                     noHandlerFound(processedRequest, response);
                     return;
                 }
    
                 // Determine handler adapter for the current request.
                 //步骤4.1~4.2,根据HandlerExecutionChain中的处理器Handler获取处理器适配器
                 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.
                 //5.1~5.3通过处理器适配器HandlerApapter来调用处理器完成对请求的处理
                 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?
             // 渲染返回的 modelAndView
             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);
             }
         }
     }
    
  3. 1 getHandler(HttpServletRequest request),经由 HandlerMapping 对象获取 HandlerExecutionChain(处理器和拦截器)

     /**
     * Return the HandlerExecutionChain for this request.
     * <p>Tries all handler mappings in order.
     * @param request current HTTP request
     * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
     */
     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;
     }
    
  4. 2.1 getHandler(HttpServletRequest request),经由 request 获取处理器,获取处理器 Handler 后,再获取拦截器,最后组成 HandlerExecutionChain

     /**
     * Look up a handler for the given request, falling back to the default
     * handler if no specific one is found.
     * @param request current HTTP request
     * @return the corresponding handler instance, or the default handler
     * @see #getHandlerInternal
     */
     public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
         Object handler = getHandlerInternal(request);
         if (handler == null) {
             handler = getDefaultHandler();
         }
         if (handler == null) {
             return null;
         }
         // Bean name or resolved handler?
         if (handler instanceof String) {
             String handlerName = (String) handler;
             handler = getApplicationContext().getBean(handlerName);
         }
         return getHandlerExecutionChain(handler, request);
     }
    
  5. 2.2 根据查找到的处理器 Handler 和 request 获取包含 Handler 和 AdaptedInterceptors 的 HandlerExecutionChain

     protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
         HandlerExecutionChain chain = 
             (handler instanceof HandlerExecutionChain) ?
                 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
    
         chain.addInterceptors(getAdaptedInterceptors());
    
         String lookupPath = urlPathHelper.getLookupPathForRequest(request);
         for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
             if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
                 chain.addInterceptor(mappedInterceptor.getInterceptor());
             }
         }
    
         return chain;
         }
         /**
         * Return the adapted interceptors as HandlerInterceptor array.
         * @return the array of HandlerInterceptors, or <code>null</code> if none
         */
         protected final HandlerInterceptor[] getAdaptedInterceptors() {
         int count = adaptedInterceptors.size();
         return (count > 0) ? adaptedInterceptors.toArray(new HandlerInterceptor[count]) : null;
     }
    
  6. 4 lookupHandler(lookupPath, request) 根据给定 url path 和 request 获取 Handler

     protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
         // Direct match?
         Object handler = this.handlerMap.get(urlPath);
         if (handler != null) {
             // Bean name or resolved handler?
             if (handler instanceof String) {
                 String handlerName = (String) handler;
                 handler = getApplicationContext().getBean(handlerName);
             }
             validateHandler(handler, request);
             return buildPathExposingHandler(handler, urlPath, urlPath, null);
         }
         // Pattern match?
         List<String> matchingPatterns = new ArrayList<String>();
         for (String registeredPattern : this.handlerMap.keySet()) {
             if (getPathMatcher().match(registeredPattern, urlPath)) {
                 matchingPatterns.add(registeredPattern);
             }
         }
         String bestPatternMatch = null;
         Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
         if (!matchingPatterns.isEmpty()) {
             Collections.sort(matchingPatterns, patternComparator);
             if (logger.isDebugEnabled()) {
                 logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
             }
             bestPatternMatch = matchingPatterns.get(0);
         }
         if (bestPatternMatch != null) {
             handler = this.handlerMap.get(bestPatternMatch);
             // Bean name or resolved handler?
             if (handler instanceof String) {
                 String handlerName = (String) handler;
                 handler = getApplicationContext().getBean(handlerName);
             }
             validateHandler(handler, request);
             String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
    
             // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
             // for all of them
             Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
             for (String matchingPattern : matchingPatterns) {
                 if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
                     uriTemplateVariables
                         .putAll(getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath));
                 }
             }
             if (logger.isDebugEnabled()) {
                 logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
             }
             return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
         }
         // No handler found...
         return null;
     }
    
  7. 1 HandlerAdapter getHandlerAdapter(Object handler),根据 Handler 获取 HandlerAdapter 适配器

     /**
     * Return the HandlerAdapter for this handler object.
     * @param handler the handler object to find an adapter for
     * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
     */
     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
         for (HandlerAdapter ha : this.handlerAdapters) {
             if (logger.isTraceEnabled()) {
                 logger.trace("Testing handler adapter [" + ha + "]");
             }
             if (ha.supports(handler)) {
                 return ha;
             }
         }
         throw new ServletException("No adapter for handler [" + handler +
                 "]: Does your handler implement a supported interface like Controller?");
         }
    
  8. 2 supports(Object handler) 检测是否是 Controller

     public boolean supports(Object handler) {
         return (handler instanceof Controller);
     }
    
  9. 1 使用处理器完成对请求的处理

     public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {
    
         ((Servlet) handler).service(request, response);
         return null;
         }
     public void service(ServletRequest req, ServletResponse res)
         throws ServletException, IOException
     {
         HttpServletRequest  request;
         HttpServletResponse response;
    
         if (!(req instanceof HttpServletRequest &&
                 res instanceof HttpServletResponse)) {
             throw new ServletException("non-HTTP request or response");
         }
    
         request = (HttpServletRequest) req;
         response = (HttpServletResponse) res;
    
         service(request, response);
     }
     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 = req.getDateHeader(HEADER_IFMODSINCE);
                 if (ifModifiedSince < lastModified) {
                     // 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);
         }
     }
    

剩下的原文没了,但大概逻辑清晰了,有时间再研究

总结

请求,Tomcat 交给 DispatcherServlet -> 交给 HandlerMapping 生产 controller(handler) 链 -> 交回 DispatcherServlet 给 controller 适配器交由 controller 处理 -> 返回 ModelAndView 给 DispatcherServlet 给视图解析器 -> 视图解析器解析返回内容交给 DispatcherServlet -> 交给视图生成界面还给用户

可以看到,所有的步骤都由 DispatcherServlet 分发给各组件。各组件完成各自的一小部分功能,充分解耦合。这就是前段控制器模式

Bean 的生命周期

比较方便的理解方法:链接

Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

springbean

springbean2

作者:潜龙勿用 链接:https://www.zhihu.com/question/38597960/answer/248763219 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  1. 实例化一个Bean,也就是我们通常说的new
  2. 按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
  3. 如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是Spring配置文件中Bean的ID
  4. 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他Bean)
  5. 如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式同样可以实现步骤4,但比4更好,以为ApplicationContext是BeanFactory的子接口,有更多的实现方法
  6. 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术
  7. InitializingBean 的 afterPropertiesSet 方法
  8. 如果这个Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法
  9. 如果这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj, String s)方法注意:以上工作完成以后就可以用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例
  10. 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法
  11. 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法

接口示例

注解方式

在 bean 初始化时会经历几个阶段,首先可以使用注解 @PostConstruct(在 init() 之前也就是 new 之前), @PreDestroy 来在 bean 的创建和销毁阶段进行调用:

@Component
public class AnnotationBean {
    private final static Logger LOGGER = LoggerFactory.getLogger(AnnotationBean.class);

    @PostConstruct
    public void start(){
        LOGGER.info("AnnotationBean start");
    }

    @PreDestroy
    public void destroy(){
        LOGGER.info("AnnotationBean destroy");
    }
}

实现 *Aware 接口

*Aware 接口可以用于在初始化 bean 时获得 Spring 中的一些对象,如获取 Spring 上下文等。

@Component
public class SpringLifeCycleAware implements ApplicationContextAware {
    private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleAware.class);

    private ApplicationContext applicationContext ;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext ;
        LOGGER.info("SpringLifeCycleAware start");
    }
}

这样在 springLifeCycleAware 这个 bean 初始化会就会调用 setApplicationContext 方法,并可以获得 applicationContext 对象。

BeanPostProcessor 增强处理器

实现 BeanPostProcessor 接口,Spring 中所有 bean 在做初始化时都会调用该接口中的两个方法,可以用于对一些特殊的 bean 进行处理,这个方法是可以获取到 bean 的:

@Component
public class SpringLifeCycleProcessor implements BeanPostProcessor {
    private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleProcessor.class);

    /**
     * 预初始化 初始化之前调用
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("annotationBean".equals(beanName)){
            LOGGER.info("SpringLifeCycleProcessor start beanName={}",beanName);
        }
        return bean;
    }

    /**
     * 后初始化  bean 初始化完成调用
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("annotationBean".equals(beanName)){
            LOGGER.info("SpringLifeCycleProcessor end beanName={}",beanName);
        }
        return bean;
    }
}

InitializingBean, DisposableBean 接口

InitializingBean,DisposableBean 这两个接口,也是在初始化以及销毁阶段调用,但是它们无法获取 bean,他的时机在 BeanPostProcessor 中间:

@Service
public class SpringLifeCycleService implements InitializingBean,DisposableBean{
    private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleService.class);
    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("SpringLifeCycleService start");
    }

    @Override
    public void destroy() throws Exception {
        LOGGER.info("SpringLifeCycleService destroy");
    }
}

输出:

2018-03-21 00:40:24.856 [restartedMain] INFO  c.c.s.p.SpringLifeCycleProcessor - SpringLifeCycleProcessor start beanName=annotationBean
2018-03-21 00:40:24.860 [restartedMain] INFO  c.c.spring.annotation.AnnotationBean - AnnotationBean start
2018-03-21 00:40:24.861 [restartedMain] INFO  c.c.s.p.SpringLifeCycleProcessor - SpringLifeCycleProcessor end beanName=annotationBean
2018-03-21 00:40:24.864 [restartedMain] INFO  c.c.s.aware.SpringLifeCycleAware - SpringLifeCycleAware start
2018-03-21 00:40:24.867 [restartedMain] INFO  c.c.s.service.SpringLifeCycleService - SpringLifeCycleService start
2018-03-21 00:40:24.887 [restartedMain] INFO  c.c.spring.SpringLifeCycle - SpringLifeCycle start
2018-03-21 00:40:25.062 [restartedMain] INFO  o.s.b.d.a.OptionalLiveReloadServer - LiveReload server is running on port 35729
2018-03-21 00:40:25.122 [restartedMain] INFO  o.s.j.e.a.AnnotationMBeanExporter - Registering beans for JMX exposure on startup
2018-03-21 00:40:25.140 [restartedMain] INFO  com.crossoverjie.Application - Started Application in 2.309 seconds (JVM running for 3.681)
2018-03-21 00:40:25.143 [restartedMain] INFO  com.crossoverjie.Application - start ok!
2018-03-21 00:40:25.153 [Thread-8] INFO  o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3913adad: startup date [Wed Mar 21 00:40:23 CST 2018]; root of context hierarchy
2018-03-21 00:40:25.155 [Thread-8] INFO  o.s.j.e.a.AnnotationMBeanExporter - Unregistering JMX-exposed beans on shutdown
2018-03-21 00:40:25.156 [Thread-8] INFO  c.c.spring.SpringLifeCycle - SpringLifeCycle destroy
2018-03-21 00:40:25.156 [Thread-8] INFO  c.c.s.service.SpringLifeCycleService - SpringLifeCycleService destroy
2018-03-21 00:40:25.156 [Thread-8] INFO  c.c.spring.annotation.AnnotationBean - AnnotationBean destroy

以上内容来源

解决循环依赖

Spring依赖注入主要的方式:1.Set 方法注入;2.构造方法注入;3.接口注入。

三个示例 Bean

public class StudentA {
 
    private StudentB studentB ;
 
    public void setStudentB(StudentB studentB) {
        this.studentB = studentB;
    }
 
    public StudentA() {
    }
    
    public StudentA(StudentB studentB) {
        this.studentB = studentB;
    }
}
public class StudentB {
 
    private StudentC studentC ;
 
    public void setStudentC(StudentC studentC) {
        this.studentC = studentC;
    }
    
    public StudentB() {
    }
 
    public StudentB(StudentC studentC) {
        this.studentC = studentC;
    }
}
public class StudentC {
 
    private StudentA studentA ;
 
    public void setStudentA(StudentA studentA) {
        this.studentA = studentA;
    }
 
    public StudentC() {
    }
 
    public StudentC(StudentA studentA) {
        this.studentA = studentA;
    }
}

可以看到这三个类互相引用,造成了循环调用。

构造器参数循环依赖

构建方式

通过 constructor-arg 标签

<bean id="a" class="com.zfx.student.StudentA">
    <constructor-arg index="0" ref="b"></constructor-arg>
</bean>
<bean id="b" class="com.zfx.student.StudentB">
    <constructor-arg index="0" ref="c"></constructor-arg>
</bean>
<bean id="c" class="com.zfx.student.StudentC">
    <constructor-arg index="0" ref="a"></constructor-arg>
</bean>

这样 abc 互相引用,造成了循环引用。

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
        //System.out.println(context.getBean("a", StudentA.class));
    }
}

会出现报错:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 
	Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

原因解释

Spring 容器会将每一个正在创建的 Bean 标识符放在一个“当前创建Bean池”中,Bean 标识符在创建过程中将一直保持 在这个池中,因此如果在创建 Bean 过程中发现自己已经在“当前创建Bean池”里时将抛出 BeanCurrentlyInCreationException 异常表示循环依赖;而对于创建完毕的 Bean 将从“当前创建 Bean 池”中清除掉。

过程分析:

  1. Spring 容器创建 A bean,首先去“当前创建 bean 池”中查找是否当前 bean 正在创建(通过 beanName),如果发现没有,则继续准备其需要的构造器参数 B,并将 A 标识符放到“当前创建 bean 池”
  2. Spring 容器创建 B bean,Spring 容器创建 B bean,首先去“当前创建 bean 池”中查找是否当前 bean 正在创建(通过 beanName),如果发现没有,则继续准备其需要的构造器参数 C,并将 B 标识符放到“当前创建bean池”
  3. Spring 容器创建 C bean,Spring 容器创建 C bean,首先去“当前创建bean池”中查找是否当前 bean 正在创建(通过 beanName),如果发现没有,则继续准备其需要的构造器参数 A,并将 C 标识符放到“当前创建 bean 池”
  4. C 的创建需要先创建 A bean,此时发现 A 已经在“当前创建 bean 池”中,检测到了循环依赖,直接抛出BeanCurrentlyInCreationException异常

setter 循环依赖(单例模式)

构建方式

修改配置文件为set方式注入,用 property 作为属性注入:

<!--scope="singleton"(默认就是单例方式)  -->
<bean id="a" class="com.zfx.student.StudentA" scope="singleton">
    <property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.zfx.student.StudentB" scope="singleton">
    <property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.zfx.student.StudentC" scope="singleton">
    <property name="studentA" ref="a"></property>
</bean>
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
        System.out.println(context.getBean("a", StudentA.class));
        // 输出:com.zfx.student.StudentA@1fbfd6
    }
}

原因解释

Spring为了解决单例的循环依赖问题,使用了三级缓存。接下来的代码中会一步一步分析。

来看代码,可以分为三级缓存

/** Cache of singleton objects: bean name --> bean instance(一级缓存) */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
/** Cache of singleton factories: bean name --> ObjectFactory(三级缓存) */
private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);
/** Cache of early singleton objects: bean name --> bean instance(二级缓存) */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); 

实例化和初始化的区别:实例化就是 new,通过构造器开辟内存空间,生成一个对象实例。初始化就是给已有的实例或者变量进行赋予初始值,不只针对于对象。

这三级缓存的作用分别是:

  • singletonFactories:存放我刚实例化的 bean,通过ObjectFactory,可以让如果有提前需要 bean 的需要可以调用该(三级缓存)
  • earlySingletonObjects:存放了刚实例化好的,但是还未配置属性和初始化的 bean(未初始化),我们在获取该 bean 的时候会调用 beanPostProcessor 的 getEarlyReference 进行一些提前获取 bean 的必要操作(二级缓存)他的目的就是为了提前可以调用,但因为没有初始化不能直接放到 singletonObjects 里。
  • singletonObjects:存放初始化好的 bean(一级缓存)

三级缓存的用法:

getSingleton(String beanName, boolean allowEarlyReference) 从第二个参数也可以看出来,这是给需要做提前引用的调用实例,所以调用的可能是没有初始化好的实例。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName); // 一级缓存调用
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
       //一级缓存没有
      synchronized (this.singletonObjects) {
          //调二级缓存
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
             // 调用三级缓存
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
                //扔到二级缓存里,注意扔的是 getObject 方法也就是仅实例化还没初始化的 object
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
  1. 先从一级缓存 singletonObjects 中去获取。(如果获取到就直接 return)
  2. 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存 earlySingletonObjects 中获取。(如果获取到就直接return)
  3. 如果还是获取不到,且允许 singletonFactories(allowEarlyReference=true)通过 getObject() 获取。就从三级缓存 singletonFactory.getObject() 获取。(如果获取到了就从 singletonFactories 中移除,并且放进 earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)

加入 singletonFactories 三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

getSingleton() 从缓存里获取单例对象步骤分析可知,Spring 解决循环依赖的诀窍:就在于 singletonFactories 这个三级缓存。这个 Cache 里面都是 ObjectFactory,它是解决问题的关键。

public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

经过 ObjectFactory.getObject() 后,此时放进了二级缓存 earlySingletonObjects 内。这个时候对象已经实例化了,还没初始化,虽然还不完美,但是对象的引用已经可以被其它引用了。

二级缓存的进入和移除

  • 添加:向里面添加数据只有一个地方,就是上面说的 getSingleton() 里从三级缓存里挪过来
  • 移除:addSingleton(在获取 bean 的时候会使用)、addSingletonFactory、removeSingleton 从语义中可以看出添加单例、添加单例工厂 ObjectFactory 的时候都会删除二级缓存里面对应的缓存值

非单例循环依赖

构建方式

<bean id="a" class="com.zfx.student.StudentA" scope="prototype">
    <property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.zfx.student.StudentB" scope="prototype">
    <property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.zfx.student.StudentC" scope="prototype">
    <property name="studentA" ref="a"></property>
</bean>
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 
	Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

scope=”prototype” 意思是每次请求都会创建一个实例对象。两者的区别是:有状态的 bean 都使用 Prototype 作用域,无状态的一般都使用 singleton 单例作用域。

对于“prototype”作用域 bean, Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓存“prototype”作用域的 bean,因此无法提前暴露一个创建中的 bean。Spring 不参与prototype 的生命周期管理。

Bean 的作用域

  • singleton:单例模式,Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个 Bean 引用它,始终指向同一对象。
  • prototype:原型模式,每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态
  • request:在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效
  • session:在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。
  • global Session:在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用 PortletContext 时有效。

IOC(控制反转 Inversion of Control) 和 DI(依赖注入 Dependency Injection)

由 Spring IOC 容器来负责管理对象的生命周期和对象之间的关系。IoC 容器控制对象的创建和销毁,依赖对象的获取被反转了。

Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表,然后根据这张注册表实例化 Bean,装配好 Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。Bean缓存池:HashMap实现。

springioc

IOC 容器介绍

Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化 Bean 并建立 Bean 之间的依赖关系。 Spring 的 IoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、Bean 实例代理、事件发布、资源装载等高级服务。

没有 IoC 的时候我们都是在自己对象中主动去创建被依赖的对象,这是正转。但是有了 IoC 后,所依赖的对象直接由 IoC 容器创建后注入到被注入的对象中,依赖的对象由原来的主动获取变成被动接受,所以是反转。

BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;

ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合我们都直接使用 ApplicationContext(继承了 BeanFactory)而非底层的 BeanFactory。

默认从类路径加载配置文件情况下一般使用:

ApplicationContext context = new ClassPathXmlApplicationContext
            ("Beans.xml");
    HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
    obj.getMessage();

反射与工厂模式实现 IOC

Spring 中的 IoC 的实现原理就是工厂模式加反射机制。 我们首先看一下不用反射机制时的工厂模式:

interface Fruit{
    public abstract void eat();
} 
class Apple implements Fruit{
     public void eat(){
         System.out.println("Apple");
     }
} 
class Orange implements Fruit{
     public void eat(){
         System.out.println("Orange");
     }
}
//构造工厂类
//也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了
class Factory{
     public static fruit getInstance(String fruitName){
         Fruit f=null;
         if("Apple".equals(fruitName)){
             f=new Apple();
         }
         if("Orange".equals(fruitName)){
             f=new Orange();
         }
         return f;
     }
}
class hello{
     public static void main(String[] a){
         Fruit f=Factory.getInstance("Orange");
         f.eat();
     }
}

上面写法的缺点是当我们再添加一个子类的时候,就需要修改工厂类中的判断。如果我们添加太多的子类的时候,改动就会很多。

下面用反射机制实现工厂模式:

interface Fruit{
     public abstract void eat();
}
class Apple implements Fruit{
public void eat(){
         System.out.println("Apple");
     }
}
class Orange implements Fruit{
public void eat(){
        System.out.println("Orange");
    }
}
class Factory{
    public static Fruit getInstance(String ClassName){
        Fruit f=null;
        try{
            f=(Fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
class hello{
    public static void main(String[] a){
        Fruit f=Factory.getInstance("Reflect.Apple");
        if(f!=null){
            f.eat();
        }
    }
}

现在就算我们添加任意多个子类的时候,工厂类都不需要修改。使用反射机制实现的工厂模式可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。

apple=Reflect.Apple
orange=Reflect.Orange

然后增加读取配置文件部分:

class init{
    public static Properties getPro() throws FileNotFoundException, IOException{
        Properties pro=new Properties();
        File f=new File("fruit.properties");
        if(f.exists()){
            pro.load(new FileInputStream(f));
        }else{
            pro.setProperty("apple", "Reflect.Apple");
            pro.setProperty("orange", "Reflect.Orange");
            pro.store(new FileOutputStream(f), "FRUIT CLASS");
        }
        return pro;
    }
}
class Factory{
    public static fruit getInstance(String ClassName){
        fruit f=null;
        try{
            f=(fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
class hello{
    public static void main(String[] a) throws FileNotFoundException, IOException{
        Properties pro=init.getPro();
        fruit f=Factory.getInstance(pro.getProperty("apple"));
        if(f!=null){
            f.eat();
        }
    }
}

可能会疑惑,我们这里所有的被管理的 Bean 都实现了 Fruit 接口,但是在 Spring 中的 Bean 不用,这是因为在 Spring 中的 getBean 得到的是 Object 然后进行的强转。

@Override
public Object getBean(String name) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}

@Override
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name, requiredType);
}

@Override
public Object getBean(String name, Object... args) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name, args);
}

@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType);
}

@Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType, args);
}

总结

IOC 中最基本的技术就是“反射(Reflection)”编程,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才被决定到底是哪一种对象。

把 IOC 容器的工作模式看做是工厂模式的升华,可以把 IOC 容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC 是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

Spring 的 IoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载等高级服务。

DI

组件之间依赖关系由容器在运行期决定,由容器动态的将某个依赖关系注入到组件之中,提升组件重用的频率、灵活、可扩展。

通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现

注入方式:构造器注入(xml 中 )、setter 方法注入(xml 中 )、接口方式注入。

我们一般用到的都是 setter 注入。在 springboot 中 @Autowired(@Resource)注解在属性上是默认用反射赋值。

Spring AOP(Aspect Oriented Programming)

介绍

面向切面的编程,是一种编程技术,是 OOP(面向对象编程)的补充和完善。OOP 的执行是一种从上往下的流程,并没有从左到右的关系。因此在 OOP 编程中,会有大量的重复代码。而 AOP 则是将这些与业务无关的重复代码抽取出来,然后再嵌入到业务代码当中。常见的应用有:权限管理、日志、事务管理等。

AOP

实现方式

实现 AOP 的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。Spring AOP 实现用的是动态代理的方式。

Spring AOP 使用的动态代理原理

  • jdk 反射:通过反射机制生成代理类的字节码文件,调用具体方法前调用 InvokeHandler 来处理
  • cglib 工具:利用 asm 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理

如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP(因为 JDK 动态代理只能实现有接口的)

如果目标对象实现了接口,可以强制使用 CGLIB 实现 AOP

如果目标对象没有实现了接口,必须采用 CGLIB 库,spring 会自动在 JDK 动态代理和 CGLIB 之间转换

基本定义

  • 切面(Aspect):切面是通知和切点的集合,通知和切点共同定义了切面的全部功能——它是什么,在何时何处完成其功能。通俗来讲就是一个 @Aspect 的实现日志增强的类,这个切面可能有很多个切点。
  • 连接点(Joinpoint):连接点是一个虚拟的概念,可以理解为所有满足切点扫描条件的具体时机(切点是一个通配符,连接点是所有具体的点)。比如某方法调用的时候或者处理异常的时候。在 Spring AOP 中,一个连接点总是表示一个方法的执行。通俗的说就是加入切点的那个点。
  • 通知/增强(advice):在切面的某个特定的连接点上执行的动作。是一个具体的函数,例如,日志记录,权限验证,事务控制,性能检测,错误信息检测等。Spring 切面可应用的 5 种通知类型:
    • Before——在方法调用之前调用通知
    • After——在方法完成之后调用通知,无论方法执行成功与否
    • After-returning——在方法执行成功之后调用通知
    • After-throwing——在方法抛出异常后进行通知
    • Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为,类似于 Before 和 AfterReturning 的总和。可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值。
  • 切入点(Pointcut):匹配连接点的断言(一些布尔表达式)。通知和一个切入点表达式关联,并在满足这个切入点的连接点(Joinpoint)上运行。切入点表达式如何和连接点匹配是 AOP 的核心:Spring缺省使用 AspectJ 切入点语法。
  • Target Object(目标对象):包含连接点的对象。也被称作被通知或被代理对象。也就是会被增强的对象。
  • 织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象(也就是把切面加入目标对象后的对象)。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
  • 引入(Introduction):引入允许我们向现有的类中添加方法或属性

示例代码

在下面示例中,LogAspect 类是切面,logAspect 方法配置的通配符是切点,doBefore/doAfterReturning 是增强,增强的种类是 Before 和 AfternReturning。具体到 com.free4lab.sparkml.java.controller.AppController 的 public List<App> findAllApp() 的执行前和执行成功后是切入点。com.free4lab.sparkml.java.controller.AppController 就是一个增强对象。这个增强对象没有实现接口,所以会用 cglib 动态代理生成包含切面的一个新对象就是织入

/**
 * @Author: fuhua
 * @Date: 2019/9/19 4:06 下午
 */
@Aspect // LogAspect 是一个切面
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("execution(public * com.free4lab.sparkml.java.controller.*.*(..))")
    public void logAspect() {}

    // ... 这里还可以在新建更多的切入点

    @Before("logAspect()") // 在 logAspect 切点之前的增强
    public void doBefore(JoinPoint joinPoint){
        logger.info("请求开始:"+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName()+" 方法参数:"+ Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(returning = "ret",pointcut = "logAspect()")
    public void doAfterReturning(Object ret) throws Throwable {
        logger.info("请求结束。返回值是:"+ret);
    }
}

Search

    Table of Contents