上一部分简单演示使用xml配置下spring对bean的管理,这回不用xml配置,直接在java代码中使用注解,spring又是如何管理bean的?首先在Demo类的头部增加@Component注解,代码如下:
import org.springframework.stereotype.Component;
@Component(value="demo")
public class Demo {
//这里略去部分代码
}
其次,修改原先的xml配置“<bean id="demo" class="com.jvk.ken.spring.Demo" />”为“<context:component-scan base-package="com.jvk.ken.spring" />”,其它部分不作修改,运行测试代码,可知两种配置形式达到的效果完全一致。那spring是如何通过注释把javabean管理起来的呢?这里写一个简单的类来模拟spring的工厂类,代码如下:
package com.jvk.ken.spring;
import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Component;
public class MyBeanFactory {
// 保存bean的定义
Map<String, Class> beans = new HashMap<String, Class>();
public Object getBean(String id) throws Exception {
Class class1 = beans.get(id);
Constructor declaredConstructor = class1.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
return declaredConstructor.newInstance();
}
private String xmlFile;
public MyBeanFactory(String xmlFile) throws Exception {
super();
this.xmlFile = xmlFile;
init();
}
private void init() throws Exception {
// 初始化与解析XML,这里略去实际解析XML的情况,使用硬编码模仿
System.out.println("配置文件:" + xmlFile);
String basePkg = "com.jvk.ken.spring";
System.out.println("根据配置,描述注解的包为" + basePkg);
Enumeration<URL> resources = Thread.currentThread()
.getContextClassLoader()
.getResources(basePkg.replace('.', '/'));
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上,这里不考虑jar包或其它形式的引用
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 获取包下的所有class文件,这里不考虑目录递归查找
File[] classFiles = new File(filePath)
.listFiles(new FileFilter() {
public boolean accept(File file) {
return file.getName().endsWith(".class");
}
});
for (File f : classFiles) {
String filename = f.getName();
String className = filename.substring(0,
filename.length() - 6);
Class<?> loadClass = this.getClass().getClassLoader()
.loadClass(basePkg + '.' + className);
Annotation[] annotations = loadClass.getAnnotations();
for (Annotation a : annotations) {
if (a instanceof Component) {
String beanId = ((Component) a).value();
beans.put(beanId, loadClass);
break;
}
}
}
}
}
}
}
运行下面的测试代码,可以看到MyBeanFactory确实达到spring的类似的效果。
MyBeanFactory bf = new MyBeanFactory("applicationContext.xml");
Demo bean = (Demo) bf.getBean("demo");
System.out.println(bean.getClass());
bean.printName();
至此,已经演示完spring为我们创建bean的基本原理了。但spring能做到的远比演示的内容要多,比如通过构造方法注入初始值,通过setter方法注入字面值或其它bean实例,还可以为bean的方法增加前置拦截方法,后置拦截方法和异常拦截方法等等。这些神奇的功能spring又是怎样做到的?下一篇将分析这些内容,特别解构一下spring的声明式事务有什么秘密。