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

实现功能

以上的代码我们发现。我们都是将@ComponentScan扫描的路径下的所有类都加载到容器中的。

而实际需求,我们并不希望所有的类都创建对象,而是加了组件注解@Controller,@Service,@Repository,@Component的类才创建对象

而不加这些标识的类不需要创建对象。

所谓本章就是实现通过组件注解限制哪些类是可以创建对象的,哪些是不可以的。

实现思路

根据获得的类全限制名,获得它的Class对象。通过Class对象判断类的声明上是否有组件注解。有就创建对象,没有就不创建。

实现步骤

1.定义四个组件注解

--Controller-

package ioc.core.annotation.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
@Documented
public @interface Controller {
    /**
     * 用于设置对象名的属性
     * @return
     */
    String name() default "";

}

--Service--

package ioc.core.annotation.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 服务层的组件注解定义
 * @author ranger
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
@Documented
public @interface Service {
    /**
     * 定义用于设置类的对象名的属性,默认值为空字符串
     * @return
     */
    String name() default "";

}

-Repository-

package ioc.core.annotation.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
@Documented
public @interface Repository {
    /**
     * 设置对象名的属性
     * @return
     */
    String name() default "";

}

--Component--

package ioc.core.annotation.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
@Documented
public @interface Component {
    /**
     * 设置对象名的属性
     * @return
     */
    String name() default "";

}

2.修改AbstractApplicationContext类,增加了一个isComponent方法判断是否是组件类

    /**
     * 判断是否是组件
     * @param classType
     * @return
     */
    private boolean isComponent(Class<?> classType){
        //如果是接口,就不能创建对象,直接返回false
        if(classType.isInterface()){
            return false;
        }
        Component component = classType.getDeclaredAnnotation(Component.class);
        Service service = classType.getDeclaredAnnotation(Service.class);
        Controller controller = classType.getDeclaredAnnotation(Controller.class);
        Repository repository = classType.getDeclaredAnnotation(Repository.class);
        //判断只要有一个组件注解,就返回
        if(component!=null||service!=null||controller!=null||repository!=null){
            return true;
        }
        return false;
    }

3.修改AbstractApplicationContext类,修改构造函数标红处的代码

 1   /**
 2       * 将容器操作加载创建对象的代码写抽象类里面,这样可以方便以后扩展多种实现。
 3       * @param classType
 4       */
 5     public AbstractApplicationContext(Class<?> classType) {
 6          //判断配置类是否有Configuration注解
 7          Configuration annotation = classType.getDeclaredAnnotation(Configuration.class);
 8          if(annotation!=null){
 9              //获得组件扫描注解
10              ComponentScan componentScan = classType.getDeclaredAnnotation(ComponentScan.class);
11              //获得包名
12              this.basePackage = componentScan.basePackages();
13              //根据包名获得类全限制名
14              //Set<String> classNames = PackageUtils.getClassName(this.basePackage[0], true);
15              //将扫描一个包,修改为多个包
16              Set<String> classNames = PackageUtils.getClassNames(this.basePackage, true);
17              //通过类名创建对象
18              Iterator<String> iteratorClassName = classNames.iterator();
19              while(iteratorClassName.hasNext()){
20
21                  String className = iteratorClassName.next();
22                  //System.out.println(className);
23                  try {
24                      //通过类全名创建对象
25                      Class<?> objectClassType = Class.forName(className);
26                      /*
27                       * 判断如果类权限名对应的不是接口并且包含有
28                       * @Component|@Controller|@Service|@Repository
29                       * 才可以创建对象
30                       */
31                      if(this.isComponent(objectClassType)){
32                         Object instance = objectClassType.newInstance();
33                         //将对象加到容器中,对象名就类全名
34                         this.getContext().addObject(instance.getClass().getSimpleName(),instance);
35                      }
36                 } catch (InstantiationException e) {
37                     e.printStackTrace();
38                 } catch (IllegalAccessException e) {
39                     e.printStackTrace();
40                 } catch (ClassNotFoundException e) {
41                     e.printStackTrace();
42                 }
43              }
44          }
45     }

测试代码

1.配置类,扫描ioc.core.test的所有类

 1 package ioc.core.test.config;
 2
 3 import ioc.core.annotation.ComponentScan;
 4 import ioc.core.annotation.Configuration;
 5
 6 //使用定义@Configuration定义该类是一个配置类
 7 @Configuration
 8 //使用ComponentScan设置扫描包的路径
 9 @ComponentScan(basePackages={"ioc.core.test"})
10 public class Config {
11
12 }

2.增加不同组件注解的普通类

--Controller测试类--

package ioc.core.test.controller;

import ioc.core.annotation.stereotype.Controller;

@Controller
public class UserController {

    public void login(){
        System.out.println("-登录Controller-");
    }

}

-UserService--

package ioc.core.test.controller;

import ioc.core.annotation.stereotype.Controller;

@Controller
public class UserController {

    public void login(){
        System.out.println("-登录Controller-");
    }

}

package ioc.core.test.dao;

import ioc.core.annotation.stereotype.Repository;

@Repository
public class UserDAO {

    public void save(){
        System.out.println("-save保存数据-");
    }

}

package ioc.core.test.service;

import ioc.core.annotation.stereotype.Service;

/**
 * 一个普通的类,用于测试是否可以创建对象
 * @author ranger
 *
 */
@Service
public class UserService {

    public void login(){
        System.out.println("-登录Service-");
    }

}

-UserDAO--

package ioc.core.test.dao;

import ioc.core.annotation.stereotype.Repository;

@Repository
public class UserDAO {

    public void save(){
        System.out.println("-save保存数据-");
    }

}

3.测试类输出容器内的对象

package ioc.core.test;

import org.junit.Test;

import ioc.core.impl.AnntationApplicationContext;
import ioc.core.test.config.Config;

public class AnntationApplicationContextTest {

    @Test
    public void login(){
        try {
            AnntationApplicationContext context=new AnntationApplicationContext(Config.class);
            System.out.println(context.getContext().getObjects());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

--测试结果--

加了组件注解的三个类的对象可以获得,没有加组件注解的Config类和AnntationApplicationContextTest,说明测试成功。

时间: 2024-10-31 09:53:12

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Elite Container DELPHI下的一个轻量级IoC对象容器

一.简介: Elite Container是DELPHI下的一个轻量级IoC对象容器(IoC:Inverse of Control,反转控制).它是参考了Java中的Spring框架(主要是配置文件的写法),并结合DELPHI的特点来构建的.相比Spring的对象容器,它提供的功能更为精简常用(如对象延迟创建.对象属性自动注入等),降低了学习的难度,并且提供了很多扩展点,你只需简单地写一个插件实现类,并在配置文件中进行简单配置,就可以让Elite Container拥有你的自定义功能! Elit

0001 - Spring 框架和 Tomcat 容器扩展接口揭秘

前言 在 Spring 框架中,每个应用程序上下文(ApplicationContext)管理着一个 BeanFactory,BeanFactory 主要负责 Bean 定义的保存.Bean 的创建.Bean 之间依赖的自动注入等.应用程序上下文则是对 BeanFactory 和 Bean 的生命周期中的各个环节进行管理,并且提供扩展接口允许用户对 BeanFactory 和 Bean 的各个阶段进行定制,本文从以下三个点进行切入讲解. refresh()是应用上下文刷新阶段. getBean(