Spring_模拟基于注解方式的SpringIOC(模拟ByType自动注入,ByName自动注入)

在阅读本博文前, 要对模拟基于XML方式的SpringIOC有所了解
可以先看这篇博文:
Spring源码_模拟基于XML方式的SpringIOC(模拟属性注入, 构造方法注入, 自动注入)

1.思路分析

  1. 对于注解方式, 无非就是在类上声明注解,告诉bean工厂这个类需要注册到容器中, 并且维护好bean之间的关系;
  2. 让工厂去维护bean对象,我们需要告诉工厂哪些包下的哪些类需要注册, 因此需要配置包扫描; 常用的开启包扫描方式有两种: 一种是XML配置方式,另一种是Java Config方式
  3. 既然配置了包扫描, 那我们可以扫描这下包下面所有添加注解的Class信息, 得到Class信息后就可以对对象进行实例化了
  4. 在实例化之前我们需要先将所有添加注解的Class信息加入一个标识为未注册bean的map容器中, 这样我们可以解决依赖顺序问题, 当维护一个bean对象的依赖时, 如果该依赖还没有被实例化, 那么可以先对该依赖实现实例化, 并且在维护依赖之前我们需要先对该实例进行实例化, 这样可以解决循环依赖问题(A里面依赖B,B里面依赖A)
  5. 在维护依赖时, 获取到bean对象所有的依赖属性后, 我们只需要对添加自动注入标签的属性进行注入
  6. 属性注入时也分为ByType和ByName两种方式

[项目源码已托管到github上,需要的自行下载] -->下载源码


2. 模拟基于注解方式的SpringIOC

2.1创建自定义注解

包扫描注解:

package com.anno.spring;

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

@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    public String value();
}

类声明注解:

package com.anno.spring;

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

@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnno {
    public String value();
}

自动注入注解:

package com.anno.spring;

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

@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAutowire {
    public String value() default "byType";
}

2.2创建自定义异常

不能解析属性或标签异常:

package com.anno.expections;

public class CannotParseExecption extends RuntimeException{
    public CannotParseExecption(String msg) {
        super(msg);
    }
}

查找到多个预期对象异常:

package com.anno.expections;

public class TooManyExpectClassrExecption  extends RuntimeException{
    public TooManyExpectClassrExecption(String msg) {
        super(msg);
    }
}

未使用注解标识的类异常:

package com.anno.expections;

public class UndeclaredClassExecption extends RuntimeException{
    public UndeclaredClassExecption(String msg) {
        super(msg);
    }
}

2.3创建dao层接口

package com.anno.dao;

public interface UserDao {
    public void query();
}

2.4创建dao层实现类

package com.anno.dao;

import com.anno.spring.CustomAnno;

@CustomAnno("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void query() {
        System.out.println("模拟查询");
    }
}

2.5创建service层接口

package com.anno.service;

public interface UserService {
    public void query();
}

2.6创建service层实现类

package com.anno.service;

import com.anno.dao.UserDao;
import com.anno.spring.CustomAutowire;
import com.anno.spring.CustomAnno;


@CustomAnno("userService")
public class UserServiceImpl implements UserService {

    @CustomAutowire
    private UserDao userDao;

    public void query() {
        userDao.query();
    }
}

2.7 创建XML配置文件(配置包扫描)

<?xml version='1.0' encoding='UTF-8'?>

<beans>
    <!--XML配置模式开启包扫描-->
    <component-scan base-package="com.anno"></component-scan>
</beans>

2.8 创建Java Config类(配置包扫描)

package com.anno.spring;

/**
 *  Java config方式开启包扫描
 */
@ComponentScan("com.anno")
public class AppConfig {
}

2.9创建AnnotationConfigApplicationContext类

内容很多, 都是我根据自己的思路一行一行写出来的, 请耐心研读 (旨在体会Spring注解方式的思路, 所以只考虑标准情况下)

package com.anno.spring;

import com.anno.expections.CannotParseExecption;
import com.anno.expections.TooManyExpectClassrExecption;
import com.anno.expections.UndeclaredClassExecption;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class AnnotationConfigApplicationContext {
    //存储已注册的bean对象
    private Map<String, Object> registedMap = null;
    //存储未注册的bean对象
    private Map<String, Class> noRegisterMap = null;

    public AnnotationConfigApplicationContext(String classpath) {
        //通过配置文件获取包扫描信息,并解析
        this();
        parseXml(classpath);
    }

    public AnnotationConfigApplicationContext(Class clazz) {
        //通过Java Config获取包扫描信息,并解析
        this();
        register(clazz);
    }

    public AnnotationConfigApplicationContext() {
        if (registedMap == null) {
            registedMap = new HashMap<>();
        }
        if (noRegisterMap == null) {
            noRegisterMap = new HashMap<>();
        }
    }

    public Object getBean(String beanName){
        if(beanName != null){
            return registedMap.get(beanName);
        }
        return null;
    }

    public void register(Class clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        String packagePath = null;
        //遍历该类上的所有注解, 找到@CustomAnno注解,并获取到需要扫描的包路径
        for (Annotation annotation : annotations) {
            if (annotation instanceof CustomAnno) {
                CustomAnno customAnno = (CustomAnno) annotation;
                packagePath = customAnno.value();
                break;
            }
        }
        scan(packagePath);
    }

    public void parseXml(String path) {
        File file = new File(this.getClass().getResource("/").getPath() + "//" + path);
        //需要导入dom4j jar包
        SAXReader reader = new SAXReader();
        try {
            //使用dom4j解析配置文件
            Document document = reader.read(file);
            //获取根节点<beans>
            Element elementRoot = document.getRootElement();

            //获取根节点的子节点(可能配置多个包扫描标签)
            for (Iterator<Element> beanFir = elementRoot.elementIterator(); beanFir.hasNext(); ) {
                Element elementChild = beanFir.next();
                //注解扫描
                if ("component-scan".equals(elementChild.getName())) {
                    String packagePath = elementChild.attribute("base-package").getValue();
                    //获取到包路径信息,开始进行包扫描
                    scan(packagePath);
                } else {
                    System.out.println("这里仅考虑注解模式,其他标签不再解析...");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //当所有的需要扫描的包都扫描完毕之后开始注册
        //对noRegisterMap容器中的使用@CustomAnno注解的类进行实例化
        doRegister();
    }

    //根据包路径扫描并注册使用注解标识的bean
    public void scan(String basePackage) {
        String rootPath = this.getClass().getResource("/").getPath();
        rootPath = rootPath.substring(1, rootPath.length());
        String basePackagePath = basePackage.replaceAll("\\.", "\\/");
        // 获取包路径的真实路径信息 fileRoot = D:/Desktop/source_code/spring/licSpring/target/classes/com/anno
        String fileRoot = rootPath + basePackagePath;
        // 对包路径下的信息进行递归扫描
        doScan(fileRoot, basePackage);

    }

    //递归扫描, 扫描出所有的class类, 并封装到容器中
    public void doScan(String fileRoot, String basePackage) {
        //获取fileRoot路径下的所有文件名称
        File file = new File(fileRoot);
        String[] fileNames = file.list();
        //遍历该目录下所有的文件名; 如果是文件夹,则递归遍历; 如果是class类则尝试注册
        for (String fileName : fileNames) {
            if (fileName.indexOf(".") < 0) {
                //注意每次循环都需要扩充包路径
                doScan(fileRoot + "/" + fileName, basePackage + "." + fileName);
            } else if (fileName.indexOf(".class") > 0) {
                String className = fileName.substring(0, fileName.lastIndexOf(".class"));
                String classPath = basePackage + "." + className;
                try{

                Class clazz = Class.forName(classPath);
                if (clazz.isAnnotationPresent(CustomAnno.class)) {
                    CustomAnno customAnno = (CustomAnno) clazz.getAnnotation(CustomAnno.class);
                    //获取到注解中声明的beanName
                    String beanName = customAnno.value();
                    //先将class信息加入到与注册容器noRegisterMap中(注解中声明的beanName为key, Class对象为value)
                    noRegisterMap.put(beanName, clazz);
                }
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

    //注册bean对象
    public void doRegister() {
        for (String beanName : noRegisterMap.keySet()) {
            if(! registedMap.containsKey(beanName))
                doRegisterBean(beanName);
        }

    }

    public void doRegisterBean(String beanName) {
            //用于暂存实例化的对象
            Object currentRegObject = null;
            Class clazz = noRegisterMap.get(beanName);
            if(clazz == null){
                throw new UndeclaredClassExecption("未声明的类...");
            }

            try {
                //获取该类的所有依赖
                Field[] fields = clazz.getDeclaredFields();
                //在解决依赖注入前先进行实例化,并加入registedMap中, 解决循环依赖问题(不考虑构造函数相关问题...)
                currentRegObject = clazz.newInstance();
                registedMap.put(beanName, currentRegObject);

                for (Field field : fields) {
                    String fieldClassName = field.getType().getName();

                    CustomAutowire customAutowire = field.getAnnotation(CustomAutowire.class);
                    //只对使用@CustomAutowire注解的属性进行自动注入
                    if (customAutowire != null) {
                       // CustomAutowire customAutowire = (CustomAutowire) annotatedType.getAnnotation(CustomAutowire.class);
                        String autowireType = customAutowire.value();
                        //判断是哪种注入方式(默认为按类型注入)
                        if ("byType".equalsIgnoreCase(autowireType)) {
                            Object tempFieldObj = null;
                            int suitableClassNum = 0;
                            for (String fieldKey : registedMap.keySet()) {
                                //获取接口的类路径
                                String tempFieldClassName = registedMap.get(fieldKey).getClass().getInterfaces()[0].getName();
                                if (fieldClassName.equals(tempFieldClassName)) {
                                    tempFieldObj = registedMap.get(fieldKey);
                                    suitableClassNum++;
                                }
                            }
                            //如果循环结束还没有找到符合的class, 那么先去实例化依赖的对象
                            if (suitableClassNum == 0) {
                                doRegisterBean(field.getName());
                            } else if (suitableClassNum == 1) {
                                //查找到符合条件的属性对象,注入
                                field.setAccessible(true);
                                field.set(currentRegObject,tempFieldObj);
                                System.out.println("根据类型注入...");
                            } else {
                                throw new TooManyExpectClassrExecption("期望找到一个对象, 但是找到了两个! ");
                            }

                        } else if ("byName".equalsIgnoreCase(autowireType)) {
                            Object tempFieldObj = null;
                            for (String fieldKey : registedMap.keySet()) {
                                if(fieldKey.equalsIgnoreCase(field.getName())){
                                    tempFieldObj = registedMap.get(fieldKey);
                                    System.out.println("根据名称注入...");
                                    break;

                                }
                            }
                            //如果依赖的属性还没有进行实例化,则先对依赖的属性进行实例化
                            if(tempFieldObj == null){
                                doRegisterBean(field.getName());
                            }else{
                                field.setAccessible(true);
                                field.set(currentRegObject,tempFieldObj);
                            }

                        } else {
                            throw new CannotParseExecption("自动注入类型没见过, 无法解析...");
                        }

                        }

                    }//for

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

        }//doRegisterBean


}

2.10 创建测试类

package com.anno.test;

import com.anno.service.UserService;
import com.anno.spring.AnnotationConfigApplicationContext;
import com.anno.spring.AppConfig;

public class SpringTest {
    public static void main(String[] args) {
        //AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("springAnno.xml");
       // ctx.register(AppConfig.class);

        UserService userService = (UserService) ctx.getBean("userService");
        userService.query();

    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值