单例池

使用concurrenthashmap

@PostConstruct

遍历方法判断是否有该注解,执行注解的方法

初始化

继承InitializingBean,初始化时判断Bean是否是这种类型的,强转执行注解的方法

初始化和实例化

初始化如上,实例化执行构造方法

初始化后

可用于实现AOP,在放入单例池前执行

推断构造方法

多种构造方法执行无参构造方法,或@Autowire指定的构造方法,多种构造方法不指定也无无参,报错

AOP底层

JDK动态代理和cjlib动态代理

在代理对象中设置一个目标对象的引用,代理对象在重写方法中,用target调用目标对象的方法

事务底层

  1. 代理对象在重写方法中,先判断是否有@Transcational注解
  2. 有的话创建事务管理器,事务管理器新建一个数据库连接ThreadLocal<DateSource,conn>,同一个数据源拿到的是同一个连接
  3. 关闭连接的自动提交
  4. 调用普通方法中的jdbctemplate就使用这个连接
  5. 自动提交

事务失效

在代理对象重写的方法中调用目标对象的方法,目标对象的方法是没有事物的

可以注入自己

@Configuration

加了@Configuration首先是一个Bean,并且是一个配置Bean

加了该注解的类也是一个代理对象,使用cglib,但调用目标对象时使用的是super.方法名(),子类重写了父类方法,执行子类方法

在事务管理器和JdbcTemplate在@Bean中都要调用datasource(),如果不加@Configuration,则每次创建新对象,加了就会去单例池找,这样数据库连接关闭自动提交就能用到了

image-20221027133234068

@Import导入的类是配置Bean

三级缓存

完整对象创建分两个步骤:实例化 + 初始化,只要初始化没进行完毕,就不完整
对象分三种类型:刚实例化的普通对象(放在三级缓存),提前进行AOP的不完整对象(放在二级缓存),完整对象(一级缓存/单例池)

循坏依赖带来了什么问题?重复创建,如何解决?第三级缓存通过存储刚创建的实例化普通对象来打破循环,但循环依赖需要的是完成了AOP的代理对象,所以将三级缓存中的对象取出,提前进行AOP并放入二级缓存

那为什么要二级缓存?因为需要区分“已进行AOP的不完整对象”和“完整的代理对象”,并防止多次AOP(多个循环依赖叠加可导致)

image.png

1.A创建将() -> getEarlyBeanReference(beanName, mbd, bean)表达式放入三级缓存singletonFactories

getEarlyBeanReference(beanName, mbd, bean)首先得到一个cachekey,cachekey就是beanName。然后把beanName和bean(这是原始对象)存入earlyProxyReferences中
调用wrapIfNecessary进行AOP,得到一个代理对象

2.A属性注入B,需要创建B

3.B创建时属性注入需要A,从三级缓存拿出表达式执行,将A的执行完AOP的代理对象放入二级缓存earlySingletonObjects中,B属性注入注入的时A的代理对象,只是未初始化完,和A最后放入单例池的是同一个对象

4.A接着生命周期,由于已经AOP因此不用再AOP,利用earlyProxyReferences判断是否已经AOP,在BeanPostProcessor的执行之后,就需要把A对应的对象放入singletonObjects中了

**注意:**earlyProxyReferences中的对象还未AOP

三级缓存用hashmap

@Async

BeanPostProcessor:利用earlyProxyReferences判断是否已经AOP,根据结果返回bean

不出现循环依赖传入的bean和返回的bean不同

出现循环依赖传入的bean和返回的bean不同(不会再AOP,直接返回)

如果添加@Async,会多一个BeanPostProcessor,将传入的bean进行AOP,导致有两个代理对象,报错

@Lazy

在属性注入上添加@Lazy,这样就不会有循环依赖,不会有两个代理对象

@Lazy是将属性生成一个代理对象赋值,在使用时再去单例池找

构造方法的循环依赖

  1. 属性注入循环依赖
1
2
3
4
5
@Component
class A{
@Autowire
B b;
}
1
2
3
4
5
@Component
classB{
@Autowire
A a;
}
  1. 构造方法循环依赖,直接卡在创建普通对象,解决办法@Lazy,这样参数就能使用代理对象注入
1
2
3
4
5
6
7
8
9

@Component
class A{
private B b;
@Lazy
puclic A(B b){
this.b = b
}
}
1
2
3
4
5
6
7
8
@Component
class B{
private A a;

puclic B(Aa b){
this.a = a
}
}
  1. 构造方法循环依赖加多例会导致不停循环创建

@Vaule

  1. @Value(“${key}”),赋值配置文件,环境变量等中的值,不存在直接将字符串赋值
  2. @Value(“key}”),赋值字符串
  3. @Value(“#{key}”),类似@Autowire,@Resource的名字注入
  4. 自定义注解,避免耦合

@Bean

1
@Bean(autowire = Autowire.BY_TYPE)
  1. 使用到这个bean时利用bean的set方法进行自动注入
1
@Bean(autowireCandidate = false)
  1. 不能作为候选bean,不会用来注入
  2. 可以单独使用
  3. 自定义注解,加在自己的注解上,避免重复写
1
2
3
4
5
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Bean
@Scope("prototype")

**注意:**不和@Configration一起使用,调用类种方法生成对象时,会调用类中的方法,返回1个实例对象,和@Configration一起使用会从单例池返回

加@Configration会生成一个代理对象,执行对应方法时查看对应方法的bena是否创建,创建就直接使用,否则创建放入单例池

@ComponentScan

注解参数

1
2
3
4
5
@ComponentScan(value = "",includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Zhouyu.class))



excludeFilters =

路径下的@Zhouyu注解也会被当作bean

扫描索引

在资源目录下创建META-INF/spring.components文件,key=value,key为类全类名,value为注解类型,有了这个文件,注解会失效

@Conditional

可以放在类或@Bean上

1
@Conditional(value = {})

value存放Class<? extends Condition>数组

Condition类需要自定义类去实现Condition接口

通过matches返回true或false判断是否类需要加载

1
2
3
4
5
6
public class Test implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return false;
}
}

image-20221027104423059

@Autowire

  1. 放在属性上

    1
    2
    //这里使用的是unsafe直接将值填充到内存
    unsafe.putObject(var1, this.fieldOffset, var2);
  2. 放在set方法上(类似@PostConstruct,不过执行实际不同)

  3. 放在构造方法上

    • 解决推断构造方法
    • 属性注入
  4. 参数前(支持测试时注入)

  5. 自定义注解上,自己的注解会加上注解功能(可以加上一些参数)

注意:

  1. 不支持使用static,如果支持使用static会导致每次创建新的实例,static修饰的类对象会变化,其他实例由于用的时类对象,所以也会变化
  2. require属性表示是否必须有值

@Lazy

  1. 类上(创建bean的时机,创建Spring容器时或getBean时创建)
  2. 属性上,会先赋值一个代理对象,直到使用时才去单例池中找
  3. 方法上(加了@Autowire的set方法上,和属性上类似,先创建代理对象赋值)
  4. 参数上(同上类似)
  5. 构造方法上(可以解决循环依赖)

@Autowire和@Resource

JDK包中的@Resource,spring提供的@Autowire

1
2
3
4
@Resource(name="")

@Autowire
@Qualifier(value = "")

@Import

image-20221027134348814

ImportSelector和DeferredImportSelector都是接口,自定义类去实现接口和相关方法,可以批量导入类

ImportBeanDefinitionRegistrar也是接口,可以重写相关方法,自己去注册bean

image-20221027134709636

@Lookup

在抽象类中的方法上标注@Lookup,抽象类会创建一个代理bean,被标注@Lookup的抽象方法会返回value参数中的实例对象

image-20221027135225154

@Primary

发现多个相同的bean,使用带有@Primary的bean,不能注解多个

注册一个bean的方式

image-20221027135704799

FactoryBean

  1. 自定义类实现FactoryBean

  2. 会创建两个Bean,一个是ZhouyFactoryBean的bean,启动时创建,一个是UserService的bean,获取时创建

  3. @Component不设置名字,getBean(“zhouyuFactoryBean”)会得到UserService的Bean

  4. 加一个& getBean(“&zhouyuFactoryBean”)获得ZhouyFactoryBean的bean

  5. 不会属性注入

image-20221027140609620

SmartFactoryBean

可以设置spring启动时创建

image-20221027140955835

依赖注入有哪些方式

image-20221027135758022

  1. 自动注入,依赖@Bean里的autowire = Autowre.BY_NAME

获取ApplicationContext的方式

image-20221027142417581

Bean的作用域

image-20221027142433259

Spring类型有哪些转化方式

image-20221027142454244

SpringMVC启动

  1. tomcat启动

  2. 解析web.xml Listener中创建Spring容器(ServerletContext)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <listener>  
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--2、部署applicationContext的xml文件-->
    <!--如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml,
    在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。
    如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
    在<param-value> </param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔。
    也可以这样applicationContext-*.xml采用通配符,比如这那个目录下有applicationContext-ibatis-base.xml,
    applicationContext-action.xml,applicationContext-ibatis-dao.xml等文件,都会一同被载入。
    在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。-->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>
  3. DispatcherServerlet实例化化

  4. DispatcherServerlet.init() 创建Spring容器

在web.xml中配置拦截器, listener,以及DispatcherServelet

springmvc.drawio

web.xml中的Listener

用来创建父容器