还不懂实现Spring IOC的底层原理的话,一定得看看!

·  阅读 200

什么是IOC?

springIOC的意思是控制反转,传统的对象是我们自己去创建和管理,现在是交给spring去处理,由它来负责控制对象的生命周期和对象间的关系。

实现

在使用spring注入bean的时候,我们有两种方式,一种是xml,一种是注解,现在我们通过实现注解的方式来实现简单的ioc,这里我们需要实现自定义注解,关于自定义注解可以参考另一篇文章,首先展示目录结构图,看实现了哪些注解。

@Component,通过value设置bean的id。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
     String value() default "";
}

复制代码

@Controller,@Service类似

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    String value() default "";
}

复制代码

@Scope,设置bean的作用域,singleton,prototype,request,session,global session,这里主要实现前面两种。

@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value() default "";
}

复制代码

@Value,为属性注入具体的值。

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
    //定义value属性
    String value();
}

复制代码

接下来就是核心类。首先定义两个map,一个map存放bean,一个map存放单例对象,第一个map里面存放类定义对象是为了方便在getBean的时候实现prototype,注意,这里为了保证线程安全使用了ConcurrentHashMap。

//定义两个map容器存储对象
    //存储类定义对象
    private Map<String,Class<?>> beanDefinationFactory = new ConcurrentHashMap<>();

    //存储单例对象
    private Map<String,Object> singletonBeanFactory = new ConcurrentHashMap<>();

复制代码

定义构造方法,在初始化的时候就根据传入的路径扫描包。

  //定义有参构造 传入要扫描的包路径
    public AnnotationConfigApplicationContext(String packageName) {
        //扫描指定的包
        scanPKG(packageName);
    }

复制代码

实现扫描包的方法,将所有类上有@Controller、@Component、@Service的注解的类将key和类定义对象存入map。

 /**
     * 扫描指定包
     * 对于类上有注解的类反射创建类定义文件并加入容器中
     * @param packageName
     */
    private void scanPKG(final String packageName) { //用final 修饰 防止传入参数被修改调用
        //首先将传入的包路径转换为目录结构 将.替换成/
        String pkgDir = packageName.replaceAll("\\.","/");
        //获取目录结构在类路径中的位置 URL封装了具体资源的路径
        String url = getClass().getClassLoader().getResource(pkgDir).getPath();
        //防止路径名有空格等出错
        try {
            url = URLDecoder.decode(url, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        //基于路径创建一个文件对象
        File file = new File(url);
        //获取此路径下所有以.class结尾的文件
        //listFiles返回某个目录下所有的文件和目录的绝对路径 list返回某个目录下所有文件和目录的文件名 返回String数组
        File[] files = file.listFiles(new FileFilter() { //文件过滤  也可以实现FileNameFilter接口
            @Override
            public boolean accept(File file) {
                //获取文件名
                String fileNmae = file.getName();
                //判断是否为目录,如果是,进一步扫描目录下的文件
                if(file.isDirectory()){
                    scanPKG(packageName + "." + fileNmae);
                }else{
                    //判断文件的后缀是否为class
                    if (fileNmae.endsWith(".class")){
                        return true;
                    }
                }
                return false;
            }
        });

        //遍历所有符合标准的file文件
        for (File f:files) {
            String fileName = f.getName();
            //获取文件名.class之前的类容
            fileName = fileName.substring(0,fileName.lastIndexOf("."));
            //获取类的全路径
            String pkgCls = packageName + "." + fileName;
            try {
                //通过反射创建对象
                Class<?> clazz = Class.forName(pkgCls);
                //判断是否需要注入bean的注解
                if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Component.class)
                || clazz.isAnnotationPresent(Service.class)){
                    String key = getKey(clazz);
                    if (key == null){
                        //将名字首字母小写 作为map中的key
                        key = String.valueOf(fileName.charAt(0)).toLowerCase() + fileName.substring(1);
                    }
                    beanDefinationFactory.put(key, clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

复制代码

实现getKey方法,判断注解的value值是否为默认值,如果不是,则返回value值,如果是默认值则用类名首字母小写作为key。

private String getKey(Class<?> clazz) {
        String key = null;
        //判断这个类是否有Component的注解
        if (clazz.isAnnotationPresent(Component.class)){
            //如果有这个注解 就获取value值,如果有自定义的value值,就将这个值设置为key,否则用默认的key
            if (!"".equals(clazz.getDeclaredAnnotation(Component.class).value())){
                key = clazz.getDeclaredAnnotation(Component.class).value();
            }
        }
        if (clazz.isAnnotationPresent(Controller.class)){
            //如果有这个注解 就获取value值,如果有自定义的value值,就将这个值设置为key,否则用默认的key
            if (!"".equals(clazz.getDeclaredAnnotation(Controller.class).value())){
                key = clazz.getDeclaredAnnotation(Controller.class).value();
            }
        }
        if (clazz.isAnnotationPresent(Service.class)){
            //如果有这个注解 就获取value值,如果有自定义的value值,就将这个值设置为key,否则用默认的key
            if (!"".equals(clazz.getDeclaredAnnotation(Service.class).value())){
                key = clazz.getDeclaredAnnotation(Service.class).value();
            }
        }
        return key;
    }

复制代码

应该实现了的bean都已经存入map中了,接下来就是getBean,通过beanId,获取具体的对象。

 /**
     * 根据传入的beanId获取容器中的对象
     * @param beanId
     * @return
     */
    public Object getBean(String beanId){

        //根据传入的beanId获取map中的对象
        Class<?> clazz = beanDefinationFactory.get(beanId);
        if (clazz == null){
            throw new NoSuchBeanDefinitionException(beanId, "No matching bean found for bean name '" + beanId + "'! (Note: Qualifier matching not supported because given BeanFactory does not implement ConfigurableListableBeanFactory.)");
        }
        String scope = null;
        //判断类上面是否有@Scope这个注解 如果有的话,获取里面的值
        if (clazz.isAnnotationPresent(Scope.class)){
            scope = clazz.getDeclaredAnnotation(Scope.class).value();
        }
        if ("".equals(scope)){
            //如果scope为空 没有设置,默认设置为单例模式 有五种 singleton prototype request session global session
            scope = "singleton";
        }
        //根据获取的值判断bean的作用域
        try{
            //如果是单例模式
            if("singleton".equals(scope)){
                //判断容器中是否有这个对象,如果没有 就创建一个对象
                if (singletonBeanFactory.get(beanId) == null){
                    Object instance = clazz.newInstance();
                    //获取对象时为其成员属性赋值
                    setFiledValues(clazz, instance);
                    singletonBeanFactory.put(beanId, instance);
                }
                return singletonBeanFactory.get(beanId);
            }

            if ("prototype".equals(scope)){
                Object instance = clazz.newInstance();
                setFiledValues(clazz, instance);
                return instance;
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        //如果遭遇异常
        return null;
    }

复制代码

实现上面用到的自定义异常方法

/**
 * 自定义异常 显示没有当前的bean对象
 */
public class NoSuchBeanDefinitionException extends RuntimeException {

    private String beanName;

    public  NoSuchBeanDefinitionException(String beanName, String message){
        super("No bean named '" + beanName + "' available: " + message);
        this.beanName = beanName;
    }
}

复制代码

实现为成员属性赋值的方法setFiledValues()。

   /**
     * 为对象的属性赋值
     *  获取成员属性上注解的值 转换为类型后 通过反射为对象赋值
     * @param clazz 类定义对象
     * @param obj 要为其赋值的实例对象
     */
    private void setFiledValues(Class<?> clazz, Object obj){
        //获取所有的属性
        Field[] fields = clazz.getDeclaredFields();
        //遍历所有属性 如果属性上面有注解 对其进行赋值
        for (Field field:fields){
            field.setAccessible(true);
            if (field.isAnnotationPresent(Value.class)){
                //获取注解内的值
                String value = field.getAnnotation(Value.class).value();
                //获取属性定义的类型
                String type = field.getType().getSimpleName();
                try{
                    if ("Integer".equals(type) || "int".equals(type)){
                        int intValue = Integer.valueOf(value);
                        field.set(obj,intValue);
                    }else if("String".equals(type)){
                        field.set(obj,value);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }
    }

复制代码

实现getBean的重载方法,返回传入的class对象的类型

   /**
     * 重载方法,返回传入的class对象的类型
     * @param beanId
     * @param c
     * @param <T>
     * @return
     */
    public <T> T getBean(String beanId, Class<T> c){
        return (T)getBean(beanId);
    }

复制代码

销毁方法,用于释放资源

/**
     * 销毁方法 用于释放资源
     */
    public void close(){
        beanDefinationFactory.clear();
        singletonBeanFactory.clear();
        beanDefinationFactory = null;
        singletonBeanFactory = null;
    }

复制代码

至此,基本实现了注解方式实现ioc,写的比较简单,因为注释比较详细,所以就没有过多的解释,但是很多该有的判断也没有添加,主要是便于理解,如果有什么不对的地方请多多指教。

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改