java实现 ioc_JAVA模拟Spring实现IoC过程(附源码)

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

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

传统开发都是需要对象就new,但这样做有几个问题:

效率低下,创建对象时比较耗时,我立马要用对象,可系统说让你等一下,创建和初始化对象需要一定的时间。

对象关联关系,例如:用户有所属部门,那创建用户对象时,如果部门对象不存在,还得创建部门对象。

代码耦合度较高

于是有人就提出了IoC控制反转概念,干嘛我不先创建好呢?如果用户要使用时,我都已经创建好了,用户不就能立马使用了?这就有点像好莱坞法则:“你别找我,我去找你”。

IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活;

我个人对IoC是这样简单理解的:

1)对象不是new出来的,而是通过反射事先创建出来的

2)既然事先创建出来,在用之前就得找个地方放起来,放哪里呢?就放在一个Map集合中

二、实现IoC过程中需要考虑的一些问题

•怎么获取要创建的对象?

•怎么把目录名转换为类名?

•怎么把类名转换为beanName?

•哪些类需要创建对象?怎么过滤掉不需要创建对象的类?

•容器中该怎么存放对象?

•怎么调用容器中存放的对象?

•设计分层结构:高内聚,低耦合(相当于专做一件事,不要把使用功能都写在一起)

三、模拟Spring实现IoC

在这里简单模拟查询用户信息,实际应用中更复杂,但是万变不离其宗,弄懂原理才是重要的

1)架构图

c349a670e716801051e686002ef07e6f.png

2)代码结构图

在IDEA中的结构示意图(eclipse也差不多,我用的是IDEA):

bae94ffed9040f397dccafe554797571.png

各包名以及文件名含义(文件名可能会和架构图中的一些文件名不一致,因为架构图是之前画的,但不影响阅读,大同小异):

annotation包:用来存放注解,在本案例中需要创建对象的类为控制层类、业务层类、持久层类;所有需要3个注解,通过注解的方式来过滤掉不需要创建对象的类,Controller注解是加在控制层,Service注解加在业务层,Respository注解加在持久层;3个注解都是位于类上,保留到运行时期;

pojo包:设置一个pojo类,里面含有一些简单属性;

dao包:模拟持久层,由于本案例是一个简单例子,主要用于初学者查看,所有没有安装数据库,正常情况下持久层应该与数据库进行对接,没有安装数据库,所有就由持久层模拟数据库;

service包:模拟业务层,业务层调用持久层;

controller包:模拟控制层,控制层调用业务层;

utils包:工具类包;

parse包:因为我们采用了分层结构,所以在使用之前,应当把这些组件扫描加载到一起;现在都是面向接口进行开发,这样可以提高程序的灵活性,面向接口开发,同时也体现了JAVA的多态性;

Run文件就相当于整个程序的入口。

四、案例源码

1)pojo类(User.java)

1 packagespring.pojo;2

3 /**

4 * @Auther: 林安杰5 * @Date: 2019/9/28 19:286 * @Description:创建pojo对象,简单设置属性以及get,set,toString方法7 */

8 public classUser {9 privateInteger id;10 privateString name;11

12 publicInteger getId() {13 returnid;14 }15

16 public voidsetId(Integer id) {17 this.id =id;18 }19

20 publicString getName() {21 returnname;22 }23

24 public voidsetName(String name) {25 this.name =name;26 }27

28 @Override29 publicString toString() {30 return "User{" +

31 "id=" + id +

32 ", name='" + name + '\'' +

33 '}';34 }35 }

2)注解

①Respository.java

1 packagespring.annotation;2

3 importjava.lang.annotation.ElementType;4 importjava.lang.annotation.Retention;5 importjava.lang.annotation.RetentionPolicy;6 importjava.lang.annotation.Target;7

8 /**

9 * @Auther: 林安杰10 * @Date: 2019/9/28 19:3111 * @Description:dao层类注解12 */

13

14 @Target(ElementType.TYPE) //注解加在类上

15 @Retention(RetentionPolicy.RUNTIME) //保留到运行时

16 public @interfaceRespository {17 }

②Service.java

1 packagespring.annotation;2

3 importjava.lang.annotation.ElementType;4 importjava.lang.annotation.Retention;5 importjava.lang.annotation.RetentionPolicy;6 importjava.lang.annotation.Target;7

8 /**

9 * @Auther: 林安杰10 * @Date: 2019/9/28 19:3311 * @Description:业务层注解12 */

13

14 @Target(ElementType.TYPE)15 @Retention(RetentionPolicy.RUNTIME)16 public @interfaceService {17 }

③Controller.java

1 packagespring.annotation;2

3 importjava.lang.annotation.ElementType;4 importjava.lang.annotation.Retention;5 importjava.lang.annotation.RetentionPolicy;6 importjava.lang.annotation.Target;7

8 /**

9 * @Auther: 林安杰10 * @Date: 2019/9/28 19:3411 * @Description:控制层注解12 */

13

14 @Target(ElementType.TYPE)15 @Retention(RetentionPolicy.RUNTIME)16 public @interfaceController {17 }

3)持久层(UserDao.java)

1 packagespring.dao;2

3 importspring.annotation.Respository;4 importspring.pojo.User;5

6 importjava.util.ArrayList;7 importjava.util.List;8

9 /**

10 * @Auther: 林安杰11 * @Date: 2019/9/28 19:3612 * @Description:dao层,模拟数据库数据13 */

14

15 @Respository16 public classUserDao {17 //模拟查询一条记录,根据进行id查询

18 publicUser selectOne(Integer id) {19 User user = newUser();20 user.setId(id);21 user.setName("林安杰");22 returnuser;23 }24

25 //模拟查询所有记录

26 public Listget() {27 //用一个集合来存放所有对象

28 List userList = new ArrayList<>();29

30 User u1 = newUser();31 u1.setId(6);32 u1.setName("linanjie");33 userList.add(u1);34

35 User u2 = newUser();36 u2.setId(7);37 u2.setName("linlin");38 userList.add(u2);39 returnuserList;40 }41

42

43 }

4)业务层(UserService.java)

1 packagespring.service;2

3 importspring.annotation.Service;4 importspring.dao.UserDao;5 importspring.pojo.User;6

7 importjava.util.List;8

9 /**

10 * @Auther: 林安杰11 * @Date: 2019/9/28 19:4812 * @Description:业务层,从dao层中获取数据13 */

14

15 @Service16 public classUserService {17 private UserDao userDao; //把dao层的对象设置为私有成员变量,才能访问dao层中的方法(组合)18

19 //关系绑定(set注入)

20 public voidsetUserDao(UserDao userDao) {21 this.userDao =userDao;22 }23

24 //查询一条记录

25 publicUser getOne(Integer id) {26 returnuserDao.selectOne(id);27 }28

29 //查询所有记录

30 public ListqueryAll() {31 returnuserDao.get();32 }33 }

5)控制层(UserController.java)

1 packagespring.controller;2

3 importspring.annotation.Controller;4 importspring.pojo.User;5 importspring.service.UserService;6

7 importjava.util.List;8

9 /**

10 * @Auther: 林安杰11 * @Date: 2019/9/28 19:5312 * @Description:控制层,调用业务层13 */

14

15 @Controller16 public classUserController {17 private UserService userService; //把业务层对象设置为私有成员变量18

19 //set注入,实现关系绑定

20 public voidsetUserService(UserService userService) {21 this.userService =userService;22 }23

24 //查询一条记录

25 publicUser getOne(Integer id) {26 returnuserService.getOne(id);27 }28

29 //查询多条记录

30 public ListqueryAll() {31 returnuserService.queryAll();32 }33 }

6)工具类

①FileUtil.java

1 packagespring.utils;2

3 importjava.io.File;4 importjava.util.List;5

6 /**

7 * @Auther: 林安杰8 * @Date: 2019/9/28 19:589 * @Description:文件扫描工具类10 */

11

12 public classFileUtil {13 //扫描所有.class文件,并存入数组中

14 public static List getFileNameList(String dir, ListfileNameList) {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 returnfileNameList;26 }27 }

②CoverUtil.java

1 packagespring.utils;2

3 importjava.util.List;4

5 /**

6 * @Auther: 林安杰7 * @Date: 2019/9/28 21:308 * @Description:统一把路径符替换为“/”,路径符有“//”、“\”,统一后就没有差异性了9 */

10 public classCoverUtil {11 //从存放所有class文件的fileNameList集合中取出每一个文件名(全局限定名),替换为类名形式(包名.类名)

12

13 /**

14 *@parambaseDir 基准路径15 *@paramfileNameList 存放所有文件全集限定名的数组16 *@return

17 */

18 public static List getClassNameList(String baseDir, ListfileNameList) {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 returnfileNameList;42 }43 }

③BeanNameUtil.java

1 packagespring.utils;2

3

4 /**

5 * @Auther: 林安杰6 * @Date: 2019/9/28 23:067 * @Description:把className转换为beanName8 */

9 public classBeanNameUtil {10 public staticString 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 returnbeanName;17 }18 }

7)组件扫描

①BeanFactory.java

1 packagespring.parse;2

3 importjava.util.List;4

5 /**

6 * @Auther: 林安杰7 * @Date: 2019/9/28 23:268 * @Description:面对接口开发;需要设计一个接口9 */

10 public interfaceBeanFactory {11 public Listscan(String dir);12

13 public void creat(String baseDir, List fileNameList) throwsException;14

15 public T getObject(String beanName);16 }

②ComponentScan.java

1 packagespring.parse;2

3 importspring.annotation.Controller;4 importspring.annotation.Respository;5 importspring.annotation.Service;6 importspring.utils.BeanNameUtil;7 importspring.utils.CoverUtil;8 importspring.utils.FileUtil;9

10 importjava.util.ArrayList;11 importjava.util.HashMap;12 importjava.util.List;13 importjava.util.Map;14

15 /**

16 * @Auther: 林安杰17 * @Date: 2019/9/28 22:3018 * @Description:扫描所有需要的组件,并利用反射创建对象,把对象放在容器中19 */

20 public class ComponentScan implementsBeanFactory{21

22 private static final Map beans = new HashMap<>();23

24 //扫描所有class文件

25 public Listscan(String dir) {26 List fileNameList = new ArrayList<>();27 FileUtil.getFileNameList(dir, fileNameList);28 //遍历查看数组的内容

29 for(String fileName :30 fileNameList) {31 System.out.println(fileName);32 }33 returnfileNameList;34 }35

36 //通过反射根据注解创建对象,并把对象放在容器中

37 public void creat(String baseDir, List fileNameList) throwsException {38 List 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转换成beanName54 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 getObject(String beanName){73 return(T) beans.get(beanName);74 }75 }

8)程序入口(Run.java)

1 packagespring;2

3 importspring.controller.UserController;4 importspring.dao.UserDao;5 importspring.parse.BeanFactory;6 importspring.parse.ComponentScan;7 importspring.pojo.User;8 importspring.service.UserService;9

10 importjava.util.List;11

12

13 /**

14 * @Auther: 林安杰15 * @Date: 2019/9/29 12:5316 * @Description:程序入口17 */

18 public classRun {19 public static void main(String[] args) throwsException {20 //面对接口开发,体现了多态性(向上造型)

21 BeanFactory context = newComponentScan();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 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 users =userController.queryAll();50 for(User u : users) {51 System.out.println(u);52 }53 }54 }

五、运行结果

7644e0c907a99b31a47ba665d44e91b0.png

31e4b79f718de6ce1e1d8f142b595806.png

72a81c51d8aacc200f63dff97d1f3533.png

这就是本期分享的内容,大家可以相互学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值