这次来讲讲Spring家族里最基础的Spring Framework。为什么SpringBoot可以这么强大?Framewordk到底做了什么?参考最新的5.3.10-SNAPSHOT
Spring的Hello World
学习Spring你学的第一段代码是什么?我觉得应该是这一段
//读取xml
ClassPathXmlApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
//读取配置类
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService.class)applicationContext.getBean("userService");
userService.test();
得到一个Bean对象,然后就可以去调用。前面两行是创建Spring容器的方法。无论是前者的xml,还是3.0版本后新增的class方式去充当配置文件用注解定义。
xml目前基本已经out了,mvc时代会用的多一点。现在主要会用config.Class的方式,Spring Boot就是用了这种方式。
Spring容器管理
到底getBean和我们平时直接new一个有什么区别吗?相信这是每个入门者的疑问
简单贴一下UserService的代码
@Component
public class UserService{
@Autowired
private UserService self;
public void test(){
System.out.println("test");
}
}
这里@Component就是取代xml配置文件的(后续会讲这个原理,@Bean是可以覆盖)实现Bean注入。
看到这里都懂了吧,容器管理的对象里面的self属性是有值的,而自己new的UserService对象是没有值的。——这个就叫依赖注入
表面就是给AutoWired注解属性赋值。
对象怎么变成一个Bean
我们知道返回的对象肯定不是一个普通的new出来的对象,是一个Spring代理的一个对象Bean对象。
- 利用类的构造方法实例化一个对象(这里会推断构造方法)
- 有了对象后会开始依赖注入
- BeanNameAware接口、 BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,调用其并传入相应参数setBeanName()、setBeanClassLoader()、 setBeanFactory()(Aware回调)
- 是否有某个方法被@PostConstruct注解,如果有则执行(JSR-250的规范)
- InitializingBean接口,如果实现了,就 调用中的afterPropertiesSet()方法
- Spring会判断当前对象需不需要进行AOP,如果不需要那么Bean就创建完 了,如果需要进行AOP,则会进行动态代理并生成一个代理对象做为Bean(初始化 后)
值怎么来?
依赖注入的值怎么来? (特指Autowired)
Spring容器里面找
Spring会根据入参的类型和入参的名字去Spring中找Bean对象(以单例Bean为例, Spring会从单例池那个Map中去找):
- 先根据入参类型找,如果只找到一个,那就直接用来作为入参
- 如果根据类型找到多个,则再根据入参名字来确定唯一一个
- 最终如果没有找到,则会报错,无法创建当前Bean对象
先匹配类型,类型对了再看名字。还有JSR-250规范定义的@Resource默认按名字,也可以配置按类型。
AOP代理
创建一个Bean后会判断是否需要动态代理
- 找出所有的切面Bean (切片也是一个正常的Bean被Component修饰)
- 遍历切面中的每个方法,看是否写了@Before、@After等注解
- 如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配
- 如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP
所有要经过的方法会缓存起来
确认了需要动态代理后,通过cglib生成
cglib真正对类完全代理。
1.查找目标类上的所有非final 的public类型的方法定义;
2.将这些方法的定义转换成字节码;
3.将组成的字节码转换成相应的代理的class对象;
4.实现 MethodInterceptor接口,用来处理对代理类上所有方法的请求
基于ASM,字节码。对final修饰无能为力
- 生成代理类UserServiceProxy,代理类继承UserService
- 代理类中重写了父类的方法,比如UserService中的test()方法
- 代理类中还会有一个target属性,该属性的值为被代理对象(就是UserServer)
- 代理类中的test()方法被执行时的逻辑如下:
a. 执行切面逻辑
b. 调用target.void()
Spring事务
基于AOP,那真正代理的逻辑是怎样呢?
- 有没有@Transa
- 创建数据库链接conn(事务管理器datasource)
- 设置autoCommit=false,为了rollback
- @After 执行提交或者rollback
那怎么传播呢?
相信我们肯定都遇到过在Serve内调用旁边的方法发生错误没回滚的情况。
原因就是内部调用方法没用Bean去调用,所以我们这里需要self.a()去调用。要代理对象方法去调用@Transa的方法。就是自己注入自己
@Configurtaion不加的话事务会不生效,这里改变了代理逻辑,对于同一个方法不重复调用,确保jdbcTemplate和transactionManager的datasource是同一个