文章目录
前言
Spring是一个开源的Java框架,旨在简化企业级Java应用程序的开发。它提供了一个全面的编程和配置模型,可用于构建各种类型的应用程序,包括Web应用程序、企业应用程序、移动应用程序等,在项目的开发中每位研发都会与之打交道。
一、Spring是什么?
Spring是一个大的bean 容器
,管理各种各样的bean 对象,以spirng为基础,衍生处理Spring-mybatis,Spring-mvc ,spring-boot, spring-cloud 等框架;
二、Spring的两大特性:
Spring 的两大特性IOC和AOP 都依赖于Spring容器中各种bean 来实现
,所以想要了解Spring 的特性,就先需要知道Spring 是怎么创建和管理Bean的;
2.1 IOC(控制反转):
控制反转,Spring 帮我们管理各种bean ,我们可以通过注入的方式使用这个bean,而不关系Bean 具体的创建和销毁过程
;
2.1.1 Bean 文件的加载:
Bean 的创建首先要知道需要去创建哪些bean,spring 中可以通过多种方式如xml ,或者通过 properties,yml ,类中通过注解注入等方式来加载需要创建bean 的资源。
通过配置文件 进行bean 的读取,可以定义xml ,yaml ,properties 等文件格式,所以就需要解析不同类型的文件,spring 就通过BeanDefinitionReader 接口
,来扩展实现读取不同的格式文件,在对文件解析后将其解析的结果放入到 beanDefinition
中 ;
2.1.2 Bean的创建:
从beanDefinition 获取需要创建的bean 然后进行遍历,通过改bean 对应的class ,进而获取到对应class 中的构造方法,然后通过反射进行bean 对象的创建
;
2.1.3 Bean的属性注入:
Bean 对象创建后,bean 中的属性都是null 或者初始值,所以需要通过依赖注入
的方式完成属性注入;
2.1.4 Bean 的初始化:
Bean 对象在完成属性注入后,执行初化 方法完成资源的预加载工作;在Bean 初始化之前和之后 spring 扩展了监听的方法postProcessBeforeInitialization和 postProcessAfterInitialization 可以对bean 的创建过程进行干预
;
初始化方法的执行:
2.1.5 Bean 的代理增强:
Bean 初始化完成之后,如果Bean 有AOP 切面,需要为其创建代理对象,然后将代理对象放入的Bean 的单例池中;
2.2 AOP :
Spring 在启动时获取所以定义的AOP切面,并获取到对应类及其对应方法,Bean 在创建完成,如果发现改Bean 需要进行AOP 则为其生成代理类,并将其原有的普通对象以代理对象属性的方式注入到代理类中
。
2.2.1 代码演示:
(1)在springboot 项目中 引入aop 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)定义配置类 MyConfig:
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example.springdabaihua.annotion.bean.service")
public class MyConfig {
}
(3)在对应的ComponentScan 扫描路径下定义Aop切面和测试类:
@Component
public class UserService {
@Resource
private OrderService OrderService;
public void test() {
System.out.println("userService bean finish");
UserService userService1 = MyConfigTest.context.getBean(UserService.class);
this.test1();// 断点查看当前 this对象 和 userService1 的区别
}
private void test1() {
}
}
@Component
public class OrderService {
}
public class MyConfigTest {
public static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = new AnnotationConfigApplicationContext(MyConfig.class);
UserService userService = context.getBean(UserService.class);
System.out.println(userService);
userService.test();
}
}
(4)代码演示结果:
- userService1 是cglib 代理对象;
- userService1 中的OrderService 属性值是null
- userService1 中有个target 属性对应 UserService的普通bean 对象
- UserService的普通bean 对象 中的OrderService 属性是有值的
(5)代码演示结果总结:
spring 发现创建出来的Bean 对象需要进行AOP代理时,会为其生成代理对象,但是代理对象的属性值并没有被赋值,会将改类的普通对象以target 属性注入到代理对象,因为普通对象进行了属性注入,所以普通对象是有值的;
2.2.2 代码演示mysql @Transactional 注解普通对象调用失效的场景:
(1) 在pom 增加mybatis-plus 的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
(2) MyConfig DataSource注入:
package com.example.springdabaihua.annotion.bean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
//@Component
@EnableTransactionManagement
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example.springdabaihua.annotion.bean.service")
public class MyConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3406/testaop?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("ddsoft");
return dataSource;
}
// @Bean
// public JdbcTemplate jdbcTemplate(DataSource dataSource) {
// return new JdbcTemplate(dataSource);
// }
//
//
// @Bean
// public DataSourceTransactionManager transactionManager(DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
(3)测试代码:
@Component
public class ShopService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void testShopService(){
jdbcTemplate.execute("insert into user (name,age) value ('张三',23)");
System.out.println("testShopService 调用");
// "123".substring(8); 测试事务是否生效
a();
}
// 测试事务传播
@Transactional(propagation = Propagation.NEVER)
public void a() {
}
}
public class MyConfigTest {
public static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = new AnnotationConfigApplicationContext(MyConfig.class);
// UserService userService = context.getBean(UserService.class);
// System.out.println(userService);
// userService.test();
ShopService shopService = context.getBean(ShopService.class);
System.out.println(shopService);
shopService.testShopService();
}
}
mysql table:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(30) DEFAULT '' COMMENT '姓名',
`age` int(11) DEFAULT '0' COMMENT '年龄',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';
(4)测试场景:
- 验证事务的传播:
通过直接调用a() ,数据可以正常插入,事务失效,失效的原因,a()方法的调用时通过改类普通对象调用的而不是通过增强的代理对象调用,导致事务失效,如果在改类中重新注入该类的bean 在执行会报错,因为此时使用的shopService 的代理对象:
- 验证事务的回滚和提交:
注释掉a() 方法,注释掉 MyConfig 的 @Configuration 注解
虽然报错了,但是数据也被正常插入到数据库,事务失效原因,在注释掉@Configuration 后, 此时 jdbcTemplate 和 DataSourceTransactionManager 中放入的dataSource() 是两个不同的对象,导致在开启事务后 ,从DataSourceTransactionManager和 jdbcTemplate 获取到的连接不是同一个连接,造成事务失效;通过 @Configuration 注解在调用 dataSource() 方法时, 实际上使用了MyConfig 的代理对象,然后执行dataSource() 方法,此时会先找是否存在dataSource 这个bean 有就直接返回,没有在进行创建,所以开启了 @Configuration 之后获取到的 dataSource bean 是同一个;
(5)测试场景结果总结:
类似于上图中的UserService ,我们知道事务的实现方式是通过Spring 的AOP 切面完成的,也就是说,在执行sql 时,AOP 的方法中 获取jdbc 的连接之后,会先将改连接的自动提交设置为false ,然后执行sql ,如果发生异常则进行rollback 成功则进行commit ; 而目标方法的执行时通过调用 代理对象中 target 属性的普通对象,所以在 目标方法中不管怎么调用其他方法事务都是失效的;
2.3 IOC和 AOP的关系 :
IOC(Inversion of Control)和AOP(Aspect-Oriented Programming)是两种不同的编程概念,它们在某些方面是互补关系。
-
控制反转(IOC):IOC是一种设计原则,它将应用程序的控制权从代码中转移到外部容器中。通过IOC容器,开
发者将对象的创建、依赖注入和生命周期管理交给容器来处理
。IOC的目标是解耦应用程序组件之间的依赖,提高代码的可扩展性和可测试性。 -
面向切面编程(AOP):AOP是一种编程范式,它允许开发者在不修改原始代码的情况下,
关注点定义为切面,并将它们与核心业务逻辑的连接点进行织入
,可以实现横向关注点的复用和模块化。 -
IOC与AOP的关系:IOC和AOP可以结合使用,以实现更灵活和可维护的代码结构。在使用IOC容器(如Spring)的同时,开发者可以通过AOP机制将横切关注点应用到应用程序中的不同连接点上。这样可以将通用的横切关注点(例如日志记录、安全性控制)从核心业务逻辑中解耦,并实现代码的重用和模块化。
-
IOC 中创建的Bean 会判断是否改Bean 有aop 的逻辑,从而会
为其生成代理对象
,然后将代理对象的bean注入到容器中,以便于后续再调用代理对象的方法时可以顺利进入aop 逻辑;
2.4 扩展点 :
2.4.1 接口和抽象类的区别:
除了语法不同外,接口和抽象类的编程方式是不同的:
- 接口:
自上向下,只需要定义方法不关心实现
; - 抽象类类:
自下向上,将共性的东西进行抽离 ,定义共有属性的类对象
;
2.4.2 beanFactory和 beanPost :
-
BeanFactory(Bean工厂):BeanFactory是Spring框架的核心接口,它负责创建和管理应用程序中的所有Bean。BeanFactory是一个IoC容器,它通过读取配置文件或注解等方式,根据配置将Bean实例化并将它们的依赖关系注入到相应的对象中。BeanFactory是Spring的基础架构,提供了IoC和DI功能的核心实现,
bean 操作的根接口,BeanFactory可以访问并修改所有的bean 对象,开发层面基本不使用
。 -
BeanPostProcessor(Bean后置处理器):BeanPostProcessor是Spring框架提供的扩展点之一,它允许用户在Bean实例化、依赖注入和初始化等过程中对Bean进行自定义的处理操作。BeanPostProcessor接口包括两个主要的方法:postProcessBeforeInitialization()在Bean初始化之前被调用,postProcessAfterInitialization()在Bean初始化之后被调用。用户可以通过实现BeanPostProcessor接口来自定义Bean的初始化逻辑、修改Bean的属性或实现其他自定义操作,
开发者可以实现 BeanPostProcessor 干预bean 的创建过程
。 -
BeanFactory是一个根接口,它定义了在应用程序中对Bean进行管理的基本操作,而BeanPostProcessor是一个扩展接口,它允许开发者在Bean的生命周期中插入自定义的处理逻辑。在Spring框架中,BeanFactory被作为Bean的工厂使用,负责创建和管理Bean的生命周期,而BeanPostProcessor用于在Bean的和初始化过程中添加自定义的处理逻辑。
总结
此篇文章只对Spring 的核心原理进行大概的讲解,后续文章会对bean 的推断构造方法,三级缓存,AOP 代理增强,Bean的生命周期进行详细讲解。