1:spring 创建对象的过程和spring bean生命周期
在如下代码中,我们通过application接口读取配置文件之后,通过debug代码查看源代码
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Service1Impl service1impl= (Service1Impl) context.getBean("service1");
进入到refresh方法中,可以看到如下的bean实例化过程,整个过程的流程图如下:
代码的执行逻辑如下:
2:循环依赖和三级缓存
2.1:什么是循环依赖
两个类互相引用
2.2:怎么解决循环依赖(三级缓存)
循环依赖的解决,首先我们知道构造函数创建对象的时候,对象会先初始化分配内存空间,然后实例化给属性赋值。这里两个步骤是分开。就像我们单例的双重验证一样。所以这就给循环依赖提供了解决空间。
1:我们先初始化A,发现A依赖B,然后把A的初始化放到三级缓存中,此时的A不完整。
2:然后去初始化B,B循环下边的代码,在一二级缓存中找不到A,A没有完成完全的初始化,三级缓存可以找到A.此时B完成了完整的初始化,把B放入到一级缓存
3:然后A接着初始化,在一级缓存中找到了B,也就完成了A的初始化了
我们查看源码分析解决方案:
public static void main(String[] args) {
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
//1:一级缓存中有没有已经存在的bean
Object singletonObject = this.singletonObjects.get(beanName);
//2:一级缓存单例池中不存在 并且正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//3:从二级缓存中获取bean
singletonObject = this.earlySingletonObjects.get(beanName);
//4:二级缓存也没有 并且允许循环依赖
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
//5:前边可能在创建的过程中,此处一级缓存再次查询bean
singletonObject = this.singletonObjects.get(beanName);
//6:一级缓存不存在
if (singletonObject == null) {
//7:二级缓存再次查询
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//8:二级缓存不存在 查询三缓
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//9:三缓存在 将三缓存入二缓,并且移除三缓
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
}
循环依赖解决依赖spring的内部三级缓存:
/** 一级缓存:完整的bean,没有循环依赖 Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 二级缓存:用来解决AOP切面 Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** 三级缓存:存放不完整的bean,用来解决循环依赖,并且将aop的切面代理bean放入二级缓存 Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
3:spring的注解
注解 | 作用 | 备注 |
@Component(value = "people") | 定义一个bean,并且指定bean的名字 | 作用在类上能实现对所有的类进行注解,但是Component不利于三层架构,所以一个注解扩展成了三个来对应不同的业务逻辑层。 |
@Controller | 对应业务的controller | 作用在类上 |
@Service | 对应业务的service | 作用在类上 |
@Dao | 对应业务的dao | 作用在类上 |
@Autowired @Qualifier(value = "dog222") | Autowired自动注入通过byType,但是有时候接口有两个实现类,需要Qualifier指定具体的接口实现名字 | 作用在属性字段上 |
@Resource(name = "cat11") | 作用和Autowired一致,是java注 解,按照类型ByType和ByName指定bean的名字 | 作用在属性字段上 |
@scope("singleton") | 设置bean的作用域 默认是单例 | 作用的类上 |
@Lazy(value = false) | 设置bean的加载机制 默认容器启动的时候加载 | 作用在类上 |
@PostConstruct | 设置bean的初始化的时候执行的方法 | 作用在方法上 |
@PreDestroy | 设置bean在容器关闭的时候实行的方法,只有单例有效 | 作用在方法上 |
@Value("huyiju") private String name; | 给属性注入值 | 作用在属性字段上 |
@Configuration public class ConfigBean { @Bean //bean的名字就是方法名字 public Cat getCat(){ return new Cat(); } @Bean//bean的名字就是方法名字 public Dogs getDog(){ return new Dogs(); } } | @Configuration的作用跟Component相似但是 Configuration里边看可以有多个bean,通过bean的id就是方法名 |
4:springIoc
bean的ioc就是将以前我们手动创建的对象现在由容器管理
5:springAop
通过jdk和cglig来实现代理,将接口或者类的方法实现增强,从而实现比如事务,日志等通用操作
8:spring的设计模式
9:spring的事务和事务失效
9.1:spring的事务传播行为
/**
* 1:REQUIRED
* 事务代码测试 简单的插入两个表的数据
* 插入class表和student表
*/
@Test
public void REQUIRED() {
ClassService classService = applicationContext.getBean("classServiceImpl", ClassService.class);
Classs classs = new Classs();
classs.setCaption("大学一年3");
Student student = new Student();
student.setSname("测试2333333");
student.setSsex("男");
student.setSdatetime(new Date());
classService.REQUIRED(student, classs);
}
/**
* 2:SUPPORTS
* 调用者不开启事务就没有,开启的话按照事务执行
* 自己失败 调用者也会回滚
*
*/
@Test
public void SUPPORTS() throws Exception {
ClassService classService = applicationContext.getBean("classServiceImpl", ClassService.class);
Classs classs = new Classs();
classs.setCaption("大学1");
Student student = new Student();
student.setSname("测试");
student.setSsex("男");
student.setSdatetime(new Date());
classService.SUPPORTS(student, classs);
}
/**
* 3:NOT_SUPPORTED
* 不会跟随调用者事务
* 调用者提交 回滚 都不会影响自己
*
* NEVER 报错 父方法不能有事务
*/
@Test
public void NOT_SUPPORTED() {
ClassServiceController classServiceController = applicationContext.getBean("classServiceController", ClassServiceController.class);
Classs classs = new Classs();
classs.setCaption("一年级");
Student student = new Student();
student.setSname("测试hu");
student.setSsex("男");
student.setSdatetime(new Date());
classServiceController.NOT_SUPPORTED(student, classs);
}
/**
* 4:REQUIRES_NEW
* 不管调用者有没有事务 自己都会开启事务 自己的事务提交或者回滚不影响调用者
*/
@Test
public void REQUIRES_NEW() {
ClassService classService = applicationContext.getBean("classServiceImpl", ClassService.class);
Classs classs = new Classs();
classs.setCaption("一年级2");
Student student = new Student();
student.setSname("测试hu");
student.setSsex("男");
student.setSdatetime(new Date());
classService.REQUIRES_NEW(student,classs);
}
/**
* 5:MANDATORY
* 调用者必须得有事务 否则报错
*/
@Test
public void MANDATORY() {
ClassService classService = applicationContext.getBean("classServiceImpl", ClassService.class);
Classs classs = new Classs();
classs.setCaption("一年级5");
Student student = new Student();
student.setSname("测试5");
student.setSsex("男");
student.setSdatetime(new Date());
classService.mandatory(student,classs);
}
/**
* 6:NESTED
* 自己出异常不会影响调用者
* 调用者异常 全部的自己回滚
*/
@Test
public void NESTED() {
ClassService classService = applicationContext.getBean("classServiceImpl", ClassService.class);
Classs classs = new Classs();
classs.setCaption("一年级6");
Student student = new Student();
student.setSname("测试6");
student.setSsex("男");
student.setSdatetime(new Date());
classService.NESTED(student,classs);
}
/**
* propagation 事务传播行为
*
* REQUIRED(0),表示当前运行在事务中,有的话直接用 没有的话就开启事务 执行结果:多表插入有事务,要么成功要么失败
* SUPPORTS(1),表示跟随事务,有的话也可以,没有的话也可以, 执行结果:多表插入没有事务 有的成功有的失败
* MANDATORY(2),调用者必须得有事务 否则报错
* REQUIRES_NEW(3),不管调用者有没有事务 自己都会开启事务 自己的事务提交或者回滚不影响调用者
* NOT_SUPPORTED(4),表示不跟随事务,老大的事务跟我没有关系
* NEVER(5),表示不能有事务,老大有事务就会报错
* NESTED(6);自己出异常不会影响调用者,调用者事务回滚 自己也要回滚
*/
9.2:spring的事务失效
//1:异常问题:在业务代码中如果抛出RuntimeException异常,事务回滚;但是抛出Exception,事务不回滚;
//2:异常问题:如果在加有事务的方法内,使用了try...catch..语句块对异常进行了捕获,
// 而catch语句块没有throw new RuntimeExecption异常,事务也不会回滚
//3:方法内部调用:(切面切的是方法) 方法里边 链式调用 不行因为事务的本质是动态代理生成代理对象,然后根据方法进行增强 链式调用无效
// 在类A里面有方法a 和方法b,
// 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b
// 方法b里面的事务不会生效。原因是在同一个类之中,方法互相调用,切面无效 ,而不仅仅是事务。这里事务之所以无效,是因为spring的事务是通过aop实现的。
//4:错误的传播行为
//5:spring管理异常
10:applicationcontext和beanfactory
beanfactory是个顶级接口,里边都是getbean的各种方法,用来获取bean。但是这个接口的实现类不会实例化bean,只会被动的获取的时候创建bean。该代码已经不推荐使用了
ClassPathResource res = new ClassPathResource("beans.xml");
BeanFactory beanFactory=new XmlBeanFactory(res);
beanFactory.getBean("bean名字");
application接口继承的bean factory接口。里边的方法很多,继承了各种接口,他的实现类ClassPathXmlApplicationContext,会有bean工厂,能够按照我们上边的生命周期来实例化bean,bean都会存在,我们通过getbean获取即可
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Service1Impl service1impl= (Service1Impl) context.getBean("service1");
service1impl.say();
结论:几乎所有的应用场合我们都直接使用ApplicationContext 而非底层的BeanFactory。