最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring依賴注入與自動裝配原理解析

 更新時間:2026年02月09日 11:26:31   作者:識君啊  
本文講解Spring依賴注入核心知識:闡釋 Bean、IoC 等概念,介紹三種注入方式(推薦構(gòu)造器注入),解析 @Autowired 自動裝配原理、注解差異及循環(huán)依賴等問題的解決辦法,感興趣的朋友跟隨小編一起看看吧

寫在前面

如果你想深入學(xué)習(xí)SpringBoot的自動配置原理,那么本文是你必須掌握的基礎(chǔ)知識。很多人在使用SpringBoot時,只知道用@Autowired注入依賴,卻不知道背后的原理。

本文將從最基礎(chǔ)的概念講起,帶你徹底理解Spring的依賴注入機(jī)制。無論你是剛接觸Spring的新手,還是想復(fù)習(xí)鞏固的老手,都能從本文獲益。

適合人群: Spring初學(xué)者、準(zhǔn)備深入學(xué)習(xí)SpringBoot的開發(fā)者、需要復(fù)習(xí)基礎(chǔ)的工程師

一、核心概念:什么是Bean、依賴和注入

在學(xué)習(xí)依賴注入之前,我們必須先理解三個基本概念。

1.1 什么是Bean?

Bean就是由Spring容器管理的對象。

在傳統(tǒng)Java開發(fā)中,我們這樣創(chuàng)建對象:

UserService userService = new UserService();

而在Spring中,我們不需要自己new對象,而是讓Spring容器幫我們創(chuàng)建和管理:

@Service  // 告訴Spring:這是一個Bean,請幫我管理。@Service是@Component的衍生注解
public class UserService {
    // ...
}

形象的比喻:

  • 傳統(tǒng)方式:你需要什么對象,自己new一個(自己做飯)
  • Spring方式:你告訴Spring需要什么,Spring給你創(chuàng)建好(外賣送餐)

Bean的特點:

  • 由Spring容器創(chuàng)建
  • 由Spring容器管理生命周期
  • 默認(rèn)是單例的(整個應(yīng)用只有一個實例)
  • 可以被其他Bean引用

1.2 什么是依賴?

依賴就是一個對象需要使用另一個對象來完成工作。

舉個例子:

public class UserService {
    private UserDao userDao;  // UserService依賴UserDao
    public User getUser(Long id) {
        return userDao.findById(id);  // 需要用到UserDao
    }
}

在這個例子中:

  • UserService 需要 UserDao 才能完成查詢用戶的功能
  • 我們說:UserService 依賴 UserDao

生活中的例子:

  • 你要開車上班,你依賴汽車
  • 你要做飯,你依賴廚具
  • 你要寫代碼,你依賴電腦

1.3 什么是注入依賴?

注入依賴就是把一個對象需要的其他對象"送"給它。

傳統(tǒng)方式(自己創(chuàng)建依賴):

public class UserService {
    private UserDao userDao;
    public UserService() {
        this.userDao = new UserDaoImpl();  // 自己創(chuàng)建依賴
    }
}

依賴注入方式(Spring幫你注入):

@Service
public class UserService {
    @Autowired  // 告訴Spring:請把UserDao注入給我
    private UserDao userDao;
}

形象的比喻:

  • 傳統(tǒng)方式:你要喝咖啡,自己買豆子、磨豆、煮咖啡(自己創(chuàng)建依賴)
  • 依賴注入:你要喝咖啡,告訴咖啡店,咖啡店做好送給你(Spring注入依賴)

依賴注入的好處:

  1. 解耦:UserService不需要知道UserDao的具體實現(xiàn)
  2. 易測試:可以輕松替換為Mock對象進(jìn)行測試
  3. 易維護(hù):要換實現(xiàn),不需要修改UserService代碼
  4. 統(tǒng)一管理:所有對象由Spring容器統(tǒng)一創(chuàng)建和管理

二、控制反轉(zhuǎn)(IoC):理解Spring的核心思想

2.1 什么是控制反轉(zhuǎn)?

控制反轉(zhuǎn)(Inversion of Control,IoC)是一種設(shè)計思想:把對象創(chuàng)建和管理的控制權(quán)交給框架。

簡單來說:就相當(dāng)于框架幫你創(chuàng)建對象,只創(chuàng)建一次,那個類調(diào)用就把對象注入給誰

好處

  1. 徹底解放雙手:不用再手動new對象、手動傳依賴
  2. 統(tǒng)一管理對象:單例復(fù)用 + 生命周期全托管
  • 單例復(fù)用:框架創(chuàng)建的 Bean 默認(rèn)是單例(只創(chuàng)建 1 次),所有需要這個對象的類,注入的都是同一個實例,避免重復(fù)創(chuàng)建對象浪費內(nèi)存(比如一個UserDao對象,所有Service都用同一個,不用每次都new);
  • 生命周期托管:對象的 “創(chuàng)建→初始化→使用→銷毀” 全由框架控制,你可以通過注解(如@PostConstruct初始化、@PreDestroy銷毀)自定義流程,不用手動管理對象的生滅。
  1. 降低代碼耦合:類之間解耦,項目易維護(hù)、易擴(kuò)展

傳統(tǒng)方式: 你控制一切

public class UserService {
    private UserDao userDao;
    public UserService() {
        // 你決定創(chuàng)建什么對象
        this.userDao = new UserDaoImpl();
    }
}

IoC方式: Spring控制一切

@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // Spring決定注入什么對象
}

"反轉(zhuǎn)"體現(xiàn)在哪里?

維度傳統(tǒng)方式IoC方式
誰創(chuàng)建對象你自己newSpring容器創(chuàng)建
誰管理對象你自己管理Spring容器管理
誰決定依賴你在代碼中寫死Spring根據(jù)配置決定

核心思想: 不要打電話給我們,我們會打電話給你(Don’t call us, we’ll call you)

2.2 IoC容器的作用

Spring的IoC容器(ApplicationContext)就像一個對象工廠,負(fù)責(zé):

  1. 創(chuàng)建對象:根據(jù)配置創(chuàng)建Bean
  2. 管理生命周期:從創(chuàng)建到銷毀
  3. 注入依賴:自動裝配Bean之間的依賴關(guān)系
  4. 提供服務(wù):提供Bean的查找、獲取等功能
// Spring容器啟動時
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 從容器獲取Bean
UserService userService = context.getBean(UserService.class);
// 這個userService已經(jīng)注入好了所有依賴,可以直接使用
User user = userService.getUser(1L);

三、依賴注入的三種方式

Spring支持三種依賴注入方式,每種都有各自的適用場景。

方式1:字段注入(最常見但不推薦)

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    @Autowired
    private EmailService emailService;
}

優(yōu)點: 代碼簡潔,寫起來方便

缺點:

  • 無法注入final字段(不能保證不可變性)
  • 不利于單元測試(必須啟動Spring容器或使用反射)
  • 違反了面向?qū)ο蟮姆庋b原則
  • 依賴關(guān)系不明確

方式2:構(gòu)造器注入(強(qiáng)烈推薦)

@Service
//@RequiredArgsConstructor      
//Lombok注解,寫了這個就不用寫構(gòu)造器了,但是要加入Lombok依賴。(依賴在博主之前的關(guān)于Lombok的文章中有提到過)
public class UserService {
    private final UserDao userDao;
    private final EmailService emailService;
    // Spring 4.3+ 單構(gòu)造器可省略@Autowired
    public UserService(UserDao userDao, EmailService emailService) {
        this.userDao = userDao;
        this.emailService = emailService;
    }
}

優(yōu)點:

  • 可以使用final,保證不可變性和線程安全
  • 便于單元測試(直接new對象傳參,無需Spring容器)
  • 依賴關(guān)系一目了然
  • 如果依賴過多,構(gòu)造器參數(shù)會很長,提醒你類職責(zé)可能過重

這是Spring官方推薦的方式!

方式3:Setter方法注入

@Service
public class UserService {
    private UserDao userDao;
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

優(yōu)點: 可以在對象創(chuàng)建后重新注入依賴(適合可選依賴)

缺點: 依賴可能為null,不夠安全

三種方式對比:

特性字段注入構(gòu)造器注入Setter注入
代碼簡潔度最簡潔中等較繁瑣
是否支持final
測試友好度中等
依賴明確度不明確非常明確較明確
官方推薦度不推薦強(qiáng)烈推薦適合可選依賴

四、@Autowired與自動裝配詳解

4.1 什么是自動裝配?

自動裝配(Auto-Wiring)是Spring自動為Bean注入依賴的機(jī)制。

簡單來說:你只需要告訴Spring"我需要這個依賴"(通過@Autowired注解),Spring會自動幫你找到合適的Bean并注入進(jìn)來。

傳統(tǒng)方式(手動裝配):

<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
<bean id="userService" class="com.example.service.UserService">
    <property name="userDao" ref="userDao"/>  <!-- 手動指定依賴 -->
</bean>

自動裝配方式:

@Service
public class UserService {
    @Autowired  // 自動裝配,Spring自動找到UserDao并注入
    private UserDao userDao;
}

4.2 自動裝配的演進(jìn)歷史

階段1:XML時代(Spring 1.x - 2.x)

<!-- 按類型自動裝配 -->
<bean id="userService" class="com.example.service.UserService" 
      autowire="byType"/>
<!-- 按名稱自動裝配 -->
<bean id="userService" class="com.example.service.UserService" 
      autowire="byName"/>

階段2:注解時代(Spring 2.5+)

@Service
public class UserService {
    @Autowired  // 注解方式,更簡潔
    private UserDao userDao;
}

階段3:純Java配置時代(Spring 3.0+)

Spring 3.0 引入了 @Configuration@Bean

@Configuration
public class AppConfig {
    @Bean
    public UserDao userDao() {
        return new UserDaoImpl();
    }
    @Bean
    public UserService userService() {
        return new UserService();  // 自動裝配
    }
}

階段4:SpringBoot時代(Spring 4.0+)

@SpringBootApplication  // 自動掃描,自動裝配
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4.3 自動裝配的工作原理

核心處理器: AutowiredAnnotationBeanPostProcessor

完整流程:

1. Spring容器啟動,掃描所有Bean定義
   ↓
2. 創(chuàng)建Bean實例(調(diào)用構(gòu)造函數(shù))
   ↓
3. AutowiredAnnotationBeanPostProcessor介入
   ↓
4. 掃描Bean中的@Autowired注解
   ↓
5. 解析依賴類型
   ↓
6. 在容器中查找匹配的Bean
   ├─ 按類型查找(byType)- 默認(rèn)策略
   ├─ 如果有多個,按@Primary篩選
   ├─ 如果還有多個,按@Qualifier篩選
   └─ 如果還有多個,按字段名稱匹配
   ↓
7. 通過反射注入依賴
   ↓
8. Bean初始化完成

4.4 自動裝配的匹配策略

策略1:按類型匹配(byType)- 默認(rèn)

@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 查找UserDao類型的Bean
}
  • 找到1個 → 直接注入
  • 找到0個 → 拋出異常(除非required=false)
  • 找到多個 → 繼續(xù)下一步匹配

策略2:按@Primary匹配 - 優(yōu)先級

@Repository
@Primary  // 標(biāo)記為首選
public class UserDaoMysqlImpl implements UserDao { }
@Repository
public class UserDaoMongoImpl implements UserDao { }
@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 自動選擇@Primary標(biāo)記的Bean
}

策略3:按@Qualifier匹配 - 明確指定

@Service
public class UserService {
    @Autowired
    @Qualifier("userDaoMongoImpl")  // 明確指定使用哪個Bean
    private UserDao userDao;
}

策略4:按字段名稱匹配 - 輔助

@Service
public class UserService {
    @Autowired
    private UserDao userDaoMysqlImpl;  // 字段名與Bean名稱一致
}

策略5:注入集合 - 獲取所有實現(xiàn)

@Service
public class UserService {
    // 注入所有UserDao類型的Bean
    @Autowired
    private List<UserDao> userDaoList;
    
    // 注入所有UserDao類型的Bean到Map,key為Bean名稱
    @Autowired
    private Map<String, UserDao> userDaoMap;
}

匹配優(yōu)先級:

@Qualifier(最高優(yōu)先級)
    ↓
@Primary
    ↓
字段名稱匹配
    ↓
類型匹配(默認(rèn))

4.5 處理多個候選Bean

當(dāng)容器中有多個相同類型的Bean時:

// 有兩個UserDao的實現(xiàn)
@Repository
public class UserDaoMysqlImpl implements UserDao { }

@Repository
public class UserDaoMongoImpl implements UserDao { }

// 注入時會報錯
@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 錯誤:找到2個候選Bean
}

解決方案總結(jié):

方案代碼示例適用場景
@Qualifier@Qualifier("userDaoMysqlImpl")明確知道要用哪個Bean
@Primary在Bean上加@Primary有一個默認(rèn)首選實現(xiàn)
字段名匹配private UserDao userDaoMysqlImpl簡單場景
注入List/MapList<UserDao> userDaoList需要使用所有實現(xiàn)

4.6 可選依賴的處理

方式1:使用required=false

@Service
public class UserService {
    @Autowired(required = false)  // 找不到Bean不報錯,注入null
    private CacheService cacheService;
    public User getUser(Long id) {
        if (cacheService != null) {
            User cached = cacheService.get(id);
            if (cached != null) return cached;
        }
        return userDao.findById(id);
    }
}

方式2:使用Optional(更優(yōu)雅)

@Service
public class UserService {
    @Autowired
    private Optional<CacheService> cacheService;
    public User getUser(Long id) {
        return cacheService
            .map(cache -> cache.get(id))
            .orElseGet(() -> userDao.findById(id));
    }
}

4.7 自動裝配的注意事項

注意1:理解裝配時機(jī)

自動裝配發(fā)生在Bean的屬性注入階段,在構(gòu)造函數(shù)之后:

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    public UserService() {
        // 錯誤:此時userDao還未注入,為null
        // userDao.findAll();
    }
    @PostConstruct
    public void init() {
        // 正確:此時userDao已經(jīng)注入
        userDao.findAll();
    }
}

注意2:避免循環(huán)依賴

// 不推薦:循環(huán)依賴
@Service
public class UserService {
    @Autowired
    private OrderService orderService;
}
@Service
public class OrderService {
    @Autowired
    private UserService userService;
}
// 推薦:重新設(shè)計,單向依賴
@Service
public class OrderService {
    @Autowired
    private UserService userService;  // 單向依賴
}

4.8 自動裝配與SpringBoot自動配置的區(qū)別

很多人容易混淆這兩個概念:

自動裝配(Auto-Wiring):

  • Spring的核心功能
  • 負(fù)責(zé)注入依賴
  • 通過@Autowired實現(xiàn)
  • Bean已存在,只是注入

自動配置(Auto-Configuration):

  • SpringBoot的特性
  • 負(fù)責(zé)創(chuàng)建Bean
  • 通過@EnableAutoConfiguration實現(xiàn)
  • 根據(jù)條件自動創(chuàng)建Bean

關(guān)系:

SpringBoot自動配置 → 創(chuàng)建Bean
         ↓
Spring自動裝配 → 注入Bean

舉例:

// SpringBoot自動配置:自動創(chuàng)建DataSource Bean
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }
}
// 自動裝配:自動注入DataSource依賴
@Service
public class UserService {
    @Autowired  // 自動裝配
    private DataSource dataSource;  // 這個Bean是自動配置創(chuàng)建的
}

五、其他注入注解對比

5.1 @Resource(JSR-250規(guī)范)

@Service
public class UserService {
    @Resource  // 默認(rèn)按名稱裝配
    private UserDao userDao;
    @Resource(name = "userDaoMysqlImpl")  // 指定Bean名稱
    private UserDao userDao2;
}

@Autowired vs @Resource:

特性@Autowired@Resource
來源Spring框架Java EE規(guī)范
默認(rèn)裝配方式按類型(byType)按名稱(byName)
指定Bean配合@Qualifier使用name屬性
支持required支持不支持
支持構(gòu)造器注入支持不支持

建議: 優(yōu)先使用@Autowired,功能更強(qiáng)大,是Spring原生注解。

5.2 @Inject(JSR-330)

來自Java依賴注入規(guī)范:

@Service
public class UserService {
    @Inject  // 功能類似@Autowired
    private UserDao userDao;
}

需要額外引入依賴:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

@Inject vs @Autowired:

特性@Autowired@Inject
來源SpringJSR-330
是否支持required支持不支持
配合注解@Qualifier@Named

建議: 除非需要跨框架兼容,否則使用 @Autowired 即可。

5.3 @Value(注入配置值)

用于注入配置文件中的值,這是學(xué)習(xí)SpringBoot配置的基礎(chǔ):

@Service
public class UserService {
    @Value("${app.name}")  // 從配置文件讀取
    private String appName;
    @Value("${app.max-users:100}")  // 指定默認(rèn)值100
    private int maxUsers;
    @Value("#{systemProperties['user.home']}")  // SpEL表達(dá)式
    private String userHome;
}

配置文件(application.properties):

app.name=我的應(yīng)用
app.max-users=500

六、常見問題與解決方案

問題1:循環(huán)依賴

什么是循環(huán)依賴?

兩個或多個Bean相互依賴,形成閉環(huán):

@Service
public class UserService {
    @Autowired
    private OrderService orderService;  // UserService依賴OrderService
}
@Service
public class OrderService {
    @Autowired
    private UserService userService;  // OrderService依賴UserService
}

Spring如何解決?

Spring通過三級緩存機(jī)制解決單例Bean的字段注入循環(huán)依賴:

  1. 一級緩存:存放完整的Bean實例
  2. 二級緩存:存放早期的Bean引用(已實例化但未完成依賴注入)
  3. 三級緩存:存放Bean工廠(用于創(chuàng)建代理對象)

無法解決的情況:構(gòu)造器循環(huán)依賴

@Service
public class UserService {
    private final OrderService orderService;
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }
}
@Service
public class OrderService {
    private final UserService userService;
    public OrderService(UserService userService) {
        this.userService = userService;  // 報錯!無法解決
    }
}

解決方法:

  1. 重新設(shè)計,消除循環(huán)依賴(最佳方案)
  2. 使用@Lazy延遲加載
@Service
public class UserService {
    private final OrderService orderService;
    public UserService(@Lazy OrderService orderService) {
        this.orderService = orderService;  // 注入代理對象,延遲初始化
    }
}
  1. 改用字段注入(不推薦,只是權(quán)宜之計)

問題2:注入的Bean為null

常見原因:

原因1:類沒有被Spring管理

// 錯誤:忘記加@Service注解
public class UserService {
    @Autowired
    private UserDao userDao;  // 注入失敗,為null
}
// 正確:添加@Service注解
@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 正常注入
}

原因2:包掃描路徑不對

@SpringBootApplication
@ComponentScan("com.example.service")  // 只掃描service包
public class Application {
    // UserDao在com.example.dao包下,掃描不到
}
// 正確:掃描父包
@SpringBootApplication
@ComponentScan("com.example")  // 掃描整個com.example包
public class Application { }

原因3:直接new對象,不從Spring容器獲取

// 錯誤:直接new的對象,Spring無法注入依賴
UserService service = new UserService();
service.getUser(1L);  // userDao為null,報NPE
// 正確:從Spring容器獲取
@RestController
public class UserController {
    @Autowired
    private UserService userService;  // 從容器獲取,依賴已注入
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);  // 正常使用
    }
}

原因4:在靜態(tài)字段上使用@Autowired

// 錯誤:無法注入靜態(tài)字段
@Service
public class UserService {
    @Autowired
    private static UserDao userDao;  // 無法注入
}
// 正確:使用非靜態(tài)字段
@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 正常注入
}

問題3:注入時機(jī)問題

錯誤示例:在構(gòu)造函數(shù)中使用依賴

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    private List<User> cache;
    // 錯誤:構(gòu)造函數(shù)執(zhí)行時,userDao還未注入
    public UserService() {
        this.cache = userDao.findAll();  // NPE!
    }
}

正確做法:使用@PostConstruct

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    private List<User> cache;
    @PostConstruct  // 在依賴注入完成后執(zhí)行
    public void init() {
        this.cache = userDao.findAll();  // 正確
    }
}

Bean的生命周期:

1. 實例化(執(zhí)行構(gòu)造函數(shù))
   ↓
2. 依賴注入(@Autowired生效)
   ↓
3. 初始化前(@PostConstruct執(zhí)行)
   ↓
4. 初始化(InitializingBean.afterPropertiesSet())
   ↓
5. Bean可用
   ↓
6. 銷毀前(@PreDestroy執(zhí)行)
   ↓
7. 銷毀(DisposableBean.destroy())

七、最佳實踐

1. 優(yōu)先使用構(gòu)造器注入

// 推薦
@Service
public class UserService {
    private final UserDao userDao;  // final保證線程安全
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}
//或者可以這樣寫,一個意思
@Service
@@RequiredArgsConstructor 
public class UserService {
    private final UserDao userDao;
}

2. 使用final保證不可變性

@Service
public class UserService {
    private final UserDao userDao;  // final保證線程安全
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

3. 多個相同類型Bean時明確指定

@Service
public class UserService {
    private final UserDao userDao;
    public UserService(@Qualifier("userDaoMysqlImpl") UserDao userDao) {
        this.userDao = userDao;
    }
}

4. 可選依賴使用Optional

@Service
public class UserService {
    @Autowired
    private Optional<CacheService> cacheService;
}

5. 避免循環(huán)依賴

重新設(shè)計類的職責(zé),消除循環(huán)依賴。如果實在無法避免,使用@Lazy。

八、面試高頻問題

問題1:@Autowired和@Resource的區(qū)別?

答案:

維度@Autowired@Resource
來源Spring框架Java EE規(guī)范(JSR-250)
裝配方式默認(rèn)byType,可配合@Qualifier按名稱默認(rèn)byName,可通過name屬性指定
作用范圍字段、構(gòu)造器、方法、參數(shù)字段、方法
required屬性支持不支持
推薦使用Spring項目推薦需要跨框架兼容時使用

問題2:什么是循環(huán)依賴?Spring如何解決?

答案:

循環(huán)依賴是指兩個或多個Bean相互依賴,形成閉環(huán)。

Spring通過三級緩存解決單例Bean的循環(huán)依賴:

  1. 一級緩存:完整的Bean實例
  2. 二級緩存:早期的Bean引用(未完成依賴注入)
  3. 三級緩存:Bean工廠(用于創(chuàng)建代理對象)

注意: 構(gòu)造器循環(huán)依賴無法解決,需要使用@Lazy或重新設(shè)計。

問題3:為什么推薦使用構(gòu)造器注入?

答案:

  1. 依賴明確:所有依賴在構(gòu)造器中聲明,一目了然
  2. 不可變性:可以使用final修飾,保證線程安全
  3. 便于測試:可以直接new對象傳參,無需Spring容器
  4. 避免NPE:如果依賴為null,在創(chuàng)建時就會失敗
  5. 強(qiáng)制完整性:必須提供所有依賴才能創(chuàng)建對象

問題4:@Autowired的裝配流程是什么?

答案:

  1. Bean實例化(調(diào)用構(gòu)造函數(shù))
  2. AutowiredAnnotationBeanPostProcessor 掃描@Autowired注解
  3. 解析依賴類型
  4. 在容器中查找匹配的Bean:
    • 先按類型查找
    • 如果有多個,按@Primary篩選
    • 如果還有多個,按@Qualifier篩選
    • 如果還有多個,按字段名稱匹配
  5. 通過反射注入依賴
  6. 調(diào)用@PostConstruct方法
  7. Bean初始化完成

問題5:如何解決多個相同類型Bean的注入問題?

答案:

  1. 使用 @Qualifier 指定Bean名稱
  2. 使用 @Primary 標(biāo)記主要Bean
  3. 使用字段名稱匹配Bean名稱
  4. 注入 List<T>Map<String, T> 獲取所有Bean
  5. 使用 @Resource(name="beanName") 按名稱注入

問題6:依賴注入和工廠模式有什么區(qū)別?

答案:

工廠模式:

  • 對象自己通過工廠獲取依賴
  • 對象知道工廠的存在
  • 耦合度較高

依賴注入:

  • 容器主動將依賴注入到對象
  • 對象不知道容器的存在
  • 完全解耦

依賴注入是控制反轉(zhuǎn)的一種實現(xiàn)方式,比工廠模式更徹底地解耦。

九、與SpringBoot的關(guān)系

理解了Spring的依賴注入,你就能更好地理解SpringBoot的自動配置原理。

SpringBoot如何簡化依賴注入?

  1. 自動包掃描
@SpringBootApplication  // 包含@ComponentScan,自動掃描當(dāng)前包及子包
//目標(biāo)類標(biāo)注 @Component 或其衍生注解,且類處于啟動類所在包/子包下,SpringBoot 就會自動掃描并將其注冊為 Bean 實例。
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. 自動配置Bean

SpringBoot會自動配置常用的Bean(如DataSource、JdbcTemplate等),你只需注入使用:

@Service
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;  // SpringBoot自動配置,直接注入
}
  1. Starter依賴

引入starter依賴,相關(guān)Bean自動配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
@Service
public class UserService {
    @Autowired
    private EntityManager entityManager;  // 自動配置,直接注入
}

本文是學(xué)習(xí)SpringBoot的基礎(chǔ):

  • SpringBoot的自動配置本質(zhì)上就是自動創(chuàng)建Bean并注入依賴
  • 理解了依賴注入,才能理解@ConditionalOnBean等條件注解
  • 理解了依賴注入,才能理解Starter的工作原理

下一步學(xué)習(xí): 閱讀《SpringBoot自動配置原理深度解析》,理解SpringBoot如何自動創(chuàng)建和配置Bean。

十、推薦資源

官方文檔

  • Spring Framework Reference:https://docs.spring.io/spring-framework/docs/current/reference/html/
  • Spring IoC Container:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans

源碼閱讀

  • Spring Framework:https://github.com/spring-projects/spring-framework
  • 重點類:
    • AutowiredAnnotationBeanPostProcessor
    • DefaultListableBeanFactory
    • AbstractAutowireCapableBeanFactory

十一、總結(jié)

核心要點:

  1. Bean是由Spring容器管理的對象
  2. 依賴是一個對象需要使用另一個對象
  3. 依賴注入是Spring把依賴對象注入給需要的對象
  4. 控制反轉(zhuǎn)是把對象創(chuàng)建和管理的控制權(quán)交給Spring
  5. @Autowired默認(rèn)按類型裝配,可配合@Qualifier按名稱
  6. 構(gòu)造器注入是最佳實踐,優(yōu)于字段注入
  7. 循環(huán)依賴通過三級緩存解決,但構(gòu)造器循環(huán)依賴無法解決

學(xué)習(xí)路線:

Spring依賴注入(本文)
    ↓
SpringBoot自動配置原理
    ↓
自定義Starter開發(fā)
    ↓
Spring AOP原理
    ↓
Spring事務(wù)管理

給讀者的建議:

  1. 動手實踐:寫代碼,打斷點調(diào)試
  2. 閱讀源碼:看看AutowiredAnnotationBeanPostProcessor的實現(xiàn)
  3. 理解原理:知其然更要知其所以然
  4. 實際應(yīng)用:在項目中使用最佳實踐

到此這篇關(guān)于Spring依賴注入與自動裝配原理解析的文章就介紹到這了,更多相關(guān)spring依賴注入與自動裝配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 新手入門Jvm--Jvm垃圾回收

    新手入門Jvm--Jvm垃圾回收

    JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,JVM是一種用于計算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計算機(jī),是通過在實際的計算機(jī)上仿真模擬各種計算機(jī)功能來實現(xiàn)的
    2021-06-06
  • SpringMvc請求處理參數(shù)?和?響應(yīng)數(shù)據(jù)處理的示例詳解

    SpringMvc請求處理參數(shù)?和?響應(yīng)數(shù)據(jù)處理的示例詳解

    這篇文章主要介紹了SpringMvc請求處理參數(shù)和響應(yīng)數(shù)據(jù)處理,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09
  • Java List中數(shù)據(jù)的去重

    Java List中數(shù)據(jù)的去重

    今天小編就為大家分享一篇關(guān)于Java List中數(shù)據(jù)的去重,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • SpringBoot啟動失敗的解決方法:A component required a bean of type ‘xxxxxxx‘ that could not be found.

    SpringBoot啟動失敗的解決方法:A component required a&nb

    這篇文章主要介紹了解決SpringBoot啟動失?。篈 component required a bean of type ‘xxxxxxx‘ that could not be found.,目前解決方法有兩種,一種是不注入bean的方式,另一種是使用@Component的方式,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2023-02-02
  • Spring Security架構(gòu)以及源碼詳析

    Spring Security架構(gòu)以及源碼詳析

    這篇文章主要給大家介紹了關(guān)于Spring Security架構(gòu)以及源碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06
  • Java泛型的使用限制實例分析

    Java泛型的使用限制實例分析

    這篇文章主要介紹了Java泛型的使用限制,結(jié)合實例形式分析了不能使用java泛型的情況以及泛型使用的相關(guān)注意事項,需要的朋友可以參考下
    2019-08-08
  • Java實現(xiàn)異步執(zhí)行的8種方式總結(jié)

    Java實現(xiàn)異步執(zhí)行的8種方式總結(jié)

    這篇文章主要給大家介紹了關(guān)于Java實現(xiàn)異步執(zhí)行的8種方式,異步編程不會阻塞程序的執(zhí)行,它將耗時的操作提交給后臺線程或其他執(zhí)行環(huán)境,并立即返回,使得程序可以繼續(xù)執(zhí)行其他任務(wù),需要的朋友可以參考下
    2023-09-09
  • 2020最新IDEA SpringBoot整合Dubbo的實現(xiàn)(zookeeper版)

    2020最新IDEA SpringBoot整合Dubbo的實現(xiàn)(zookeeper版)

    這篇文章主要介紹了2020最新IDEA SpringBoot整合Dubbo(zookeeper版),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java中Connection timed out和Connection refused的區(qū)別講解

    Java中Connection timed out和Connection refused的區(qū)別講解

    今天小編就為大家分享一篇關(guān)于Java中Connection timed out和Connection refused的區(qū)別講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Java實現(xiàn)文件的加密解密功能示例

    Java實現(xiàn)文件的加密解密功能示例

    這篇文章主要介紹了Java實現(xiàn)文件的加密解密功能,結(jié)合具體實例形式詳細(xì)分析了java針對文件的讀取、判斷、加密、解密等相關(guān)步驟與操作技巧,需要的朋友可以參考下
    2017-10-10

最新評論

绍兴市| 通化县| 岳普湖县| 芦溪县| 临海市| 固原市| 庆安县| 江华| 鹤庆县| 湖北省| 大庆市| 麦盖提县| 衡南县| 台东市| 邹平县| 班戈县| 建德市| 双流县| 通许县| 德清县| 稻城县| 孝昌县| 青川县| 八宿县| 太和县| 长治市| 福安市| 日土县| 漯河市| 延安市| 余江县| 铜鼓县| 南乐县| 兴业县| 桃江县| 灵石县| 乌审旗| 沧州市| 松桃| 景泰县| 九龙城区|