SpringBoot源码
效果:加入springBoot注解后,在run方法中传入application.class就能实现自动启动并匹配到对象的controller返回结果。
-
导入Spring依赖
-
定义注解(在自己的应用Apllication加入该注解,作为SpringBoot项目启动),注解上加入@ComponentScan。当应用添加后,相当于添加@ComponentScan注解在自己的类上,就会默认扫描自己包下的所有文件,就可以扫描到controller文件夹下的文件。
-
自定义Run方法,并在其中启动tomcat
-
添加DispatchServlet(SpringMVC),需要接受一个spring容器,因为DispathServlet是在该容器中找对应的Controller.
-
添加Spring容器,需要传入一个配置类
Spring源码
基础准备
使用Spring框架时,用到的创建语句
public class MiaoboApplicationContext {
Class configClass;
public MiaoboApplicationContext(Class configClass){
this.configClass = configClass;
}
public Object getBean(){
return null;
}
}
定义ApplicationContext类,定义需要填入的配置文件属性以及getBean方法
public class MiaoboApplicationContext {
Class configClass;
public MiaoboApplicationContext(Class configClass){
this.configClass = configClass;
}
public Object getBean(){
return null;
}
}
用户的配置类添加@ComponentScan,自定义@ComponentScan,定义属性指定扫描路径
@ComponentScan("com.miaobo.service")
public class AppConfig {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value(); //传入扫描路径的值
}
Service类添加@Component注解表示定义的Bean,自定义@Component
@Component("userService")
public class UserService {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value(); //传入BeanName
}
基础完成后,Application需要在初始化后解析配置类,通过@ComponentScan扫描带有@Component
扫描
获取扫描路径,并通过类加载器得到所有该路径下的文件
Class configClass;
public MiaoboApplicationContext(Class configClass) throws ClassNotFoundException {
this.configClass = configClass;
//配置类注解获取扫描路径
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value();
path = path.replace(".","/");
//获取应用类加载器
ClassLoader configClassLoader = MiaoboApplicationContext.class.getClassLoader();
//加载扫描路径,获取目录。特别注意所有目录不允许空格和中文
URL resource = configClassLoader.getResource(path);//类加载器位于的目录为\Spring-miaobo\target\classes"
File directory = new File(resource.getFile());
//遍历文件,查看是否带有@Component注解
if(directory.isDirectory()){
File[] files = directory.listFiles();
for(File file: files){
System.out.println(file);//获取到扫描路径下的文件地址
}
}
}
在获取到的文件中寻找带有@Component注解的类。即先根据包名得到所有的类,再判断是否有@Component注解。
//遍历文件,查看是否带有@Component注解
if(directory.isDirectory()){
File[] files = directory.listFiles();
for(File file: files){
//绝对路径转为类加载器使用的路径格式
String fileName = file.getAbsolutePath();
if (fileName.endsWith(".class")) {
fileName = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
fileName = fileName.replace("\\", ".");
//使用类加载器加载类
Class<?> clazz = configClassLoader.loadClass(fileName);
//判断是否有@Component注解
if (clazz.isAnnotationPresent(Component.class)) {
System.out.println("yes");
}
}
}
}
实例化Bean(单例/原型)
getBean方法中需要通过传入的BeanName去寻找类的信息(困难),因此在ApplicationContex初始化时就创建每个Bean的BeanDefinition对象(包含类信息、是否单例等)。
HashMap singlePool = new HashMap<String, Object>();//单例池
ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();//放入BeanDefinition
发现@Component后,需要判断是否为原型Bean或单例Bean。添加@Scope(“Prototype”)表示原型Bean。单例就直接创建Bean放入单例池。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value();
}
//判断是否有@Component注解
if (clazz.isAnnotationPresent(Component.class)) {
//获取BeanName
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//判断是否为单例是则创建并返回单例池
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
if (scopeAnnotation.value().equals("prototype")) {
beanDefinition.setScope("prototype");
}
else {
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);