Spring IOC核心原理詳解與運(yùn)用實(shí)戰(zhàn)教程
1. Spring IOC核心原理深度解析
1.1 BeanFactory體系與內(nèi)部結(jié)構(gòu)
Spring IOC容器的根基是BeanFactory接口,它定義了容器的基本行為規(guī)范,是所有Spring容器實(shí)現(xiàn)的最小化契約。作為Spring框架的基礎(chǔ)設(shè)施,BeanFactory不僅負(fù)責(zé)Bean的創(chuàng)建與管理,還提供了類型判斷、依賴注入等核心功能。與通常理解不同,BeanFactory本身并不直接涉及Bean的加載方式,而是聚焦于定義IOC容器的基本行為模式。
1.1.1 核心接口層級(jí)體系
BeanFactory的設(shè)計(jì)體現(xiàn)了接口隔離原則(ISP),通過(guò)分層接口逐步擴(kuò)展功能:
- BeanFactory :最基礎(chǔ)接口,提供getBean()、containsBean()等核心方法
- HierarchicalBeanFactory :支持父子容器分層結(jié)構(gòu),實(shí)現(xiàn)Bean的層級(jí)查找
- ListableBeanFactory :支持枚舉所有Bean實(shí)例,提供類型掃描能力
- AutowireCapableBeanFactory :擴(kuò)展自動(dòng)裝配能力,支持resolveDependency等依賴解析方法
- ConfigurableBeanFactory :提供配置能力,允許注冊(cè)作用域、類型轉(zhuǎn)換器等
- ConfigurableListableBeanFactory :集合所有可配置與可枚舉特性,是完整的容器契約
1.1.2 關(guān)鍵實(shí)現(xiàn)類分析
DefaultListableBeanFactory是Spring注冊(cè)及加載Bean的核心實(shí)現(xiàn)類,整合了所有接口功能。其內(nèi)部維護(hù)多個(gè)關(guān)鍵數(shù)據(jù)結(jié)構(gòu):
// 核心數(shù)據(jù)結(jié)構(gòu)示意
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
// Bean定義注冊(cè)表:存儲(chǔ)所有BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
// 單例Bean緩存:一級(jí)緩存singletonObjects
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 三級(jí)緩存結(jié)構(gòu)(解決循環(huán)依賴)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
}AbstractAutowireCapableBeanFactory作為創(chuàng)建、自動(dòng)裝配、初始化和銷毀Bean的核心抽象類,提供了完整的Bean生命周期模板方法。其createBean方法是整個(gè)Bean創(chuàng)建流程的入口:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 解析Bean類型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
// 2. 準(zhǔn)備方法覆蓋(處理@Lookup等)
mbd.prepareMethodOverrides();
// 3. 實(shí)例化前的BeanPostProcessor處理
Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) return bean;
// 4. 執(zhí)行實(shí)際創(chuàng)建
Object beanInstance = doCreateBean(beanName, mbd, args);
return beanInstance;
}DefaultSingletonBeanRegistry負(fù)責(zé)單例Bean的注冊(cè)與管理,維護(hù)三級(jí)緩存機(jī)制。其getSingleton方法是解決循環(huán)依賴的關(guān)鍵:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 一級(jí)緩存查詢
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 二級(jí)緩存查詢
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 三級(jí)緩存獲取工廠并創(chuàng)建
ObjectFactory<?> factory = this.singletonFactories.get(beanName);
if (factory != null) {
singletonObject = factory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}1.2 依賴注入機(jī)制詳解
依賴注入(DI)是IOC的具體實(shí)現(xiàn),Spring通過(guò)反射機(jī)制將對(duì)象的創(chuàng)建和依賴管理交給容器完成?,F(xiàn)代Spring應(yīng)用主要采用注解驅(qū)動(dòng)注入,其核心處理器是AutowiredAnnotationBeanPostProcessor。
1.2.1 注入方式演進(jìn)
- 構(gòu)造器注入:Spring官方推薦方式,強(qiáng)制依賴完整性,支持不可變對(duì)象
- Setter注入:傳統(tǒng)方式,提供靈活性但破壞封裝性
- 字段注入:使用@Autowired直接標(biāo)注字段,簡(jiǎn)潔但難以測(cè)試
1.2.2 @Autowired處理核心流程
AutowiredAnnotationBeanPostProcessor(簡(jiǎn)稱AOP)實(shí)現(xiàn)了BeanPostProcessor和MergedBeanDefinitionPostProcessor接口,其處理流程分為三個(gè)階段:
階段一:元數(shù)據(jù)提取與緩存
在Bean定義合并階段,postProcessMergedBeanDefinition方法掃描類中的@Autowired、@Value、@Inject注解,構(gòu)建InjectionMetadata對(duì)象。為提高性能,Spring使用injectionMetadataCache緩存已解析的元數(shù)據(jù),避免重復(fù)反射掃描。
private final Map<Class<?>, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>();
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// 從緩存獲取
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 檢查是否需要刷新
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = buildAutowiringMetadata(clazz); // 反射解析
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
return metadata;
}階段二:依賴解析
在屬性填充階段,resolveDependency方法通過(guò)DefaultListableBeanFactory的依賴解析器定位目標(biāo)Bean。解析策略包括:
- 按類型匹配:首選策略,利用ResolvableType進(jìn)行泛型精確匹配
- 按名稱匹配:當(dāng)存在多個(gè)同類型Bean時(shí),結(jié)合@Qualifier注解
- @Value解析:通過(guò)EmbeddedValueResolver處理SpEL表達(dá)式和占位符
階段三:實(shí)際注入
postProcessProperties方法調(diào)用InjectionMetadata.inject()完成最終注入。對(duì)于字段注入,使用反射的Field.set()方法;對(duì)于方法注入,使用Method.invoke()。
1.3 循環(huán)依賴與三級(jí)緩存機(jī)制
Spring通過(guò)三級(jí)緩存機(jī)制解決單例Bean的循環(huán)依賴問(wèn)題。
1.3.1 三級(jí)緩存結(jié)構(gòu)
// DefaultSingletonBeanRegistry中的三級(jí)緩存
public class DefaultSingletonBeanRegistry {
// 一級(jí)緩存:完全初始化完成的單例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二級(jí)緩存:早期曝光的單例Bean(已實(shí)例化但尚未初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三級(jí)緩存:?jiǎn)卫鼴ean工廠,用于生成早期Bean引用
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}1.3.2 循環(huán)依賴解決流程
假設(shè)場(chǎng)景:A依賴B,B依賴A
- 創(chuàng)建A實(shí)例:doCreateBean調(diào)用createBeanInstance實(shí)例化A,此時(shí)A未填充屬性
- 暴露早期引用:將A的ObjectFactory放入三級(jí)緩存`singletonFactories
- 填充A的屬性:populateBean發(fā)現(xiàn)需要B,觸發(fā)getBean(B)
- 創(chuàng)建B實(shí)例:同樣流程實(shí)例化B,并將其工廠放入三級(jí)緩存
- 填充B的屬性:B需要A,調(diào)用getBean(A)
- 從緩存獲取A:此時(shí)A不在一級(jí)緩存,但三級(jí)緩存存在其工廠,調(diào)用getObject()返回A的早期引用(未完全初始化),并將A移至二級(jí)緩存
- 完成B初始化:B獲得A的早期引用后完成屬性填充和初始化,將B放入一級(jí)緩存
- 完成A初始化:B創(chuàng)建完成后,A繼續(xù)填充屬性,最終完成初始化并放入一級(jí)緩存
重要限制:此機(jī)制僅支持單例Bean的屬性注入循環(huán)依賴,構(gòu)造器注入的循環(huán)依賴無(wú)法解決,因?yàn)閷?shí)例化階段就需要依賴對(duì)象,無(wú)法提前暴露工廠。
2. Spring Boot中的IOC實(shí)踐與應(yīng)用
2.1 Spring Boot自動(dòng)配置與IOC的協(xié)同
Spring Boot的自動(dòng)配置機(jī)制是其核心特性,建立在Spring IOC容器之上,通過(guò)條件注解實(shí)現(xiàn)智能裝配。
2.1.1 @Conditional體系與條件評(píng)估
@Conditional是Spring 4.0引入的基礎(chǔ)設(shè)施,通過(guò)Condition接口實(shí)現(xiàn)條件化裝配。Spring Boot擴(kuò)展了多個(gè)派生注解:
@ConditionalOnClass // 類路徑存在指定類 @ConditionalOnMissingBean // 容器中不存在指定Bean @ConditionalOnProperty // 配置屬性滿足條件 @ConditionalOnWebApplication // 當(dāng)前是Web應(yīng)用
內(nèi)部工作原理:
AutoConfigurationImportSelector負(fù)責(zé)加載META-INF/spring.factories中定義的自動(dòng)配置類。在filter方法中,調(diào)用ConditionEvaluator對(duì)每個(gè)配置類上的條件注解進(jìn)行評(píng)估:
// AutoConfigurationImportSelector核心邏輯
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
// 遍歷每個(gè)配置類
for (String configuration : configurations) {
// 獲取其上的@Conditional注解
// 調(diào)用ConditionEvaluator評(píng)估
if (conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
continue; // 不滿足條件則跳過(guò)
}
result.add(configuration);
}
return result;
}ConditionEvaluator通過(guò)ConditionContext獲取容器上下文、環(huán)境變量、類加載器等信息,調(diào)用Condition.matches()方法進(jìn)行最終決策。
2.1.2 @ConditionalOnMissingBean實(shí)踐示例
在Starter開(kāi)發(fā)中,@ConditionalOnMissingBean允許用戶輕松覆蓋默認(rèn)配置:
@Configuration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MyService.class)
public MyService defaultMyService() {
return new DefaultMyServiceImpl();
}
}
// 用戶自定義Bean將覆蓋默認(rèn)實(shí)現(xiàn)
@Component
public class CustomMyService implements MyService {
// 自定義實(shí)現(xiàn)
}2.2 Bean作用域與生命周期管理
Spring Boot簡(jiǎn)化了作用域配置,通過(guò)@Scope注解靈活控制Bean生命周期。
2.2.1 內(nèi)置作用域詳解
- singleton :默認(rèn)作用域,IOC容器中僅一個(gè)實(shí)例
- prototype :每次請(qǐng)求創(chuàng)建新實(shí)例,適合有狀態(tài)Bean
- request :HTTP請(qǐng)求級(jí)別,Web應(yīng)用中每個(gè)請(qǐng)求一個(gè)實(shí)例
- session :HTTP會(huì)話級(jí)別,每個(gè)用戶會(huì)話一個(gè)實(shí)例
- application :ServletContext級(jí)別,整個(gè)應(yīng)用共享
- websocket :WebSocket會(huì)話級(jí)別
2.2.2 自定義作用域?qū)崿F(xiàn)步驟
實(shí)現(xiàn)線程局部作用域的完整示例:
實(shí)現(xiàn)Scope接口:
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class ThreadLocalScope implements Scope {
private final ThreadLocal<Map<String, Object>> threadLocal =
ThreadLocal.withInitial(HashMap::new);
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = threadLocal.get();
return scope.computeIfAbsent(name, k -> objectFactory.getObject());
}
@Override
public Object remove(String name) {
return threadLocal.get().remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
// 線程銷毀時(shí)清理
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}注冊(cè)作用域:
@Configuration
public class ScopeConfig {
@Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope("threadLocal", new ThreadLocalScope());
}
}使用自定義作用域:
@Component
@Scope("threadLocal")
public class RequestContext {
private String traceId;
// getter/setter
}
// 或通過(guò)@Scope的proxyMode創(chuàng)建代理
@Component
@Scope(value = "threadLocal", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TenantDataSource {
// 多租戶數(shù)據(jù)源實(shí)現(xiàn)
}2.3 配置方式演進(jìn)與最佳實(shí)踐
2.3.1 @ConfigurationProperties構(gòu)造函數(shù)綁定(Spring Boot 2.2+)
構(gòu)造函數(shù)綁定實(shí)現(xiàn)不可變配置類,提升線程安全性:
@ConfigurationProperties(prefix = "app.datasource")
@ConstructorBinding // Spring Boot 2.2+支持
public class DataSourceProperties {
private final String url;
private final String username;
private final int poolSize;
// 單構(gòu)造器可省略@ConstructorBinding
public DataSourceProperties(String url, String username, int poolSize) {
this.url = url;
this.username = username;
this.poolSize = poolSize;
}
// 僅提供getter,無(wú)setter實(shí)現(xiàn)不可變
public String getUrl() { return url; }
}
// 啟用配置類
@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class AppConfig {
}配置步驟:
- 添加@ConfigurationProperties和@ConstructorBinding注解
- 定義final字段和參數(shù)化構(gòu)造函數(shù)
- 使用@EnableConfigurationProperties或@ConfigurationPropertiesScan啟用掃描
- 在application.yml中提供配置值
注意事項(xiàng):@ConstructorBinding不能與@Component、@Bean或@Import一起使用。
3. Bean創(chuàng)建過(guò)程的完整剖析
3.1 BeanDefinition的解析與合并
3.1.1 BeanDefinition繼承機(jī)制
Spring支持通過(guò)parent屬性實(shí)現(xiàn)Bean定義的繼承,類似于面向?qū)ο笾械念惱^承。子定義可以覆蓋父定義的屬性,也可以新增特有配置:
<bean id="abstractDataSource" abstract="true"
class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
</bean>
<bean id="masterDataSource" parent="abstractDataSource">
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>合并算法:AbstractBeanFactory.getMergedBeanDefinition()方法遞歸合并父定義,最終生成RootBeanDefinition。合并過(guò)程確保父定義的通用配置被子定義繼承,同時(shí)子定義可以覆蓋特定屬性。
3.1.2 ClassPathBeanDefinitionScanner掃描機(jī)制
ClassPathBeanDefinitionScanner是注解驅(qū)動(dòng)開(kāi)發(fā)的核心,負(fù)責(zé)掃描類路徑并解析@Component及其派生注解:
// 掃描入口
public int scan(String... basePackages) {
int beanCount = 0;
for (String basePackage : basePackages) {
// 查找候選組件
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 解析作用域、代理模式等
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 生成Bean名稱
String beanName = beanNameGenerator.generateBeanName(candidate, registry);
// 注冊(cè)到容器
registry.registerBeanDefinition(beanName, candidate);
beanCount++;
}
}
return beanCount;
}掃描過(guò)程通過(guò)ASM字節(jié)碼操作直接讀取Class文件的元數(shù)據(jù),無(wú)需類加載,提升性能。
3.1.3 BeanDefinitionReader的多源配置處理
BeanDefinitionReader是配置解析的統(tǒng)一入口,支持XML、Properties、注解等多種配置源:
- XmlBeanDefinitionReader :解析XML配置,通過(guò)BeanDefinitionParserDelegate處理< bean>、< import>、< alias>等標(biāo)簽
- AnnotatedBeanDefinitionReader :處理@Configuration、@Component等注解,直接注冊(cè)BeanDefinition
- PropertiesBeanDefinitionReader :兼容舊版Properties格式配置
3.2 實(shí)例化、屬性注入與初始化
3.2.1 實(shí)例化策略
Spring通過(guò)InstantiationStrategy接口抽象實(shí)例化過(guò)程,支持兩種實(shí)現(xiàn):
- SimpleInstantiationStrategy :通過(guò)反射調(diào)用構(gòu)造器
- CglibSubclassingInstantiationStrategy :當(dāng)存在方法注入時(shí),生成CGLIB子類
// AbstractAutowireCapableBeanFactory中的實(shí)例化
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 使用工廠方法實(shí)例化
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 2. 構(gòu)造器自動(dòng)裝配
Constructor<?>[] constructors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (constructors != null) {
return autowireConstructor(beanName, mbd, constructors, args);
}
// 3. 默認(rèn)無(wú)參構(gòu)造器
return instantiateBean(beanName, mbd);
}3.2.2 屬性注入與Aware接口
populateBean方法負(fù)責(zé)屬性填充,支持byName、byType、constructor等多種注入模式。在注入前,Spring會(huì)回調(diào)各類Aware接口:
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(this);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}3.3 BeanPostProcessor的執(zhí)行機(jī)制
BeanPostProcessor是Spring擴(kuò)展機(jī)制的核心,允許在Bean生命周期的關(guān)鍵節(jié)點(diǎn)進(jìn)行攔截:
// 初始化前回調(diào) Object postProcessBeforeInitialization(Object bean, String beanName) // 初始化后回調(diào) Object postProcessAfterInitialization(Object bean, String beanName)
執(zhí)行順序:BeanPostProcessor的調(diào)用遵循明確的順序,通過(guò)Ordered接口或@Order注解控制優(yōu)先級(jí)。典型應(yīng)用場(chǎng)景包括:
- @Autowired處理 :AutowiredAnnotationBeanPostProcessor
- AOP代理創(chuàng)建:AnnotationAwareAspectJAutoProxyCreator
- 初始化驗(yàn)證:CommonAnnotationBeanPostProcessor處理@PostConstruct
到此這篇關(guān)于Spring IOC核心原理詳解與運(yùn)用實(shí)戰(zhàn)教程的文章就介紹到這了,更多相關(guān)Spring IOC原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis實(shí)現(xiàn)一對(duì)多映射處理
MyBatis是一種流行的Java持久化框架,這篇文章主要為大家介紹了Mybatis如何實(shí)現(xiàn)一對(duì)多映射處理,文中的示例代碼講解詳細(xì),需要的可以參考下2023-08-08
RabbitMQ消息單獨(dú)與批量的TTL詳細(xì)介紹
這篇文章主要介紹了RabbitMQ消息單獨(dú)與批量的TTL,TTL全名是Time To Live存活時(shí)間,表示當(dāng)消息由生產(chǎn)端存入MQ當(dāng)中的存活時(shí)間,當(dāng)時(shí)間到達(dá)的時(shí)候還未被消息就會(huì)被自動(dòng)清除,感興趣的同學(xué)可以參考下文2023-05-05
MyBatis-Plus進(jìn)行分頁(yè)查詢優(yōu)化的實(shí)踐指南
在實(shí)際開(kāi)發(fā)中,分頁(yè)查詢是常見(jiàn)的需求,尤其是需要關(guān)聯(lián)其他表獲取額外信息的場(chǎng)景,本文將介紹如何使用 MyBatis-Plus 進(jìn)行高效分頁(yè)查詢,并結(jié)合關(guān)聯(lián)查詢優(yōu)化數(shù)據(jù)填充,需要的可以了解下2025-07-07
SpringBoot利用可視化服務(wù)管理腳本部署應(yīng)用
在SpringBoot應(yīng)用的生產(chǎn)環(huán)境部署中,傳統(tǒng)的手動(dòng)啟停服務(wù)方式不僅效率低下,還容易出錯(cuò),所以本文將分享一個(gè)功能強(qiáng)大的可視化服務(wù)管理腳本,讓SpringBoot應(yīng)用的部署和運(yùn)維變得簡(jiǎn)單高效2025-08-08
java?for循環(huán)內(nèi)執(zhí)行多線程問(wèn)題
這篇文章主要介紹了java?for循環(huán)內(nèi)執(zhí)行多線程問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
解決springcloud-gateway限流遇到的問(wèn)題
這篇文章主要介紹了解決springcloud-gateway限流遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
java客戶端線上Apollo服務(wù)端的實(shí)現(xiàn)
這篇文章主要介紹了java客戶端線上Apollo服務(wù)端的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Mybatis批量更新報(bào)錯(cuò)問(wèn)題
這篇文章主要介紹了Mybatis批量更新報(bào)錯(cuò)的問(wèn)題及解決辦法,包括mybatis批量更新的兩種方式,需要的的朋友參考下2017-01-01

