1. 程式人生 > >一起寫框架-Ioc內核容器的實現-基礎功能-ComponentScan支持組件註解限制(七)

一起寫框架-Ioc內核容器的實現-基礎功能-ComponentScan支持組件註解限制(七)

name oid true int declare 修改 tac .cn 測試結果

實現功能

以上的代碼我們發現。我們都是將@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,說明測試成功。

技術分享

一起寫框架-Ioc內核容器的實現-基礎功能-ComponentScan支持組件註解限制(七)