本文会把Spring中核心知识点大概解释下。可以对Spring的底层有一个整体的大致了解。主要内容包括:
代码点击链接自取
但都只是大致流程,后续会针对每个流程详细深入的讲解并分析源码实现。
看源码前了解spring的部分概念和原理有利于理解源码。个人认为跳过部分复杂细节,紧跟主线是看懂源码的最重要的技巧。
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
"spring.xml");
UserService bean = classPathXmlApplicationContext.getBean(UserService.class);
bean.test("huazige");
}
spring.xml内容:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="org.huazige.service.UserService" />
beans>
这是学习Spring的helloworld。可是,这三行代码底层都做了什么?
光看这三行代码,其实并不能体现出来Spring的强大之处,也不能理解为什么需要ClassPathXmlApplicationContext和getBean()方法,随着深入将会改变你此时的观念,而对于上面的这些疑问,也会随着深入逐步得到解决。对于这三行代码,你现在可以认为:如果你要用Spring,你就得这么写。就像你要用Mybatis,你就得写各种Mapper接口。
但是用ClassPathXmlApplicationContext其实已经过时了,在新版的SpringMVC和SpringBoot的底层主要用的都是AnnotationConfigApplicationContext
public static void main(String[] args) {
// ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
// "spring.xml");
// UserService bean = classPathXmlApplicationContext.getBean(UserService.class);
// bean.test("huazige");
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(
AppConfig.class);
UserService bean1 = annotationConfigApplicationContext.getBean(UserService.class);
bean1.test("huazige");
}
AppConfig文件内容:
package org.huazige.config;
import org.huazige.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
/**
* @ClassName AppConfig
* @Description TODO
* @Author huazige
* @Date 2022-10-08 23:07
* @Version 1.0
*/
@ComponentScan("org.huazige")
public class AppConfig {
@Bean
UserService getUserService(){
return new UserService();
}
}
所以spring.xml和AppConfig.class本质上是一样的。目前,我们基本很少直接使用上面这种方式来用Spring,而是使用SpringMVC,或者SpringBoot,但是它们都是基于上面这种方式的,都需要在内部去创建一个ApplicationContext的,只不过:
因为AnnotationConfigApplicationContext
是比较重要的,并且AnnotationConfigApplicationContext和ClassPathXmlApplicationContext大部分底层都是共同的,后续我会着重将AnnotationConfigApplicationContext的底层实现,对于ClassPathXmlApplicationContext,可以在业余时间看看相关源码即可。
其实不管是AnnotationConfigApplicationContext还是ClassPathXmlApplicationContext,目前,我们都可以简单的将它们理解为就是用来创建Java对象的,比如调用getBean()就会去创建对象(此处不严谨,getBean可能也不会去创建对象,后续详解)。
在Java语言中,肯定是根据某个类来创建一个对象的。
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(
AppConfig.class);
UserService bean1 = annotationConfigApplicationContext.getBean(UserService.class);
bean1.test("huazige");
当我们调用context.getBean(“userService”)时,就会去创建一个对象,但是getBean方法内部怎么知道"userService"对应的是UserService类呢?所以,我们就可以分析出来,在调用AnnotationConfigApplicationContext的构造方法时,也就是第一行代码,会去做一些事情:
这样,但调用context.getBean(“userService”)时,就可以根据"userService"找到UserService类,从而就可以去创建对象了。
那么Spring到底是如何来创建一个Bean的呢,这个就是Bean创建的生命周期,大致过程如下:
通过最后一步,我们可以发现,当Spring根据UserService类来创建一个Bean时:
1.如果不用进行AOP,那么Bean就是UserService类的构造方法所得到的对象。
2.如果需要进行AOP,那么Bean就是UserService的代理类所实例化得到的对象,而不是UserService本身所得到的对象。
Bean对象创建出来后:
7. 如果当前Bean是单例Bean,那么会把该Bean对象存入一个Map
8. 如果当前Bean是原型Bean,那么后续没有其他动作,不会存入一个Map,下次getBean时会再次执行上述创建过程,得到一个新的Bean对象。
Spring在基于某个类生成Bean的过程中,需要利用该类的构造方法来实例化得到一个对象,但是如果一个类存在多个构造方法,Spring会使用哪个呢?
Spring的判断逻辑如下:
Spring的设计思想是这样的:
- 如果一个类只有一个构造方法,那么没得选择,只能用这个构造方法
- 如果一个类存在多个构造方法,Spring不知道如何选择,就会看是否有无参的构造方法,因为无参构造方法本身表示了一种默认的意义
- 不过如果某个构造方法上加了@Autowired注解,那就表示程序员告诉Spring就用这个加了注解的方法,那Spring就会用这个加了@Autowired注解构造方法了
需要重视的是,如果Spring选择了一个有参的构造方法,Spring在调用这个有参构造方法时,需要传入参数,那这个参数是怎么来的呢?
Spring会根据入参的类型和入参的名字去Spring中找Bean对象(以单例Bean为例,Spring会从单例池那个Map中去找):
确定用哪个构造方法,确定入参的Bean对象,这个过程就叫做推断构造方法
。
AOP就是进行动态代理,在创建一个Bean的过程中,Spring在最后一步会去判断当前正在创建的这个Bean是不是需要进行AOP,如果需要则会进行动态代理。
如何判断当前Bean对象需不需要进行AOP:
利用cglib进行AOP的大致流程:
当我们从Spring容器得到UserService的Bean对象时,拿到的就是UserServiceProxy所生成的对象,也就是代理对象。UserService代理对象.test()—>执行切面逻辑—>target.test(),注意target对象不是代理对象,而是被代理对象。
当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。Spring事务的代理对象执行某个方法时的步骤:
Spring事务是否会失效的判断标准:某个加了@Transactional注解的方法被调用时,要判断到底是不是直接被代理对象调用的,如果是则事务会生效,如果不是则失效。