天天看点

模拟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();

    }
}

           

结果:

成功保存了账户