經過annotation(註解)實現BeanFactory工廠模式(三)

工廠模式是你們熟知的一種設計模式,在spring BeanFactory將這模式運用自如。 前面講過若是經過xml配置的方式實現,今天咱們來說講如何經過註解的方式實現工廠模式。 主要思路算法

  1. 掃描classPath下的的類,將這些class存儲到setmap中
  2. 遍歷set中的class,找出被自定義facory註解註解過的的class,以beanId,class的對象形式封裝到一個map集合裏
  3. 暴露一個方法getBean,經過beanId查找對於的class的對象,匹配成功後返回該對象

一樣回顧下常見的簡單工廠寫法

建立一個接口類Pizza

public interface Pizza{
    public float getPrice();
}

MargheritaPizza 類

public class MargheritaPizza implements Pizza{
    public float getPrice() {
        System.out.println("8.5f");
        return 8.5f;
       
    }
}

CalzonePizza 類

public class CalzonePizza implements Pizza{
   public float getPrice() {
       System.out.println("2.5f");
       return 2.5f;
   }
   

}

創建工廠類PizzaFactory

經過傳入參數id,選擇不一樣的實例類,若是後續不斷的增長新類,會頻繁的修改create方法,不符合開閉原則spring

public class PizzaFactory {
    public Pizza create(String id) {
        if (id == null) {
          throw new IllegalArgumentException("id is null!");
        }
        if ("Calzone".equals(id)) {
          return new CalzonePizza();
        }

        if ("Margherita".equals(id)) {
          return new MargheritaPizza();
        }

        throw new IllegalArgumentException("Unknown id = " + id);
      }

}

使用annotation註解方式

註解方式減小對代碼的侵入,避免xml配置的繁瑣,是spring高版喜歡使用的方式設計模式

建立ClassPathSpringScanner 掃描類

獲取當前classLoad下的全部class文件ide

public class ClassPathSpringScanner {
        

    public static final String CLASS_SUFFIX = ".class";

    private ClassLoader classLoader = getClass().getClassLoader();
    
    
    public Set<Class<?>> getClassFile(String packageName) throws IOException {
         
        Map<String, String> classMap = new HashMap<>(32);
        String path = packageName.replace(".", "/");
        /**
         * 經過classLoader加載文件,循環遍歷文件,轉換class文件
         */
        Enumeration<URL> urls = classLoader.getResources(path);
        
        while (urls!=null && urls.hasMoreElements()) {
            URL url = urls.nextElement();
            String protocol = url.getProtocol();
            /**
             * 若是是文件
             */
            if ("file".equals(protocol)) {
                String file = URLDecoder.decode(url.getFile(), "UTF-8");
                File dir = new File(file);
                if(dir.isDirectory()){
                    parseClassFile(dir, classMap);
                }else {
                    throw new IllegalArgumentException("file must be directory");
                }
            } 
        }

        Set<Class<?>> set = new HashSet<>(classMap.size());
        for(String key : classMap.keySet()){
            String className = classMap.get(key);
            try {
                set.add(getClass().forName(className));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return set;
    }

    /**
     * 遞歸算法把class封裝到map集合裏
     * @param dir
     * @param packageName
     * @param classMap
     */
    protected void parseClassFile(File dir, Map<String, String> classMap){
        if(dir.isDirectory()){
            File[] files = dir.listFiles();
            for (File file : files) {
                parseClassFile(file, classMap);
            }
        } else if(dir.getName().endsWith(CLASS_SUFFIX)) {
            String name = dir.getPath();
            name = name.substring(name.indexOf("classes")+8).replace("\\", ".");
            addToClassMap(name, classMap);
        }
    }

  

    private boolean addToClassMap(String name, Map<String, String> classMap){
        if(!classMap.containsKey(name)){
            classMap.put(name, name.substring(0, name.length()-6)); //去掉.class
        }
        return true;
    }

自定義工廠註解@Factory

只要被Factory註解過的類,都能經過beanId實例化對象。函數

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME)
public @interface Factory {

    /**
     * 用來表示對象的惟一id
     */
    String id();
}

建立 BeanFactory 接口

public interface BeanFactory {
    public Object getBean(String id);
}

建立BeanFactory 的實現類AnnApplicationContext

將掃描後獲得的class封裝到一個map裏,找出有被Factory註解的類,以beanId,class對象的鍵值對形式存儲。測試

public class AnnApplicationContext implements BeanFactory{

    
   
    private Map<String, Object> factoryClasses = new LinkedHashMap<String, Object>();
    
    private Set<Class<?>> classSet = new HashSet();
    
   
    ClassPathSpringScanner scanner = new ClassPathSpringScanner();
    /*
     * 構造函數初始化掃描獲取全部類
     */
    public AnnApplicationContext(String packageName) {
      
        try {
            //掃描classPath下的全部類,並返回set
            classSet = scanner.getClassFile(packageName);
            
            /**
             * 遍歷全部類,找出有factory註解的類,並封裝到linkedHashMap裏
             */
            for (Class<?> cls : classSet){
                Factory factory = (Factory) cls.getAnnotation(Factory.class);
                if(factory != null) 
                try {
                    factoryClasses.put(factory.id(), cls.newInstance());
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                 
            }
        } catch (IOException e) {
            e.printStackTrace();
        }  
    }
    
    
    /**
     * 輸入的id,對應到factoryGroupedClasses的關係,實例化工廠對象
     * @param beanId
     * @return
     */
    @Override
    public Object getBean(String id) {
        
        return factoryClasses.get(id);
    }

MargheritaPizza 類

添加註釋Factory,定義beanId:Margheritaurl

@Factory(id = "margherita")
public class MargheritaPizza implements Pizza{
    public float getPrice() {
        System.out.println("8.5f");
        return 8.5f;
       
    }
}

CalzonePizza 類

添加註釋Factory,定義beanId:Calzone.net

@Factory(id = "calzone")
public class CalzonePizza implements Pizza{
   public float getPrice() {
       System.out.println("2.5f");
       return 2.5f;
   }
   

}

測試下

public static void main(String[] args) {
        /**
         * 掃描com.annotation.factory下的類
         */
        AnnApplicationContext factoryProcessor = new AnnApplicationContext("com.annotation.factory.spring");
        Pizza p= (Pizza) factoryProcessor.getBean("Calzone");
        p.getPrice();
        
    }

好了,看完代碼應該很清楚了,註解是否是給咱們帶來不少方便了。 留個思考題,如何默認經過類的名字,首個字母小寫來做爲beanId設計

相關文章
相關標籤/搜索