Spring的事务
Spring声明式事务管理是通过AOP技术实现的事务管理,其本质是对方法前后进行拦截,在目标方法开始之前创建一个事务,在目标方法执行之后,根据执行情况提交或回滚事务。
事务在逻辑上是一组操作,要么执行,要不都不执行。主要是针对数据库而言的,为了保证事务是正确可靠的,在数据库进行写入或者更新操作时,就必须得表现出 ACID 的 4 个重要特性:
原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
事务隔离(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
Spring事务其实就是在上一个AOP内容中切面逻辑中实现的,在开启事务后,Spring容器会用事务管理器新建一个数据库连接,并且一开始设置autocommint=false,然后执行普通对象中的相关方法如果没有抛异常就会提交,否则就会回滚。然后下面我们来分析个案例代码如下
@Component() public class Test{ @Transactional public void hello(){ System.out.println("good"); a(); } @Transactional(propagation = Propagation.NEVER) public void a(){ System.out.println("a"); } } @SpringBootApplication @MapperScan("com.example.demo.mybatisplus") @EnableTransactionManagement public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); Test bean = run.getBean(Test.class); bean.hello(); } }
PROPAGATION_NEVER该传播机制不支持外层事务,即如果外层有事务就抛出异常,当我们获取Test Bean对象的时候,然后执行hello方法的时候他会抛出异常吗?实际上是不会的,那到底是什么原因呢?我们通过上面的内容我们知道了Spring事务是在代理类中的相关方法的代理逻辑中执行的,在执行方法切面逻辑的时候是由我们的代理对象执行事务管理器相关操作的,而a方法实际的执行的是普通对象并不是代理对象,普通对象在执行a方法的时候是不会有事务的操作的,注解仅仅就成为了一个摆设。如果有兴趣的小伙伴可以尝试一下。那么怎么解决问题呢?当有@Transactional的类就会在Spring容器中生成代理对象放到单例池当中,那么可以在这个对象中使用@Autowired注解依赖注入代理对象,把a方法放在这个代理对象对应的普通类中,然后通过这个依赖注入的代理对象调用a方法,就可以正常的解决。还有一个办法就是如下面代码也可以正常的解决。
@Transactional
public void hello(){
System.out.println("good");
Test o = (Test) AopContext.currentProxy();
o.a();
}
循环依赖
这里引用
被 Spring 管理的对象叫做 Bean 。Bean的生成步骤如下:
Spring 扫描 class 得到 BeanDefinition;
根据得到的 BeanDefinition 去生成 bean;
首先根据 class 推断构造方法;
根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象);
填充原始对象中的属性(依赖注入);
如果原始对象中的某个方法被 AOP 了,那么则需要根据原始对象生成一个代理对象;
把最终生成的代理对象放入单例池(源码中叫做 singletonObjects)中,下次 getBean 时就直接从单例池拿即可;
对于 Spring 中的 Bean 的生成过程,步骤还是很多的,并且不仅仅只有上面的7步,还有很多很多,这里不详细说了。
我们可以发现,在得到一个原始对象后,Spring 需要给对象中的属性进行依赖注入,那么这个注入过程是怎样的?
比如上文说的 A 类,A 类中存在一个 B 类的 b 属性,所以,当 A 类生成了一个原始对象之后,就会去给 b 属性去赋值,此时就会根据 b 属性的类型和属性名去 BeanFactory 中去获取 B 类所对应的单例bean。
1. 如果此时 BeanFactory 中存在 B 对应的 Bean,那么直接拿来赋值给 b 属性;
2. 如果此时 BeanFactory 中不存在 B 对应的 Bean,则需要生成一个 B 对应的 Bean,然后赋值给 b属性。
问题就出现在「第二种」情况,如果此时 B 类在 BeanFactory 中还没有生成对应的 Bean,那么就需要去生成,就会经过 B 的 Bean 的生命周期。
那么在创建 B 类的 Bean 的过程中,如果 B 类中存在一个 A 类的 a 属性,那么在创建 B 的 Bean 的过程中就需要 A 类对应的 Bean,但是,触发 B 类 Bean 的创建的条件是 A 类 Bean 在创建过程中的依赖注入,所以这里就出现了循环依赖:
A Bean创建–>依赖了 B 属性–>触发 B Bean创建—>B 依赖了 A 属性—>需要 A Bean(但A Bean还在创建过程中)
从而导致 A Bean 创建不出来,B Bean 也创建不出来。
这里就用到三级缓存了,这里设置两个类Aservice,Bservice。Aservice中有Bservice属性的注入,Bservice中有Aservice属性的注入。那么三级缓存是如何解决问题的呢?这里先对三级缓存进行简单的描述。
「singletonObjects」:缓存某个 beanName 对应的经过了完整生命周期的bean也就是我们的单例池;
「earlySingletonObjects」:缓存提前拿原始对象进行了 AOP 之后得到的代理对象,原始对象还没有进行属性注入和后续的 BeanPostProcesso r等生命周期;
「singletonFactories」:缓存的是一个 ObjectFactory ,主要用来去生成原始对象进行了 AOP之后得到的「代理对象」,在每个 Bean 的生成过程中,都会提前暴露一个工厂,这个工厂可能用到,也可能用不到,如果没有出现循环依赖依赖本 bean,那么这个工厂无用,本 bean 按照自己的生命周期执行,执行完后直接把本 bean 放入 singletonObjects 中即可,如果出现了循环依赖依赖了本 bean,则另外那个 bean 执行 ObjectFactory 提交得到一个 AOP 之后的代理对象(如果有 AOP 的话,如果无需 AOP ,则直接得到一个原始对象)。
那么如何打破循环依赖呢?
摘用网上的图片
Aservice 在Spring容器创建过程中,在实例化后把Aservice普通对象放在缓存中,然后进行Bservice属性的依赖注入,首先从单例池中寻找Bservice,如果找到就会赋值,找不到就会创建Bservice,在进行Aservice注入的时候从单例池寻找,找不到然后从缓存中寻找进行属性的注入。此时循环依赖问题得以解决。因为在整个过程中AService都是单例的 , 所以即使从缓存中拿到的AService的原始对象也没有关系 , 因为在后续的Bean生命周期中 ,AService在堆内存中没有发生变化。这种情况当Aservice对象没有AOP的时候这种情况是没有问题的,如果Aservice类有AOP,从上文可知那么单例池中的该对象是代理对象,而我们在Bservice中依赖注入的Aservice是普通对象,这显而易见就有问题了。
所以就需要二级缓存了,在Bservice进行Aservice属性注入的时候,要进行提前AOP,而上面的缓存就相当于三级缓存存储原始对象,出现循环依赖后从二级缓存earlySingletonObjects中获取如果获取不到对应的对象,然后就会从三级缓存中获取原始对象,如果是AOP就生成代理对象,不是就是普通对象然后放在二级缓存中,此时这个对象还不能放入单例池中,为什么呢?假如这里是个代理对象,代理对象的Target原始对象还没有完成生命周期属性还没有完全注入完成,如果在这里放入单例池,在多线程环境下在这时从单例池中获取这个bean对象就会发生不可预期的错误。当Bservice Bean对象创建完成后然后在Aservice中填充完所有属性后,就可以从二级缓存中获取该对象然后放到单例池中了。
手写源码篇
通过手写模拟,了解Spring的底层源码启动过程
通过手写模拟,了解扫描逻辑和依赖注入等底层源码工作流程
通过手写模拟,了解初始化机制工作流程
通过手写模拟,了解BeanDefinition、BeanPostProcessor的概念
通过手写模拟,了解Spring AOP的底层源码工作流程
目录结构
启动代码
public class test { public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { ApplicationContext applicationContext = new ApplicationContext(AppConfig.class); UserInterface userservice = (UserInterface) applicationContext.getBean("userservice"); userservice.test(); } }
运行代码如下图
Component注解
这里其余的注解代码省略
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component { String value() default ""; }
ApplicationContext类
这个类主要是扫描@ComponentScan指定的包路径生成其中类含有@Componet注解的Bean对象。
public class ApplicationContext { private Class configClass; private ConcurrentHashMap<String,Object> singletonObjects=new ConcurrentHashMap<>();//单列池 private ConcurrentHashMap<String,BeanDefinition> beanDefinitionConcurrentHashMap=new ConcurrentHashMap<>(); private List<BeanPostProcessor> beanPostProcessorList=new ArrayList<>(); public ApplicationContext(Class configClass) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { this.configClass = configClass; scan(configClass); for (Map.Entry<String, BeanDefinition> stringBeanDefinitionEntry : beanDefinitionConcurrentHashMap.entrySet()) { String beanName = stringBeanDefinitionEntry.getKey(); BeanDefinition beanDefinition = stringBeanDefinitionEntry.getValue(); if(beanDefinition.getScope().equals("singleton")){ Object bean = createBean(beanDefinition); singletonObjects.put(beanName,bean); } } } //创造Bean对象 public Object createBean(BeanDefinition beanDefinition){ Class clazz = beanDefinition.getClazz(); Object o = null; try { o = clazz.getDeclaredConstructor().newInstance(); for (Field declaredField : clazz.getDeclaredFields()) { if(declaredField.isAnnotationPresent(Autowired.class)){ Object bean = getBean(declaredField.getName()); declaredField.setAccessible(true); declaredField.set(o,bean);//此处有bug,当singleton时注入失败为null,当注入的属性对象bean在单例池排序靠前可以成功。 } }//getFields为获得所有public的属性,这里为所有 //后置处理器,这里没有对beanName进行设计 for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { beanPostProcessor.postProcessBeforeInitialization(o,null); } //下面为初始化机制 if (o instanceof InitializingBean){ try { ((InitializingBean) o).afterPropertiesSet(); } catch (Exception e) { e.printStackTrace(); } } for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { o = beanPostProcessor.postProcessAfterInitialization(o, null); } return o; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return null; } //扫描包路径 public void scan(Class configClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class); String value = componentScan.value(); // System.out.println(value); String replace = value.replace('.', '/'); ClassLoader classLoader = ApplicationContext.class.getClassLoader(); URL resource = classLoader.getResource(replace);//根据相对路径 com.example.service // System.out.println(resource); File file=new File(resource.getFile()); if(file.isDirectory()){ File[] files=file.listFiles(); for (File file1 : files) { String absolutePath = file1.getAbsolutePath(); if (absolutePath.endsWith(".class")) { // System.out.println(absolutePath); String com = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class")); String replace1 = com.replace("\\", ".");//注意\是转义字符,而/不是,com.example.service.xx // System.out.println(replace1); Class<?> aClass = null; try { aClass = classLoader.loadClass(replace1); if (aClass.isAnnotationPresent(Component.class)) { if(BeanPostProcessor.class.isAssignableFrom(aClass)){//该class对象是否实现了BeanPostProcessor接口,不能用instanceof BeanPostProcessor beanPostProcessor = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance(); beanPostProcessorList.add(beanPostProcessor); } Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class); String beanName = declaredAnnotation.value(); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setClazz(aClass); if (aClass.isAnnotationPresent(Scope.class)) { Scope scope = aClass.getDeclaredAnnotation(Scope.class); beanDefinition.setScope(scope.value()); } else { beanDefinition.setScope("singleton"); } beanDefinitionConcurrentHashMap.put(beanName,beanDefinition); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } } //从Spring容器中获取Bean对象 public Object getBean(String beanName){ if(beanDefinitionConcurrentHashMap.containsKey(beanName)){ BeanDefinition beanDefinition = beanDefinitionConcurrentHashMap.get(beanName); if(beanDefinition.getScope().equals("singleton")){ Object o = singletonObjects.get(beanName); return o; } else { Object bean = createBean(beanDefinition); return bean; } }else { throw new NullPointerException(); } } }
BeanDefinition类
这个类主要包含Bean对象的class类型和scope。
public class BeanDefinition { private Class clazz; private String scope; public BeanDefinition(Class clazz, String scope) { this.clazz = clazz; this.scope = scope; } public BeanDefinition() { } public Class getClazz() { return clazz; } public void setClazz(Class clazz) { this.clazz = clazz; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } }
BeanPostProcessor接口实现
继承BeanPostProcessor接口主要是实现初始化前和初始化后的操作。
@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if(bean instanceof UserInterface) System.out.println("初始化前"); return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { //AOP模拟 if(bean instanceof UserInterface){ Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理前"); Object invoke = method.invoke(bean, args); System.out.println("代理后"); System.out.println("初始化后"); return invoke; } }); return proxyInstance; } return bean; } }
相关代码可以在我的文件资源下载https://download.csdn.net/download/qq_43649937/87506483?spm=1001.2014.3001.5503