自己动手写Spring-2-简单的AnnotationIOC实现

在上一篇文章中我们开启了自己动手写Spring的trip,一个基于xml的ioc看起来还阔以,但是实际中我们还是会遇到一些麻烦,就让我来看看这一次的魔幻旅程:
c叔的健身房每次采购新器材时,他都要看着总的配置文件,一个个取零件,自己完成组装,然后放到相应位置。
在这里插入图片描述
上面的模型就像我们上一篇文章给出的代码一样,但是如果有一次c叔要组装上百个器材时,我们要给出的配置文件是不是得老长了。而且还要保证我们给出得配置文件不会出错、不会混淆,这是很难保证的,对不(就算命名不重复,那么这么多属性,写出来的xml文件可读性是不是也不高)?而且要是碰上像c叔这样粗心的不能识别一个器材配件的配置项出错时,就可能把后面所有的都配错。所有我们就希望在器件上能标注好需要配置吗?那需要配置什么?如下:
在这里插入图片描述
这样我们就省去了撰写总配置文件(xml文件),而且采用这种标注的方式,更加灵活,不易出错。聪明的你肯定知道了这种方式,就是本次实现的注解咯。🆗,废话少说,代码先行。源码地址,项目结构:
在这里插入图片描述
实现是三个bean类,这就是我们组装的器材了:
A.class:


import AnnotationIOC.annotation.Component;
import AnnotationIOC.annotation.Value;
  
 //设置别名为“A”的单例
@Component("A")
public class A {
   //为brand注入属性
    @Value("A.store")
    private String brand;
    @Value("1000")
    private int price;
    public String getBrand() {
        return brand;
    }
    public int getPrice() {
        return price;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public void setPrice(int price) {
        this.price = price;
    }
}

B.class:

import AnnotationIOC.annotation.Autowired;
import AnnotationIOC.annotation.Component;
import AnnotationIOC.annotation.Value;

@Component
public class B {
    @Value("B.store")
    private String brand;
    @Value("2000")
    private int price;
   //采取自动装配
    @Autowired
    private A a;
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
    public String getBrand() {
        return brand;
    }
    public int getPrice() {
        return price;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public void setPrice(int price) {
        this.price = price;
    }
}

C.class:

import AnnotationIOC.annotation.Autowired;
import AnnotationIOC.annotation.Value;

public class C {
    @Value("C.store")
    private String brand;
    @Value("3000")
    private int price;
    @Autowired
    private A a;
    @Autowired
    private B b;
    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
    public String getBrand() {
        return brand;
    }
    public int getPrice() {
        return price;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public void setPrice(int price) {
        this.price = price;
    }
}

到这我们发现注解没有啊,是的这个注解是得我们自己实现的。这次我们实现的是Spring中装配bean常用得三个注解,我们在这实现的三个注解,在原理上与Spring是一致的哦:
Component .Class:@Component声明在类上,表示当前类是一个组件,我们在这里对声明了@Component的类都采用单例模式进行缓存。@Component(“beanname”)还有一个属性,用来为该bean声明beanname的。

import java.lang.annotation.Documented;
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)
@Documented
public @interface Component {
    /*
     *设置beanname。如果没有设置,则在后面处理时将beannama设置为类名
     */
    String value() default "";
}

Value.Class: 当然这个就很简单了,@Value是作用在类的非引用类型属性上的,为该属性设定其值。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
    /*
     * 完成属性的值注入
     */
    String value();
}

Autowired.Class: @Autowired是作用在类的引用类型属性上的,为该属性设定其引用依赖,完成自动装配。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    /*
     * 完成引用类型的bean注入
     */
    boolean required() default true;
}

最后就是我们的实际处理过程了,我们这么完成这样的bean的装配呢。代码如下:
ApplicationContext.Class: 在这个类里面我们将完成bean的加载。先对ApplicationContext有一个简单了解。
在这里插入图片描述

public class ApplicationContext {
    //作为存储单例的bean容器
    private static Map<String, Object> beansMap = new ConcurrentHashMap<>();
    public ApplicationContext(Class<?>... Classes) {
        this.register(Classes);
    }

    /*
     *对传入的需要解析的class文件,逐个加载成bean实例
     */
    public  void register(Class<?>[] annotatedClasses) {
        Class[] var2 = annotatedClasses;
        int var3 = annotatedClasses.length;
        for(int var4 = 0; var4 < var3; ++var4) {
            Class<?> annotatedClass = var2[var4];
            this.registerBean(annotatedClass);
        }
    }


    /*
     *开始注册bean,并分析这个bean是否为@component标注,标注了则默认为单例模式
     * 并存入beanmap
     */
    protected  <T> T  registerBean(Class<?> annotatedClass) {
        try {
            Component component = annotatedClass.getAnnotation(Component.class);
            //如果类没有被@Component注解,则直接创建一个实例并返回。
            if(component==null) {
                return createInstance(annotatedClass);
            }
            String beanName = "";
            if(component.value().equals("")){
                beanName = annotatedClass.getSimpleName();
            }else {
                beanName = component.value();
            }
            Object obj = beansMap.get(beanName);
            //如果类被@Component注解,且已经被加载进入beanmap,直接返回即可
            if(obj != null) {
                return (T) obj;
            }
            //如果类被@Component注解没有加载,则开始加载
            synchronized (annotatedClass) {
                obj = beansMap.get(beanName);
                if(obj == null) {
                    obj = createInstance(annotatedClass);
                    beansMap.put(beanName, obj);
                }
            }
            return (T) obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /*
     * 完成实例化,并解析注解
     */
    private  <T> T createInstance(Class<?> cls) throws Exception {
        T obj = (T) cls.newInstance();
        Field[] fields = cls.getDeclaredFields();
        for(Field f : fields) {
            if(f.isAnnotationPresent(Value.class)) {
                if(!f.isAccessible()) {
                    f.setAccessible(true);
                }
                Value value=f.getAnnotation(Value.class);
                this.setFieldValue(f,obj,value.value());
            }
            if(f.isAnnotationPresent(Autowired.class)) {
                if(!f.isAccessible()) {
                    f.setAccessible(true);
                }
                Class<?> fieldCls = f.getType();
                //完成对@Autowired所注解的引用属性的自动装配。
                f.set(obj, this.registerBean(fieldCls));
            }
        }
        return obj;
    }
    /**
     *完成对属性类型判断,并填充对应类型的值
     *这里少了一个异常输出。
     */
    private  void setFieldValue(Field f, Object obj, String value)
            throws Exception {
        Class<?> type = f.getType();
        if(type == int.class) {
            f.setInt(obj, Integer.parseInt(value));
        } else if(type == byte.class) {
            f.setByte(obj, Byte.parseByte(value));
        } else if(type == short.class) {
            f.setShort(obj, Short.parseShort(value));
        } else if(type == long.class) {
            f.setLong(obj, Long.parseLong(value));
        } else if(type == float.class) {
            f.setFloat(obj, Float.parseFloat(value));
        } else if(type == double.class) {
            f.setDouble(obj, Double.parseDouble(value));
        } else if(type == char.class) {
            f.setChar(obj, value.charAt(0));
        } else if(type == boolean.class) {
            f.setBoolean(obj, Boolean.parseBoolean(value));
        } else if(type == String.class) {
            f.set(obj, value);
        } else {
            Constructor<?> ctor = type.getConstructor(
                    new Class[] { String.class });
            f.set(obj, ctor.newInstance(value));
        }
    }
    /*
     * 获取bean,如果已经初始化的bean直接从beanmap返回,
     * 没有初始化的bean,阔以通过再加载,生成实例返回
     */
    public  <T> T getBean(String name) throws Exception {
        if (beansMap.get(name)==null){
               try {
                   Class cls=Class.forName(name);
                   return this.registerBean(cls);
               }catch (Exception e){
                   throw new Exception("No bean named '"+name+ "'available,you can input 'package.ClassName' to get the bean");
               }
        }
        return (T) beansMap.get(name);
    }
}

SimpleTest.Class(测试案列):

public class SimpleTest {
    public static void main(String[] args) throws Exception{
        ApplicationContext context=new ApplicationContext(A.class, B.class);
        A a=context.getBean("A");
        System.out.println("这说A组件价格:"+a.getPrice());
        //这里的b1和b2输出一样,因为他们是单例模式的
        B b1=context.getBean("B");
        System.out.println(b1.getA().getBrand());
        B b2=context.getBean("B");
        System.out.println(b2.getA().getBrand());
        //这的路径需注意,不要写错。
        C c=context.getBean("AnnotationIOC.bean.C");
        System.out.println("这是C需要的组件来源:"+c.getA().getBrand()+c.getB().getBrand());
        //这是一个演示,路径出错将报错
        C c1=context.getBean("bean.c");
    }
}

测试结果:
在这里插入图片描述
看起来还不错,但是ApplicationContext.Class的运行过程还是阔以仔细看看,这次的案列,仅仅是对Spring的注解实现ioc进行了一个简单的模仿,采用注解的方式去加载bean相对于读取xml更加灵活方便,特别是当bean类的个数比较多的情况下,这样积大的省去了我们去书写xml的时间。

附上源码地址:https://github.com/sanshi-07/simple-spring/tree/master/simplr-spring-v1.0.0/src/annotationioc

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值