在上一篇文章中我们开启了自己动手写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