介绍
Spring框架是一个庞大且复杂的Java应用开发框架,其源码包含了众多核心功能和设计模式的实现。以下是Spring源码的简单介绍,涵盖其核心模块和一些关键概念:
核心模块与关键概念
1. Spring Core(核心模块)
Spring Core是Spring框架的基础,提供了依赖注入(DI)和控制反转(IoC)的核心功能。
-
BeanFactory
:Spring容器的基础接口,用于创建和管理Bean。 -
ApplicationContext
:继承自BeanFactory
,提供了更多高级功能,如事件传播、国际化消息支持等。 -
BeanDefinition
:描述Bean的元信息,包括类名、作用域、初始化方法等。
2. Spring AOP(面向切面编程)
Spring AOP允许开发者将横切关注点(如日志记录、事务管理)与业务逻辑分离。
-
ProxyFactoryBean
:用于创建代理对象,是AOP的核心实现之一。 -
AspectJ
:与Spring AOP集成,用于定义切面和增强逻辑。
3. Spring MVC
Spring MVC是Spring框架中的Web模块,用于构建Web应用程序。
-
DispatcherServlet
:Spring MVC的核心,负责将HTTP请求分发到合适的处理器。 -
@Controller
、@RequestMapping
:用于定义控制器和请求映射。
4. Spring事务管理
Spring提供了声明式和编程式事务管理。
-
@Transactional
:声明式事务的核心注解,用于标注事务方法。 -
事务传播行为和隔离级别:定义事务的传播方式和隔离级别。
5. Spring Boot
Spring Boot简化了Spring应用的开发,提供了自动配置和快速启动的能力。
-
@SpringBootApplication
:Spring Boot应用的入口注解,集成了@EnableAutoConfiguration
、@ComponentScan
等。 -
@EnableAutoConfiguration
:启用Spring Boot的自动配置。
6. Spring Data
Spring Data简化了数据访问层的开发,支持多种数据存储方式。
-
Repository
、CrudRepository
:定义了数据访问的基本接口。
设计模式的应用
Spring源码中广泛使用了多种设计模式,如:
-
工厂模式:
BeanFactory
和ApplicationContext
用于创建和管理Bean。 -
单例模式:Spring中的Bean默认是单例的,通过
DefaultSingletonBeanRegistry
实现。 -
代理模式:AOP的实现依赖于代理模式,如
JdkDynamicAopProxy
。
Bean
singleton | 单例 |
prototype | 每次返回一个新的 |
request | 每次Http请求创建新的 |
session | 同一个session共享一个Bean |
globalSession | Portlet |
生命周期
-
实例化(Instantiation)
-
属性赋值(Populate)
-
初始化(Initialization)
-
销毁(Destruction)
事务隔离级别
无
脏读,幻读,不可重复读
幻读,不可重复读
脏读,不可重复读
完全服从ACID的隔离级别,影响性能
脏读:事务 B 去查询了事务 A 修改过的数据,但是此时事务 A 还没提交,所以事务 A 随时会回滚导致事务 B 再次查询就读不到刚才事务 A 修改的数据了。
幻读:事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读。
不可重复读:事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
Spring IOC的初始化过程
1. 资源加载
-
加载配置文件或注解:Spring IoC容器的启动通常从加载配置信息开始。配置信息可以是XML文件、注解或者Java配置类。
-
如果是基于XML配置,Spring会通过
ClassPathResource
或FileSystemResource
等类加载XML配置文件。 -
如果是基于注解或Java配置,Spring会扫描指定的包路径,查找带有
@Configuration
注解的类。
-
-
创建
BeanDefinitionRegistry
:Spring会创建一个BeanDefinitionRegistry
,用于存储Bean的定义信息。对于XML配置,XmlBeanDefinitionReader
会解析XML文件并注册Bean定义;对于注解配置,ClassPathBeanDefinitionScanner
会扫描类路径并注册Bean定义。
2. 解析Bean定义
-
解析XML配置:如果使用XML配置,Spring会解析
<bean>
标签,提取Bean的类名、属性值、依赖关系等信息,并将其封装为BeanDefinition
对象。 -
解析注解配置:如果使用注解,Spring会扫描类上的注解(如
@Component
、@Service
、@Controller
、@Repository
等),并根据注解信息创建BeanDefinition
对象。同时,还会解析@Autowired
、@Value
等注解,记录依赖关系。 -
注册Bean定义:将解析得到的
BeanDefinition
对象注册到BeanDefinitionRegistry
中,以便后续的Bean实例化和依赖注入。
3. 实例化Bean
-
创建Bean实例:Spring根据
BeanDefinition
中的类信息,通过反射调用无参构造函数创建Bean实例。如果Bean定义中指定了工厂方法,Spring会调用工厂方法来创建Bean实例。 -
处理Aware接口:如果Bean实现了
Aware
接口(如BeanFactoryAware
、ApplicationContextAware
等),Spring会在Bean实例化后,将对应的上下文信息注入到Bean中。
4. 依赖注入
-
注入属性值:Spring根据
BeanDefinition
中记录的属性值信息,通过反射调用set
方法或直接操作字段,将属性值注入到Bean实例中。 -
注入依赖Bean:如果Bean之间存在依赖关系,Spring会根据
BeanDefinition
中记录的依赖信息,调用set
方法或构造函数注入依赖的Bean实例。
5. 初始化Bean
-
调用
@PostConstruct
注解的方法:如果Bean类中存在带有@PostConstruct
注解的方法,Spring会在依赖注入完成后调用该方法。 -
调用
InitializingBean
接口的afterPropertiesSet
方法:如果Bean实现了InitializingBean
接口,Spring会调用afterPropertiesSet
方法。 -
调用
init-method
指定的方法:如果在Bean定义中指定了init-method
属性,Spring会调用该方法。
6. 完成初始化
-
注册到容器:完成初始化后,Bean实例会被注册到Spring IoC容器中,供后续使用。
-
触发
ApplicationEvent
:Spring会触发一些应用事件(如ContextRefreshedEvent
),通知其他组件容器已经初始化完成。
7. 容器销毁
-
调用销毁方法:当Spring IoC容器关闭时,会调用Bean的销毁方法。如果Bean实现了
DisposableBean
接口,会调用destroy
方法;如果指定了destroy-method
属性,会调用该方法。 -
清理资源:Spring会清理容器中的资源,释放内存。
AOP实现原理
Spring AOP(面向切面编程)的实现原理主要基于动态代理技术,通过将横切关注点(如日志记录、事务管理、权限检查等)从业务逻辑中分离出来,实现代码的模块化和可维护性。以下是Spring AOP实现原理的详细解析:
1. 核心概念
-
切面(Aspect):切面是AOP的核心概念,它将横切关注点(如日志记录、事务管理等)封装成独立的模块。
-
切入点(Pointcut):定义了哪些类和方法需要被增强,通常通过表达式语言(如AspectJ表达式)来指定。
-
通知(Advice):定义了具体的增强逻辑,例如在方法执行前、执行后或抛出异常时执行的代码。
-
连接点(Join Point):程序执行过程中的某个特定点,如方法调用或异常抛出。
-
织入(Weaving):将切面逻辑插入到目标对象的过程。
2. 动态代理技术
Spring AOP主要通过动态代理技术实现,动态代理分为两种:
-
JDK动态代理:当目标对象实现了接口时,Spring使用JDK动态代理。通过
java.lang.reflect.Proxy
类动态生成代理类,代理类实现了与目标对象相同的接口。 -
CGLIB代理:如果目标对象没有实现接口,Spring会使用CGLIB库生成目标类的子类作为代理类,覆盖目标方法并插入增强逻辑。
3. 代理对象的创建
Spring AOP的代理对象创建过程如下:
-
启用AOP功能:通过在Spring Boot应用的启动类上添加
@EnableAspectJAutoProxy
注解来开启AOP功能。 -
定义切面:使用
@Aspect
注解标识一个类为切面类,其中可以定义切入点和通知。 -
生成代理对象:Spring根据目标对象是否实现接口选择使用JDK动态代理或CGLIB代理,并创建代理对象。
4. 通知的执行
Spring AOP通过ReflectiveMethodInvocation
类实现责任链模式,管理通知的执行顺序。当方法被调用时,Spring会根据配置的切面信息,依次执行所有匹配的前置、后置、环绕通知,最后调用目标方法。
5. 应用场景
Spring AOP广泛应用于以下场景:
-
日志记录:在方法执行前后记录日志。
-
事务管理:在方法执行前后管理事务。
-
权限检查:在方法执行前进行权限验证。
-
性能监控:记录方法的执行时间。
6. 实现机制
Spring AOP的实现机制可以分为以下几个步骤:
-
解析切面:Spring解析带有
@Aspect
注解的类,提取切入点和通知信息。 -
创建代理对象:根据目标对象是否实现接口,选择JDK动态代理或CGLIB代理。
-
方法拦截:通过动态代理技术拦截方法调用,并根据切面配置执行相应的通知。
-
织入切面:将切面逻辑动态地织入到目标对象的方法中。
动态代理和静态代理
1. 静态代理
静态代理是一种传统的代理方式,代理类和目标类在编译时就已经确定,代理类需要手动实现与目标类相同的接口或继承目标类。
原理
-
目标类(Subject):定义了业务逻辑接口或类。
-
代理类(Proxy):实现与目标类相同的接口或继承目标类,在代理类中调用目标类的方法,并在调用前后添加额外逻辑(如日志记录、权限检查等)。
-
客户端:通过代理类调用目标类的方法,而无需直接调用目标类。
2. 动态代理
动态代理是Spring AOP的核心实现方式。代理类在运行时动态生成,不需要手动编写代理类代码。Spring支持两种动态代理技术:JDK动态代理和CGLIB动态代理。
原理
-
JDK动态代理:
-
使用
java.lang.reflect.Proxy
类动态生成代理类。 -
代理类实现与目标类相同的接口。
-
通过
InvocationHandler
接口处理方法调用,插入额外逻辑。
-
-
CGLIB动态代理:
-
使用CGLIB库动态生成目标类的子类。
-
覆盖目标类的方法,插入额外逻辑。
-
适用于目标类没有实现接口的情况。
-
IOC原理
1. IoC 的核心概念
-
控制反转(IoC):传统的程序中,对象的创建和依赖关系的管理是由代码直接控制的。而在 IoC 中,这些控制权被反转到容器中,由容器负责创建对象和管理对象之间的依赖关系。
-
依赖注入(DI):依赖注入是 IoC 的一种实现方式,通过将依赖对象注入到目标对象中,从而实现对象之间的解耦。
-
容器(Container):Spring 容器是 IoC 的核心,它负责管理对象的生命周期、依赖注入和配置信息。
2. IoC 的实现原理
Spring IoC 的实现主要通过以下步骤完成:
1. 资源加载
-
加载配置文件或注解:Spring 容器的启动通常从加载配置信息开始。配置信息可以是 XML 文件、注解或者 Java 配置类。
-
XML 配置:通过
ClassPathResource
或FileSystemResource
等类加载 XML 配置文件。 -
注解配置:通过
@Configuration
注解标记的类,Spring 会扫描指定的包路径,查找带有@Component
、@Service
、@Controller
、@Repository
等注解的类。 -
Java 配置:通过
@Configuration
注解的类,定义@Bean
方法,返回需要管理的 Bean 实例。
-
2. 解析配置信息
-
解析 XML 配置:如果使用 XML 配置,Spring 会解析
<bean>
标签,提取 Bean 的类名、属性值、依赖关系等信息,并将其封装为BeanDefinition
对象。 -
解析注解配置:如果使用注解,Spring 会扫描类上的注解(如
@Component
、@Service
等),并根据注解信息创建BeanDefinition
对象。同时,还会解析@Autowired
、@Value
等注解,记录依赖关系。 -
注册 BeanDefinition:将解析得到的
BeanDefinition
对象注册到BeanDefinitionRegistry
中,以便后续的 Bean 实例化和依赖注入。
3. Bean 实例化
-
创建 Bean 实例:Spring 根据
BeanDefinition
中的类信息,通过反射调用无参构造函数创建 Bean 实例。如果 Bean 定义中指定了工厂方法,Spring 会调用工厂方法来创建 Bean 实例。 -
处理 Aware 接口:如果 Bean 实现了
Aware
接口(如BeanFactoryAware
、ApplicationContextAware
等),Spring 会在 Bean 实例化后,将对应的上下文信息注入到 Bean 中。
4. 依赖注入
-
注入属性值:Spring 根据
BeanDefinition
中记录的属性值信息,通过反射调用set
方法或直接操作字段,将属性值注入到 Bean 实例中。 -
注入依赖 Bean:如果 Bean 之间存在依赖关系,Spring 会根据
BeanDefinition
中记录的依赖信息,调用set
方法或构造函数注入依赖的 Bean 实例。
5. 初始化 Bean
-
调用
@PostConstruct
注解的方法:如果 Bean 类中存在带有@PostConstruct
注解的方法,Spring 会在依赖注入完成后调用该方法。 -
调用
InitializingBean
接口的afterPropertiesSet
方法:如果 Bean 实现了InitializingBean
接口,Spring 会调用afterPropertiesSet
方法。 -
调用
init-method
指定的方法:如果在 Bean 定义中指定了init-method
属性,Spring 会调用该方法。
6. 完成初始化
-
注册到容器:完成初始化后,Bean 实例会被注册到 Spring IoC 容器中,供后续使用。
-
触发
ApplicationEvent
:Spring 会触发一些应用事件(如ContextRefreshedEvent
),通知其他组件容器已经初始化完成。
7. 容器销毁
-
调用销毁方法:当 Spring IoC 容器关闭时,会调用 Bean 的销毁方法。如果 Bean 实现了
DisposableBean
接口,会调用destroy
方法;如果指定了destroy-method
属性,会调用该方法。 -
清理资源:Spring 会清理容器中的资源,释放内存。
自己怎么实现一个IOC容器
1. 理解 IoC 容器的核心功能
-
注册(Register):将类或对象与一个标识符(如字符串)关联起来,存储在容器中。
-
解析(Resolve):根据标识符从容器中获取对象,并自动处理对象的依赖关系。
2. 实现步骤
(1)定义一个容器类
容器类需要维护一个字典,用于存储注册的类或对象。
(2)实现注册功能
提供一个方法,允许用户将类或实例与一个标识符关联起来。
(3)实现解析功能
根据标识符获取对象,并处理依赖注入。如果对象有依赖关系,需要递归解析依赖。
spring和springboot区别
Spring 和 Spring Boot 是 Java 开发中非常重要的框架,但它们在设计理念、功能和使用方式上存在一些区别。以下是它们的主要区别:
1. 定义和目标
-
Spring:
-
定义:Spring 是一个轻量级的 Java 开发框架,提供了依赖注入(DI)、面向切面编程(AOP)、事务管理、数据访问抽象等功能。
-
目标:Spring 的核心目标是简化 Java 企业级开发,通过提供各种模块(如 Spring Core、Spring MVC、Spring Data 等)来解决开发中的复杂问题。
-
-
Spring Boot:
-
定义:Spring Boot 是基于 Spring 框架的进一步封装,旨在简化 Spring 应用的初始搭建和开发过程。
-
目标:Spring Boot 的核心目标是“约定优于配置”,通过自动配置(Auto-Configuration)和“启动器”(Starter)来减少开发者的配置工作,快速启动和运行 Spring 应用。
-
2. 配置方式
-
Spring:
-
配置复杂:Spring 需要大量的配置,包括 XML 配置文件、注解配置等。开发者需要手动配置各种 Bean、事务管理器、数据源等。
-
灵活性高:虽然配置复杂,但提供了高度的灵活性,开发者可以自定义几乎所有的配置细节。
-
-
Spring Boot:
-
配置简化:Spring Boot 通过“启动器”(如
spring-boot-starter-web
)和自动配置机制,自动配置了许多常见的场景,减少了大量的配置工作。 -
约定优于配置:遵循 Spring Boot 的默认约定,开发者可以快速搭建应用,而无需手动配置。如果需要自定义配置,也可以通过少量的配置文件(如
application.properties
或application.yml
)来实现。
-
3. 项目启动速度
-
Spring:
-
启动速度较慢:由于需要加载大量的配置文件和初始化各种组件,Spring 项目的启动速度相对较慢。
-
-
Spring Boot:
-
启动速度快:Spring Boot 通过自动配置和优化的类加载机制,大幅提高了项目的启动速度。它还提供了“嵌入式”服务器(如 Tomcat、Jetty),无需部署 WAR 文件,进一步简化了开发流程。
-
4. 依赖管理
-
Spring:
-
依赖管理复杂:在 Spring 项目中,开发者需要手动管理依赖,包括版本号和依赖之间的兼容性。
-
-
Spring Boot:
-
依赖管理简化:Spring Boot 提供了“启动器”(Starter),这些启动器预定义了一组依赖,开发者只需引入相应的启动器即可。Spring Boot 还会自动管理这些依赖的版本,确保兼容性。
-
5. 功能和模块
-
Spring:
-
功能丰富:Spring 提供了丰富的功能模块,如 Spring Core、Spring MVC、Spring Data、Spring Security 等。开发者可以根据需要选择和组合这些模块。
-
模块化设计:Spring 的模块化设计使得开发者可以灵活地使用其中的部分模块,而无需引入整个框架。
-
-
Spring Boot:
-
功能集成:Spring Boot 集成了 Spring 的核心功能,并提供了许多额外的特性,如自动配置、嵌入式服务器、健康检查、度量指标等。
-
一站式解决方案:Spring Boot 提供了一站式的解决方案,适合快速开发和部署微服务和小型项目。
-
6. 适用场景
-
Spring:
-
适用场景:适用于大型企业级应用,尤其是那些需要高度定制化和复杂配置的项目。Spring 提供了强大的功能和灵活性,适合对性能和资源管理有严格要求的场景。
-
-
Spring Boot:
-
适用场景:适用于快速开发和部署小型到中型项目,尤其是微服务架构。Spring Boot 的“约定优于配置”理念使得开发和部署变得非常简单,适合敏捷开发和快速迭代。
-
反射
在Java,中反射(Reflection) 是一种强大的机制,允许程序在运行时动态地获取和操作类的信息,包括类的属性、方法、构造函数等。它提供了在运行时检查和操作对象的能力,使得Java代码更加灵活和动态。
1. Java反射的基本概念
Java反射机制主要依赖于java.lang.Class
类以及java.lang.reflect
包中的类和接口,例如Field
、Method
、Constructor
等。
-
Class
类:每个类在Java中都有一个对应的Class
对象,它包含了该类的所有信息。通过Class
对象,可以获取类的名称、父类、接口、字段、方法等信息。 -
Field
类:表示类的成员变量(字段)。 -
Method
类:表示类的方法。 -
Constructor
类:表示类的构造函数。
2. Java反射的主要功能
(1)获取类的Class
对象
-
通过
Class.forName()
方法:java复制
Class<?> clazz = Class.forName("com.example.MyClass");
-
通过对象的
.getClass()
方法:java复制
MyClass obj = new MyClass(); Class<?> clazz = obj.getClass();
-通过 类名的.class
属性:
java复制
Class<?> clazz = MyClass.class;
(2)获取类的构造函数
-
获取所有构造函数:
java复制
Constructor<?>[] constructors = clazz.getConstructors(); // 获取所有公共构造函数 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); // 获取所有构造函数(包括私有)
-
获取指定构造函数:
java复制
Constructor<?> constructor = clazz.getConstructor(String.class, int.class); // 获取公共构造函数 Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class); // 获取指定构造函数(包括私有)
(3)获取类的字段
-
获取所有字段:
java复制
Field[] fields = clazz.getFields(); // 获取所有公共字段 Field[] declaredFields = clazz.getDeclaredFields(); // 获取所有字段(包括私有)
-
获取指定字段:
java复制
Field field = clazz.getField("fieldName"); // 获取公共字段 Field declaredField = clazz.getDeclaredField("fieldName"); // 获取指定字段(包括私有)
(4)获取类的方法
-
获取所有方法:
java复制
Method[] methods = clazz.getMethods(); // 获取所有公共方法(包括父类方法) Method[] declaredMethods = clazz.getDeclaredMethods(); // 获取所有方法(包括私有,但不包括父类方法)
-
获取指定方法:
java复制
Method method = clazz.getMethod("methodName", String.class, int.class); // 获取公共方法 Method declaredMethod = clazz.getDeclaredMethod("methodName", String.class, int.class); // 获取指定方法(包括私有)
(5)通过反射创建对象
-
使用
Constructor
对象:java复制
Constructor<?> constructor = clazz.getConstructor(String.class, int.class); Object obj = constructor.newInstance("example", 123);
-
使用
Class
对象:java复制
Object obj = clazz.newInstance(); // 调用无参构造函数(已弃用,建议使用Constructor.newInstance)
(6)通过反射调用方法
-
调用公共方法:
java复制
Method method = clazz.getMethod("methodName", String.class, int.class); Object result = method.invoke(obj, "example", 123);
-
调用私有方法:
java复制
Method declaredMethod = clazz.getDeclaredMethod("methodName", String.class, int.class); declaredMethod.setAccessible(true); // 打开访问权限 Object result = declaredMethod.invoke(obj, "example", 123);
(7)通过反射访问字段
-
访问公共字段:
java复制
Field field = clazz.getField("fieldName"); Object value = field.get(obj); // 获取字段值 field.set(obj, "newValue"); // 设置字段值
-
访问私有字段:
java复制
Field declaredField = clazz.getDeclaredField("fieldName"); declaredField.setAccessible(true); // 打开访问权限 Object value = declaredField.get(obj); // 获取字段值 declaredField.set(obj, "newValue"); // 设置字段值
3. Java反射的优缺点
优点
-
动态性:可以在运行时动态地获取类的信息,调用方法,访问字段等,使得代码更加灵活。
-
解耦:可以减少代码之间的直接依赖,提高代码的可维护性和可扩展性。
缺点
-
性能开销:反射操作比直接调用代码要慢,因为需要在运行时解析类信息。
-
安全性问题:可以访问和修改私有成员,可能会破坏封装。 性- 可读性差:反射代码通常比普通代码更复杂,可读性较差。
4. Java反射的典型应用场景
-
框架开发:如Spring框架,通过反射动态加载和管理Bean对象。
-
动态代理:实现AOP(面向切面编程)等功能。
-
序列化和反序列化:如JSON、XML等格式的序列化工具,通过反射动态访问对象的字段。
-
插件机制:动态加载和使用插件,无需在编译时确定插件的具体实现。
Java反射是一个非常强大的工具,但需要谨慎使用,以避免性能和安全问题。