一起写框架-Ioc内核容器的实现-对象的调用-属性注入容器的对象(十)

实现功能

需求:在类的成员属性使用@Autowirde注解注入容器中的对象。

实现思路

要实现这个功能。我们首先要思考一个问题:类与类的关系是在调用的建立的,还是说在创建对象的时候就就将建立了?

---我实现的方案是,在在程序启动后,所有对象创建后直接就将对象的属性和属性之间的关系创建了。接下来我就用这个思路来实现,将根据@Autowirde建立对象与对象之间的关系。

为什么一定要对象全部创建后再实现对象与对象直接的关系呢?

这个是逻辑问题,如果对象没有创建完就建立对象与对象之间的关系,人家都还没有创建,你怎么引用呢?对吧。所有一定在所有对象创建完后建立对象与对象的关系。

实现步骤

1.Context接口增加一个方法。用于通过Map的和属性名对象或者对象的类型与属性的类型对象,给属性匹配对象。定义如代码的说明

1      /**
2       * 根据类的类型以及设置的对象名返回容器对象
3       * 如果传入的类型容器中有对应key的对象,而且返回类型是兼容的,直接返回对应的对象。
4       * 如果传入的类型容器中有没有对应key的对象,那么判断传入的类型是否和容器的对象的找到唯一配置的。
5       * 如果传入类型唯一匹配,返回对象。如果没有或者配配多个对象,都报一个RuntimeException异常
6       * @param classType
7       * @return
8       */
9      Object getObject(Class<?> classType,String key);

2.在ContextImpl容器实现类实现这个方法

 1 @Override
 2     public Object getObject(Class<?> classType, String key) {
 3         // 1.判断是否有对应key的对象
 4         Object object = objects.get(key);
 5         // 2.如果有,而且类型也兼容。直接返回该对象。
 6         if (object != null && classType.isAssignableFrom(object.getClass())) {
 7             return object;
 8         } else {
 9             // 3.如果没有对应key的对象,那么就在容器里检索,是否有兼容类型的对象。
10             Collection<Object> values = objects.values();
11             Iterator<Object> iterator = values.iterator();
12             int count = 0;
13             Object currentObject = null;
14             while (iterator.hasNext()) {
15                 Object nextObject = iterator.next();
16                 //判断classType是否是nextObject.getClass()的兼容类型。
17                 boolean from = classType.isAssignableFrom(nextObject.getClass()) ;
18                 if (from) {
19                     //如果发现有对象,计数加1
20                     count++;
21                     //并将对象赋予当前对象
22                     currentObject = nextObject;
23                 }
24             }
25             // 如果兼容类型的对象只有一个,返回这个对象。如果大于一个,返回null
26             if (count == 1) {
27                 return currentObject;
28             } else {
29                 //如果发现一个类型容器中有多个异常,抛异常
30                 throw new RuntimeException("容器中找不到对应的对象或者找到的对象不是唯一的!请确认是否一个接口继承了多个类");
31             }
32
33         }
34
35     }

3.在AbstractApplicationContext容器操作类实现属性的注入方法 autowired()

 1 /**
 2      * 给对象的属性注入关联的对象
 3      * @throws IllegalArgumentException
 4      * @throws IllegalAccessException
 5      */
 6     private void autowired() throws IllegalArgumentException, IllegalAccessException {
 7         // 1.获得容器
 8         Context context = contexts.get();
 9         // 2.获得容器中的所有对象。
10         Map<String, Object> objects = context.getObjects();
11         // 3.获得容器中所有的对象值
12         Collection<Object> values = objects.values();
13         // 4.获得对象的迭代器
14         Iterator<Object> iterator = values.iterator();
15         while (iterator.hasNext()) {
16             Object object = iterator.next();
17             // 5.获得对象的表结构
18             Class<? extends Object> classType = object.getClass();
19             // 6.获得字段的结构
20             Field[] fields = classType.getDeclaredFields();
21             for (int i = 0; i < fields.length; i++) {
22                 // autowired获得注解
23                 Autowired autowired = fields[i].getAnnotation(Autowired.class);
24                 if (autowired != null) {
25                     Class<?> fieldType = fields[i].getType();
26                     String fieldName = fields[i].getName();
27                     // 如果容器里面有对应的对象
28                     Object fieldObject = context.getObject(fieldType, fieldName);
29                     // 允许访问私有方法
30                     if (fieldObject != null) {
31                         // 属性是私有的也可以访问
32                         fields[i].setAccessible(true);
33                         // 将属性值赋予这个对象的属性
34                         fields[i].set(object, fieldObject);
35                     }
36
37                 }
38             }
39         }
40     }

4. 在AbstractApplicationContext构造方法最后调用属性注入方法autowired,注意标红处

 1 public AbstractApplicationContext(Class<?> classType) {
 2         try {
 3             // 判断配置类是否有Configuration注解
 4             Configuration annotation = classType.getDeclaredAnnotation(Configuration.class);
 5             if (annotation != null) {
 6                 // 获得组件扫描注解
 7                 ComponentScan componentScan = classType.getDeclaredAnnotation(ComponentScan.class);
 8                 // 获得包名
 9                 this.basePackage = componentScan.basePackages();
10                 // 根据包名获得类全限制名
11                 // Set<String> classNames =
12                 // PackageUtils.getClassName(this.basePackage[0], true);
13                 // 将扫描一个包,修改为多个包
14                 Set<String> classNames = PackageUtils.getClassNames(this.basePackage, true);
15                 // 通过类名创建对象
16                 Iterator<String> iteratorClassName = classNames.iterator();
17                 while (iteratorClassName.hasNext()) {
18
19                     String className = iteratorClassName.next();
20                     // System.out.println(className);
21
22                     // 通过类全名创建对象
23                     Class<?> objectClassType = Class.forName(className);
24                     /*
25                      * 判断如果类权限名对应的不是接口,并且包含有@Component|@Controller|@Service|
26                      *
27                      * @Repository 才可以创建对象
28                      */
29                     if (this.isComponent(objectClassType)) {
30                         Object instance = objectClassType.newInstance();
31                         // 修改为,默认对象支持首字符小写
32                         String objectName = null;
33                         // 获得组件注解的name属性值
34                         String componentName = this.getComponentOfName(objectClassType);
35
36                         if (componentName == null) {
37                             // 如果组件注解的name属性没有值,使用默认命名对象
38                 objectName = NamingUtils.firstCharToLower(instance.getClass().getSimpleName());
39                         } else {
40                             // 如果组件注解的name属性有值,使用自定义命名对象
41                             objectName = componentName;
42                         }
43                         this.getContext().addObject(objectName, instance);
44                     }
45
46                 }
47             }
48             //1.注入对象到属性中。
49             autowired();
50         } catch (InstantiationException e) {
51             e.printStackTrace();
52         } catch (IllegalAccessException e) {
53             e.printStackTrace();
54         } catch (ClassNotFoundException e) {
55             e.printStackTrace();
56         }
57
58     }

测试代码

测试类目录结构

1.修改UserController代码,增加注入UserService的代码

 1 package ioc.core.test.controller;
 2
 3 import ioc.core.annotation.Autowired;
 4 import ioc.core.annotation.stereotype.Controller;
 5 import ioc.core.test.service.UserService;
 6
 7 @Controller
 8 public class UserController {
 9
10     /**
11      * 通过@Autowired可以注入UserService的对象。
12      */
13     @Autowired
14     private UserService userServiceImpl;
15
16     public void login(){
17         System.out.println("-登录Controller-");
18         userServiceImpl.login();
19     }
20
21 }

2.调用UserController 对象

 1 package ioc.core.test;
 2
 3 import org.junit.Test;
 4
 5 import ioc.core.impl.AnntationApplicationContext;
 6 import ioc.core.test.config.Config;
 7 import ioc.core.test.controller.UserController;
 8
 9 public class AnntationApplicationContextTest {
10
11     @Test
12     public void login(){
13         try {
14             AnntationApplicationContext context=new AnntationApplicationContext(Config.class);
15              UserController userController = context.getBean("userController", UserController.class);
16              userController.login();
17             System.out.println(context.getContext().getObjects());
18
19         } catch (Exception e) {
20             e.printStackTrace();
21         }
22     }
23
24 }

3.输出结果

同时输出了UserController的内容和UserService的内容

时间: 2024-09-28 16:05:45

一起写框架-Ioc内核容器的实现-对象的调用-属性注入容器的对象(十)的相关文章

一起写框架-Ioc内核容器的实现-对象的调用-方法注入容器的对象(十一)

实现功能 就是在方法的上面加入@Autowired注解,容器中的对象会注入到对应类型的参数. 注意:暂时实现注入一个对象.所有方法的参数列表的参数只能有一个. 实现思路 其实实现的思路和给字段注入的逻辑是一样的.遍历类所有的方法有没有@Autowired,有的就给它赋予容器中对应的对象. 实现步骤 1. 在AbstractApplicationContext类增加两个方法区分属性注入(autowiredField)和方法注入(autowiredMethod) 1 /** 2 * 属性注入 3 *

一起写框架-Ioc内核容器的实现-基础功能-ComponentScan支持组件注解限制(七)

实现功能 以上的代码我们发现.我们都是将@ComponentScan扫描的路径下的所有类都加载到容器中的. 而实际需求,我们并不希望所有的类都创建对象,而是加了组件注解@Controller,@Service,@Repository,@Component的类才创建对象 而不加这些标识的类不需要创建对象. 所谓本章就是实现通过组件注解限制哪些类是可以创建对象的,哪些是不可以的. 实现思路 根据获得的类全限制名,获得它的Class对象.通过Class对象判断类的声明上是否有组件注解.有就创建对象,没

一起写框架-Ioc内核容器的实现-对象的调用[email&#160;protected]注解注入容器的对象(十二)

实现功能 现实需求中,有一些类似无法加入扫描组件注解的.如jdk里面的类. 那么框架必须要支持将没有组件注解标识的类也可以有一个方式注入到容器里面. 那么,我们通过类似Spring的@Bean的方案,来实现这个需求. 通过在配置类的方法的上面,使用@Bean注解,将返回的对象加到容器中. 实现思路 获得有@Configuration注解标识的类.检索它的方法,如果有@Bean,执行这个方法并将返回的对象放在容器中. 实现步骤 1.定义一个Bean注解 1 package ioc.core.ann

一起写框架-Ioc内核容器的实现-基础功能-容器对象名默认首字母小写(八)

实现功能 --前面实现的代码-- 默认的对象名就类名.不符合Java的命名规范.我们希望默认的对象名首字母小写. 实现思路 创建一个命名规则的帮助类.实现将对大写开头的对象名修改为小写开头. 实现步骤 1.创建一个命名规则帮助类 1 package ioc.core.utils; 2 3 /** 4 * 创建命名规则帮助类 5 * 6 * @author ranger 7 * 8 */ 9 public class NamingUtils { 10 /** 11 * 将类名修改为对象名,首字母小

一起写框架-Ioc内核容器的实现-基础功能-组件注解支持自定义的对象名(九)

实现功能 如果扫描组件注解(@Controller,@Service,@Repository,@Component)默认对象名,已经实现了默认使用首字母小写类名的作为对象名. 但,现实需求中.我们有时候希望可以自己定义对象的名. 实现思路 1.获得扫描组件注解的name属性的值. 2.将这个值作为对象名 实现步骤 1.在AbstractApplicationContext增加一个方法getComponentOfName,用于判断组件注解是否设置了name属性.如果设置了就获得该值 1 /** 2

spring IOC快速入门,属性注入,注解开发

我们使用spring框架也会使用到配置文件,我们需要在src下创建一个关于spring的配置文件,一般情况名称叫applicationContext.xml 基本约束: <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> IOC快速入门 inversion of Controller

[原创]java WEB学习笔记98:Spring学习---Spring Bean配置及相关细节:如何在配置bean,Spring容器(BeanFactory,ApplicationContext),如何获取bean,属性赋值(属性注入,构造器注入),配置bean细节(字面值,包含特殊字符,引用bean,null值,集合属性list map propert),util 和p 命名空间

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

从零开始手写 spring ioc 框架,深入学习 spring 源码

IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过. 但是 spring 源码存在一个问题,那就是过于抽象,导致学习起来成本上升. 所以本项目由渐入深,只实现 spring 的核心功能,便于自己和他人学习 spring 的核心原理. spring 的核心 Spring 的核心就是 spring-beans,后面的一切 spring-boot,spr

Spring框架—— IOC容器和Bean的配置

 1 IOC和DI ①IOC(Inversion of Control):反转控制. 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率. 反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向--改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效