手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】【任务阶段2】
程序框架图
需先实现任务阶段1才能开始实现阶段2,点下面链接跳转
实现任务阶段 1- 编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象
二、实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map
1.在ioc包定义BeanDefinition类,用于封装/记录Bean的信息[1.scope 2.Bean对应的Class对象,反射可以生成对应的对象
package com.hykedu.spring.ioc;
/**
* @author 程序员蛇皮
* @version 1.0
* BeanDefinition 用于封装/记录Bean的信息[1.scope 2.Bean对应的Class对象,反射可以生成对应的对象
*/
@SuppressWarnings("all")
public class BeanDefinition {
private String scope;
private Class clazz;
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "BeanDefinition{" +
"scope='" + scope + '\'' +
", clazz=" + clazz +
'}';
}
}
2.在annotation包定义Scope注解,可以指定bean的作用范围[singleton(单例),prototype(多例)]
package com.hykedu.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 程序员蛇皮
* @version 1.0
* Scope可以指定bean的作用范围[singleton,prototype]
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
String value() default "";
}
3.在pom.xml引入commons-lang包(需要用到里面的StringUtils工具类)
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
4.在CodeSnakeSpringApplicationContext类中定义存放BeanDefinition的容器,并将Bean对象放入其中,同时将其封装成scanBeanDefinition(Class configClass)方法,在构造器中调用
package com.hykedu.spring.ioc;
import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.ComponentScan;
import com.hykedu.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author 程序员蛇皮
* @version 1.0
* SpringApplicationContext 类的作用类似Spring原生ioc容器
*/
@SuppressWarnings("all")
public class CodeSnakeSpringApplicationContext {
private Class configClass;
//定义beanDefinitionMap,存放BeanDefinition对象
private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public CodeSnakeSpringApplicationContext(Class configClass) {
scanBeanDefinition(configClass);
}
//该方法完成的对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入map
private void scanBeanDefinition(Class configClass) {
this.configClass = configClass;
System.out.println(configClass);
//获取要扫描的包
//1.先得到SpringConfig配置的@ComponentScan(value = "com.hykedu.spring.component")
ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2.componentScan的value就是我们要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包:" + path);
//得到要扫描的包下所有的资源(类.class)
//1.得到类的加载器
ClassLoader classLoader = CodeSnakeSpringApplicationContext.class.getClassLoader();
//2.通过类加载器获取到要扫描包的url
path = path.replace(".", "/");//把 . 替换成路径间隔符 /
URL resource = classLoader.getResource(path);
System.out.println("要扫描包的url:" + resource);
//3.将要加载的资源(.class) 路径下的文件进行遍历=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {//pand
File[] files = file.listFiles();
for (File f : files) {
String absolutePath = f.getAbsolutePath();
System.out.println("文件的绝对路径:" + absolutePath);
//这里我们只处理.class文件
if (absolutePath.endsWith(".class")) {
//1.获取到类名
String className = absolutePath.substring
(absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
System.out.println("类名:" + className);
//2.获取类的完整路径(全类名)
String classFullName = path.replace("/", ".") + "." + className;
System.out.println("全类名:" + classFullName);
//3.判断该类是否需要注入容器,判断该类是不是有@Component/@Controller/@Repository/@Service注解
try {
Class<?> clazz = classLoader.loadClass(classFullName);
if (clazz.isAnnotationPresent(Component.class)) {
//如果在注解指定了value,将其赋值给className
System.out.println("这是一个Spring bean" + clazz);
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
if (beanName.equals("")) {
//将类名首字母小写作为beanName
beanName = StringUtils.uncapitalize(className);
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
if (clazz.isAnnotationPresent(Scope.class)) {
//如果配置了Scope,获取他配置的值
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果没有配置值,就默认Singleton
beanDefinition.setScope("singleton");
}
//将beanDefinition放入beanDefinitionMap
beanDefinitionMap.put(beanName, beanDefinition);
} else {
System.out.println("这不是一个Spring bean" + clazz);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
}
我们在AppMain测试方法中进行Debug,Bean对象成功存放至beanDefinitionMap中