天天看點

模拟spring IoC基于注解底層解耦過程模拟spring IoC基于注解底層解耦過程

模拟spring IoC基于注解底層解耦過程

在xml中開啟包掃描,spring就會去掃描指定包下面的元件
    通過反射機制去過濾元件上是否有spring提供的注解(Controller、service)
    如果有注解,spring會通過反射機制建立執行個體儲存到容器中
    然後spring會根據反射機制檢測該類中是否有屬性需要注入(也就是屬性上是否有spring提供的注解)
    如果有注解,spring會根據反射機制進行注入

    反射+設計模式+注解(自定義)
           

Dao

package com.guoy.spring.ioc.account.dao;

public interface AccountDao {

    void saveAccount();

}

           
package com.guoy.spring.ioc.account.dao;

import com.guoy.spring.ioc.annotation.Repository;

@Repository
public class AccountDaoImpl implements AccountDao {


    @Override
    public void saveAccount() {
        System.out.println("成功儲存了賬戶");
    }
}

           

Service

package com.guoy.spring.ioc.account.service;

public interface AccountService {

    void saveAccount();

}

           
package com.guoy.spring.ioc.account.service;

import com.guoy.spring.ioc.account.dao.AccountDao;
import com.guoy.spring.ioc.annotation.Autowired;
import com.guoy.spring.ioc.annotation.Service;

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void saveAccount() {

        accountDao.saveAccount();

    }
}

           

Servlet

package com.guoy.spring.ioc.account.servlet;

import com.guoy.spring.ioc.account.service.AccountService;
import com.guoy.spring.ioc.annotation.Autowired;
import com.guoy.spring.ioc.annotation.Controller;

@Controller
public class AccountServlet {

    @Autowired
    private AccountService accountService;

    public void saveAccount(){

        accountService.saveAccount();

    }



}
           

Annotation

  • 自定義注解Autowired
package com.guoy.spring.ioc.annotation;


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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {



}
           
  • 自定義注解Controller
package com.guoy.spring.ioc.annotation;

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

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



}
           
  • 自定義持久層注解Repository
package com.guoy.spring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
           
  • 自定義業務層注解Service
package com.guoy.spring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {}
           

BeanFactory

package com.guoy.spring.ioc.factory;

import java.util.HashMap;
import java.util.Map;

/**
 * 工廠對象,用于存放元件的執行個體
 */
public class BeanFactory {

    private static Map<String,Object> beanMap = new HashMap<>();

    public static Map<String, Object> getBeanMap() {
        return beanMap;
    }

    public static void setBeanMap(Map<String, Object> beanMap) {
        BeanFactory.beanMap = beanMap;
    }


    /**
     * //使用者用于擷取執行個體的方法
     * @return
     */
    public static Object getBean(String beanId){

         Object obj = beanMap.get(beanId);

        return obj;
    }



}

           

ComponentScan

package com.guoy.spring.ioc.scan;

import com.guoy.spring.ioc.annotation.Autowired;
import com.guoy.spring.ioc.annotation.Controller;
import com.guoy.spring.ioc.annotation.Repository;
import com.guoy.spring.ioc.annotation.Service;
import com.guoy.spring.ioc.factory.BeanFactory;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 模拟spring底層基于注解的ioc解耦
 *
 * @author ShuaiGUO
 */
public class ComponentScan {

    //聲明一個集合,用于儲存全類名
    private static List<String> fileList = new ArrayList<>();



    /**
     * 模拟裝配執行個體、注入執行個體的整個過程
     * pathName 掃描的包
     */
    public static void getCompoentScan(String pathName){

        pathName = pathName.replace(".","/");


        //擷取目前工程的絕對路徑
        String path = ClassLoader.getSystemResource("").getPath()+pathName;


        //建立file對象,file對象可以表示一個檔案夾、或者是一個檔案、或者是檔案所對應的目錄
         File file = new File(path);

         //調用遞歸方法,用于所有的檔案夾過濾
          addFiles(file);

          //周遊集合
        for (String className : fileList) {

            try {
                //建立位元組碼對象
                Class aClass = Class.forName(className);

                //根據反射機制檢測類上有沒有自定義注解
                Controller controller = (Controller)aClass.getAnnotation(Controller.class);
                Service service = (Service)aClass.getAnnotation(Service.class);
                Repository repository = (Repository)aClass.getAnnotation(Repository.class);

                //判斷是否三個注解中任意注解不為空,将執行個體添加到工廠
                if (controller !=null || service != null || repository != null){

                    //根據反射建立執行個體對象
                     Object obj = aClass.newInstance();

                     //擷取類的簡類名
                     String simpleName = aClass.getSimpleName();

                    //添加執行個體到工廠
                    BeanFactory.getBeanMap().put(simpleName,obj);

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

        }


        //再次周遊集合,将需要注入的屬性進行注入
        for (String className : fileList) {

            try {
                //建立位元組碼對象
                Class aClass = Class.forName(className);

                //根據反射機制檢測類上有沒有自定義注解
                Controller controller = (Controller)aClass.getAnnotation(Controller.class);
                Service service = (Service)aClass.getAnnotation(Service.class);
                Repository repository = (Repository)aClass.getAnnotation(Repository.class);

                //如果類上有其中一個注解,就把該類執行個體添加到容器中
                if (controller != null || service != null || repository != null){

                    //根據反射擷取類中的屬性
                    Field[] declaredFields = aClass.getDeclaredFields();

                    //周遊屬性數組
                    for (Field declaredField : declaredFields) {

                        //檢測屬性上是否有自定義的注解(Autowired)
                         Autowired autowired = declaredField.getAnnotation(Autowired.class);

                         //如果這個注解不為空,那就意味着這個屬性需要被注入
                        if (autowired != null){

                            //擷取map集合
                            Map<String, Object> beanMap = BeanFactory.getBeanMap();

                            //将map集合轉成set集合
                            Set<Map.Entry<String, Object>> entries = beanMap.entrySet();

                            //周遊map集合
                            for (Map.Entry<String, Object> entry : entries) {

                                //因為我們聚合的執行個體是接口,擷取接口集合
                                Class[] interfaces = entry.getValue().getClass().getInterfaces();

                                //周遊集合
                                for (Class anInterface : interfaces) {

                                    //接口與屬性名相同
                                    if (anInterface == declaredField.getType()){

                                        //打破封裝,因為是private
                                        declaredField.setAccessible(true);

                                        //給屬性指派
                                        declaredField.set(BeanFactory.getBean(aClass.getSimpleName()),entry.getValue());

                                    }
                                }



                            }



                        }
                    }

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

        }


    }

    /**
     * 定義一個遞歸的方法
     * file.listFiles()   将所有檔案或者檔案夾切成一個數組
     * FileFilter接口, 用于檔案過濾的接口,可以将檔案夾過濾掉
     */
    public static void addFiles(File file){


        File[] files = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {

                //isDirectory()判斷目前檔案是否是檔案夾
                if (pathname.isDirectory()){

                    //繼續調用遞歸方法
                    addFiles(pathname);

                }

                //擷取符合要求的檔案,應該是.class結尾的檔案
                return pathname.getPath().endsWith(".class");
            }
        });

        //周遊檔案數組,将符合要求的檔案路徑切割成全類名
        for (File f : files) {

            String path = f.getPath();
            //System.out.println(path);//E:\dev\spring_Ioc\out\production\spring_Ioc\com\guoy\spring\ioc\scan\CompoentScan.class

            //将斜杠替(\)換成點(.)
            path = path.replace("\\",".");
            //System.out.println(path);//E:.dev.spring_Ioc.out.production.spring_Ioc.com.guoy.spring.ioc.scan.CompoentScan.class

            //将com包前面的字元串删掉
            path = path.substring(path.lastIndexOf("com"),path.length());
            //System.out.println(path);//com.guoy.spring.ioc.scan.CompoentScan.class

            //将.class删除
            path = path.replace(".class","");
            //System.out.println(path);//com.guoy.spring.ioc.scan.CompoentScan

            //将符合要求的全類名放入集合
            fileList.add(path);

        }





    }


}

           

Test

package com.guoy.spring.ioc.scan;

import com.guoy.spring.ioc.account.servlet.AccountServlet;
import com.guoy.spring.ioc.factory.BeanFactory;

public class Test {

    public static void main(String[] args) {

        //開啟掃描包
        ComponentScan.getCompoentScan("com.guoy");

        //從工廠擷取執行個體
        AccountServlet accountServlet =(AccountServlet) BeanFactory.getBean("AccountServlet");

        //調用儲存賬戶的方法
        accountServlet.saveAccount();

    }
}

           

結果:

成功儲存了賬戶