背景
更好的理解框架:通过手写Spring Boot启动类,你将深入了解框架的内部机制和工作原理。你可以手动配置各种组件、定义Bean以及设置各种属性,从而更好地理解框架的运行方式。
过程
2.1自启动实现原理
1、Spring Boot是一个用于构建Java应用程序的开源框架,它简化了Spring应用程序的配置和部署。以下是一个模拟的Spring Boot启动过程的简要描述:
2、加载配置:当应用程序启动时,Spring Boot首先会加载应用程序的配置。这些配置可以包括应用程序的属性、数据库配置、日志配置等。
3、创建应用程序上下文:Spring Boot会创建一个应用程序上下文,该上下文包含了所有被管理的Bean以及它们之间的依赖关系。这个上下文可以让您访问和管理应用程序中的各个组件。
4、扫描和注册Bean:Spring Boot会扫描应用程序中的所有类,查找带有注解的Bean,并将它们注册到应用程序上下文中。常见的注解包括@Component、@Service、@Controller等。
6、自动配置:Spring Boot具有自动配置的特性,它根据应用程序的依赖和配置自动配置各种功能。例如,如果应用程序依赖于数据库,Spring Boot可以自动配置数据源和持久化框架。
7、启动应用程序:一旦所有的Bean都注册到应用程序上下文中,Spring Boot会启动应用程序,开始监听HTTP请求或其他入口点。
8、运行应用程序:应用程序开始运行,处理来自客户端的请求。根据应用程序的配置,Spring Boot可以处理路由、控制器、服务等,以满足客户端的需求。
2.2手动实现SpringBoot自启动
2.2.1宏观
2.2.1微观
2.2.1.1三个服务之间调用
@MyComponent("classService") public class ClassService { @MyAutowired private TestService testService; public void test(){ this.testService.test(); } }
@MyComponent("testService") public class TestService { @MyAutowired private UserService userService; private String name; public void test(){ userService.test(); } }
@MyComponent("userService") public class UserService { // @MyAutowired // ClassService classService; public void test(){ // classService.test(); System.out.println("你好"); } }
2.2.1.2自定义注解
MyComponent :装配注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyComponent { String value(); }
MyAutowired :属性注入的注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface MyAutowired { }
MyComponentScan :扫包的注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyComponentScan { String value(); }
2.2.1.1业务组装
BeanDefinition:对对象的信息进行包装
public class BeanDefinition { private Class beanClass; public Class getBeanClass() { return beanClass; } public void setBeanClass(Class beanClass) { this.beanClass = beanClass; } }
上下文类:
public class MyApplicationContext { private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>(); private Map<String,Object> singletonObjects =new HashMap<>(); //1、获取自动配置类相关信息 public void run(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException { //2、扫包 this.scanPackage(configClass); //4、属性注入 initAutowired(); } private void scanPackage(Class configClass) throws ClassNotFoundException { MyComponentScan myComponentScan = (MyComponentScan) configClass.getAnnotation(MyComponentScan.class); String packagePath = myComponentScan.value(); packagePath = packagePath.replace(".","/"); //拿到service包中所有的类的class对象 List<Class> beanClass = getBeanClass(packagePath, configClass); for (Class aClass : beanClass) { //3、创建bean对象 if (aClass.isAnnotationPresent(MyComponent.class)){ BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setBeanClass(aClass); MyComponent myComponent = (MyComponent) aClass.getAnnotation(MyComponent.class); String beanName = myComponent.value(); //将创建的bean对象放到map中 beanDefinitionMap.put(beanName,beanDefinition); } } } private List<Class> getBeanClass(String packagePath,Class configClass) throws ClassNotFoundException { List<Class> beanClassList = new ArrayList<>(); URL resource = configClass.getClassLoader().getResource(packagePath); File file = new File(resource.getFile()); File[] files = file.listFiles(); for (File f : files) { String fileName = f.getAbsolutePath(); System.out.println(fileName); fileName = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class")); fileName = fileName.replace("\\","."); Class clazz = Class.forName(fileName); beanClassList.add(clazz); } return beanClassList; } //将注入的属性进行初始化 private void initAutowired() throws InstantiationException, IllegalAccessException { for (String beanName : beanDefinitionMap.keySet()) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); Class beanClass = beanDefinition.getBeanClass(); Object instance = beanClass.newInstance(); //TestService //读取每个类中的属性 Field[] declaredFields = beanClass.getDeclaredFields(); for (Field field : declaredFields) { if (field.isAnnotationPresent(MyAutowired.class)){ field.setAccessible(true); String fieldName = field.getName(); BeanDefinition definition = beanDefinitionMap.get(fieldName); Object newInstance = definition.getBeanClass().newInstance(); field.set(instance,newInstance); } } //属性注入的对象放到另外一个容器里边 singletonObjects.put(beanName,instance); } } public Object getBean(String beanName){ return singletonObjects.get(beanName); } }
2.2.1.3启动类
@MyComponentScan("com.example.demo.service") public class DemoApplication { public static void main(String[] args) throws Exception { MyApplicationContext myApplicationContext = new MyApplicationContext(); myApplicationContext.run(DemoApplication.class); Object testService = myApplicationContext.getBean("classService"); Method method = testService.getClass().getMethod("test"); method.invoke(testService); } }
升华
自定义注解:
通过自定义注解(@MyComponent和@MyAutowired),你可以标识需要被Spring容器管理的组件,以及需要进行属性注入的依赖关系。这样可以实现基于注解的组件装配和依赖注入,简化了配置和编码工作。
手动装配组件:
在MyApplicationContext类中,你手动进行了组件的扫描、创建和属性注入过程。这样可以实现自定义的组件装配逻辑,灵活控制组件的创建和初始化过程。
简化启动过程:
通过MyApplicationContext类的run()方法,你可以一次性完成组件的扫描、创建和属性注入等启动过程。这样可以简化启动代码,减少了手动配置的工作量。
自动化注入依赖:
在MyApplicationContext类的initAutowired()方法中,你实现了自动注入属性依赖的逻辑。当一个组件需要依赖其他组件时,通过@MyAutowired注解,你可以自动将依赖的实例注入到目标组件中。这样可以减少手动编写属性注入的代码,提高开发效率。
简化启动类:
在启动类DemoApplication中,你只需实例化MyApplicationContext并调用run()方法即可完成启动过程。这样可以将启动类的逻辑简化为几行代码,提高了可读性和维护性。
这个示例是一个简化版的Spring Boot启动器,并没有涵盖完整的Spring Boot功能和特性。实际的Spring Boot启动器通常会涉及更复杂的配置和功能,例如自动配置、条件化装配等。手写Spring Boot启动器可以让你更深入地理解Spring Boot的原理和机制,并根据实际需求进行定制和扩展。
如果要解决各个Service之间互相依赖的问题又该如何进行解决呢,是不是就要引入三级缓存呢?请大家思考。