網(wǎng)站首頁 編程語言 正文
我們知道,ioc(控制反轉(zhuǎn))和di(依賴注入)是spring里面很重要的東西,那么,我們?nèi)绾巫约菏謱懗鲞@樣的代碼呢?
什么是IOC
IOC,Inversion of Control,即控制反轉(zhuǎn),這個(gè)不是一種什么技術(shù),而是一種思想,平時(shí)我們寫代碼,都是把對(duì)象自己new出來,而現(xiàn)在我們用控制反轉(zhuǎn)這種思想,把對(duì)象的創(chuàng)建交給了程序,那么我們就不需要手動(dòng)創(chuàng)建對(duì)象了,這樣降低了程序的耦合度。
這是spring的核心,貫穿始終。對(duì)于spring框架來說,就是由spring來負(fù)責(zé)控制對(duì)象的生命周期和對(duì)象間的關(guān)系。這是什么意思呢,舉個(gè)簡(jiǎn)單的例子,我們是如何找女朋友的?常見的情況是,我們到處去看哪里有長(zhǎng)得漂亮身材又好的mm,然后打聽她們的興趣愛好、qq號(hào)、電話號(hào)、wx號(hào),想辦法認(rèn)識(shí)她們,投其所好送其所要,然后嘿嘿……這個(gè)過程是復(fù)雜深?yuàn)W的,我們必須自己設(shè)計(jì)和面對(duì)每個(gè)環(huán)節(jié)。傳統(tǒng)的程序開發(fā)也是如此,在一個(gè)對(duì)象中,如果要使用另外的對(duì)象,就必須得到它(自己new一個(gè),或者從JNDI中查詢一個(gè)),使用完之后還要將對(duì)象銷毀(比如Connection等),對(duì)象始終會(huì)和其他的接口或類藕合起來。
那么IoC是如何做的呢?有點(diǎn)像通過婚介找女朋友,在我和女朋友之間引入了一個(gè)第三者:婚姻介紹所?;榻楣芾砹撕芏嗄心信馁Y料,我可以向婚介提出一個(gè)列表,告訴它我想找個(gè)什么樣的女朋友,比如長(zhǎng)得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術(shù)像齊達(dá)內(nèi)之類的,然后婚介就會(huì)按照我們的要求,提供一個(gè)mm,我們只需要去和她談戀愛、結(jié)婚就行了。
簡(jiǎn)單明了,如果婚介給我們的人選不符合要求,我們就會(huì)拋出異常。整個(gè)過程不再由我自己控制,而是有婚介這樣一個(gè)類似容器的機(jī)構(gòu)來控制。Spring所倡導(dǎo)的開發(fā)方式就是如此,所有的類都會(huì)在spring容器中登記,告訴spring你是個(gè)什么東西,你需要什么東西,然后spring會(huì)在系統(tǒng)運(yùn)行到適當(dāng)?shù)臅r(shí)候,把你要的東西主動(dòng)給你,同時(shí)也把你交給其他需要你的東西。所有的類的創(chuàng)建、銷毀都由 spring來控制,也就是說控制對(duì)象生存周期的不再是引用它的對(duì)象,而是spring。對(duì)于某個(gè)具體的對(duì)象而言,以前是它控制其他對(duì)象,現(xiàn)在是所有對(duì)象都被spring控制,所以這叫控制反轉(zhuǎn)。
什么是DI
DI—Dependency Injection,即“依賴注入”:組件之間依賴關(guān)系由容器在運(yùn)行期決定,形象的說,即由容器動(dòng)態(tài)的將某個(gè)依賴關(guān)系注入到組件之中。依賴注入的目的并非為軟件系統(tǒng)帶來更多功能,而是為了提升組件重用的頻率,并為系統(tǒng)搭建一個(gè)靈活、可擴(kuò)展的平臺(tái)。通過依賴注入機(jī)制,我們只需要通過簡(jiǎn)單的配置,而無需任何代碼就可指定目標(biāo)需要的資源,完成自身的業(yè)務(wù)邏輯,而不需要關(guān)心具體的資源來自何處,由誰實(shí)現(xiàn)。
理解DI的關(guān)鍵是:“誰依賴誰,為什么需要依賴,誰注入誰,注入了什么”,那我們來深入分析一下:
●誰依賴于誰:當(dāng)然是應(yīng)用程序依賴于IoC容器;
●為什么需要依賴:應(yīng)用程序需要IoC容器來提供對(duì)象需要的外部資源;
●誰注入誰:很明顯是IoC容器注入應(yīng)用程序某個(gè)對(duì)象,應(yīng)用程序依賴的對(duì)象;
●注入了什么:就是注入某個(gè)對(duì)象所需要的外部資源(包括對(duì)象、資源、常量數(shù)據(jù))。
IoC和DI由什么關(guān)系呢?其實(shí)它們是同一個(gè)概念的不同角度描述,由于控制反轉(zhuǎn)概念比較含糊(可能只是理解為容器控制對(duì)象這一個(gè)層面,很難讓人想到誰來維護(hù)對(duì)象關(guān)系),所以2004年大師級(jí)人物Martin Fowler又給出了一個(gè)新的名字:“依賴注入”,相對(duì)IoC 而言,“依賴注入”明確描述了“被注入對(duì)象依賴IoC容器配置依賴對(duì)象”。
IOC也叫依賴注入(DI)
2004年,Martin Fowler探討了同一個(gè)問題,既然IOC是控制反轉(zhuǎn),那么到底是“哪些方面的控制被反轉(zhuǎn)了呢?”,經(jīng)過詳細(xì)地分析和論證后,他得出了答案:“獲得依賴對(duì)象的過程被反轉(zhuǎn)了”??刂票环崔D(zhuǎn)之后,獲得依賴對(duì)象的過程由自身管理變?yōu)榱擞蒊OC容器主動(dòng)注入。于是,他給“控制反轉(zhuǎn)”取了一個(gè)更合適的名字叫做“依賴注入(Dependency Injection)”。他的這個(gè)答案,實(shí)際上給出了實(shí)現(xiàn)IOC的方法:注入。所謂依賴注入,就是由IOC容器在運(yùn)行期間,動(dòng)態(tài)地將某種依賴關(guān)系注入到對(duì)象之中。
實(shí)現(xiàn)IOC和DI
首先,我們需要掌握兩項(xiàng)必要的知識(shí)
- 反射
- 注解
如果對(duì)注解和反射沒有怎么了解的,可以看看我之前的寫的博客,java的反射和注解
建立項(xiàng)目
首先,我們先創(chuàng)建基本的項(xiàng)目,并且分好包
- dao
- entity
- service
- Annoation
- reflect
我們模擬的例子是學(xué)生借書,所以,我們先創(chuàng)建好每個(gè)包下的java文件
實(shí)體類下的User,Book
public class Book {
private int id;
private String name;
public Book(int id, String name) {
this.id = id;
this.name = name;
}
public Book(){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public class User implements Serializable {
private int id;
private String username;
private String password;
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public User(){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
dao層的UserDao,UserDaoImpl,BookDao,BookDaoImpl
public interface BookDao {
User findBookById();
List<User> findAllBook();
List<User>findBookByBookName(String username);
void save(Book book);
}
public class BookDaoImpl implements BookDao {
@Override
public User findBookById() {
System.out.println("這里是BookDao-findBookById");
return null;
}
@Override
public List<User> findAllBook() {
System.out.println("這里是BookDao-findAllBook");
return null;
}
@Override
public List<User> findBookByBookName(String username) {
System.out.println("這里是BookDao-findBookByBookName");
return null;
}
@Override
public void save(Book book) {
System.out.println("這里是BookDao-save");
}
}
public interface UserDao {
User findUserById();
List<User>findAllUser();
List<User>findUserByUserName(String username);
void save(User user);
}
public class UserDaoImpl implements UserDao {
@Override
public User findUserById() {
System.out.println("這里是UserDao-findUserById");
return null;
}
@Override
public List<User> findAllUser() {
System.out.println("這里是UserDao-findAllUser");
return null;
}
@Override
public List<User> findUserByUserName(String findUserByUserName) {
System.out.println("這里是UserDao-findUserById");
return null;
}
@Override
public void save(User user) {
System.out.println("這里是UserDao-save");
}
}
service層的UserService,UserserviceImpl,BookService,BookServiceImpl
public interface BookService {
void borrow(User user, Book book);
}
public class BookServiceImpl implements BookService {
@Override
public void borrow(User user,Book book) {
}
}
public interface UserService {
void login();
void regist();
}
public class UserServiceImpl implements UserService {
private UserDao userDao=new UserDaoImpl();
@Override
public void login() {
userDao.findUserByUserName("劉水龍");
System.out.println("登錄業(yè)務(wù)的實(shí)現(xiàn)");
}
@Override
public void regist() {
userDao.save(new User(1,"劉水龍","222"));
System.out.println("注冊(cè)業(yè)務(wù)的實(shí)現(xiàn)");
}
}
基本思路
通過配置文件把類放入ioc容器
我們知道ioc有一個(gè)ioc容器,把全部的對(duì)象放入ioc容器之中,這樣我們就不需要自己去手動(dòng)創(chuàng)建對(duì)象了,那么我們是怎么把對(duì)象放入ioc容器之中的呢?很簡(jiǎn)單,在這里我們使用了Map
我們?cè)趓efect包下創(chuàng)建ApplicationContext的java文件
public class ApplicationContext<T> {
private HashMap<Class,Object> beanFactory=new HashMap<>();
private static String filePath;
public T getBean(Class clazz){
return (T)beanFactory.get(clazz);
}
}
這里我們創(chuàng)建了一個(gè)bean工廠,在ApplicationContext被實(shí)例化后,就會(huì)去自動(dòng)讀取配置類文件,把全部的需要放入ioc的容器放入bean工廠,那么如何把配置類文件放入bean工廠呢?我們需要新建議一個(gè)類
我們?cè)贏pplicationContext下寫一個(gè)initContext方法,同時(shí)創(chuàng)建一個(gè)和com文件同級(jí)的config文件夾,寫好配置文件
com.znb.service.BookService=com.znb.service.BookServiceImpl
com.znb.dao.BookDao=com.znb.dao.BookDaoImpl
public void initContext() throws Exception {
InputStream resource = ApplicationContext.class.getClassLoader().getResourceAsStream("config/bean.config");
Properties properties=new Properties();
properties.load(resource);
Set<Object> keys=properties.keySet();
System.out.println(properties);
System.out.println(keys);
for (Object key : keys) {
// System.out.println(key.toString());
beanFactory.put(Class.forName(key.toString()), Class.forName(properties.getProperty(key.toString())).newInstance());
}
System.out.println(beanFactory.keySet());
System.out.println(beanFactory);
}
在這里我們通過properties類獲取配置文件,然后把properties的文件的key放進(jìn)set集合中,
然后通過反射,將全部的類放進(jìn)bean工廠,之后需要的時(shí)候,就可以直接從bean工廠獲取
現(xiàn)在,我們測(cè)試一下,寫一個(gè)BootStrap類
public class BookStrap {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext=new ApplicationContext();
applicationContext.initContext();
}
}
可以看到,當(dāng)我們需要的時(shí)候,直接從bean工廠里面拿就可以了,這就是通過配置文件把類放入ioc容器
通過注解把類放入ioc容器
我們通過配置文件把類放入ioc容器,但是這樣似乎很麻煩,每次需要放入ioc容器的時(shí)候,我們都需要自己去寫一次配置文件,那么我們也沒有什么方法簡(jiǎn)化這個(gè)步驟呢?
新建注解
我們?cè)贏nnotation文件下新建一個(gè)注解 Bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
在ApplicationContext下寫兩個(gè)方法,一個(gè)initContextByAnnotation,一個(gè)loadOne
public void initContextByAnnotation() throws Exception {
//掃描包
filePath = ApplicationContext.class.getClassLoader().getResource("").getFile();
loadOne(new File(filePath));
}
private void loadOne(File fileParent) {
if (fileParent.isDirectory()) {
File[] childrenFiles = fileParent.listFiles();
if(childrenFiles == null || childrenFiles.length == 0){
return;
}
for (File child : childrenFiles) {
if (child.isDirectory()) {
//如果是個(gè)文件夾就繼續(xù)調(diào)用該方法,使用了遞歸
loadOne(child);
} else {
//通過文件路徑轉(zhuǎn)變成全類名,第一步把絕對(duì)路徑部分去掉
String pathWithClass = child.getAbsolutePath().substring(filePath.length() - 1);
//選中class文件
if (pathWithClass.contains(".class")) {
// com.xinzhi.dao.UserDao
//去掉.class后綴,并且把 \ 替換成 .
String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
try {
Class<?> aClass = Class.forName(fullName);
//把非接口的類實(shí)例化放在map中
if(!aClass.isInterface()){
Bean annotation = aClass.getAnnotation(Bean.class);
if(annotation != null){
Object instance = aClass.newInstance();
//判斷一下有沒有接口
if(aClass.getInterfaces().length > 0) {
//如果有接口把接口的class當(dāng)成key,實(shí)例對(duì)象當(dāng)成value
System.out.println("正在加載【"+ aClass.getInterfaces()[0] +"】,實(shí)例對(duì)象是:" + instance.getClass().getName());
beanFactory.put(aClass.getInterfaces()[0], instance);
}else{
//如果有接口把自己的class當(dāng)成key,實(shí)例對(duì)象當(dāng)成value
System.out.println("正在加載【"+ aClass.getName() +"】,實(shí)例對(duì)象是:" + instance.getClass().getName());
beanFactory.put(aClass, instance);
}
}
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
}
然后,我們給全部的接口實(shí)現(xiàn)類寫上@Bean注解
@Bean
public class BookDaoImpl implements BookDao {
@Override
public User findBookById() {
System.out.println("這里是BookDao-findBookById");
return null;
}
@Override
public List<User> findAllBook() {
System.out.println("這里是BookDao-findAllBook");
return null;
}
@Override
public List<User> findBookByBookName(String username) {
System.out.println("這里是BookDao-findBookByBookName");
return null;
}
@Override
public void save(Book book) {
System.out.println("這里是BookDao-save");
}
}
都按照這個(gè)格式寫,其余的我就省略了,然后我們測(cè)試一下
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext=new ApplicationContext();
applicationContext.initContextByAnnotation();
}
結(jié)果:
這個(gè)時(shí)候,寫了@Bean的類都全部進(jìn)入了IOC容器,當(dāng)我們需要,只需要自己調(diào)用就可以了
依賴注入
我們已經(jīng)簡(jiǎn)單的實(shí)現(xiàn)了IOC,那么現(xiàn)在來實(shí)現(xiàn)一下依賴注入,我們?cè)贏pplicationContext里面新寫一個(gè)assembleObject方法,然后在initContextByAnnotation調(diào)用
private void assembleObject() {
for(Map.Entry<Class,Object> entry : beanFactory.entrySet()){
//就是咱們放在容器的對(duì)象
Object obj = entry.getValue();
Class<?> aClass = obj.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields){
AutoWired annotation = field.getAnnotation(AutoWired.class);
if( annotation != null ){
field.setAccessible(true);
try {
System.out.println("正在給【"+obj.getClass().getName()+"】屬性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");
field.set(obj,beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
public void initContextByAnnotation() throws Exception {
//掃描包
filePath = ApplicationContext.class.getClassLoader().getResource("").getFile();
loadOne(new File(filePath));
assembleObject();
}
然后我們?cè)谌ソ涌趯?shí)現(xiàn)類上寫上自己需要的接口,以及注釋
public class BookServiceImpl implements BookService {
@AutoWired
private UserDao userDao;
@AutoWired
private BookDao bookDao;
@Override
public void borrow(User user,Book book) {
userDao.save(user);
System.out.println(user.getUsername()+"借了"+book.getName());
}
}
測(cè)試一下
public class BookStrap {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext=new ApplicationContext();
// applicationContext.initContext();
// System.out.println("======================");
// System.out.println(applicationContext.getBean(BookDao.class));
applicationContext.initContextByAnnotation();
BookService bean = (BookService)applicationContext.getBean(BookService.class);
bean.borrow(new User(1,"劉水龍","00"),new Book(1,"黃書"));
}
}
結(jié)果:
總結(jié):
在我們寫ssm項(xiàng)目,或者springboot項(xiàng)目的時(shí)候,我們都會(huì)要用到ioc和di,因?yàn)檫@樣可以降低系統(tǒng)的耦合性,以后修改迭代起來也很方便,但是由于ioc和di采用了大量的反射,所以性能消耗很大,而且由于ioc容器注入了大量的對(duì)象,所以啟動(dòng)起來會(huì)比較的慢。
整體的思想,是通過反射,來將對(duì)象注入ioc的容器里面,然后直接把接口的實(shí)現(xiàn)類創(chuàng)建,這樣就不需要自己new對(duì)象,就可以需要什么就拿什么了。
原文鏈接:https://blog.csdn.net/qq_45191024/article/details/125881393
相關(guān)推薦
- 2022-10-20 詳解Go?語言如何通過測(cè)試保證質(zhì)量_Golang
- 2022-08-30 python在文件中倒序查找個(gè)關(guān)鍵詞
- 2022-06-11 FreeRTOS進(jìn)階之調(diào)度器啟動(dòng)過程分析_操作系統(tǒng)
- 2022-05-04 分享3個(gè)非常實(shí)用的?Python?模塊_python
- 2022-07-28 超級(jí)詳細(xì)講解C++中的多態(tài)_C 語言
- 2023-02-07 Redis?中ZSET數(shù)據(jù)類型命令使用及對(duì)應(yīng)場(chǎng)景總結(jié)(案例詳解)_Redis
- 2023-07-14 react 如何實(shí)現(xiàn)富文本編輯器
- 2022-10-30 C語言算法練習(xí)之?dāng)?shù)組元素排序_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支