JAVA模擬Spring實現IoC過程(附源碼)


前言:本人大四學生,第一次寫博客,如果有寫得不好的地方,請大家多多指正

一、IoC(Inversion of Control)反轉控制

  傳統開發都是需要對象就new,但這樣做有幾個問題:

 

    1. 效率低下,創建對象時比較耗時,我立馬要用對象,可系統說讓你等一下,創建和初始化對象需要一定的時間。 
    2. 對象關聯關系,例如:用戶有所屬部門,那創建用戶對象時,如果部門對象不存在,還得創建部門對象。
    3. 代碼耦合度較高

 

於是有人就提出了IoC控制反轉概念,干嘛我不先創建好呢?如果用戶要使用時,我都已經創建好了,用戶不就能立馬使用了?這就有點像好萊塢法則:“你別找我,我去找你”。

 

IoC不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導我們如何設計出松耦合、更優良的程序。傳統應用程序都是由我們在類內部主動創建依賴對象,從而導致類與類之間高耦合,難於測試;有了IoC容器后,把創建和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,所以對象與對象之間是松散耦合,這樣也方便測試,利於功能復用,更重要的是使得程序的整個體系結構變得非常靈活;

 

我個人對IoC是這樣簡單理解的:

1)對象不是new出來的,而是通過反射事先創建出來的

2)既然事先創建出來,在用之前就得找個地方放起來,放哪里呢?就放在一個Map<String, Object>集合中

 

 

二、實現IoC過程中需要考慮的一些問題

•  怎么獲取要創建的對象?

•  怎么把目錄名轉換為類名?

•  怎么把類名轉換為beanName?

•  哪些類需要創建對象?怎么過濾掉不需要創建對象的類?

•  容器中該怎么存放對象?

•  怎么調用容器中存放的對象?

•  設計分層結構:高內聚,低耦合(相當於專做一件事,不要把使用功能都寫在一起)

 

三、模擬Spring實現IoC

在這里簡單模擬查詢用戶信息,實際應用中更復雜,但是萬變不離其宗,弄懂原理才是重要的

 

1)架構圖

 

 

 

2)代碼結構圖

在IDEA中的結構示意圖(eclipse也差不多,我用的是IDEA):

各包名以及文件名含義(文件名可能會和架構圖中的一些文件名不一致,因為架構圖是之前畫的,但不影響閱讀,大同小異):

  annotation包:用來存放注解,在本案例中需要創建對象的類為控制層類、業務層類、持久層類;所有需要3個注解,通過注解的方式來過濾掉不需要創建對象的類,Controller注解是加在控制層,Service注解加在業務層,Respository注解加在持久層;3個注解都是位於類上,保留到運行時期

  pojo包:設置一個pojo類,里面含有一些簡單屬性;

  dao包:模擬持久層,由於本案例是一個簡單例子,主要用於初學者查看,所有沒有安裝數據庫,正常情況下持久層應該與數據庫進行對接,沒有安裝數據庫,所有就由持久層模擬數據庫;

  service包:模擬業務層,業務層調用持久層;

  controller包:模擬控制層,控制層調用業務層;

  utils包:工具類包;

  parse包:因為我們采用了分層結構,所以在使用之前,應當把這些組件掃描加載到一起;現在都是面向接口進行開發,這樣可以提高程序的靈活性,面向接口開發,同時也體現了JAVA的多態性;

  Run文件就相當於整個程序的入口。

 

 

四、案例源碼

    1)pojo類(User.java)

 1 package spring.pojo;
 2 
 3 /**
 4  * @Auther: 林安傑
 5  * @Date: 2019/9/28 19:28
 6  * @Description:創建pojo對象,簡單設置屬性以及get,set,toString方法
 7  */
 8 public class User {
 9     private Integer id;
10     private String name;
11 
12     public Integer getId() {
13         return id;
14     }
15 
16     public void setId(Integer id) {
17         this.id = id;
18     }
19 
20     public String getName() {
21         return name;
22     }
23 
24     public void setName(String name) {
25         this.name = name;
26     }
27 
28     @Override
29     public String toString() {
30         return "User{" +
31                 "id=" + id +
32                 ", name='" + name + '\'' +
33                 '}';
34     }
35 }

 

    2)注解

    ①Respository.java

 1 package spring.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * @Auther: 林安傑
10  * @Date: 2019/9/28 19:31
11  * @Description:dao層類注解
12  */
13 
14 @Target(ElementType.TYPE)   // 注解加在類上
15 @Retention(RetentionPolicy.RUNTIME) // 保留到運行時
16 public @interface Respository {
17 }

     ②Service.java

 1 package spring.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * @Auther: 林安傑
10  * @Date: 2019/9/28 19:33
11  * @Description:業務層注解
12  */
13 
14 @Target(ElementType.TYPE)
15 @Retention(RetentionPolicy.RUNTIME)
16 public @interface Service {
17 }

      ③Controller.java

 1 package spring.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * @Auther: 林安傑
10  * @Date: 2019/9/28 19:34
11  * @Description:控制層注解
12  */
13 
14 @Target(ElementType.TYPE)
15 @Retention(RetentionPolicy.RUNTIME)
16 public @interface Controller {
17 }

    3)持久層(UserDao.java)

 1 package spring.dao;
 2 
 3 import spring.annotation.Respository;
 4 import spring.pojo.User;
 5 
 6 import java.util.ArrayList;
 7 import java.util.List;
 8 
 9 /**
10  * @Auther: 林安傑
11  * @Date: 2019/9/28 19:36
12  * @Description:dao層,模擬數據庫數據
13  */
14 
15 @Respository
16 public class UserDao {
17     // 模擬查詢一條記錄,根據進行id查詢
18     public User selectOne(Integer id) {
19         User user = new User();
20         user.setId(id);
21         user.setName("林安傑");
22         return user;
23     }
24 
25     // 模擬查詢所有記錄
26     public List<User> get() {
27         // 用一個集合來存放所有對象
28         List<User> userList = new ArrayList<>();
29 
30         User u1 = new User();
31         u1.setId(6);
32         u1.setName("linanjie");
33         userList.add(u1);
34 
35         User u2 = new User();
36         u2.setId(7);
37         u2.setName("linlin");
38         userList.add(u2);
39         return userList;
40     }
41 
42 
43 }

    4)業務層(UserService.java)

 

 

 1 package spring.service;
 2 
 3 import spring.annotation.Service;
 4 import spring.dao.UserDao;
 5 import spring.pojo.User;
 6 
 7 import java.util.List;
 8 
 9 /**
10  * @Auther: 林安傑
11  * @Date: 2019/9/28 19:48
12  * @Description:業務層,從dao層中獲取數據
13  */
14 
15 @Service
16 public class UserService {
17     private UserDao userDao;    // 把dao層的對象設置為私有成員變量,才能訪問dao層中的方法(組合)
18 
19     // 關系綁定(set注入)
20     public void setUserDao(UserDao userDao) {
21         this.userDao = userDao;
22     }
23 
24     // 查詢一條記錄
25     public User getOne(Integer id) {
26         return userDao.selectOne(id);
27     }
28 
29     // 查詢所有記錄
30     public List<User> queryAll() {
31         return userDao.get();
32     }
33 }

    5)控制層(UserController.java)

 1 package spring.controller;
 2 
 3 import spring.annotation.Controller;
 4 import spring.pojo.User;
 5 import spring.service.UserService;
 6 
 7 import java.util.List;
 8 
 9 /**
10  * @Auther: 林安傑
11  * @Date: 2019/9/28 19:53
12  * @Description:控制層,調用業務層
13  */
14 
15 @Controller
16 public class UserController {
17     private UserService userService;    // 把業務層對象設置為私有成員變量
18 
19     // set注入,實現關系綁定
20     public void setUserService(UserService userService) {
21         this.userService = userService;
22     }
23 
24     // 查詢一條記錄
25     public User getOne(Integer id) {
26         return userService.getOne(id);
27     }
28 
29     // 查詢多條記錄
30     public List<User> queryAll() {
31         return userService.queryAll();
32     }
33 }

    6)工具類

    ①FileUtil.java

 

 1 package spring.utils;
 2 
 3 import java.io.File;
 4 import java.util.List;
 5 
 6 /**
 7  * @Auther: 林安傑
 8  * @Date: 2019/9/28 19:58
 9  * @Description:文件掃描工具類
10  */
11 
12 public class FileUtil {
13     // 掃描所有.class文件,並存入數組中
14     public static List<String> getFileNameList(String dir, List<String> fileNameList) {
15         File file = new File(dir);  // 把string類型轉換為file類型
16         File[] files = file.listFiles();    // 遍歷當前路徑下的所有文件(遍歷出來的是:所有文件名+所有目錄名)
17         for (File f :
18                 files) {
19             if (f.isDirectory()) {  // 判斷當前文件是不是目錄,是的話就遞歸遍歷目錄中的內容
20                 getFileNameList(f.getAbsolutePath(), fileNameList);
21             } else {
22                 fileNameList.add(f.getAbsolutePath());  // 不是目錄就是文件,將文件添加到數組當中
23             }
24         }
25         return fileNameList;
26     }
27 }

 

     ②CoverUtil.java

 

 1 package spring.utils;
 2 
 3 import java.util.List;
 4 
 5 /**
 6  * @Auther: 林安傑
 7  * @Date: 2019/9/28 21:30
 8  * @Description:統一把路徑符替換為“/”,路徑符有“//”、“\”,統一后就沒有差異性了
 9  */
10 public class CoverUtil {
11     // 從存放所有class文件的fileNameList集合中取出每一個文件名(全局限定名),替換為類名形式(包名.類名)
12 
13     /**
14      * @param baseDir      基准路徑
15      * @param fileNameList 存放所有文件全集限定名的數組
16      * @return
17      */
18     public static List<String> getClassNameList(String baseDir, List<String> fileNameList) {
19         // 把基准路徑中的路徑符統一
20         baseDir = baseDir.replace("\\", "/");
21         for (int i = 0; i < fileNameList.size(); i++) {
22             // 獲取數組中的元素
23             String fileName = fileNameList.get(i);
24 
25             // 把元素(全集限定名)中的路徑統一
26             fileName = fileName.replace("\\", "/");
27 
28             // 把全集限定名中前面一樣的基准路徑替換為空串
29             fileName = fileName.replace(baseDir, "");
30 
31             // 把替換掉的字符串去掉.class后綴
32             int pos = fileName.lastIndexOf(".");
33             fileName = fileName.substring(0, pos);
34 
35             // 把字符串的路徑符替換為“.”
36             fileName = fileName.replace("/", ".");
37 
38             // 把最終的類名繼續放回數組中
39             fileNameList.set(i, fileName);
40         }
41         return fileNameList;
42     }
43 }

      ③BeanNameUtil.java

 1 package spring.utils;
 2 
 3 
 4 /**
 5  * @Auther: 林安傑
 6  * @Date: 2019/9/28 23:06
 7  * @Description:把className轉換為beanName
 8  */
 9 public class BeanNameUtil {
10     public static String getbeanName(String className) {
11         // 截取后面的類名
12         int pos = className.lastIndexOf(".") + 1;
13         className = className.substring(pos);
14         // 類名的第一個字母小寫,后面的不變
15         String beanName = className.toLowerCase().charAt(0) + className.substring(1);
16         return beanName;
17     }
18 }

    7)組件掃描

     ①BeanFactory.java

 1 package spring.parse;
 2 
 3 import java.util.List;
 4 
 5 /**
 6  * @Auther: 林安傑
 7  * @Date: 2019/9/28 23:26
 8  * @Description:面對接口開發;需要設計一個接口
 9  */
10 public interface BeanFactory {
11     public List<String> scan(String dir);
12 
13     public void creat(String baseDir, List<String> fileNameList) throws Exception;
14 
15     public <T> T getObject(String beanName);
16 }

      ②ComponentScan.java

 1 package spring.parse;
 2 
 3 import spring.annotation.Controller;
 4 import spring.annotation.Respository;
 5 import spring.annotation.Service;
 6 import spring.utils.BeanNameUtil;
 7 import spring.utils.CoverUtil;
 8 import spring.utils.FileUtil;
 9 
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14 
15 /**
16  * @Auther: 林安傑
17  * @Date: 2019/9/28 22:30
18  * @Description:掃描所有需要的組件,並利用反射創建對象,把對象放在容器中
19  */
20 public class ComponentScan implements BeanFactory{
21 
22     private static final Map<String, Object> beans = new HashMap<>();
23 
24     // 掃描所有class文件
25     public List<String> scan(String dir) {
26         List<String> fileNameList = new ArrayList<>();
27         FileUtil.getFileNameList(dir, fileNameList);
28         // 遍歷查看數組的內容
29         for (String fileName :
30                 fileNameList) {
31             System.out.println(fileName);
32         }
33         return fileNameList;
34     }
35 
36     // 通過反射根據注解創建對象,並把對象放在容器中
37     public void creat(String baseDir, List<String> fileNameList) throws Exception {
38         List<String> classNameList = CoverUtil.getClassNameList(baseDir, fileNameList);
39 
40         // 遍歷類名數組查看內容,並根據是否有注解創建對象
41         for (String className : classNameList) {
42             String beanName = "";
43             Class<?> clazz = Class.forName(className);
44             // 獲取類上的注解
45             Controller ca = clazz.getAnnotation(Controller.class);
46             Service sa = clazz.getAnnotation(Service.class);
47             Respository ra = clazz.getAnnotation(Respository.class);
48 
49             // 若注解不為空,就創建對象
50             if (ca != null || sa != null || ra != null) {
51                 Object obj = clazz.newInstance();
52 
53                 /*把對象存放在容器中,容器為Map鍵值對,值就是對象,key用className表示太復雜,把className轉換成beanName
54                   className的形式:spring.pojo.User;beanName的形式:user;所以需要寫一個轉換的方法
55                  */
56                 beanName = BeanNameUtil.getbeanName(className);
57 
58                 // 把對象存放到容器當中
59                 beans.put(beanName, obj);
60             }
61             System.out.println(className + " | " + beanName);
62         }
63 
64         // 查看容器中存放的對象
65         System.out.println("\n容器中存放的對象為:");
66         for (String key : beans.keySet()) {
67             System.out.println(beans.get(key));
68         }
69     }
70 
71     // 從容器中獲取對象
72     public <T> T getObject(String beanName){
73         return (T) beans.get(beanName);
74     }
75 }

    8)程序入口(Run.java)

 1 package spring;
 2 
 3 import spring.controller.UserController;
 4 import spring.dao.UserDao;
 5 import spring.parse.BeanFactory;
 6 import spring.parse.ComponentScan;
 7 import spring.pojo.User;
 8 import spring.service.UserService;
 9 
10 import java.util.List;
11 
12 
13 /**
14  * @Auther: 林安傑
15  * @Date: 2019/9/29 12:53
16  * @Description:程序入口
17  */
18 public class Run {
19     public static void main(String[] args) throws Exception {
20         // 面對接口開發,體現了多態性(向上造型)
21         BeanFactory context = new ComponentScan();
22 
23         // 先掃描所有class文件
24         System.out.println("第一步,掃描當前主目錄下的所有class文件:");
25         // 不能把路徑寫死,可以根據API獲取路徑
26         String baseDir = Run.class.getResource("/").getPath().substring(1);
27         String packageDir = Run.class.getPackage().getName();
28         String dir = baseDir + packageDir.replace(".", "/");
29         List<String> fileNameList = context.scan(dir);
30 
31         System.out.println("\n第二步,獲得className,並且用beanName把對象存入容器中:");
32         context.creat(baseDir, fileNameList);
33 
34         System.out.println("\n第三步,業務處理:");
35         // 不能用new創建對象,需要從容器中獲取對象
36         UserController userController = context.getObject("userController");
37         UserService userService = context.getObject("userService");
38         UserDao userDao = context.getObject("userDao");
39 
40         // 關系注入,調用set方法
41         userController.setUserService(userService);
42         userService.setUserDao(userDao);
43 
44         System.out.println("查詢一條記錄:");
45         User user = userController.getOne(1);
46         System.out.println(user);
47 
48         System.out.println("查詢所有記錄:");
49         List<User> users = userController.queryAll();
50         for (User u : users) {
51             System.out.println(u);
52         }
53     }
54 }

五、運行結果

 

 

 

 

 

 

這就是本期分享的內容,大家可以相互學習

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM