【java】 SpringFramework ———— 注入
作者:互联网
我们这次的目的有三个:
1.组建Bean工厂(上下文,容器);
2.能够对Bean的成员进行注入;
3.能够取得Bean;
Bean,又称Java Bean,或者说POJO,平凡的Java类。
将这些平凡的Java类对象组织到一个容器中,就形成了所谓的“上下文”或“容器”,容器的形成需要配置手段,可以采用注解或者Xml文件的形式,在这里我们采用注解的形式。
@Component//该注解用来表明该类为容器中的类
public class Classone {
@Autowired//该注解用于表示待注入的成员
private Complex complex;
private String str;
@Autowired
private Point point;
public Classone() {
}
}
接下来我们要完成对Bean工厂的编写
public class BeanFactory {
private static final Map<String,BeanDefinition> beanpool;//容器
static {
beanpool = new HashMap<String, BeanDefinition>();
}
public static void scanpackage(String pagename) {//包扫描
new PackageScanner() {
//处理扫描到的类,如果不是带有Component注解的就直接跳过
@Override
public void dealClass(Class<?> klass) {
if(klass.isPrimitive()
|| klass.isAnnotation()
||klass.isEnum()
||klass.isArray()
||!klass.isAnnotationPresent(Component.class)) {
return;
}
try {
Object obj = klass.newInstance();
/*BeanDefinition类是对我们扫描到的类进行进一步提炼,
保留我们想要的值*/
BeanDefinition bd = new BeanDefinition();
bd.setKlass(klass);
bd.setObj(obj);
//将其储存到map中,键为该类的类名称
beanpool.put(klass.getName(), bd);
} catch (Exception e) {
e.printStackTrace();
}
}
}.packageScanner(pagename);
}
BeanDefinition getBeanDefinition(String klassname) {//通过类名称取得值
return beanpool.get(klassname);
}
BeanDefinition getBeanDefinition(Class<?> klassname) {//通过类取得值
return getBeanDefinition(klassname.getName());
}
public <T> T getBean(String klassname){//该方法用于直接在map获得对象值,返回值用泛型而不用Object类型是因为用泛型在调用时不需要强转;
BeanDefinition bd = getBeanDefinition(klassname);
if(bd = null){
return null;
}
return (T) bd.getObject();
}
通过传递Classone.class.getName()测试getBean我们发现得到的结果是:Classone [complex=null, str=null,Point=null],理应如此,因为map中存的对象是newInstance()的得到的,所以其成员值都为null,接下来我们就要对该对象的成员进行注入,在注入时我们要注意的问题(这里不做对循环注入的讨论,即A对象中的B成员待注入,而B中还有A成员待注入形成死循环):
1.我们采用的是“懒汉模式”,即等到相关的Bean被获取时才会注入,且Bean工厂中的Bean都是单例的,对同一个bean只用注入一次即可。
2.对成员注入时应该在map中通过该成员类名称进行查找,即注入的是map中的值。
3.在注入时应该深度注入,即在对一个对象的成员进行注入时,我们因该先查找该成员的类中有没有成员需要注入。
例如:A中有B成员,B中有C成员,对A中的B进行注入时,理所应当的先判断B中有没有也需要注入的,即先给B中的C注入
4.如果该对象中有被标记了Autowired的待注入成员,但是map中却没有该成员的Bean则应报异常并且终止运行。
在了解上面的信息后来看看我们的实现:
//获得Bean的方法
@SuppressWarnings("unchecked")
public <T> T getBean(String klassname) throws RuntimeException {
BeanDefinition bd = getBeanDefinition(klassname);
if(bd == null) {
return null;
}
Object res = bd.getObj();
if(!bd.isInject()) {//判断该对象的成员是否已经被注入过了
injectPro(bd);//没注入则调用注入方法
}
return (T) res;
}
//给该对象待注入的成员注入
private void injectPro(BeanDefinition bd) throws RuntimeException {
Class<?> klass = bd.getKlass();
Object obj = bd.getObj();
Field[] field = klass.getDeclaredFields();//反射机制调用获取该对象所有成员
for(int i = 0; i < field.length;i++) {
if(!field[i].isAnnotationPresent(Autowired.class)) {
continue; //如果没有注入标记就跳过
}
//有标记的话就先修改权限,然后在map中找对应的值
field[i].setAccessible(true);
// String klassname = field[i].getType().getName();
// BeanDefinition bdm = getBeanDefinition(klassname);//在map中找
// if(bdm == null) {//如果map中没找到,则说明有错那就抛异常
// throw new Exception
// ("类{"+klass.getName()+"}中成员{"+field[i]+"}在map中没有对应的值");
// }
// field[i].set(obj, bdm.getObj());//给该成员注入
Object value = getBean(field[i].getType());//递归
/*如果用上面的被注释的代码就只能对该成员进行查找注入(浅层次),但是万一该成员内还有需要注入的成员,就会出错,就会导致深度不够深。
例如:A中有B成员,B中有C成员,对A中的B进行注入时,理所应当的先判断B中有没有也需要注入的,即先给B中的C注入,所以这里的递归调用没有问题 */
if(value == null) {
//如果value == null,就说明该成员待注入却没在map中没找到,则说明有错那就抛异常,一定要抛运行时异常,不然无法终止运行。
throw new StopException
("类{"+klass.getName()+"}中成员{"+field[i].getName()+"}在map中没有对应的值");
}
try {
field[i].set(obj,value);//给该成员注入
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("成员["+field[i].getName()+"]以完成注入");
}
bd.setInject(true);//对所有成员注入完成之后一定要对对象的注入标记做更改,这样才能保证只注入一次
}
//该方法的重载,直接用类类型作为参数
public <T> T getBean(Class<?> klassname) throws RuntimeException {
return getBean(klassname.getName());
}
以下是BeanDefinition.java:
public class BeanDefinition {
private Class<?> klass;
private Object obj;
private boolean inject;
BeanDefinition() {
this.inject = false;
}
boolean isInject() {
return inject;
}
void setInject(boolean inject) {
this.inject = inject;
}
Class<?> getKlass() {
return klass;
}
void setKlass(Class<?> klass) {
this.klass = klass;
}
Object getObj() {
return obj;
}
void setObj(Object obj) {
this.obj = obj;
}
@Override
public String toString() {
return klass.getSimpleName() + " : " + obj;
}
}
以下是对我们注入和获取的测试:
public static void main(String[] args) {
BeanFactory bf = new BeanFactory();
BeanFactory.scanpackage("com.mec.test");
Classone co = bf.getBean(Classone.class);
Classone cp = bf.getBean(Classone.class);
System.out.println(co);
System.out.println(cp);
}
/*输出结果:
成员[complex]以完成注入
成员[point]以完成注入
Classone [complex=(0.0,0.0), str=null,Point [a=0, b=0]]
Classone [complex=(0.0,0.0), str=null,Point [a=0, b=0]]*/
顺便对我们的异常处理进行测试,我们将Point类上的标记去掉,让map中不会存在被标记了Autowired的待注入成员point:
public static void main(String[] args) {
BeanFactory bf = new BeanFactory();
BeanFactory.scanpackage("com.mec.test");
Classone co = bf.getBean(Classone.class);
System.out.println(co);
}
/*输出结果:
Exception in thread "main" com.mec.spring.core.StopException: 类{com.mec.test.Classone}中成员{point}在map中没有对应的值
at com.mec.spring.core.BeanFactory.injectPro(BeanFactory.java:77)
at com.mec.spring.core.BeanFactory.getBean(BeanFactory.java:102)
at com.mec.spring.core.BeanFactory.getBean(BeanFactory.java:110)
at com.mec.test.text.main(text.java:13)*/
可见我们的异常处理也没有问题。
标签:bd,map,java,BeanFactory,成员,SpringFramework,klass,注入 来源: https://blog.csdn.net/weixin_44938441/article/details/101438938