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

实现功能

现实需求中,有一些类似无法加入扫描组件注解的。如jdk里面的类。

那么框架必须要支持将没有组件注解标识的类也可以有一个方式注入到容器里面。

那么,我们通过类似Spring的@Bean的方案,来实现这个需求。

通过在配置类的方法的上面,使用@Bean注解,将返回的对象加到容器中。

实现思路

获得有@Configuration注解标识的类。检索它的方法,如果有@Bean,执行这个方法并将返回的对象放在容器中。

实现步骤

1.定义一个Bean注解

 1 package ioc.core.annotation;
 2
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.ElementType;
 5 import java.lang.annotation.Retention;
 6 import java.lang.annotation.RetentionPolicy;
 7 import java.lang.annotation.Target;
 8
 9 //表示用于运行时的注解
10 @Retention(RetentionPolicy.RUNTIME)
11 //表示只能在类或者接口的上面使用
12 @Target(value=ElementType.METHOD)
13 @Documented
14 public @interface Bean {
15
16     /**
17      * 用于设置对象名
18      * @return
19      */
20     String name() default "";
21
22 }

2.修改AbstractApplicationContext代码

(1)创建一个bean的方法处理从bean注解的方法中获得对象

(2)将从组件注解的类扫描的方式创建对象放在scan方法内

根据这个修改方法,首先将AbstractApplicationContext类构造方法原来使用@ComponentScan创建对象的代码移到scan()方法中,然后创建一个bean()方法编写通过@Bean获得对象

 1 private void bean(Class<?> classType,Context context) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
 2         Map<String, Object> objects = context.getObjects();
 3         //1.获得配置类的对象
 4         Object configuration = classType.newInstance();
 5         //2.获得配置类的所有方法
 6         Method[] methods = classType.getMethods();
 7         for(Method method:methods){
 8             Bean bean = method.getDeclaredAnnotation(Bean.class);
 9             if(bean!=null){
10                 Object object = method.invoke(configuration);
11                 if(bean.name()!=null&&!"".equals(bean.name())){
12                     objects.put(bean.name(), object);
13                 }else{
14                     objects.put(NamingUtils.firstCharToLower(object.getClass().getSimpleName()), object);
15                 }
16             }
17         }
18
19     }
20
21     private void scan(Class<?> classType,Context context) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
22
23             // 获得组件扫描注解
24             ComponentScan componentScan = classType.getDeclaredAnnotation(ComponentScan.class);
25             // 获得包名
26             this.basePackage = componentScan.basePackages();
27             // 根据包名获得类全限制名
28             // Set<String> classNames =
29             // PackageUtils.getClassName(this.basePackage[0], true);
30             // 将扫描一个包,修改为多个包
31             Set<String> classNames = PackageUtils.getClassNames(this.basePackage, true);
32             // 通过类名创建对象
33             Iterator<String> iteratorClassName = classNames.iterator();
34             while (iteratorClassName.hasNext()) {
35
36                 String className = iteratorClassName.next();
37                 // System.out.println(className);
38
39                 // 通过类全名创建对象
40                 Class<?> objectClassType = Class.forName(className);
41                 /*
42                  * 判断如果类权限名对应的不是接口,并且包含有@Component|@Controller|@Service|
43                  *
44                  * @Repository 才可以创建对象
45                  */
46                 if (this.isComponent(objectClassType)) {
47                     Object instance = objectClassType.newInstance();
48                     // 修改为,默认对象支持首字符小写
49                     String objectName = null;
50                     // 获得组件注解的name属性值
51                     String componentName = this.getComponentOfName(objectClassType);
52
53                     if (componentName == null) {
54                         // 如果组件注解的name属性没有值,使用默认命名对象
55                         objectName = NamingUtils.firstCharToLower(instance.getClass().getSimpleName());
56                     } else {
57                         // 如果组件注解的name属性有值,使用自定义命名对象
58                         objectName = componentName;
59                     }
60                     System.out.println("1."+instance);
61                     //将对象加入到容器
62                     context.addObject(objectName, instance);
63                 }
64
65             }
66         }
67     

2.修改AbstractApplicationContext的构造方法的代码。如下:

 1     /**
 2      * 将容器操作加载创建对象的代码写抽象类里面,这样可以方便以后扩展多种实现。
 3      *
 4      * @param classType
 5      */
 6     public AbstractApplicationContext(Class<?> classType) {
 7         try {
 8             // 判断配置类是否有Configuration注解
 9             Configuration annotation = classType.getDeclaredAnnotation(Configuration.class);
10             if(annotation!=null){
11                 bean(classType, this.getContext());
12                 scan(classType, this.getContext());
13
14             // 注入对象到有@Autowired注解属性和方法中。
15             autowired();
16         } catch (InstantiationException e) {
17             e.printStackTrace();
18         } catch (IllegalAccessException e) {
19             e.printStackTrace();
20         } catch (ClassNotFoundException e) {
21             e.printStackTrace();
22         } catch (IllegalArgumentException e) {
23             // TODO Auto-generated catch block
24             e.printStackTrace();
25         } catch (InvocationTargetException e) {
26             // TODO Auto-generated catch block
27             e.printStackTrace();
28         } catch (SecurityException e) {
29             // TODO Auto-generated catch block
30             e.printStackTrace();
31         }
32
33     }

测试代码

1.创建一个没有注解的普通Java类

1 package ioc.core.test;
2
3 public class TestUtils {
4
5     public void test(){
6         System.out.println("--测试没有组件注解的类-");
7     }
8
9 }

2.在配置类使用@Bean注入这个类

 1 package ioc.core.test.config;
 2
 3 import ioc.core.annotation.Bean;
 4 import ioc.core.annotation.ComponentScan;
 5 import ioc.core.annotation.Configuration;
 6 import ioc.core.test.TestUtils;
 7
 8 //使用定义@Configuration定义该类是一个配置类
 9 @Configuration
10 //使用ComponentScan设置扫描包的路径
11 @ComponentScan(basePackages={"ioc.core"})
12 public class Config {
13
14     @Bean
15     public Object getTestUtils(){
16         TestUtils tu=new TestUtils();
17         return tu;
18     }
19
20 }

3.测试输出TestUtils的test() 方法

 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
 8 public class ConfigruationTest {
 9
10     @Test
11     public void bean(){
12         try {
13             AnntationApplicationContext context=new AnntationApplicationContext(Config.class);
14              TestUtils testUtils = context.getBean("testUtils", TestUtils.class);
15              System.out.println(context.getContext().getObjects().keySet()+"-----");
16              testUtils.test();
17         } catch (Exception e) {
18             // TODO Auto-generated catch block
19             e.printStackTrace();
20         }
21     }
22
23 }

4.测试结果,输出了这个方法的内容,成功!

时间: 2024-10-13 22:17:06

一起写框架-Ioc内核容器的实现-对象的调用[email protected]注解注入容器的对象(十二)的相关文章

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

实现功能 需求:在类的成员属性使用@Autowirde注解注入容器中的对象. 实现思路 要实现这个功能.我们首先要思考一个问题:类与类的关系是在调用的建立的,还是说在创建对象的时候就就将建立了? ---我实现的方案是,在在程序启动后,所有对象创建后直接就将对象的属性和属性之间的关系创建了.接下来我就用这个思路来实现,将根据@Autowirde建立对象与对象之间的关系. 为什么一定要对象全部创建后再实现对象与对象直接的关系呢? 这个是逻辑问题,如果对象没有创建完就建立对象与对象之间的关系,人家都还

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

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

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

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

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

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

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

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

spring事务(4)-----复习[email&#160;protected]注解、@Resource注解和@Service注解(为手写做准备)

什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点: 1.如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大:如果按需求分开.xml文件,那么.xml文件又会非常多.总之这将导致配置文件的可读性与可维护性变得很低 2.在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率 为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java

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

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

Spring框架IOC容器和AOP解析

主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置  一.Spring开源框架的简介  Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来.它是为了解决企业应用开

一起写框架-说在前面的话(一)

本人的想法: 互联网中,一堆框架.浮躁的社会,到处都是秒天秒地的广告. 各种Struts,Spring,Hibernate,Mybatis的学习教程充斥互联网.让人有一种错觉,只要学会框架,就可以解决一切问题! 而我,从入行就开始的思考一个问题:作为一个Java程序员,难道仅仅就会几个框架就满足了吗? 有没有想过试试,自己也写一个! 然而:事与人违!充满冲劲的时候,小白一个,什么也不懂.当开始入行后,一直忙碌活地为生存而活着,早已将这个曾经强烈的欲望压到了脑后. 最近,终于开始有了一点点时间,而