1. 注解类
Spring有三个常用注解分别是@Component、@ComponentScan、@Autowired
1.1 @Component
@Component注解的作用是给要注入SpringIOC容器的类做标记
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String name() default "";
}
1.2 @ComponentScan
@ComponentScan用来扫描指定路径下的类是否包含@Component注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
String basePackages() default "";
}
1.3 @Autowired
@Autowired用来自动给某个属性注入被Component标记的类对象
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
2. ApplicationContext
ApplicationContext因此也称之为Spring上下文。Spring容器负责管理Bean与Bean之间的依赖关系。
这个类需要做两件事情
- 扫描配置信息中指定包下的类
- 实例化扫描到的类
2.1 扫描类信息
首先我们先做扫描类信息的功能,扫描类信息需要做两件事
- 解析配置类获取到我指定的扫描包路径
- 使用获取到的扫描包路径进行文件遍历操作如果是目录则递归进行2否则进入3
- 被Component注解标识的类进行注册
// 1.扫描配置信息中指定包下的类
private void scan(Class<?> configClass) {
// 解析配置类,获取到扫描包路径
String basePackages = this.getBasePackages(configClass);
// 使用扫描包路径进行文件遍历操作
this.doScan(basePackages);
}
2.1.1 解析配置类,获取到扫描包路径
这个就是简单获取配置类中@ComponentScan注解的basePackages值
使用反射机制解析注解的步骤:
- 获取被注解注释的类对象
Class<Annotation> annotationClass = Annotation.class;
- 获取这个类对象中的注解对象
MyAnnotation myAnnotation = annotationClass.getAnnotation(MyAnnotation.class);
- 调用注解对象的方法获取属性值
myAnnotation.num()
第一步不用做因为注解类是作为参数直接传入的,所以我们只需要做后两步
// 解析配置类,获取到扫描包路径
private String getBasePackages(Class<?> configClass) {
// 获取这个类对象中的注解对象
ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
// 调用注解对象的方法获取属性值
String basePackages = componentScan.basePackages();
return basePackages;
}
2.1.2 使用扫描包路径进行文件遍历操作
// 使用扫描包路径进行文件遍历操作
private void doScan(String basePackages) {
// 将.转化为/
String[] split = basePackages.split("\\.");
String filePath = System.getProperty("user.dir") + "/src/" + split[0];
for (int i = 1; i < split.length; i++) {
filePath += "/" + split[i];
}
// 获取资源信息
File dir = new File(filePath);
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
// 递归扫描
doScan(basePackages + "." + file.getName());
}
else {
// com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
String className = basePackages + "." + file.getName().replace(".java", "");
// 将class存放到classMap中
this.registerClass(className);
}
}
}
2.1.3 注册含有Component注解的类
我们需要注册也就是存储含有Component的类,这里为了保证线程安全采用了ConcurrentHashMap
步骤:
- 使用类加载器通过类名加载类信息
- 获取这个类的Component注解
- 如果存在Component注解则将其放入类信息池中
// 如果该类含有Component注解那么就将其加入到classMap中
private void registerClass(String className){
// 使用类加载器通过类名加载类信息
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
Class<?> clazz = classLoader.loadClass(className);
// 获取这个类的Component注解
Component component = clazz.getAnnotation(Component.class);
// 如果存在Component注解则将其放入类信息池中
if (component != null) {
classPool.put(className,clazz);
}
} catch (ClassNotFoundException e) {
System.out.println("类加载失败");
e.printStackTrace();
}
}
2.2 实例化扫描到的类
首先我们要用一个ConcurrentHashMap来存储bean对象
// 定义一个线程安全的类对象池
private Map<String, Object> beanPool = new ConcurrentHashMap<>();
流程图:
2.2.1 实例化扫描到的类
// 2. 实例化扫描到的类
private void instantiateBean() {
// 将classPool中的所有值都实例化,并获取
for (Class<?> clazz : classPool.values()) {
getBean(clazz);
}
}
2.2.2 获取bean对象
private Object getBean(Class<?> clazz) {
// 先从缓存中获取
Object bean = beanPool.get(clazz.getName());
if (bean != null) return bean;
// 如果没有则创建
return createBean(clazz);
}
2.2.3 创建bean对象
// 创建bean对象
private Object createBean(Class<?> clazz) {
// 1. 实例化bean
Object bean = this.newInstance(clazz);
// 2. 填充字段,将含有Autowired注解的字段注入bean对象
try {
this.populateBean(bean, clazz);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 3. 实例好并且注入完的bean放入beanPool中
beanPool.put(clazz.getName(),bean);
return bean;
}
2.2.4 实例化bean
private Object newInstance(Class<?> clazz) {
Object bean = null;
try {
bean = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
} catch (IllegalAccessException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
} catch (InvocationTargetException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
}
return bean;
}
2.2.5 填充字段,将含有Autowired注解的字段注入bean对象
// 填充字段,将含有Autowired注解的字段注入bean对象
private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {
// 解析class信息,判断类中是否有需要进行依赖注入的字段
Field[] fields = clazz.getDeclaredFields();
// 遍历每一个属性
for (Field field : fields) {
// 获取属性的Autowired注解
Autowired autowired = field.getAnnotation(Autowired.class);
// 如果有Autowired这个注解
if (autowired != null) {
// 解析这个属性对应的类
Object value = resolveBean(field.getType());
// 爆破,因为属性可能是private
field.setAccessible(true);
// 将value值保存到bean对象的属性中
field.set(bean,value);
}
}
}
2.2.6 解析bean用来实现声明接口时注入其子类对象
// 解析bean用来实现声明接口时注入其子类对象
private Object resolveBean(Class<?> clazz){
// 判断clazz是不是一个接口
if (clazz.isInterface()) {
// 遍历classPool
for (Map.Entry<String,Class<?>> entry : classPool.entrySet()) {
// 如果clazz对应的类是entry对应的类的父类或接口
if (clazz.isAssignableFrom(entry.getValue())) {
return getBean(entry.getValue());
}
}
throw new RuntimeException("找不到可以注入的bean");
}
else {
return getBean(clazz);
}
}
2.3 组合
public class ApplicationContext {
// 定义一个线程安全的类信息池
private Map<String, Class<?>> classPool = new ConcurrentHashMap<>();
// 定义一个线程安全的类对象池
private Map<String, Object> beanPool = new ConcurrentHashMap<>();
public ApplicationContext(Class<?> configClass) {
// 1.扫描配置信息中指定包下的类
this.scan(configClass);
// 2.实例化扫描到的类
this.instantiateBean();
}
// 1.扫描配置信息中指定包下的类
private void scan(Class<?> configClass) {
// 解析配置类,获取到扫描包路径
String basePackages = this.getBasePackages(configClass);
// 使用扫描包路径进行文件遍历操作
this.doScan(basePackages);
}
// 解析配置类,获取到扫描包路径
private String getBasePackages(Class<?> configClass) {
// 获取这个类对象中的注解对象
ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
// 调用注解对象的方法获取属性值
String basePackages = componentScan.basePackages();
return basePackages;
}
// 使用扫描包路径进行文件遍历操作
private void doScan(String basePackages) {
// 将.转化为/
String[] split = basePackages.split("\\.");
String filePath = System.getProperty("user.dir") + "/src/" + split[0];
for (int i = 1; i < split.length; i++) {
filePath += "/" + split[i];
}
// 获取资源信息
File dir = new File(filePath);
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
// 递归扫描
doScan(basePackages + "." + file.getName());
}
else {
// com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
String className = basePackages + "." + file.getName().replace(".java", "");
// 将class存放到classMap中
this.registerClass(className);
}
}
}
// 如果该类含有Component注解那么就将其加入到classMap中
private void registerClass(String className){
// 使用类加载器通过类名加载类信息
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
Class<?> clazz = classLoader.loadClass(className);
// 获取这个类的Component注解
Component component = clazz.getAnnotation(Component.class);
// 如果存在Component注解则将其放入类信息池中
if (component != null) {
classPool.put(className,clazz);
}
} catch (ClassNotFoundException e) {
System.out.println("类加载失败");
e.printStackTrace();
}
}
// 2. 实例化扫描到的类
private void instantiateBean() {
// 将classPool中的所有值都实例化,并获取
for (Class<?> clazz : classPool.values()) {
getBean(clazz);
}
}
// 获取bean对象
private Object getBean(Class<?> clazz) {
// 先从缓存中获取
Object bean = beanPool.get(clazz.getName());
if (bean != null) return bean;
// 如果没有则创建
return createBean(clazz);
}
// 创建bean对象
private Object createBean(Class<?> clazz) {
// 1. 实例化bean
Object bean = this.newInstance(clazz);
// 2. 填充字段,将含有Autowired注解的字段注入bean对象
try {
this.populateBean(bean, clazz);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 3. 实例好并且注入完的bean放入beanPool中
beanPool.put(clazz.getName(),bean);
return bean;
}
// 实例化bean
private Object newInstance(Class<?> clazz) {
Object bean = null;
try {
bean = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
} catch (IllegalAccessException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
} catch (InvocationTargetException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("bean实例化失败");
e.printStackTrace();
}
return bean;
}
// 填充字段,将含有Autowired注解的字段注入bean对象
private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {
// 解析class信息,判断类中是否有需要进行依赖注入的字段
Field[] fields = clazz.getDeclaredFields();
// 遍历每一个属性
for (Field field : fields) {
// 获取属性的Autowired注解
Autowired autowired = field.getAnnotation(Autowired.class);
// 如果有Autowired这个注解
if (autowired != null) {
// 解析这个属性对应的类
Object value = resolveBean(field.getType());
// 爆破,因为属性可能是private
field.setAccessible(true);
// 将value值保存到bean对象的属性中
field.set(bean,value);
}
}
}
// 解析bean用来实现声明接口时注入其子类对象
private Object resolveBean(Class<?> clazz){
// 判断clazz是不是一个接口
if (clazz.isInterface()) {
// 遍历classPool
for (Map.Entry<String,Class<?>> entry : classPool.entrySet()) {
// 如果clazz对应的类是entry对应的类的父类或接口
if (clazz.isAssignableFrom(entry.getValue())) {
return getBean(entry.getValue());
}
}
throw new RuntimeException("找不到可以注入的bean");
}
else {
return getBean(clazz);
}
}
}