上一篇讲了生成的Bean这样放在Map容器里已经属性的注入过程,这一篇中将讲到Class怎么被扫描以及怎么被实例化的。
对于web工程而言,要扫描的class有两个来源:
1、项目中写的class,放在classpath路径下
2、class被打包成jar,放在classpath路径或者classpath的子路径下,如:lib等。
对于要扫描的路径,windows和Linux、MacOS有些不同,所以对windows特殊处理一下,ScanPackageInitialization中主要是init方法。
public void init() throws Exception {
logger.info("开始获取需要扫描的类");
String classpath = getClass().getResource("/").getPath();
// windows下
if ("\\".equals(File.separator)) {
classpath = classpath.substring(1);
}
getCommonClassPaths(classpath);
getJarClassPaths(classpath);
logger.info("获取需要扫描的类结束");
}
init方法中有两个主要方法,先来说getCommonClassPaths,这个方法是获取放在classpath路径下的所有class的全名,包括完整包路径。
private void getCommonClassPaths(String classpath) {
for (String basePackage : SystemVariable.baseScanPackages) {
String searchPath = classpath + basePackage.replace(".", File.separator);
getClassPath(new File(searchPath));//递归获取class的文件路径及文件名
}
for (String filePath : filePaths) {
String relativeClassPath = filePath.substring(classpath.length()).replace(CLASS_SUFFIX, "")
.replace(File.separator, ".");
classPaths.add(relativeClassPath);
}
filePaths = null;// 便于释放内存
}
getJarClassPaths是获取classpath路径及其子路径下所有jar内的class路径
private void getJarClassPaths(String classpath) throws Exception {
getJarPath(new File(classpath).getParentFile());// 获取或有jar
for (String jarPath : jarPaths) {
JarFile jarFile = new JarFile(jarPath);
Enumeration<JarEntry> entrys = jarFile.entries();
while (entrys.hasMoreElements()) {
JarEntry jarEntry = entrys.nextElement();
String file = jarEntry.getName();
if (file.endsWith(CLASS_SUFFIX)) {
for (String basePackage : SystemVariable.baseScanPackages) {
if (file.startsWith(basePackage.replace(".", "/"))) {
String clazz = file.replace(CLASS_SUFFIX, "").replace("/", ".");
classPaths.add(clazz);
}
}
}
}
jarFile.close();
}
}
获取到class的全路径以及名称后,下一步就是加载了,我们需要自定义一个类加载器
public class ClassLoaderUtil {
private static URLClassLoader loader;
static {
try {
URL[] urls = new URL[ScanPackageInitialization.jarPaths.size() + 1];
for (int i = 0; i < ScanPackageInitialization.jarPaths.size(); i++) {
urls[i] = new URL("file", null, ScanPackageInitialization.jarPaths.get(i));
}
urls[urls.length - 1] = new URL("file", null, ClassLoaderUtil.class.getResource("/").getPath());
loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
public static <T> Class<T> loadClass(String className) throws Exception {
return (Class<T>) loader.loadClass(className);
}
}
这里定义一个URLClassLoader,把所有jar包的路径以及classpath路径初始化给它,并指定一个父类加载器,Thread.currentThread().getContextClassLoader()实际上是由web容器生成,根据双亲委托模型,可以防止类被重复加载,双亲委托模型将在后面的博客中说明。
有了类加载器,我们可以把类装载进内存,然后实例化了。类的实例化都写在BeanContainerInitialization中,这里有些代理的对象,后面AOP时会说到。
public void init() throws Exception {
for (String classPath : ScanPackageInitialization.classPaths) {
Class<?> clazz = ClassLoaderUtil.loadClass(classPath);
if (clazz.isAnnotationPresent(TeaDao.class)) {
Object bean = Enhancer.create(clazz, new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
BeanProxy beanProxy = new BeanProxy(obj, args, method, proxy).setInvokeSuper(false)
.setInterfaceExecutor(OrmProxy.getInstance());
addProxy(beanProxy, clazz);// 添加代理类
beanProxy.invoke(beanProxy);
return beanProxy.getResult();
}
});
putBean(clazz, bean);
if (!"".equals(((TeaDao) clazz.getAnnotation(TeaDao.class)).value())) {
BeanContainer.getInstance().putBean(((TeaDao) clazz.getAnnotation(TeaDao.class)).value(), bean);
} else {
BeanContainer.getInstance().putBean(generateBeanName(clazz), bean);
}
} else if (clazz.isAnnotationPresent(Component.class)) {
Object bean = null;
if (isNeedProxy(clazz)) {
bean = Enhancer.create(clazz, new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
BeanProxy beanProxy = new BeanProxy(obj, args, method, proxy).setInvokeSuper(true);
addProxy(beanProxy, clazz);// 添加代理类
beanProxy.invoke(beanProxy);
return beanProxy.getResult();
}
});
} else {
bean = clazz.newInstance();
}
putBean(clazz, bean);
String componentValue = ((Component) clazz.getAnnotation(Component.class)).value();
if (!"".equals(componentValue)) {
BeanContainer.getInstance().putBean(componentValue, bean);
} else {
BeanContainer.getInstance().putBean(generateBeanName(clazz), bean);
}
Namespace namespace = clazz.getAnnotation(Namespace.class);
if (namespace != null) {
NamespaceBeanMapping.putController(namespace.value(), bean);
Method[] methods = bean.getClass().getMethods();
for (Method method : methods) {
if (!method.getDeclaringClass().equals(java.lang.Object.class)) {
NamespaceBeanMapping.putControllerMethod(namespace.value(), method);
}
}
}
}
}
BeanContainer.getInstance().inject();
}
需要被代理的对象,我们对其生成代理对象,对于不需要代理的对象,我们通过反射newInstance生成对象,生成之后有个putBean(clazz, bean)方法,该方法把对象塞进bean容器中。
private void putBean(Class<?> clazz, Object bean) {
BeanContainer.getInstance().putBean(clazz, bean);
Set<Class<?>> set = new HashSet<Class<?>>();
findAllSuperClass(clazz, set);
for (Class<?> superClass : set) {
BeanContainer.getInstance().putBean(superClass, bean);
}
}
putBean有两个映射操作,一个是映射成自身class与实例,另一个映射成父类class与实例,这样就可以实现ByType注入了。
最后,所有的bean放进Map完成,就可以逐个注入属性了,于是调用一下BeanContainer.getInstance().inject()方法就大功告成了。
项目地址:https://git.oschina.net/lxkm/teaframework
博客:https://blog.csdn.net/dong_lxkm