一、Spring概述
- 一个开源免费的框架 , 容器。
- Java SE/EE full-stack(一站式)轻量级开源框架。非侵入式的。
- 控制反转 IoC , 面向切面 Aop。
- 对事物的支持 , 对框架的支持
1.1 两个容器:BeanFactory与ApplicationContext
-
BeanFactory:
- 是ioc的基础容器,提供完整的IoC服务支持。
- 如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。即在需要获取对象时该对象才被创建。
例子:
@org.junit.Test
public void testBean() {
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
System.out.println("我的输出呢");
UserT userT = (UserT) beanFactory.getBean("aliasUserT");
System.out.println(userT);
}
结果:
我的输出呢
UserT无参构造
User{name='东', sex='男'}
-
ApplicationContext:
- ApplicationContext是在BeanFactory的基础上构建的。是相对比较高级的容器实现。
- 继承了BeanFactory接口,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级,比如事件发布、国际化信息支持等。
- ApplicationContext所管理的对象是在容器启动后默认全部初始化。
- 因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。
例子:
public static void main(String[] args) {
// ApplicationContext在容器启动后,默认全部初始化
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("我的输出呢");
User user = (User) context.getBean("aliasUser");
System.out.println(user);
}
结果:
UserT无参构造
我的输出呢
User{name='东', sex='1'}
总结:
注解开发
配置文件中添加:
<!-- 注解扫描包-->
<context:component-scan base-package="com.dong"></context:component-scan>
- @Autowired
由Spring提供,只按照byType注入
- @Resource
由J2EE提供,默认按照byName自动注入
@Resource有两个重要的属性:name和type
二、 IOC和DI
三、AOP
3.1 实现原理:
- SpringAOP底层实现原理就是两种代理模式
- 目标对象如果实现接口,使用JDK代理,如果没有实现接口,使用Cglib代理(创建子类对象)
3.2 基本概念:
- 切点(Point cut):AOP可以提供了一组规则,按照这些规则去切(匹配)连接点,匹配出来的就叫切点(被增强的方法)
- 通知(Advice):增强的逻辑
- 织入(weaving):将通知织入到目标方法的过程
- 切面(Aspect):将通知织入到切点形成切面
切面 = 切点+通知 = 目标方法+增强代码
3.3 通知配置
- 基于xml
<!-- AOP配置-->
<aop:config>
<!-- 配置切点:指定哪些方法被增强
不同包下,用..表示层级
切点表达式配置:应该限制在最小范围
-->
<aop:pointcut id="pc" expression="execution(void com..service.AccountService.*(*))"/>
<!-- 配置切面,配置织入-->
<aop:aspect ref="txManager">
<!-- 在切点方法执行前,执行增强方法start-->
<aop:before pointcut-ref="pc" method="start"/>
<!-- 后置通知,发生异常时不执行-->
<aop:after-returning pointcut-ref="pc" method="commit"/>
<!-- 后置通知,发生异常时-->
<aop:after-throwing pointcut-ref="pc" method="rollback"/>
<!-- 后置通知(最终),发生异常时也要执行-->
<aop:after pointcut-ref="pc" method="clear"/>
</aop:aspect>
<aop:aspect ref="txManager">
<!-- 环绕通知-->
<aop:around method="around" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
- 基于注解
// 注入通知
@Component
@Aspect // 配置切面
public class TxManager {
// 切点
@Pointcut("execution(* com..service.*Service.*(..))")
public void pc(){}
// 使用在方法上,表示该方法为前置通知方法
@Before("pc()")
public void start(){
System.out.println("开启事务");
}
@AfterReturning("pc()")
public void commit(){
System.out.println("提交事务");
}
@AfterThrowing("pc()")
public void rollback(){
System.out.println("回滚事务");
}
@After()
public void clear(){
System.out.println("释放资源");
}
// 环绕通知方法
// @Around()
public void around(ProceedingJoinPoint joinPoint){
try {
// 开启事务
start();
// 执行目标对象的方法 method.invoke(target, args);
joinPoint.proceed();
// 提交事务
commit();
}catch (Throwable e){
// 回滚事务
rollback();
}finally {
// 释放资源
clear();
}
}
}
-
JoinPoint和ProceedingJoinPoint区别
- ProceedingJoinPoint只适用于环绕通知,因为只有环绕通知,才能控制目标方法的运行.
- JoinPoint 适用于其它的四大通知类型,可以用来记录运行的数据。
- ProceedingJoinPoint 中有特殊的方法proceed()。
- 如果使用"JoinPoint" 则必须位于参数的第一位。
-
获取切点的返回值
ProceedingJoinPoint的proceed()
方法 -
获取切点的参数
joinPoint.getArgs()
返回的是Object类型的数组
3.4 切点表达式的演变
execution(int com.ujiuye.service.LoggerServiceImpl.add())
当前切点为add一个方法
execution(int com.ujiuye.service.LoggerServiceImpl.*())
*:表示任意个字符,代指方法名可以是任意的
execution(* com.ujiuye.service.LoggerServiceImpl.*())
*:表示任意个字符,代指返回值类型可以是任意的
execution(* com.ujiuye.service.*ServiceImpl.*())
*:表示任意个字符,代指业务类型可以是任意的
execution(* com..service.*ServiceImpl.*())
..:表示任意层级的目录
execution(* com..service.*ServiceImpl.*(..))
..:表示方法的参数可以任意
execution(* *..*.*()):错误示范
3.5 事务管理
无论哪种配置,都要在配置文件中配置事务管理器和数据源。
- 基于xml配置
①:配置事务管理器(就是一个bean),需要数据源
②:配置事务管理方式,指定给哪些功能(方法)配置事务
③:配置AOP,将事务织入到切点中
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java_0425?useSSL=false"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 具体事务管理方式-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- name:指定目标方法名称-->
<tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 配置AOP-->
<aop:config>
<aop:pointcut id="pc" expression="execution(boolean com.dong.service.AccountServiceImpl.transfer(..))"/>
<!-- 将事务织入到切点中-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
- 基于注解
①:配置事务管理器(就是一个bean),需要数据源
②:spring配置文件中开启注解支持
③:类、接口或者方法上添加@Transactional
代码:
<!-- 开启注解配置-->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional(
transactionManager = "transactionManager",
isolation = Isolation.DEFAULT,
propagation = Propagation.REQUIRED,
readOnly = false
)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public boolean transfer(String fromName, String toName, double money) {
Account account1 = accountDao.queryByName(fromName);
Account account2 = accountDao.queryByName(toName);
account1.setMoney(account1.getMoney() - money);
int i1 = accountDao.updateAccount(account1);
int i = 3 / 0;
account2.setMoney(account2.getMoney() + money);
int i2 = accountDao.updateAccount(account2);
return i1 > 0;
}
}
- 事务的传播行为
- REQUIRED : 支持当前事务。如果当前不存在事务,则创建一个事务。
- SUPPORTS:支持当前事务。如果当前不存在事务,则以非事务方式执行。
- MANDATORY:支持当前事务。如果当前不存在事务,则抛出异常
- REQUIRED_NEW:创建一个新事务。如果当前存在事务,则把当前事务挂起。
- NOT_SUPPORTED:不支持当前事务。如果当前存在事务,则将当前事务挂起。
- NEVER:不支持当前事务。如果当前存在事务,则抛出异常。
- NESTED:支持当前事务,如果当前存在事务,则在当前事务中嵌套一个事务,否则类似于REQUIRED
3.6 整合mybatis
X、bean的生命周期
- 根据Bean的配置情况,实例化一个Bean。
- 根据Spring上下文对实例化的Bean进行依赖注入,即对Bean的属性进行初始化。
- 如果Bean实现了
BeanNameAware
接口,将调用它实现的setBeanName(String beanId)
方法,此处参数传递的是Spring配置文件中Bean的ID。 - 如果Bean实现了
BeanFactoryAware
接口,将调用它实现的setBeanFactory()
方法,此处参数传递的是当前Spring工厂实例的引用。 - 如果Bean实现了
ApplicationContextAware
接口,将调用它实现的setApplicationContext(ApplicationContext)
方法,此处参数传递的是Spring上下文实例的引用。 - 如果Bean关联了
BeanPostProcessor
接口,将调用预初始化方法postProcessBeforeInitialization(Object obj, String s)
对Bean进行操作。 - 如果Bean实现了
InitializingBean
接口,将调用afterPropertiesSet()
方法。 - 如果Bean在Spring配置文件中配置了
init-method
属性,将自动调用其配置的初始化方法。 - 如果Bean关联了
BeanPostProcessor
接口,将调用postProcessAfterInitialization(Object obj, String s)
方法,由于是在Bean初始化结束时调用After方法,也可用于内存或缓存技术。 - 当Bean不再需要时,将经过销毁阶段,如果Bean实现了DisposableBean接口,将调用其实现的destroy方法将Spring中的Bean销毁。
- 如果在配置文件中通过destroy-method属性指定了Bean的销毁方法,将调用其配置的销毁方法进行销毁
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
bean的循环依赖
两个单例bean互相依赖。
通过三级缓存解决
一级缓存:singletonObjects
二级缓存:earlySingletonObjects
三级缓存:singletonFactories
先获取bean
getBean()
进入之后,再进调用DefaultSingletonBeanRegistry
的getSingleton(beanName)
方法,
然后先从一级缓冲中获取
- 如果一级没有获取到,并且当前单例对象
正在创建中
(已经实例化,但是未初始化),则从二级缓存中取 - 如果二级缓存也没获取到,就从三级缓存找
- 如果在三级缓存找到了就,添加到二级缓存,之后再从三级缓存中移除
最后返回
没获取到则创建bean
缓存中没有获取到则会创建bean实例(createBean()
→ doCreateBea()
):
实际上的,bean通过反射实例化的步骤(doCreateBean()
)
执行到doCreateBean
中的下面
添加到三级缓存为ObjectFactory<?>
类型,同时在
getEarlyBeanReference
填充属性,给属性赋值(populateBean()
)
使用三级缓存解决单例bean的循环依赖,在第一个实体类createBeanInstance()方法创建完bean实例,和populateBean()填充属性之间,添加三级缓存实例创建工厂,实现提前暴漏,这样在第二个实体类填充第一个实体类属性时,通过getSingleton()方法获取三级缓存中提前暴漏的第一个实体类创建的bean,从而解决循环依赖。
参考原文链接:https://blog.csdn.net/Maybe_9527/article/details/112303239
spring循环依赖流程分析:https://blog.csdn.net/hao134838/article/details/121239018?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-121239018-blog-112303239.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-121239018-blog-112303239.pc_relevant_aa&utm_relevant_index=1
IOC流程解析,循环依赖:https://blog.csdn.net/qq_39339965/article/details/124416399?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-124416399-blog-112303239.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-124416399-blog-112303239.pc_relevant_aa&utm_relevant_index=2
Aop实现原理
BeanFactoryAware
BeanPostProcessors
配置文件给bean的属性替换时机
BeanFactoryPostProcessor
spring所创建的所有对对象,都会被声明为BeanDefinition
Aware接口
是为了让bean获取spring容器的服务:
spring 提供的aware的接口:
BeanNameAware :可以获取容器中bean的名称
BeanFactoryAware:获取当前bean factory这也可以调用容器的服务
ApplicationContextAware: 当前的applicationContext, 这也可以调用容器的服务
MessageSourceAware:获得message source,这也可以获得文本信息
applicationEventPulisherAware:应用事件发布器,可以发布事件,
ResourceLoaderAware: 获得资源加载器,可以获得外部资源文件的内容;