core
Spring Core是Spring框架的基础API核心模块,提供了基本的IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)功能。
core核心功能举例
资源管理-系统资源加载
FileSystemResource是Spring框架中的一个实现了Resource接口的类,用于从文件系统中加载资源。它可以根据指定的文件路径来加载文件系统中的资源。
当你使用FileSystemResource加载资源时,你需要提供一个文件路径,它指向文件系统中的实际文件。FileSystemResource将会根据提供的路径去访问文件系统并加载相应的资源。
Bean
bean核心功能举例-IOC容器/DI依赖注入
Spring Core提供了一个容器,也称为应用上下文(Application Context),它负责管理和装配应用程序中的对象。它实现了控制反转(IoC),即框架负责创建和管理对象的生命周期,而不是由开发人员手动创建和管理。同时还通过依赖注入管理对象间的依赖关系。对应的接口为:BeanFactory。
DI 依赖注入
BeanFactory通过Autowired实现依赖注入,AutowiredAnnotationBeanPostProcessor是@Autowire注解的实现类,它是一个后置处理器,用于在Bean实例化后对标记了@Autowired注解的字段、构造函数或方法进行依赖注入。它会扫描Bean中的@Autowired注解,解析注解所标记的依赖关系,并将相应的依赖对象注入到对应的位置。processInjection()方法是在Spring框架中用于执行依赖注入的方法。
//缓存已检查过的成员变量private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));//缓存类的候选构造函数private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);//缓存类的依赖注入元数据private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);public void processInjection(Object bean) throws BeanCreationException {Class<?> clazz = bean.getClass();//查找Bean中的包含@Autowire注解的字段、构造方法、方法InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);try {metadata.inject(bean, null, null);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex);}}//遍历方法实现private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {// Fall back to class name as cache key, for backwards compatibility with custom callers.String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// Quick check on the concurrent map first, with minimal locking.InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}metadata = buildAutowiringMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;}
DI 依赖查找
BeanFactory定义了依赖查找的方法。依赖查找是一种主动获取依赖对象的方式,用于获取其他Bean或依赖的实例。通过依赖查找,可以在需要的时候动态的获取所需的依赖对象,并在代码中使用。
AOP
面向切面编程(Aspect-Oriented Programming)是一种编程范式,旨在通过将横切关注点与主要业务逻辑分离,提供一种更好的代码组织和模块化的方式。
AOP的思想是将这些横切关注点从主要业务逻辑中抽离出来,形成一个独立的模块,称为切面。切面可以定义通过预定义的方式或者在运行时动态的与主业务逻辑进行织入,从而实现对横切关注点的统一处理。
AOP核心功能举例-AOP可以做什么
AOP可以将横切关注点从主要业务逻辑中分离出来,使主要业务逻辑更加清晰明了。
AOP可以消除重复的代码。通过使用AOP注解或配置,可以将需要切入的代码逻辑集中到切面中,从而避免在每个地方重复编写相同的代码。当切面需要修改时,只需修改一处逻辑,即可实现全局的变更。
AOP可以降低代码的复杂性。通过将横切关注点与主要业务逻辑分离,使得主要业务逻辑更加专注和清晰,减少了代码的混杂程度,提高了代码的可读性和可维护性。
下面是一个简单的例子,说明 AOP 如何解决日志记录的问题:
假设我们有一个类 UserService,其中包含了一些用户管理的方法,如 createUser()、deleteUser() 等。我们希望在每个方法执行前后记录日志。
传统方法
public class UserService {public void createUser(User user) {Logger.log("Creating user: " + user.getName());// 执行创建用户的逻辑Logger.log("User created: " + user.getName());}public void deleteUser(String userId) {Logger.log("Deleting user: " + userId);// 执行删除用户的逻辑Logger.log("User deleted: " + userId);}
}
使用AOP
public aspect LoggingAspect {before(): execution(public void UserService.*(..)) {Logger.log("Method execution started: " + thisJoinPoint.getSignature().getName());}after(): execution(public void UserService.*(..)) {Logger.log("Method execution completed: " + thisJoinPoint.getSignature().getName());}
}
public class UserService {public void createUser(User user) {// 执行创建用户的逻辑}public void deleteUser(String userId) {// 执行删除用户的逻辑}
}
Context
事件驱动、注解驱动、模块驱动等。
Context核心功能举例
private final Set<Integer> registriesPostProcessed = new HashSet<>();private final Set<Integer> factoriesPostProcessed = new HashSet<>();/*** Prepare the Configuration classes for servicing bean requests at runtime* by replacing them with CGLIB-enhanced subclasses.*/@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {int factoryId = System.identityHashCode(beanFactory);if (this.factoriesPostProcessed.contains(factoryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);}this.factoriesPostProcessed.add(factoryId);if (!this.registriesPostProcessed.contains(factoryId)) {// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);}enhanceConfigurationClasses(beanFactory);beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));}
入参:BeanDefinitionRegistry接口定义了对Bean定义的注册和管理操作,用于注册、存储、管理Bean定义。
- 通过该方法,可以扫描和解析@Configuration注解,将其转化为相应的Bean定义。
- 通过该方法,可以处理@Bean注解,在解析@Configuration类时,processConfigBeanDefinitions()会处理其中使用@Bean注解的方法,创建对应的Bean定义,并将其注册到传入的BeanDefinitionRegistry对象中。
- 处理其他注解相关的功能:除了@Bean注解外,processConfigBeanDefinitions()方法还会处理其他与注解相关的功能。例如,它会处理@Autowired注解、@Value注解、@Conditional注解等,以实现依赖注入、属性注入、条件化配置等功能。
- 注册额外的Bean定义:在处理配置类和注解相关功能之后,processConfigBeanDefinitions()方法会将生成的Bean定义注册到传入的BeanDefinitionRegistry对象中。这样,这些配置类中定义的Bean就可以在Spring容器中被实例化和管理。
expression
Spring表达式语言模块。
public class SpelTest {public static void main(String[] args) {SpelExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression("'xxx'.concat('yyy')");String value = (String) exp.getValue();System.out.println(value);exp = parser.parseExpression("'xxx'.bytes");byte[] bytes = (byte[]) exp.getValue();exp = parser.parseExpression("'xxx'.bytes.length");Object expValue = exp.getValue();System.out.println("length: " + expValue);UserDto userDto = new UserDto();userDto.setUserName("test");userDto.setRoleNameZh("admin");userDto.setId(1);UserDto userDto2 = new UserDto();userDto2.setUserName(userDto.getUserName());//定义解析器ExpressionParser expressionParser = new SpelExpressionParser();//制定表达式Expression expression = expressionParser.parseExpression("userName");// 2 使用解析器解析表达式,获取对象的属性值String name = (String) expression.getValue(userDto2);//获取解析结果System.out.println(name);//使用解析器解析表达式,获取对象的属性值并进行运算Expression exp2 = expressionParser.parseExpression("userName == 'xxx'");// 3.1 获取解析结果boolean result = exp2.getValue(userDto2, Boolean.class);System.out.println(result);//true}
}