尝试以实现一下IoC容器和AOP的方式来学习Spring,以对Spring有更深的理解。
这只是基于当前对Spring的认识写的,实现的还是很粗糙的,感兴趣的同学可以交流一下。
IoC大致类图如下:
IoC类图
仿照SpringBoot的启动方式,在主类里
Context ctx = Application.run(App.class);
启动,run方法点进来,
public static Context run(Class<?> clz) {
return new Application(clz).run();
}
先看构造方法
private Application(Class<?> clz) {
// 扫描包的路径
path = clz.getResource("").getFile();
path = path.replaceAll("%20", " ");// 替换空格
config = new ConfigurationResolver();
packageName = clz.getPackage().getName();
this.clz = clz;
context = new WebContext();
ContextAware.setContext(context);
}
- 获取启动类的路径和包名
- 加载配置文件,就是读取yml文件。
- 初始化上下文context
上下文context主要写在AbstractContext里,注入的对象就直接放在一个叫beans的HashMap里了。
这里就一些bean的add和get方法。
在看run方法:
public Context run() {
Scanner.getInstance().scanBeans(path, packageName);
startServer();
return context;
}
- 扫描并注入对象
- 启动服务
启动服务直接用的是embed tomcat包,主要写在TomcatServer的runService方法里
public void runService() {
Tomcat tomcat = new Tomcat();
tomcat.setPort(port);
String tmpDirPath = System.getProperty("user.dir") + File.separator + WEBAPP_PATH;
org.apache.catalina.Context ctxt = tomcat.addContext(contextPath, tmpDirPath);
Tomcat.addServlet(ctxt, "servlet", new DispatcherServlet());
ctxt.addServletMappingDecoded("/", "servlet");
try {
tomcat.start();
} catch (LifecycleException e) {
e.printStackTrace();
}
tomcat.getServer().await();
}
好我们主要看对象的注入,scanBeans方法里:
public void scanBeans(String basePath, String packageName) {
scanFiles(basePath, packageName);
AnalyzerFactory.getAnalyzer(ClassAnalyzer.class).loadClasses(classNames);
}
- scanFiles获取包下面所有的类名,放在classNames数组里
- 获取类解析器ClassAnalyzer并解析所有类
loadClass点进去:
public void loadClasses(List<String> classNames) {
Class<?> clazz = null;
// 先加载所有类,再逐个解析类的属性和方法
for (String className : classNames) {
try {
clazz = loader.loadClass(className);// 默认初始化
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (clazz == null || clazz.isInterface() || clazz.isAnnotation())
continue;
// load class
analyzeClass(clazz);
}
for (Map.Entry<Class<?>, List<Object>> entry : clzInstanceMap.entrySet()) {
Class clz = entry.getKey();
for (Object o : entry.getValue()) {
analyzeFieldMethod(clz, o);// 现在就写了bean注入解析
}
}
// 解析controller
for (Object o : context.getAll()) {
Class<?> clz = o.getClass();//entry.getKey();
Controller c = clz.getAnnotation(Controller.class);
if(c != null)
MethodMappingResolver.getInstance().resolveController(clz);// 解析controller方法
}
// 解析AOP
AopAnalyzer aopAnalyzer = AnalyzerFactory.getAnalyzer(AopAnalyzer.class);
aopAnalyzer.analyze();
}
先过一遍所有的类,把类上带注解的注入进来,然后过一遍方法,把带@Bean的注入进来。
然后是解析controller和AOP。
analyzeClass方法:
public void analyzeClass(Class<?> clazz) {
Annotation[] classAnnotations = clazz.getAnnotations();
Object instance = null;
boolean inject = false;
String aValue = "";
for (Annotation a : classAnnotations) {
if (a instanceof Component) {
aValue = ((Component) a).value();
inject = true;
} else if (a instanceof Controller) {
aValue = ((Controller) a).value();
inject = true;
}
}
if(inject) {
String name = aValue.equals("") ? firstLetterToLower(clazz.getSimpleName()) : aValue;
instance = newInstance(clazz);
clzInstanceMap.add(clazz, instance);
context.addBean(name, instance);
log.info("--------------" + name + "injected----------------");
}
}
这里其实不应该写判断"a instanceof Controller", 而应该判断a有没有Component注解,待修改。
网友评论