1. 程式人生 > >深入理解Spring的非同步機制

深入理解Spring的非同步機制

一、Spring中實現非同步執行

  在這裡我先以事件的機制舉例,注意預設情況下事件的釋出與監聽都是同步執行的。那麼我們來看一看基於非同步事件的例子該怎麼寫

  首先還是定義事件:

package com.bdqn.lyrk.ssm.study.app.entity.event;

import org.springframework.context.ApplicationEvent;

/**
 * 定義一個飢餓的事件
 *
 * @author chen.nie
 * @date 2018/6/26
 **/
public class HungryEvent extends
ApplicationEvent { /** * Create a new ApplicationEvent. * * @param source the object on which the event initially occurred (never {@code null}) */ public HungryEvent(Object source) { super(source); } }

  定義一個Person類,該類主要釋出相關事件 

package com.bdqn.lyrk.ssm.study.app.entity.event;

import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Component; import static com.bdqn.lyrk.ssm.study.app.utils.Printer.print; @Component public class Person implements ApplicationEventPublisherAware {
private int hungry; private String name = "admin"; public String getName() { return name; } private ApplicationEventPublisher applicationEventPublisher; public void setHungry(int hungry) { this.hungry = hungry; } public void work(){ if (hungry < 10) { print("餓了,誰來幫我做做飯.."); applicationEventPublisher.publishEvent(new HungryEvent(this)); } print("繼續工作...."); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } }

  定義事件的監聽者

package com.bdqn.lyrk.ssm.study.app.entity;

import com.bdqn.lyrk.ssm.study.app.entity.event.HungryEvent;
import com.bdqn.lyrk.ssm.study.app.entity.event.Person;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import static com.bdqn.lyrk.ssm.study.app.utils.Printer.print;

@Component
public class TeacherEntity {


    @Async
    @EventListener(HungryEvent.class)
    public void cook(HungryEvent hungryEvent) throws InterruptedException {
        Person person = (Person) hungryEvent.getSource();

        print(person.getName() + "餓了開始做飯");
        Thread.sleep(5000);
        print("飯做好了...");
    }
}

  在這裡@Async表明呼叫該方法時,會開啟一個執行緒進行非同步執行。@EventListener表明該方法會監聽對應的事件

  AppConfig的配置

@Configuration
@ComponentScan @EnableAsync
public class AppConfig{ /** * 定義非同步執行緒任務 * * @author chen.nie * @date 2018/6/27 **/ @Bean public Executor taskExecutor() { ExecutorService executorService = Executors.newSingleThreadExecutor(); return executorService; } }

  在這裡順便用Printer方法打印出執行緒名稱

public class Printer {

    public static void print(String content) {
        System.out.printf("[%s]--%s%n", Thread.currentThread().getName(), content);
    }
}

   main方法:

 public static void main(String[] args) {

        AnnotationConfigApplicationContext appApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        Person person = appApplicationContext.getBean(Person.class);
        person.setHungry(1);
        person.work();
        appApplicationContext.close();
      
    }

  執行結果:

[main]--餓了,誰來幫我做做飯..
[main]--繼續工作....
[pool-1-thread-1]--admin餓了開始做飯
[pool-1-thread-1]--飯做好了...

二、Async的原理分析

2.1 @EnableAsync

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {

    /**
     * Indicate the 'async' annotation type to be detected at either class
     * or method level.
     * <p>By default, both Spring's @{@link Async} annotation and the EJB 3.1
     * {@code @javax.ejb.Asynchronous} annotation will be detected.
     * <p>This attribute exists so that developers can provide their own
     * custom annotation type to indicate that a method (or all methods of
     * a given class) should be invoked asynchronously.
     */
    Class<? extends Annotation> annotation() default Annotation.class;

    /**
     * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
     * to standard Java interface-based proxies.
     * <p><strong>Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}</strong>.
     * <p>The default is {@code false}.
     * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
     * Spring-managed beans requiring proxying, not just those marked with {@code @Async}.
     * For example, other beans marked with Spring's {@code @Transactional} annotation
     * will be upgraded to subclass proxying at the same time. This approach has no
     * negative impact in practice unless one is explicitly expecting one type of proxy
     * vs. another &mdash; for example, in tests.
     */
    boolean proxyTargetClass() default false;

    /**
     * Indicate how async advice should be applied.
     * <p>The default is {@link AdviceMode#PROXY}.
     * @see AdviceMode
     */
    AdviceMode mode() default AdviceMode.PROXY;

    /**
     * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor}
     * should be applied.
     * <p>The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run
     * after all other post-processors, so that it can add an advisor to
     * existing proxies rather than double-proxy.
     */
    int order() default Ordered.LOWEST_PRECEDENCE;

}

  絕大多數的特性註解@EnableXXX都有個特性使用@Import進行匯入操作,那麼我們不妨在看一下AsyncConfigurationSelector這個類

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

    /**
     * {@inheritDoc}
     * @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
     * {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
     */
    @Override
    public String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] { ProxyAsyncConfiguration.class.getName() };
            case ASPECTJ:
                return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
            default:
                return null;
        }
    }

}

  看到這裡我們已經能猜到Spring中的Async是基於AOP實現的,畢竟我們看到了AspectJ與Proxy了嘛

2.2、ProxyAsyncConfiguration

/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.config.TaskManagementConfigUtils;
import org.springframework.util.Assert;

/**
 * {@code @Configuration} class that registers the Spring infrastructure beans necessary
 * to enable proxy-based asynchronous method execution.
 *
 * @author Chris Beams
 * @author Stephane Nicoll
 * @since 3.1
 * @see EnableAsync
 * @see AsyncConfigurationSelector
 */
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
        if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
            bpp.setAsyncAnnotationType(customAsyncAnnotation);
        }
        if (this.executor != null) {
            bpp.setExecutor(this.executor);
        }
        if (this.exceptionHandler != null) {
            bpp.setExceptionHandler(this.exceptionHandler);
        }
        bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
        bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
        return bpp;
    }

}

  在這裡我們可以看到此類是一個Spring的配置類,主要用於建立AsyncAnnotationBeanPostProcessor的Bean

2.3、AsyncAnnotationBeanPostProcessor

  首先我先貼一下這個類圖:

 我們先看一下它的父類AbstractAdvisingBeanPostProcessor裡重寫的方法,在該方法裡動態改變Bean的相關屬性

@Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof AopInfrastructureBean) {
            // Ignore AOP infrastructure such as scoped proxies.
            return bean;
        }
        // 如果為Advised物件且當前的bean適配當前的Advice,則將advisor新增到Advised裡
        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
                // Add our local Advisor to the existing proxy's Advisor chain...
                if (this.beforeExistingAdvisors) {
                    advised.addAdvisor(0, this.advisor);
                }
                else {
                    advised.addAdvisor(this.advisor);
                }
                return bean;
            }
        }
        // 如果當前的bean複合當前的通知
        if (isEligible(bean, beanName)) {
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            if (!proxyFactory.isProxyTargetClass()) {
                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
            }
            proxyFactory.addAdvisor(this.advisor);
            customizeProxyFactory(proxyFactory);
            return proxyFactory.getProxy(getProxyClassLoader());
        }

        // No async proxy needed.
        return bean;
    }

而AsyncAnnotationBeanPostProcessor 重寫了BeanFactoryAware介面方法,請大家關注一下屬性Executor和setBeanFactory方法:

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {

    /**
     * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor".
     * <p>Note that the initial lookup happens by type; this is just the fallback
     * in case of multiple executor beans found in the context.
     * @since 4.2
     * @see AnnotationAsyncExecutionInterceptor#DEFAULT_TASK_EXECUTOR_BEAN_NAME
     */
    public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
            AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;


    protected final Log logger = LogFactory.getLog(getClass());

    private Class<? extends Annotation> asyncAnnotationType;

    private Executor executor;

    private AsyncUncaughtExceptionHandler exceptionHandler;


    public AsyncAnnotationBeanPostProcessor() {
        setBeforeExistingAdvisors(true);
    }


    /**
     * Set the 'async' annotation type to be detected at either class or method
     * level. By default, both the {@link Async} annotation and the EJB 3.1
     * {@code javax.ejb.Asynchronous} annotation will be detected.
     * <p>This setter property exists so that developers can provide their own
     * (non-Spring-specific) annotation type to indicate that a method (or all
     * methods of a given class) should be invoked asynchronously.
     * @param asyncAnnotationType the desired annotation type
     */
    public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
        Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
        this.asyncAnnotationType = asyncAnnotationType;
    }

    /**
     * Set the {@link Executor} to use when invoking methods asynchronously.
     * <p>If not specified, default executor resolution will apply: searching for a
     * unique {@link TaskExecutor} bean in the context, or for an {@link Executor}
     * bean named "taskExecutor" otherwise. If neither of the two is resolvable,
     * a local default executor will be created within the interceptor.
     * @see AsyncAnnotationAdvisor#AsyncAnnotationAdvisor(Executor, AsyncUncaughtExceptionHandler)
     * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
     * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
     */
    public void setExecutor(Executor executor) {
        this.executor = executor;
    }

    /**
     * Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
     * exceptions thrown by asynchronous method executions.
     * @since 4.1
     */
    public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);

        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        if (this.asyncAnnotationType != null) {
            advisor.setAsyncAnnotationType(this.asyncAnnotationType);
        }
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }

}

  在這裡指定對應的this.advisor為AsyncAnnotationAdvisor。

2.4、AsyncAnnotationAdvisor

在這裡我們先看看它的建構函式:

public AsyncAnnotationAdvisor(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
        Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(2);
        asyncAnnotationTypes.add(Async.class);
        try {
            asyncAnnotationTypes.add((Class<? extends Annotation>)
                    ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            // If EJB 3.1 API not present, simply ignore.
        }
        if (exceptionHandler != null) {
            this.exceptionHandler = exceptionHandler;
        }
        else {
            this.exceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
        }
        this.advice = buildAdvice(executor, this.exceptionHandler);
        this.pointcut = buildPointcut(asyncAnnotationTypes);
    }

在這裡主要是構建通知和切點,下面我們分別來看看怎麼實現的:

buildAdvice:

protected Advice buildAdvice(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
        return new AnnotationAsyncExecutionInterceptor(executor, exceptionHandler);
    }

AnnotationAsyncExecutionInterceptor的父類實現了MethodInterceptor介面,我們來看看它重寫的方法:

/**
     * Intercept the given method invocation, submit the actual calling of the method to
     * the correct task executor and return immediately to the caller.
     * @param invocation the method to intercept and make asynchronous
     * @return {@link Future} if the original method returns {@code Future}; {@code null}
     * otherwise.
     */
    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        //取@Async裡的value屬性對應Bean,如果沒有值取名字為taskExecutor的Bean
        AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
        if (executor == null) {
            throw new IllegalStateException(
                    "No executor specified and no default executor set on AsyncExecutionInterceptor either");
        }

        Callable<Object> task = new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                try {
                    //非同步執行本體方法
                    Object result = invocation.proceed();
                    if (result instanceof Future) {
                        return ((Future<?>) result).get();
                    }
                }
                catch (ExecutionException ex) {
                    handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
                }
                catch (Throwable ex) {
                    handleError(ex, userDeclaredMethod, invocation.getArguments());
                }
                return null;
            }
        };

        return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }

buildPointCut:

/**
     * Calculate a pointcut for the given async annotation types, if any.
     * @param asyncAnnotationTypes the async annotation types to introspect
     * @return the applicable Pointcut object, or {@code null} if none
     */
    protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
        ComposablePointcut result = null;
        for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
            Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
            Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(asyncAnnotationType);
            if (result == null) {
                result = new ComposablePointcut(cpc);
            }
            else {
                result.union(cpc);
            }
            result = result.union(mpc);
        }
        return result;
    }

在這裡使用AnnotationMatchingPointcut,該切點會在有對應的annotation方法上切入相關的Advice,此處asyncAnnotationTypes對應的就是@Async

三、總結

  Spring非同步機制用到如下幾種方式實現:

  1)重寫BeanPostProcessor來改變Bean的屬性,在這裡針對Advised或者ProxyFactory動態新增定義好的通知AsyncAnnotationAdvisor

  2) 使用AOP的機制來,非同步執行@Async標記的方法

相關推薦

深入理解Spring非同步機制

一、Spring中實現非同步執行   在這裡我先以事件的機制舉例,注意預設情況下事件的釋出與監聽都是同步執行的。那麼我們來看一看基於非同步事件的例子該怎麼寫   首先還是定義事件: package com.bdqn.lyrk.ssm.study.app.entity.event; import

深入理解spring的事務管理機制及程式碼實現

Spring的事務管理機制 Spring事務管理高層抽象主要包括3個介面,Spring的事務主要是由他們共同完成的: PlatformTransactionManager:事務管理器—主要用於平臺相關事務的管理 TransactionDefinition: 事務定義資訊(隔

深入理解Spring的容器內事件釋出監聽機制

目錄 1. 什麼是事件監聽機制 2. JDK中對事件監聽機制的支援 2.1 基於JDK實現對任務執行結果的監聽 3.Spring容器對事件監聽機制的支援 3.1 基於Spring實現對任務執行結果的監聽 4.Spring事件監聽原始碼解析

深入理解Spring的容器內事件發布監聽機制

not main alt 事件類型 http inner interface 改變 pear 目錄 1. 什麽是事件監聽機制 2. JDK中對事件監聽機制的支持 2.1 基於JDK實現對任務執行結果的監聽 3.Spring容器對事件監聽機制的支持 3.1 基於Sprin

深入理解Spring的事件監聽機制

目錄 1. 什麼是事件監聽機制 在講解事件監聽機制前,我們先回顧下設計模式中的觀察者模式,因為事件監聽機制可以說是在典型觀察者模式基礎上的進一步抽象和改進。我們可以在JDK或者各種開源框架比如Spring中看到它的身影,從這個意義上說,事件監聽機制也可以看做一種對傳統觀察者模式的具體實現,不同的框架對其實現

Spring】原創 深入理解Spring兄弟事務傳播機制

Spring事務傳播機制    開啟事務的傳播機制 建立兩個方法,這個Service類是被spring容器所掃描的。在該方法上新增@Trancational事務註解。 在insertStudent2()方法上新增上propagation_Require

深入理解Android非同步訊息處理機制

一。概述   Android 中的非同步訊息處理主要分為四個部分組成,Message、Hndler、MessageQueue 和 Looper。其關係如下圖所示:     1. Message 是執行緒之間傳遞的訊息,它可以在內部攜帶少量資訊,用於在不同執行緒之間交換資料。   2. Messag

深入理解Spring Security授權機制原理

原創/朱季謙 在Spring Security許可權框架裡,若要對後端http介面實現許可權授權控制,有兩種實現方式。 一、一種是基於註解方法級的鑑權,其中,註解方式又有@Secured和@PreAuthorize兩種。 @Secured如: 1 @PostMapping("/test") 2 @

深入理解Spring AOP之二代理對象生成

gets code 網上 none work class als post 產生 深入理解Spring AOP之二代理對象生成 spring代理對象 上一篇博客中講到了Spring的一些基本概念和初步講了實現方

深入理解Spring IOC

epo 弊端 容器 one bsp 增加 代碼 改變 直接   為什麽會出現spring,spring出現解決了什麽問題?   1.分析普通多層架構存在的問題   JSP->Servlet->Service->Dao 層與層之間的依賴很強,屬於耦

深入理解Spring的兩大特征(IOC和AOP)<轉>

編譯器 如果 定義 包括 其他 enc row 這就是 生命 在某博主的博客上看到一篇解釋Spring的兩大核心IOC與AOP的文章,借此轉發一下,希望能夠幫助到更多的人。 原文地址:https://blog.csdn.net/gloomy_114/article/deta

深入理解Spring的ImportSelector接口

system override selectors tor onf div asc 分享圖片 打印   ImportSelector接口是至spring中導入外部配置的核心接口,在SpringBoot的自動化配置和@EnableXXX(功能性註解)都有它的存在,關於Spri

深入理解 Spring 事務原理

順序 etc wid efi 這一 tran source 所在 回滾 一、事務的基本原理 Spring事務的本質其實就是數據庫對事務的支持,沒有數據庫的事務支持,spring是無法提供事務功能的。對於純JDBC操作數據庫,想要用到事務,可以按照以下步驟進行: 獲取連接

Java程式設計師從笨鳥到菜鳥之(八十二)細談Spring(十一)深入理解spring+struts2整合(附原始碼)

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

深入理解Spring事務原理

事務的基本原理 Spring事務的本質其實就是資料庫對事務的支援,沒有資料庫的事務支援,spring是無法提供事務功能的。 對於純JDBC操作資料庫,想要用到事務,可以按照以下步驟進行: 1、獲取連線 Connection con = DriverManager.getCo

手把手教你深入理解Spring原始碼-spring開篇(中)

授人以魚不如授人以漁,《手把手教你深入理解Spring原始碼》專欄教你如何學習、思考、閱讀Spring框架,並應對其它開源框架不再畏懼。 接著上篇的文章講,上篇的文章講述了什麼是IOC,這篇講述什麼又是AOP? 一樣的在看這篇文章之前,大家不妨先花點時間思考一下。 1、AOP的設計原理

深入理解js記憶體機制

原文連結:深入理解js記憶體機制 js的記憶體機制在很多前端開發者看來並不是那麼重要,但是如果你想深入學習js,並將它利用好,打造高質量高效能的前端應用,就必須要了解js的記憶體機制。對於記憶體機制理解了以後,一些基本的問題比如最基本的引用資料型別和引用傳遞到底是怎麼回事兒?比如

深入理解spring註解之@ComponentScan註解

原創 深入理解spring註解之@ComponentScan註解 知了123 0人評論 149062人閱讀 2018-05-20 10:02:23

深入理解Spring MVC 思想

目錄  一、前言 二、spring mvc 核心類與介面 三、spring mvc 核心流程圖 四、spring mvc DispatcherServlet說明 五、spring mvc 父子上下文的說明 六、springMVC-mvc.xml 配置檔案片段講解 

【死磕 Spring】----- IOC 之深入理解 Spring IoC

在一開始學習 Spring 的時候,我們就接觸 IoC 了,作為 Spring 第一個最核心的概念,我們在解讀它原始碼之前一定需要對其有深入的認識,本篇為【死磕 Spring】系列部落格的第一篇博文,主要介紹 IoC 基本概念和各個元件。 IOC 理論 Io