Java Web 基础篇 L6 模拟Spring的IOC(注解+反射)

1 使用注解模拟Spring的IOC容器

1.1 IOC容器的工作方式

Spring中的IOC容器用来生产Bean,所谓Bean就是一个类的对象,IOC容器让程序员创建对象时不再通过new的方式直接生成对象,而是从IOC容器中取得已经创建好了的对象

这样做最大的好处就是减少了依赖,降低了耦合,提高了程序的可维护性

IOC的使用方式是通过getBean(String BeanName)的方式获取一个对象,我们可以将IOC容器看成一个类似Map<String, Bean>的数据结构,根据类名,来获取该类的对象

由于Map拒绝键重复,默认情况下,所取得的对象都是单例的,即在项目中的任何一个类中,相同BeanName对应的Bean都是一样的,这样做可以直接传参,如Service层和Dao层用到了IOC容器中相同的对象,只需要更改Service层内对象的数据,Dao层中该对象的数据也会发生改变

1.2 模拟IOC容器需要的注解

通过XML文件或注解都可以模拟IOC容器,但是二者均有优劣,这里采用注解的方式来完成IOC容器的模拟

这里给出3个注解:

/**
 * @author 雫
 * @date 2021/4/16 - 14:41
 * @function 作用于类
 * 用于标识 该类能生成Bean
 * 默认值为true 代表该类生成的简单Bean是单例
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    boolean value() default true;
}

@Component 作用于类 默认值是true
拥有该注解的类,将会生成Bean
该注解将能生成Bean的类和其它类分开
默认值是true 代表默认是单例Bean false代表是非单例Bean

/**
 * @author 雫
 * @date 2021/4/16 - 14:41
 * @function 作用于成员
 * 用于标识 该成员是Bean注入时需要的一部分
 * 默认值是"" 代表该成员是对象 "xxx"是该成员字符串类型的注入值
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}

@Autowired 作用于成员 默认值是""
拥有该注解的成员,将在生成Bean时被注入
即通过反射机制调用set方法为Bean赋值
默认值是"" 代表默认是对象 "xxx"代表是该成员的注入值

/**
 * @author 雫
 * @date 2021/4/16 - 14:44
 * @function 作用于方法
 * 代表该方法能反射执行生成复杂Bean
 * 默认值是true 代表该方法生成的Bean是单例
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
    boolean value() default true;
}

@Bean 作用于方法 默认值是true
拥有该注解的方法,其返回值就是Bean的类型
当该方法参数都能找到时,反射执行该方法生成Bean
默认值是true 代表默认是单例Bean false代表是非单例Bean

我们将通过扫描一个包下的所有类,来区分哪些类能产生Bean(带Component注解),进而以不同的方式生成Bean

我们将Bean分为两种:

简单Bean
	带有@Component注解和@Autowired 
	可以通过反射直接生成Bean

复杂Bean
	带有@Component注解和@Bean注解
	代表该Bean由方法生成
	根据该方法的参数格式,可以分为
		无参复杂Bean
		带参复杂Bean

1.3 生成简单Bean前的准备

我们先创建两个简单类,为它加上Component注解和Autowired注解,以后不再new一个该类对象,而是从IOC容器中直接取得该类对象

/**
 * @author 雫
 * @date 2021/4/16 - 14:46
 * @function 生成简单Bean 生成的Bean非单例
 */
@Component(value = false)
public class Point {
    @Autowired(value = "15")
    private int x;
    @Autowired(value = "35")
    private int y;

    public Point() {}

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
    
}

/**
 * @author 雫
 * @date 2021/4/16 - 14:47
 * @function 生成简单Bean 生成的Bean单例
 * 其成员point是Point类型的对象
 */
@Component
public class SomeClass {

    @Autowired
    private Point point;

    public SomeClass() {}

    public Point getPoint() {
        return point;
    }

    public void setPoint(Point point) {
        this.point = point;
    }

    public void showPoint() {
        System.out.println(this.point);
    }

}

先讨论简单Bean生成的时机,选择饿汉模式还是懒汉模式,这里采用懒汉模式,当用户getBean()时再去注入生成Bean并返回

对于IOC容器的结构,仍然是一个Map:

Map<Class<?>, BeanDefinition>

键是Class<?> 采用String也可以,但调用getBean()时需要使用类的全名

值是BeanDefinition,这是一个类,用来存储生成/控制Bean的一些数据
	如class对象,注入时需要反射调用成员的set方法
	如object对象,存储真正的Bean,待调用时返回真正的Bean
	...

BeanDefinition类:

/**
 * @author 雫
 * @date 2021/4/16 - 14:49
 * @function 存储Bean
 * 以及生成Bean所需要的class对象 和该Bean是否以存在的标志
 */
public class BeanDefinition {
    private Class<?> klass;
    private Object object;
    private boolean complete;
    private Method method;

    BeanDefinition() {}

    Class<?> getKlass() {
        return klass;
    }

    void setKlass(Class<?> klass) {
        this.klass = klass;
    }

    Object getObject() {
        return object;
    }

    void setObject(Object object) {
        this.object = object;
    }

    boolean isComplete() {
        return complete;
    }

    void setComplete(boolean complete) {
        this.complete = complete;
    }

    Method getMethod() {
        return method;
    }

    void setMethod(Method method) {
        this.method = method;
    }

}

1.4 开始生成简单Bean

我们将扫描一个包下的所有文件,查看哪些类有Component注解,将这些类拿出来生成Bean,为此需要一个包扫描工具,获取包下的每个类的class对象进行处理
Java包扫描工具

BeanFactory:

/**
 * @author 雫
 * @date 2021/4/16 - 14:48
 * @function 用于生成和存储Bean
 */
public class BeanFactory {

    private static final Map<Class<?>, BeanDefinition> BEAN_POOL;

    static {
        BEAN_POOL = new HashMap<>();
    }

    /**
     * @Author 雫
     * @Description 扫描指定路径的包 区分简单Bean和复杂Bean分类处理
     * @Date 2021/4/16 14:52
     * @Param [packageName]
     * @return void
     **/
    public static void scanPackage(String packageName) throws Exception {
        new AbstractPackageScanner() {
            @Override
            public void dealClass(Class<?> klass) {
                if(!klass.isAnnotationPresent(Component.class)) {
                    return;
                }

                try {
                    /*
                    * 处理简单Bean 即不带@Bean注解的类 直接生成一个BD存入BEAN_POOL中即可
                    * 等到getBean时 再进行注入 即懒汉模式
                    * */
                    BeanDefinition bd = new BeanDefinition();
                    Object object = klass.newInstance();
                    bd.setKlass(klass);
                    bd.setObject(object);
                    bd.setComplete(false);
                    bd.setMethod(null);

                    BEAN_POOL.put(klass, bd);
                 
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {}

            }
        }.packageScan(packageName);

    }

	/**
     * @Author 雫
     * @Description 根据class对象从BEAN_POOL中获得对应的BD
     * 再从BD中取出真正的Bean返回 返回前需要检查需要的Bean是单例还是原型
     * @Date 2021/4/16 14:57
     * @Param [klass]
     * @return T
     **/
    public static <T> T getBeanByClass(Class<?> klass) throws Exception {
        BeanDefinition bd = BEAN_POOL.get(klass);
        if(bd == null) {
           return null;
        }

        // 完成对简单Bean的注入
        inject(bd);
        Object object = bd.getObject();

        // 查看该Bean是以单例方式还是原型方式返回
        if(bd.getMethod() == null) {
            Component component = bd.getKlass().getAnnotation(Component.class);
            boolean componentValue = component.value();
            if(componentValue) {
                return (T) object;
            } else {
               return (T) bd.getKlass().newInstance();
            }
        } 
        
    }

   /**
     * @Author 雫
     * @Description 简单Bean的注入
     * 即通过BD中的class对象 通过反射调用set方法完成赋值
     * @Date 2021/4/16 15:00
     * @Param [bd]
     * @return void
     **/
    private static void inject(BeanDefinition bd) throws Exception {
        if(bd.isComplete()) {
            return;
        }

        // 简单Bean第一次注入时 直接提前更改注入标志 这样可以避免简单Bean的循环依赖
        bd.setComplete(true);

        Class<?> klass = bd.getKlass();
        Object object = bd.getObject();

        Field[] fields = klass.getDeclaredFields();
        for(Field field : fields) {
            if(!field.isAnnotationPresent(Autowired.class)) {
                continue;
            }

            Autowired autowired = field.getAnnotation(Autowired.class);
            String autowiredString = autowired.value();

            String fieldName = field.getName();
            Class<?> fieldType = field.getType();
            String fieldTypeStr = field.getType().getName();
            Object fieldValue;

            if(autowiredString.length() == 0) {
                fieldValue = getBeanByClass(fieldType);
            } else {
                fieldValue = TypeParser.getValue(fieldTypeStr, autowiredString);
            }

            String setterMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Method setterMethod = klass.getDeclaredMethod(setterMethodName, new Class<?>[] {fieldType});

            setterMethod.invoke(object, new Object[] {fieldValue});
        }

    }

至此,就可以当getBean(Class<?> klass)时,获取一个该类的Bean,其工作流程是:

1,扫描包下所有类,为带有Component注解的类
	生成一个BeanDefinition(以下简称BD)
	
2,为BD赋值,将BD存入IOC容器,即BEAN_POOL中

3,当通过getBean(Class<?> klass)试图获取Bean时
	从容器中取得对应的BD,开始注入
	
4inject(BD bd)方法完成注入,通过遍历该类的所有成员
	通过反射机制掉用带有Autowired注解的成员的set方法赋值
	
5,注入成功,返回注入完成的Bean

1.5 生成无参复杂Bean

生成简单Bean并不麻烦,现在来看如何生成复杂Bean,复杂Bean有两种,一种是无参复杂Bean,一种是带参复杂Bean,这里先讨论无参复杂Bean的处理方式

先创建一个类,该类中有生成无参复杂Bean的方法:

/**
 * @author 雫
 * @date 2021/4/16 - 15:11
 * @function 可生成无参复杂Bean TypeParser类的对象
 * TypeParser类是我的工具包中的一个类
 */
@Component
public class BeanConfig {

    public BeanConfig() {}

    @Bean
    public TypeParser getTypeParser() {
        TypeParser typeParser = new TypeParser();
        return typeParser;
    }
    
}

观察上述方法,其实生成无参复杂Bean也不麻烦,只需要反射执行这个方法即可,该方法执行后的返回值就是我们需要的Bean,方法自然需要反射执行,method.invoke(obj, args),方法需要依赖对象来执行,生成复杂Bean的方法,反射执行需要的对象就是这里带Component注解的类的对象,即BeanConfig类的对象

我们对BeanFactory的scanPackage()方法稍作更改,就可以直接生成无参复杂Bean

/**
     * @Author 雫
     * @Description 扫描指定路径的包 区分简单Bean和复杂Bean分类处理
     * @Date 2021/4/16 14:52
     * @Param [packageName]
     * @return void
     **/
    public static void scanPackage(String packageName) throws Exception {
        new AbstractPackageScanner() {
            @Override
            public void dealClass(Class<?> klass) {
                if(!klass.isAnnotationPresent(Component.class)) {
                    return;
                }

                try {
                    /*
                    * 处理简单Bean 即不带@Bean注解的类 直接生成一个BD存入BEAN_POOL中即可
                    * 等到getBean时 再进行注入 即懒汉模式
                    * 而如果是复杂Bean 即带@Bean注解的方法生成的Bean 稍后检测方法分为无参和有参处理
                    * 且这里的object就是方法执行时 所需要的对象
                    * */
                    BeanDefinition bd = new BeanDefinition();
                    Object object = klass.newInstance();
                    bd.setKlass(klass);
                    bd.setObject(object);
                    bd.setComplete(false);
                    bd.setMethod(null);

                    BEAN_POOL.put(klass, bd);
                    BeanClassRelation.adjustClassRelation(klass);

                    // 遍历所有方法 检查方法有无 @Bean注解 且带有@Bean注解的方法被分为 无参 和 带参分开处理
                    Method[] methods = klass.getDeclaredMethods();
                    for(Method method : methods) {
                        if(!method.isAnnotationPresent(Bean.class)) {
                            continue;
                        }

                        int paraCount = method.getParameterCount();
                        // 无参复杂Bean方法 直接反射执行生成Bean存入BEAN_POOL中
                        if(paraCount == 0) {
                            BeanDefinition beanBD = new BeanDefinition();
                            beanBD.setKlass(method.getReturnType());
                            beanBD.setObject(method.invoke(object, new Object[] {}));
                            beanBD.setComplete(true);
                            beanBD.setMethod(method);

                            BEAN_POOL.put(method.getReturnType(), beanBD);
                         
                        // 带参复杂Bean方法 创建依赖关系表 依赖满足后再执行
                        } else {
                         // TODO
                        }
                    }

                } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {}

            }
        }.packageScan(packageName);

    }

这样就可以生成无参复杂Bean,在生成带参复杂Bean之前,需要考虑几个问题:(这里只考虑参数是非八大基本类型的情况)

1,带参方法什么时候执行?
2,带参方法的参数是接口类型怎么办?
3,带参方法的若干个参数之间出现循环依赖怎么办?

1.6 带Bean注解的带参方法的执行时机

答:参数都能在BEAN_POOL中找到时执行

当我们扫描到该方法是一个带Bean注解的带参方法时,我们不要急着去执行它,将该方法的必要信息保存起来,为它创建一张参数依赖表,等到它依赖的参数都能在BEAN_POOL中找到时,再通过反射来执行该方法

执行该方法前,保存该方法必要信息的BeanMethodDefinition类:

/**
 * @author 雫
 * @date 2021/4/16 - 15:32
 * @function 生成Bean的带参方法 执行时需要的数据
 */
public class BeanMethodDefinition {
    private Class<?> klass;
    private Object object;
    private Method method;
	
	// 缺失的参数个数
    private int injectCount;

    BeanMethodDefinition() {}

    Class<?> getKlass() {
        return klass;
    }

    void setKlass(Class<?> klass) {
        this.klass = klass;
    }

    Object getObject() {
        return object;
    }

    void setObject(Object object) {
        this.object = object;
    }

    Method getMethod() {
        return method;
    }

    void setMethod(Method method) {
        this.method = method;
    }

    int getInjectCount() {
        return injectCount;
    }

    void setInjectCount(int injectCount) {
        this.injectCount = injectCount;
    }

}

我们应该将BeanMethodDefinition(以下简称BMD)的对象,以及它依赖的参数存起来,构成一张表,这个表自然可以用Map完成:

Map<BeanMethodDefinition, Map<Class<?>, Integer>> METHOD_PREPARING_POOL;

键为BMD 即当前带Bean注解的带参方法想要执行需要保存的数据

值为<Map<Class<?>, Integer>> 这个Map每个键都是一个parameterType
即参数类型,值无所谓

有了这个参数依赖表,就可以得知当前方法依赖的参数有几个,这些参数的类型有哪些,但这张表还不够用,它将存储依赖尚不满足的BMD,我们需要将已满足依赖的BMD存储到一个列表中,时机到了,执行这些BMD中的method,来生成BD,存入BEAN_POOL中

已满足依赖的BMD列表

List<BeanMethodDefinition> METHOD_COMPLETE_POOL;

现在我们将
尚未满足依赖的BMD存入METHOD_PREPARING_POOL中,
将已满足依赖的BMD存入METHOD_COMPLETE_POOL中

在scanPackage()的过程,可能随时会生成新的Bean,而这些Bean可能就是BMD的参数,这时,依赖关系就发生了变化,我们需要调整依赖关系,检查BMD是否依赖于新生成的Bean,在scanPackage的过程中,每生成一个Bean,调整一次依赖关系,
将满足依赖的BMD从METHOD_PREPARING_POOL中移到METHOD_COMPLETE_POOL中

1.7 依赖关系的调整

注:调整依赖的过程中,可能还会出现新的Bean是某个BMD依赖的实现类的情况,即要考虑BMD的参数有可能是接口

当我们每生成一个Bean时,就应该及时调整依赖,我们需要根据Bean来找到所有依赖于它的BMD,很明显这也是一个Map可以完成的功能

Map<Class<?>, List<BeanMethodDefinition>> CLASS_RELATION_POOL;

每当我们检测到某个BMD依赖于某个参数时,那么同理该参数就是BMD需要的依赖,多个BMD就可以同时依赖同一个参数,构成一个列表

每当我们检测到有新生成的Bean,如果这个Bean是CLASS_RELATION_POOL中的参数,或者是CLASS_RELATION_POOL中参数的实现类,那么就可以对应的BMD列表,根据这个BMD列表找到METHOD_PREPARING_POOL去除对应的依赖个数,如果依赖为0个,则转存到METHOD_COMPLETE_POOL中

BeanMethodRelation类:

/**
 * @author 雫
 * @date 2021/4/16 - 15:34
 * @function 存储BMD想要执行还欠缺的依赖关系 和已经满足依赖关系的BMD
 */
public class BeanMethodRelation {

    private static final Map<BeanMethodDefinition, Map<Class<?>, Integer>> METHOD_PREPARING_POOL;

    private static final List<BeanMethodDefinition> METHOD_COMPLETE_POOL;

    static {
        METHOD_PREPARING_POOL = new HashMap<>();
        METHOD_COMPLETE_POOL = new ArrayList<>();
    }

    /**
     * @Author 雫
     * @Description 检测到带参方法 将方法执行时需要的数据保存到BMD中
     * 建立依赖关系并检查 将已满足依赖的存入METHOD_COMPLETE_POOL中 依赖欠缺的存入METHOD_PREPARING_POOL中
     * @Date 2021/4/16 15:38
     * @Param [object, method]
     * @return void
     **/
    static void addMethodRelation(Object object, Method method) {
        BeanMethodDefinition bmd = new BeanMethodDefinition();
        bmd.setKlass(method.getReturnType());
        bmd.setObject(object);
        bmd.setMethod(method);

        // paraTypeSet 保存该BMD欠缺的依赖类型
        Map<Class<?>, Integer> paraTypeSet = new HashMap<>();
        Parameter[] parameters = method.getParameters();

        for(Parameter parameter : parameters) {
            if(BeanFactory.hasBean(parameter.getType())) {
                continue;
            }

            paraTypeSet.put(parameter.getType(), 0);
            BeanClassRelation.addClassRelation(parameter.getType(), bmd);
        }

        int injectCount = paraTypeSet.size();
        bmd.setInjectCount(injectCount);

        if(injectCount == 0) {
            METHOD_COMPLETE_POOL.add(bmd);
        } else {
            METHOD_PREPARING_POOL.put(bmd, paraTypeSet);
        }

    }

    /**
     * @Author 雫
     * @Description 调整某个依赖已满足的BMD的依赖数量
     * 注意klass可能是某个依赖的实现类 需要检查
     * @Date 2021/4/16 16:06
     * @Param [relationKlass, bmd]
     * @return void
     **/
    static void adjustMethodRelation(Class<?> klass, BeanMethodDefinition bmd) {
        Map<Class<?>, Integer> paraTypeSet = METHOD_PREPARING_POOL.get(bmd);
        for(Class<?> beanKlass : paraTypeSet.keySet()) {
            if(beanKlass.isAssignableFrom(klass) || beanKlass == klass) {
                paraTypeSet.remove(beanKlass);
                bmd.setInjectCount(bmd.getInjectCount() - 1);
            }
        }

        if(bmd.getInjectCount() == 0) {
            METHOD_PREPARING_POOL.remove(bmd);
            METHOD_COMPLETE_POOL.add(bmd);
        }

    }

    static boolean hasNextBMD() {
        if(METHOD_COMPLETE_POOL.isEmpty()) {
            return false;
        }
        return true;
    }

    static BeanMethodDefinition nextBMD() {
        if(hasNextBMD()) {
            return METHOD_COMPLETE_POOL.remove(0);
        }
        return null;
    }

    /**
     * @Author 雫
     * @Description 根据class获取BMD
     * @Date 2021/4/16 16:43
     * @Param []
     * @return com.coisini.ioc.core.BeanMethodDefinition
     **/
    static BeanMethodDefinition getBmdByClass(Class<?> klass) {
        for(BeanMethodDefinition bmd : METHOD_PREPARING_POOL.keySet()) {
            if(bmd.getKlass() == klass) {
                return bmd;
            }
        }
        return null;
    }

    /**
     * @Author 雫
     * @Description 获取BMD的所有未满足的依赖关系
     * @Date 2021/4/16 16:45
     * @Param [bmd]
     * @return java.lang.String
     **/
    static String getBmdRelations(BeanMethodDefinition bmd) {
        Map<Class<?>, Integer> paraTypeSet = METHOD_PREPARING_POOL.get(bmd);

        StringBuffer relationStr = new StringBuffer();
        for(Class<?> klass : paraTypeSet.keySet()) {
            relationStr.append(" " + klass.getName() + " ");
        }
        return relationStr.toString();
    }

}

BeanClassRelation:

/**
 * @author 雫
 * @date 2021/4/16 - 15:34
 * @function 保存依赖某个Bean的BMD列表
 * 当生成某个Bean时 就可以即时找到依赖它的BMD 并调整依赖关系
 */
public class BeanClassRelation {

    private static final Map<Class<?>, List<BeanMethodDefinition>> CLASS_RELATION_POOL;

    static {
        CLASS_RELATION_POOL = new HashMap<>();
    }

    /**
     * @Author 雫
     * @Description 增加类对应的BMD依赖
     * 当生成带参方法的BMD 遍历BMD中method的参数时执行
     * @Date 2021/4/16 15:48
     * @Param []
     * @return void
     **/
    static void addClassRelation(Class<?> klass, BeanMethodDefinition bmd) {
        if(!CLASS_RELATION_POOL.containsKey(klass)) {
            CLASS_RELATION_POOL.put(klass, new ArrayList<>());
        }

        List<BeanMethodDefinition> relationList = CLASS_RELATION_POOL.get(klass);
        relationList.add(bmd);
    }

    /**
     * @Author 雫
     * @Description 每生成一个Bean 及时调整依赖关系
     * 注意新生成的Bean可能是某个依赖的实现类 即依赖可能是接口类型 需要检查
     * @Date 2021/4/16 15:55
     * @Param [klass]
     * @return void
     **/
    static void adjustClassRelation(Class<?> klass) {

        for(Class<?> beanKlass : CLASS_RELATION_POOL.keySet()) {
            if(beanKlass.isAssignableFrom(klass) || beanKlass == klass) {
                List<BeanMethodDefinition> relationList = CLASS_RELATION_POOL.get(beanKlass);

                CLASS_RELATION_POOL.remove(beanKlass);
                while (!relationList.isEmpty()) {
                    BeanMethodRelation.adjustMethodRelation(klass, relationList.remove(0));
                }
            }
        }

    }


}

每当生成新的Bean的时候,我们不但检查它是否是某个BMD的依赖参数,同时检查它是否是某个BMD的依赖参数的实现类,这样就能完成对参数是接口类型的依赖的调整

1.8 生成复杂带参Bean

有了上述调整依赖个数的方法,我们有一个METHOD_COMPLETE_POOL,这里存储着所有依赖关系已满足,等待生成Bean的BMD,接下来回到BeanFactory中,来依次取出这些BMD,并执行其中的method,生成Bean存储到BEAN_POOL中

这个方法执行的时机,就在scanPackage的最后,但是在执行该方法时,会出现新的问题:检测到方法的参数是接口类型

我们拒绝IOC容器中有多个类实现了同一个接口,因为这样会导致二义性,因此如果该参数是接口且在IOC容器中只有一个实现类,那么没有问题,但是该参数如果既是接口,又在IOC容器中存在多个实现类,那么直接抛异常

/**
 * @author 雫
 * @date 2021/4/16 - 16:38
 * @function 参数是接口,接口实现类不唯一异常
 */
public class ImpNotOnlyOneException extends RuntimeException {

    public ImpNotOnlyOneException() {
        super();
    }

    public ImpNotOnlyOneException(String message) {
        super(message);
    }

    public ImpNotOnlyOneException(String message, Throwable cause) {
        super(message, cause);
    }

    public ImpNotOnlyOneException(Throwable cause) {
        super(cause);
    }

    protected ImpNotOnlyOneException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

BeanFactory:

    /**
     * @Author 雫
     * @Description 查询BEAN_POOL中有几个klass接口的实现类
     * @Date 2021/4/16 16:34
     * @Param [klass]
     * @return int
     **/
    private static int getInterfaceImpCount(Class<?> klass) {
        int impCount = 0;

        for(Class<?> beanKlass : BEAN_POOL.keySet()) {
            if(klass.isAssignableFrom(beanKlass)) {
                impCount++;
            }
        }
        return impCount;
    }

    /**
     * @Author 雫
     * @Description 查询某接口在BEAN_POOL中所有实现类
     * @Date 2021/4/16 16:37
     * @Param [klass]
     * @return java.lang.String
     **/
    private static String getInterfaceImpStr(Class<?> klass) {
        StringBuffer impStr = new StringBuffer();

        for(Class<?> beanKlass : BEAN_POOL.keySet()) {
            if(klass.isAssignableFrom(beanKlass)) {
                impStr.append(" " + beanKlass.getName() + " ");
            }
        }

        return impStr.toString();
    }

    /**
     * @Author 雫
     * @Description 避免参数是接口类型 由接口类型找到BEAN_POOL中的实现类
     * @Date 2021/4/17 21:58
     * @Param [klass]
     * @return java.lang.Class<?>
     **/
    private static Class<?> getImpClass(Class<?> klass) {
        for(Class<?> beanKlass : BEAN_POOL.keySet()) {
            if(klass.isAssignableFrom(beanKlass)) {
                return beanKlass;
            }
        }
        return null;
    }

    /**
     * @Author 雫
     * @Description 执行已满足依赖的BMD
     * @Date 2021/4/16 16:20
     * @Param []
     * @return void
     **/
    private static void doBeanMethod() throws Exception {
        while (BeanMethodRelation.hasNextBMD()) {
            BeanMethodDefinition bmd = BeanMethodRelation.nextBMD();
            Method method = bmd.getMethod();
            Object object = bmd.getObject();

            int paraCount = method.getParameterCount();
            Object[] paraList = new Object[paraCount];

            Parameter[] parameters = method.getParameters();
            // 构建实参数组时 要检查参数是否为接口 且查询BEAN_POOL中实现类的个数
            for(int index = 0; index < paraCount; index++) {
                if(parameters[index].getType().isInterface()) {
                    int impCount = getInterfaceImpCount(parameters[index].getType());
                    if(impCount > 1) {
                        String impStr = getInterfaceImpStr(parameters[index].getType());
                        throw new ImpNotOnlyOneException("Imp Not Only One --> " + impStr
                                + "Are Implement The " + parameters[index].getType());
                    } else {
                        // 先找到接口对应的实现类 再根据实现类找到相应的Bean
                        paraList[index] = getBeanByClass(getImpClass(parameters[index].getType()));
                    }
                } else {
                    paraList[index] = getBeanByClass(parameters[index].getType());
                }
            }

            BeanDefinition bd = new BeanDefinition();
            bd.setKlass(method.getReturnType());
            bd.setObject(method.invoke(object, paraList));
            bd.setMethod(method);
            bd.setComplete(true);

            BEAN_POOL.put(method.getReturnType(), bd);
            BeanClassRelation.adjustClassRelation(method.getReturnType());
        }

    }

解决了参数是接口类型的问题后,又有新的问题:
1,循环依赖,参数间存在循环依赖,无法执行方法产生Bean,对于这种方法中存在循环依赖的问题,无法解决,但是可以定位到它依赖的参数类型,并抛出异常,告知使用者发生了循环依赖,并告知具体的依赖关系

2,试图获取没有经过注解配置的Bean,此时直接抛出一个该Bean未装载异常

/**
 * @author 雫
 * @date 2021/4/16 - 16:48
 * @function 循环依赖异常
 */
public class RelationNotEnoughException extends RuntimeException {

    public RelationNotEnoughException() {
        super();
    }

    public RelationNotEnoughException(String message) {
        super(message);
    }

    public RelationNotEnoughException(String message, Throwable cause) {
        super(message, cause);
    }

    public RelationNotEnoughException(Throwable cause) {
        super(cause);
    }

    protected RelationNotEnoughException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

}

/**
 * @author 雫
 * @date 2021/4/16 - 17:55
 * @function Bean尚未装载异常
 */
public class BeanNeverLoad extends RuntimeException {

    public BeanNeverLoad() {
        super();
    }

    public BeanNeverLoad(String message) {
        super(message);
    }

    public BeanNeverLoad(String message, Throwable cause) {
        super(message, cause);
    }

    public BeanNeverLoad(Throwable cause) {
        super(cause);
    }

    protected BeanNeverLoad(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

}

BeanFactory:

	/**
     * @Author 雫
     * @Description 根据class对象从BEAN_POOL中获得对应的BD
     * 再从BD中取出真正的Bean返回 返回前需要检查需要的Bean是单例还是原型
     * @Date 2021/4/16 14:57
     * @Param [klass]
     * @return T
     **/
    public static <T> T getBeanByClass(Class<?> klass) throws Exception {
        BeanDefinition bd = BEAN_POOL.get(klass);
        if(bd == null) {
            BeanMethodDefinition bmd = BeanMethodRelation.getBmdByClass(klass);
            if(bmd == null) {
                throw new BeanNeverLoad("This Bean: " + klass.getName() + " Never Load");
            } else {
                String relationStr = BeanMethodRelation.getBmdRelations(bmd);
                throw new RelationNotEnoughException("Relation Not Enough --> " + relationStr);
            }
        }

        // 完成对简单Bean的注入
        inject(bd);
        Object object = bd.getObject();

        // 查看该Bean是以单例方式还是原型方式返回
        if(bd.getMethod() == null) {
            Component component = bd.getKlass().getAnnotation(Component.class);
            boolean componentValue = component.value();
            if(componentValue) {
                return (T) object;
            } else {
               return (T) bd.getKlass().newInstance();
            }
        } else {
            Bean bean = bd.getMethod().getAnnotation(Bean.class);
            boolean beanValue = bean.value();
            if(beanValue) {
                return (T) object;
            } else {
                return (T) bd.getKlass().newInstance();
            }
        }
    }

1.9 小结

经过上述一系列处理,终于能够生成各种Bean,并且大概能解决循环依赖参数类型是接口的问题,通过模拟Spring的IOC,大致能了解到其工作原理,通过反射能让Java动态地处理问题,灵活地使用Map,能很好处理一对一和一对多的问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
综合小区管理系统管理系统按照操作主体分为管理员和用户。管理员的功能包括报修管理、车位管理、车位分配管理、出入管理、字典管理、房屋管理、物业费缴纳管理、公告管理、物业人员投诉管理、我的私信管理、物业人员管理、用户管理、管理员管理。用户的功能包括管理部门以及部门岗位信息,管理招聘信息,培训信息,薪资信息等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 综合小区管理系统管理系统可以提高综合小区管理系统信息管理问题的解决效率,优化综合小区管理系统信息处理流程,保证综合小区管理系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理综合小区管理系统信息,包括出入管理,报修管理,报修管理,物业费缴纳等,可以管理操作员。 出入管理界面,管理员在出入管理界面中可以对界面中显示,可以对招聘信息的招聘状态进行查看,可以添加新的招聘信息等。报修管理界面,管理员在报修管理界面中查看奖罚种类信息,奖罚描述信息,新增奖惩信息等。车位管理界面,管理员在车位管理界面中新增。公告管理界面,管理员在公告管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值