zoukankan      html  css  js  c++  java
  • 手撸一个IOC容器

    手撸一个IOC容器,完成依赖的注入以及类的查找。

    分两步

    1、进行类的加载,将带有特殊注解的类注册到容器当中

    2、进行依赖的注入,完成带有特定标签的属性的值的自动注入。

    一、创建一堆注解,用于表示标识类需要被加载到容器中 @Component 、@Controller、@Service、@Repository

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Component {
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Controller {
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Repository {
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Service {
    }

    二、创建一个单例BeanContainer,作为IOC容器

    /**
     * 下面是实现一个单例的容器
     */
    private  BeanContainer(){}
    public static BeanContainer getInstance(){
        return ContainerHolder.HOLDER.instance;
    }
    
    //利用枚举的特性,创建单例,可以防止反射和序列化造成多个实例的问题。
    private enum ContainerHolder{
        HOLDER;
        private BeanContainer instance;
        ContainerHolder(){
            instance = new BeanContainer();
        }
    }
      /**
      * 存放所有的被标记的目标对象
      * 想当与从一个包中查找出的带有特定标签(待注入的类)的类都放在这个MAP当中
      * object相当于newInstance创建的实例
      */
      private final static ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<Class<?>, Object>();
    
      /**
      * 将需要被处理的注解放到一个列表当中
      */
      private final static List<Class<? extends Annotation>> beanAnno = new ArrayList<Class<? extends Annotation>>(){{
        add( Component.class);
        add( Controller.class);
        add( Repository.class);
        add( Service.class);
      }
    

    核心代码loadBeans,把包内的类加载到容器当中

        /**
         * 加载所有的Bean
         * @param packageName
         */
        public void loadBeans(String packageName){
    
            Set<Class<?>> classSet = new HashSet<Class<?>>();
            classSet = ClassUtil.extractPackageClass(packageName);
            if(classSet == null || classSet.isEmpty()){
                System.out.println("包下没有任何类");
                return;
            }
    
            //如果存在class
            for (Class<?> clazz : classSet){
                //查看某个类型是不是有列表中的注解
                for (Class<? extends Annotation> annoClass:beanAnno){
                    if(clazz.isAnnotationPresent(annoClass)){
                        beanMap.put(clazz,getNewInstance(clazz,true));
                    }
                }
            }
        }
    
        /**
         * 获取新实例
         * @param clazz
         * @param accessible
         * @param <T>
         * @return
         */
        private <T> T getNewInstance(Class<?> clazz,boolean accessible )   {
            try{
                Constructor<?> constructor = clazz.getDeclaredConstructor();
                constructor.setAccessible(accessible);
                return (T)constructor.newInstance();
            }
            catch (Exception ex){
                ex.printStackTrace();
                throw  new  RuntimeException();
            }
        }
    

    三、创建一些容器的操作

        /**
         * 添加bean
         * @param clazz
         * @param obj
         * @return
         */
        public  Object addBean(Class<?> clazz,Object obj){
            return beanMap.put(clazz,obj);
        }
    
        /**
         * 删除Bean
         * @param clazz
         * @return
         */
        public  Object removeBean(Class<?> clazz){
            return  beanMap.remove(clazz);
        }
    
        /**
         * 获取bean
         * @param clazz
         * @return
         */
        public  Object getBean(Class<?> clazz){
            return beanMap.get(clazz);
    
        }
    
        /**
         * 获取所有的bean
         * @return
         */
        public  Set<Object> getBeans(){
            return  new HashSet<Object>(beanMap.values());
        }
    
        /**
         * 获取所有的Classes
         * @return
         */
        public  Set<Class<?>> getClasses(){
            return beanMap.keySet();
        }
    
        /**
         * 获取指定注解的类
         * @param annoClass
         * @return
         */
        public  Set<Class<?>> getClassesByAnno(Class<? extends Annotation> annoClass){
            Set<Class<?>> classes = getClasses();
            if(classes ==null || classes.isEmpty()){
                System.out.println("没找到指定注解的类型");
            }
    
            Set<Class<?>> classSet = new HashSet<Class<?>>();
            for(Class<?> clazz : classes){
                if(clazz.isAnnotationPresent(annoClass)){
                    classSet.add(clazz);
                }
            }
            return classSet;
        }
    
        /**
         * 获取指定接口的类
         * @param interfaceOrClass
         * @return
         */
        public  Set<Class<?>> getClassesByInterfaceOrClass(Class<?> interfaceOrClass){
            Set<Class<?>> classes = getClasses();
            if(classes ==null || classes.isEmpty()){
                System.out.println("没找到指定接口的类型");
            }
    
            Set<Class<?>> classSet = new HashSet<Class<?>>();
            for(Class<?> clazz : classes){
                if(clazz.isAssignableFrom(interfaceOrClass)){
                    classSet.add(clazz);
                }
            }
            return classSet;
        }
    

    创建一个工具类

    public class ClassUtil {
    
        public static final String FILE_PROTOCOL = "file";
    
        /**
         * 获取包下的所有的类的集合
         * @param packageName
         */
        public static Set<Class<?>> extractPackageClass(String packageName){
            Set<Class<?>> classSet = null;
            //获取类的加载器,主要为了解决实际路径的问题,包名没办法定位
            ClassLoader classLoader = getClassLoader();
            //通过类加载器加载资源
            URL url = classLoader.getResource(packageName.replace('.', '/'));
            if(url==null){
                System.out.println("资源不存在");
                return classSet;
            }
    
            //根据不同的资源类型,通过不同的方式获取资源
            //如果是文件协议
            if(url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
                classSet = new HashSet<Class<?>>();
                File packageDir = new File(url.getPath());
                extractClassFile(classSet,packageDir,packageName);
            }
    
            return classSet;
        }
    
        /**
         * 提取类文件(需要进行递归,判断是不是文件夹还是文件)
         * @param classSet
         * @param fileSoucre
         * @param packageName
         */
        private static void extractClassFile(final Set<Class<?>> classSet, File fileSoucre, final String packageName) {
            //如果是文件的情况
            if(!fileSoucre.isDirectory()){
                return;
            }
            else {
                //文件夹的情况,列出文件夹下的所有文件
                File[] files = fileSoucre.listFiles(new FileFilter() {
                    public boolean accept(File file) {
                         if(file.isDirectory()){
                             return true;
                         }
                         else {
                             //获取文件的绝对路径
                             String absolutePath = file.getAbsolutePath();
                             if(absolutePath.endsWith(".class")){
                                    //class文件直接加载
                                    add2ClassSet(absolutePath);
                                    return true;
                             }
    
                         }
                         return false;
                    }
    
                    /**
                     * 添加class到classSet当中
                     * @param absolutePath
                     */
                    private void add2ClassSet(String absolutePath) {
                        System.out.println(absolutePath);
                        //从绝对路径中获取到包名+类名
                        absolutePath = absolutePath.replace(File.separator,".");
                        //根据传进来的packagename,去掉路径中的包名,只得到
                        String className = absolutePath.substring(absolutePath.indexOf(packageName));
                        className = className.substring(0,className.lastIndexOf('.'));
                        Class<?> aClass = loadClass(className);
                        classSet.add(aClass);
                    }
    
                    private Class<?> loadClass(String className) {
                        try{
                            return Class.forName(className);
                        }
                        catch (Exception ex){
                            System.out.println("load class error");
                            throw new RuntimeException();
                        }
                    }
    
                });
    
                if(files!=null){
                    //遍历每一个文件
                    for (File f : files){
                        extractClassFile(classSet,f,packageName);
                    }
                }
            }
        }
    
        /**
         * 获取ClassLoader
         * @return
         */
        public static ClassLoader getClassLoader(){
            //通过这个方式获取ClassLoader实例
            return Thread.currentThread().getContextClassLoader();
        }
    
    }
    

      

    四、实现依赖注入,创建一个注解@Autowire,限定为Field使用

    @Retention(value = RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Autowire {
    
        /**
         * 定义一个属性,用于存储默认的实现
         * @return
         */
        public String value() default "";
    }
    

    创建一个DependcyInjector类,可以实现类中的依赖注入。

    public class DependcyInjector {
    
        private BeanContainer beanContainer;
    
        public DependcyInjector() {
            //获取容器的单例
            beanContainer = BeanContainer.getInstance();
        }
    
        /**
         * 执行依赖注入
         */
        public void doIoc(){
            //1、遍历Bean容器当中所有的Class对象
            Set<Class<?>> iocClasses = beanContainer.getClasses();
            for (Class<?> clazz : iocClasses){
                //2、遍历类中的所有的属性
                Field[] fields = clazz.getDeclaredFields();
                for(Field field : fields){
                    //3、判断属性是否带有AutoWire注解,然后通过注入该属性
                    if(field.isAnnotationPresent(Autowire.class)){
                        //4、获取这些被Autowire注解修饰的类
                        Class<?> autowireType = field.getType();
                        //5、获取这个类型的实例,要考虑多个实现的情况如何获取,获取哪一个
                        String autowireValue = field.getDeclaredAnnotation(Autowire.class).value();
    
                        Object fieldInstance = getFieldInstance(autowireType, autowireValue);
                        if(fieldInstance ==null){
                            System.out.println(MessageFormat.format("注入失败,属性{0}实现类不存在",autowireType.getSimpleName()));
                        }
                        //6、将属性的实现类实例,注入到属性当中
                        Object classBean = beanContainer.getBean(clazz);
                        //设置值到Field当中
                        SetField(field,fieldInstance,classBean,true);
                    }
                }
            }
        }
    
        /**
         * 设置属性
         */
        private void SetField(Field field,Object fieldInstance,Object target,boolean access) {
            field.setAccessible(access);
            try{
                field.set(target,fieldInstance);
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
    
        /**
         * 获取属性的实例
         * @param fieldClass
         * @param autowireValue
         * @return
         */
        private Object getFieldInstance(Class<?> fieldClass,String autowireValue) {
    
            //从容器中获取实例
            Object filedClassInstance = beanContainer.getBean(fieldClass);
            if(filedClassInstance !=null){
                return filedClassInstance;
            }
            else {
                //可能传入的不是类,而是接口,需要获取接口对应的实现类
                Class<?> implementClass = getImplementClass(fieldClass, autowireValue);
                if(implementClass!=null){
                    return beanContainer.getBean(implementClass);
                }
                return  null;
            }
    
        }
    
        /**
         * 获取接口对应的实现类
         * @param interfaceClass
         * @return
         */
        private Class<?> getImplementClass(Class<?> interfaceClass,String autowireValue) {
    
            Set<Class<?>> implClasses = beanContainer.getClassesByInterfaceOrClass(interfaceClass);
    
            //如果实现类不存在
            if(implClasses == null || implClasses.isEmpty()){
                System.out.println(MessageFormat.format("接口{0}的实现类型不存在",interfaceClass.getName().toString()));
                return null;
            }
    
            //如果autowirevalue是默认值,并且实现类只有一个,此时返回对应的默认的实现类
            if(autowireValue.equals("") && implClasses.size() ==1){
                return implClasses.iterator().next();
            }
    
            //如果指定了实现类名称
            if(!autowireValue.equals("")){
                //存在多个
                for (Class<?> clazz : implClasses){
                    if(clazz.getSimpleName().equals(autowireValue)){
                        return  clazz;
                    }
                }
                System.out.println(MessageFormat.format("接口{0}存在的实现类中不存在名为{1}的实现",interfaceClass.getName().toString(),autowireValue));
            }
    
            return null;
        }
    }
    

    五、使用测试

    创建几个测试用的类

    @Component
    public class User {
    
        private String name;
        private Integer age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    
    @Service
    public class Student extends User{
        public String getLevel() {
            return level;
        }
        public void setLevel(String level) {
            this.level = level;
        }
        private String level;
        public String Hello(){
            return "HelloWorld";
        }
    }
    
    //学生服务 @Service public class StudentService { @Autowire private Student stu ; public Student getStudent(){ stu.setLevel("2"); stu.setAge(11); stu.setName("123123"); return stu; } }
    //Hello服务 @Service public class HelloService { @Autowire private StudentService studentService; public void sayHello(){ System.out.println(studentService.getStudent().getName() + " Hello"); } }

    Main方法运行

        public static void main(String[]args){
    
            //初始化容器
            BeanContainer ioc = BeanContainer.getInstance();
            ioc.loadBeans("org.simpleframework.entity");
            ioc.loadBeans("org.simpleframework.service");
            //实现依赖注入
            DependcyInjector injector = new DependcyInjector();
            injector.doIoc();
    
            //执行代码
            HelloService helloService =(HelloService)ioc.getBean(HelloService.class);
            helloService.sayHello();
    
        }
    

      

    执行结果可以看到类都加载到BeanContainer,并且服务内方法也成功调用了:

    E:Projectsmyioc	argetclassesorgsimpleframeworkentityStudent.class
    E:Projectsmyioc	argetclassesorgsimpleframeworkentityUser.class
    E:Projectsmyioc	argetclassesorgsimpleframeworkserviceHelloService.class
    E:Projectsmyioc	argetclassesorgsimpleframeworkserviceStudentService.class
    123123 Hello

    总结,可以简单归纳如图

     

  • 相关阅读:
    网站SEO关键词优化技巧
    SEO操作流程及网站优化技巧
    Linux服务器工作常用命令总结
    【转载】Linux常用命令大全(非常全!!!)
    myBatis出现Mapped Statements collection already contains value for
    maven打成war包之后没有class文件
    查询每个类型最新的一条记录
    关于 MySQL 的 boolean 和 tinyint(1) (转)
    Mac下的eclipse按住ctrl点击无法查看类文件
    Mac 10.10下安装MySQL5.6.21提示安装失败
  • 原文地址:https://www.cnblogs.com/dcz2015/p/14025730.html
Copyright © 2011-2022 走看看