项目描述
项目描述:
- 手写Spring启动以及扫描流程
- 手下getBean()流程
- 手写Bean生命周期流程
- 手写依赖注入流程
- 手写BeanPOSTProcessor机制
- 手写AOP机制
0. Spring创建工厂的两种方式
1. 手写Spring前准备工作
注意:下面有些代码有的只是简单写了一下大致流程,具体细节会在之后实现。
Spring实现包中
Component注解
@Retention(RetentionPolicy.RUNTIME) // 表示在.class被装载时将被读取,在程序运行期间,将一直保留。
@Target(ElementType.TYPE) // 表示这个注解只能写在类上
public @interface Component {
String value() default ""; // 表示beanName
}
ComponentScan注解
@Retention(RetentionPolicy.RUNTIME) // 表示在.class被装载时将被读取,在程序运行期间,将一直保留。
@Target(ElementType.TYPE) // 表示这个注解只能写在类上
public @interface ComponentScan {
String value(); // 用来让用户指定扫描路径,默认值为空
}
ZhouyuApplicationContext类
public class ZhouyuApplicationContext {
private Class configClass;
public ZhouyuApplicationContext(Class configClass) {
this.configClass = configClass;
// 解析配置类
// 解析ComponentScan注解 ---> 扫描路径 ---> 扫描
}
public Object getBean(String beanName){
return null;
}
}
zhouyu使用包中
UserService类
@Component("userService")
public class UserService {
}
AppConfig类
@ComponentScan("com.zhouyu.service") // 扫描哪个包
public class AppConfig {
}
Test类
public class Test {
public static void main(String[] args) {
ZhouyuApplicationContext applicationContext = new ZhouyuApplicationContext(AppConfig.class);
Object userService = applicationContext.getBean("userService");
}
}
2. 启动和扫描逻辑具体实现
BeanDefinition类:
public class BeanDefinition {
private Class clazz; // 表示当前某一个bean的类型
private String scope; // 当前bean的作用域(单例还是多例)
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
Scope类:
@Retention(RetentionPolicy.RUNTIME) // 表示在.class被装载时将被读取,在程序运行期间,将一直保留。
@Target(ElementType.TYPE) // 表示这个注解只能写在类上
public @interface Scope {
String value(); // 用来让用户指定扫描路径,默认值为空
}
ZhouyuApplicationContext类
public class ZhouyuApplicationContext {
private Class configClass;
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>(); // 单例池
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public ZhouyuApplicationContext(Class configClass) {
this.configClass = configClass;
// 解析配置类
// 解析ComponentScan注解 ---> 扫描路径 ---> 扫描 ---> Beandefinition ---> BeanDefinitionMap
scan(configClass); // 扫描
// 对于单例bean在容器启动的时候就应该创建好
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
BeanDefinition beanDefinition = entry.getValue();
if(beanDefinition.getScope().equals("singleton")){
Object bean = createBean(beanDefinition); // 单例Bean
singletonObjects.put(beanName, bean);
}
}
}
public Object createBean(BeanDefinition beanDefinition){
// 创建bean
Class clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance(); // 通过反射调用无参的构造方法得到一个对象
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
private void scan(Class configClass) {
//获取ComponentScan注解
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value(); // 扫描路径 com.zhouyu.service
path = path.replace(".", "/"); // 将 . 替换为 /
// 扫描
// 三种类加载器
// Bootstrap --->jre/lib
// Ext --->jre/ext/lib
// App --->classpath 其实就是target中的classes目录
ClassLoader classLoader = ZhouyuApplicationContext.class.getClassLoader(); // App类加载器
URL resource = classLoader.getResource(path);// 相对路径,相对的就是classpath路径
File file = new File(resource.getFile()); // 将resource转成file
if(file.isDirectory()){ // 判断当前file是不是一个目录
File[] files = file.listFiles(); // 将目录下的所有文件拿出来
for (File f : files) {
String fileName = f.getAbsolutePath(); // 得到的这个路径是绝对路径,我们需要截取其中我们需要的
if(fileName.endsWith(".class")){ // 是类文件的话才处理
// 将路径中不需要的去掉,把 \ 改为 . ,去掉最后的class
// 从com开始截取,截取到.class(不含.class),例如 com\zhouyu\service\UserService
String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
// 将 \ 替换为 . \\ 中第一个\表示将第二个\转义为普通的\
className = className.replace("\\", ".");
try {
Class<?> clazz = classLoader.loadClass(className); // // 加载类\
if (clazz.isAnnotationPresent(Component.class)){ // 判断是否有Component注解
// 表示当前这个类是一个bean
// 解析类,判断当前类是单例bean还是prototype类型bean
// 解析类 --> BeanDefinition
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value(); // 当前这个类的bean名字
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
if(clazz.isAnnotationPresent(Scope.class)){ // 存在Scope注解就获取这个类的Scope注解
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value()); // 有scope注解就设置为注解中的值
} else {
// 没有Scope注解表示当前这个bean是单例的
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String beanName){
if(beanDefinitionMap.containsKey(beanName)){
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(beanDefinition.getScope().equals("singleton")){ // 单例bean
Object o = singletonObjects.get(beanName);
return o;
}else { //多例
// 每次都创建新的bean对象
Object bean = createBean(beanDefinition);
return bean;
}
} else {
// 不存在这个bean的话就抛出空指针
throw new NullPointerException();
}
}
}
实现之后可以测试一下单例bean和多例bean,看是否成功
3. 依赖注入模拟实现
@Autowired注解
@Retention(RetentionPolicy.RUNTIME) // 表示在.class被装载时将被读取,在程序运行期间,将一直保留。
@Target({ElementType.METHOD, ElementType.FIELD}) // 表示这个注解写在方法、属性上
public @interface Autowired {
}
ZhouyuApplicationContext
OrderService
@Component("orderService")
public class OrderService {
}
UserService
@Component("userService")
public class UserService {
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
}
测试类Test
public class Test {
public static void main(String[] args) {
ZhouyuApplicationContext applicationContext = new ZhouyuApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
}
}
4. BeanNameAware接口回调模拟实现
如果我们想要在 UserService 中声明一个成员变量 BeanName,让Spring在启动时候将 UserService 在工厂中的名字传给这个值,我们应该如何实现呢,这就需要用到 BeanNameAware 接口了。
BeanNameAware
public interface BeanNameAware {
void setBeanName(String name);
}
UserService
@Component("userService")
public class UserService implements BeanNameAware {
@Autowired
private OrderService orderService;
private String beanName; // 想要给这个字符串赋这个类在工厂中的名字
@Override
public void setBeanName(String name) {
// Spring 会来调这个方法,把当前bean的名字传给这个方法即name
beanName = name;
}
public void test(){
System.out.println(orderService);
System.out.println(beanName);
}
}
最后我们需要在Spring中在启动时将bean的名字传给setBeanName并且调用setBeanName方法
ZhouyuApplicationContext
还有几处调用createBean的地方需要修改
最后测试一下
5. InitializingBean初始化机制模拟实现
如果想要在初始化的时候专门有一个方法用于处理一些逻辑时,我们应该怎么做呢?
InitializingBean
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
UserService
ZhouyuApplicationContext
测试
6. BeanPostProcessor模拟实现
BeanPostProcessor 是 Spring 对外提供的扩展机制
BeanPostProcessor接口
public interface BeanPostProcessor {
// 初始化前执行这个方法
Object postProcessBeforeInitialization(Object bean, String beanName);
// 初始化后执行这个方法
Object postProcessAfterInitialization(Object bean, String beanName);
}
我们如何使用 BeanPostProcessor 呢?
我们新创建一个类,然后让其实现 BeanPostProcessor接口,实现其中的两个方法,一个在初始化bean前调用,一个在初始化bean后调用,在其中写我们自己想要写的逻辑。
/**
* 加上 @Component 注解,在spring中注册
* 扫描时发现这个类实现了 BeanPostProcessor 接口,Spring 就知道了当前这个bean是一个比较特殊的bean
* 和UserService不一样,Spring 等会还要来调用这个方法的
*/
@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {
// spring在创建任何bean的时候都会执行下面的两个方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 初始化前调用这个方法
// 针对一个bean或者多个bean同时处理都行
if(beanName.equals("userService")){
System.out.println("初始化前");
((UserService)bean).setName("周瑜好帅");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 初始化后调用这个方法
System.out.println("初始化后");
return bean;
}
}
那么在 Spring 中我们如何实现呢?
ZhouyuApplicationContext
测试
7. AOP模拟实现
AOP 其实就是利用 BeanPostProcessor 实现的,并且它就是在初始化后这一步实现的。
UserService
测试