spring笔记整理

Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。
SpringBoot:简化Spring应用初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。Spring Boot其实就是一个整合很多可插拔的组件(框架),方便开发人员快速搭建和开发的一个框架。

springIOC容器加载完成后执行某个方法(https://blog.csdn.net/qq_38366063/article/details/93200659),主要是实现ApplicationListener接口

@Component
public class ApplicationListenerPostProcessor implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    因为使用springmvc有两个容器,这个方法会执行两次,要想只执行一次通过这个判断即可
    if (event.getApplicationContext().getParent() != null) {//root application context 没有parent,他就是老大.
     return;
     }
     //或者下面这种方式
    if(event.getApplicationContext().getDisplayName().equals("Root WebApplicationContext")) {
       }
    }
    
}

使用spring的优点(简化、复用、解耦、低侵入、高扩展)
1、Spring通过DI、AOP和消除样板式代码来简化企业级Java开发
2、Spring框架之外还存在一个构建在核心框架之上的庞大生态圈,它
将Spring扩展到不同的领域,如Web服务、REST、移动开发以及NoSQL
3、低侵入式设计,代码的污染极低
4、独立于各种应用服务器,基于Spring框架的应用,可以真正实现
Write Once,Run Anywhere的承诺
5、Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦
6、Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行
集中式处理,从而提供了更好的复用
7、Spring的ORM和DAO提供了与第三方持久层框架的的良好整合,
并简化了底层的数据库访问
8、Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可
自由选用Spring框架的部分或全部

几大注解的区别,
@Repository:持久层,用于标注数据访问组件,即DAO组件。
@Service:业务层,用于标注业务逻辑层主键。
@Controller:控制层,用于标注控制层组件。
@Component:当你不确定是属于哪一层的时候使用。
@Controller、@Service还是@Repository本质都是@Component,即Spring容器中的一个组件。主要是为了逻辑分层。

事务几种实现方式
(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
(2)基于 TransactionProxyFactoryBean的声明式事务管理
(3)基于 @Transactional 的声明式事务管理
(4)基于Aspectj AOP配置事务

@Component 和 @Bean 的区别:
Spring帮助我们管理Bean分为两个部分,一个是注册Bean,一个装配Bean。
完成这两个动作有三种方式,一种是使用自动配置的方式、一种是使用JavaConfig的方式,一种就是使用XML配置的方式。
@Compent 作用就相当于 XML配置,
@Bean 需要在配置类中使用,即类上需要加上@Configuration注解。
两者都可以通过@Autowired装配,那为什么有了@Compent,还需要@Bean呢?
如果你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component注解的,因此就不能使用自动化装配的方案了,但是我们可以使用@Bean,当然也可以使用XML配置。

Spring扩展点,资料参考fox老师的有道云笔记https://note.youdao.com/ynoteshare/index.html?id=83d3b8e4598a2e071ae653de66832ae0&type=note&_time=1632280701916
HandlerInterceptor SpringMVC有用到,对请求进行拦截处理
MethodInterceptor AOP、@Transaction有用到

解析带有某些注解的类,生成beandefinition,再把beandefinition通过beanfactory,反射实例化成一个个bean。

@Autowired(required=true):当使用@Autowired注解的时候,其实默认就是@Autowired(required=true),表示注入的时候,该bean必须存在,否则就会注入失败。
@PostConstruct注解好多人以为是Spring提供的。其实是Java自己的注解。
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注解的方法)

ConditionalOnProperty注解常用属性:
1.matchIfMissing属性:从application.properties中读取某个属性值,如果该值为空,默认值为true;
2.havingValue属性:通过其两个属性name以及havingValue来实现的,其中name用来从application.properties中读取某个属性值,如果该值为空,则返回false;如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false。如果返回值为false,则该configuration不生效;为true则生效。

@Configuration
public class WebConfig { 
	@Bean
	@ConditionalOnProperty(prefix = "rest", name = "auth-open", havingValue = "true", matchIfMissing = true)
	public AuthFilter jwtAuthenticationTokenFilter() {
		return new AuthFilter();
	}
}

prefix:application.properties配置的前缀
name:属性是从application.properties配置文件中读取属性值
havingValue:配置读取的属性值跟havingValue做比较,如果一样则返回true,否则返回false;如果返回值为false,则该configuration不生效;为true则生效
matchIfMissing = true:表示如果没有在application.properties设置该属性,则默认为条件为true。
上面代码的意思:是否启动jwt的的配置,如果application.properties配置中没有设置就启动jwt,如果设置了true就启动,如果false就关闭。

application.properties 配置如下

rest:
    auth-open: true # jwt鉴权机制是否开启(true或者false)

ConditionalOnMissingBean只能在@Bean 注释的方法上使用,不能在类上使用。不存在某个bean的话,该配置才会生效。

 @Configuration(proxyBeanMethods = false)ApacheHttpClient这个类,才生效
	@ConditionalOnClass(ApacheHttpClient.class)
	
	没有com.netflix.loadbalancer.ILoadBalancer这个类,才生效
	@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
	
	没有CloseableHttpClient这个类,才生效
	@ConditionalOnMissingBean(CloseableHttpClient.class)
	
	配置文件中要设置feign.httpclient.enabled,这个配置类才生效,未配置就不会生效; matchIfMissing = true即便没有设置该属性,默认值也为true
	@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
	protected static class HttpClientFeignConfiguration {

spring的两个核心特性,依赖注入(dependencyinjection,DI)和面向切面编程(aspect-oriented programming,AOP)。IOC是依赖倒置原则的设计思想,而DI是具体的实现方式。
spring是一个轻量级的开源框架。
spring为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题
spring是一个IOC和AOP的容器框架。
IOC:控制反转 ,所谓IOC ,就简短一句话:对象由spring来创建、管理,装配!
AOP:面向切面编程
容器:包含并管理应用对象的生命周期

Spring IOC的三种主要注入方式: 构造方法注入,setter注入,基于注解的注入。

ioc优点:(集中管理、复用、解耦、高扩展)

  1. 集中管理
  2. 功能可复用(减少对象的创建和内存消耗)
  3. 使得程序的整个体系结构可维护性、灵活性、扩展性变高
  4. 解耦

配置元数据
1.使用xml的配置:简单、直观 适合入门
2.基于注解的配置: @Compont(@serivce @controller@repository) @Autowride
3.基于Java的配置: @Configuration @Bean @Import

实例化Bean
默认使用无参构造函数实例化,无法干预实例化过程。
依赖注入是基于setter方法进行依赖注入(为bean中的某个属性赋值), name是根据set方法的名字来的 比如方法名字是: setIdxx ‐> name=“idxx” < name=“realname” value=“张三”>
默认是饿汉式加载、单例。@Scope可以控制是singleton单例还是prototype原型
‐自动注入先根据bytype,再byname。
当使用AutoWired注解的时候,自动装配的时候是根据类型实现的。
1、如果只找到一个,则直接进行赋值,
2、如果没有找到,则直接抛出异常,
3、如果找到多个,那么会按照变量名作为id继续匹配,
- a、匹配上直接进行装配
- b、如果匹配不上则直接报异常
还可以使用@Qualifier注解来指定id的名称,@Qualifier(“personService”)

在使用自动装配的时候,除了可以使用@AutoWired注解之外,还可
以使用@Resource注解,大家需要知道这两个注解的区别。
1、@AutoWired:是spring中提供的注解,@Resource:是jdk中定义的
注解,依靠的是java的标准
2、@AutoWired默认是按照类型进行装配,默认情况下要求依赖的对
象必须存在,@Resource默认是按照名字进行匹配的,同时可以指定name属
性。
3、@AutoWired只适合spring框架,而@Resource扩展性更好

AOP:
在不修改原有代码的情况下 增强跟主要业务没有关系的公共功能代码到 之
前写好的方法中的指定位置 这种编程的方式叫AOP。
AOP的底层是用的动态代理,jdk动态代理 :必须保证被代理的类实现某个接口;
cglib动态代理 :被代理的类不需要实现某个接口。

可以简单地认为, 使用 @Aspect 注解的类就是切面。

  1. 前置通知(@Before):在目标方法被调用之前调用通知功能;
  2. 后置通知(@After):在目标方法完成之后调用通知,总是会执行,接着后面执行的可能是后置返回也可能是异常通知;
  3. 后置返回通知(@AfterReturning ):在目标方法成功执行之后调用通知;
  4. 异常通知(@AfterThrowing):在目标方法抛出异常后调用通知;
    通知方法的执行顺序(@After­­­(后置通知)总是会执行)
    1、正常执行:@Before(前置通知)> 方法 > @After­­­(后置通知) > @AfterReturning(后置返回通知)
    2、异常执行:@Before­­­(前置通知) > 方法 > @Afte(后置通知) ­>@AfterThrowing(后置异常通知)
    @Around环绕通知是spring中功能最强大的通知
 execution(访问修饰符 返回值类型 方法全称)  访问修饰符一般都不写
@Before("execution(* cn.tulingxueyuan.service..*.*(..))")
 public static void start(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
 String name = joinPoint.getSignature().getName();
System.out.println(name+"方法开始执行,参数是:"+ Arrays.asList(args));
}

Spring声明式事务
@Transactional 可以标记在类上面(当前类所有的方法都运用上了事务)
@Transactional 标记在方法则只是当前方法运用事务
建议:@Transactional写在方法上面,控制粒度更细, 建议@Transactional写在业务逻
辑层上,因为只有业务逻辑层才会有嵌套调用的情况。

事务配置的属性
isolation:设置事务的隔离级别,当不设置事务隔离级别将使用数据库的默认事务隔离级别,#MYSQL:REPEATABLE‐READ
propagation:事务的传播行为
noRollbackFor:那些异常事务可以不回滚
noRollbackForClassName:填写的参数是全类名
rollbackFor:哪些异常事务需要回滚
rollbackForClassName:填写的参数是全类名
readOnly:设置事务是否为只读事务
timeout:事务超出指定执行时长后自动终止并回滚,单位是秒

事务的传播特性指的是当一个事务方法被另一个事务方法调用时,这个事务方法
应该如何进行?希望如果外部存在事务就用外部的, 外部不存在就自己开启事务。
在这里插入图片描述
REQUIRED的特性,本质上还是同一个事务的不同保存点,如果涉及到外层事务回滚,则内层的也将会被回滚;

REQUIRED_NEW 的实现对应的是一个新的事务,拿到的是新的资源,所以外层事务回滚时,不影响内层事务。

spring的事务传播行为例子,默认是REQUIRED
@Transactional
trans(){
sub();
log(); // 记录流水 数据库操作
add();
}
@Transactional 
log();// 外部事务方法trans()调用内部事务方法log,默认的事务传播行为是融合到外部事务,
     //一旦 sub()出异常或者add()出异常,log()方法即使正确执行也是要进行回滚的。

设置事务只读(readOnly)。
1、设置哪些异常不回滚(noRollbackFor)
2、设置哪些异常回滚(rollbackFor )
@Transactional(timeout = 3,rollbackFor = {FileNotFoundException.class})

6、在实战中事务的使用方式
如果当前业务方法是一组 增、改、删 可以这样设置事务
@Transactional
如果当前业务方法是一组 查询 可以这样设置事务
@Transactionl(readOnly=true)
如果当前业务方法是单个 查询 可以这样设置事务
@Transactionl(propagation=propagation.SUPPORTS ,readOnly=true)

spring源码:

在这里插入图片描述

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
		@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		// 这里getBean,既能获取bean也能生产bean
		return getBeanFactory().getBean(name);
	}
}
public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/beans/beans.xml");
		ctx.getBean("car");
		
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EnableCachingConfig.class);ClassPathXmlApplicationContext或者AnnotationConfigApplicationContext
这两个衣柜店(读取配置文件的方式不同)通过设计师BeanDefinitionRegistry生成统一的图纸BeanDefinitionAbstractBeanDefinitionBeanDefinition的子类)

BeanFactoryPostProcessor bean工厂后置处理器,专门修改BeanDefinition; 配置类的解析就是由它来解析的,只不过是用它的子类ConfigurationClassPostProcessor等等去解析的。
BeanPostProcessor 是bean后置处理器,专门对bean生产期间做些扩展,贯穿bean构建的整个生命周期。

BeanFactory是ApplicationContext的父类,二者都有生产bean的能力(BeanFactory是根据BeanDefinition来生产bean的)。ApplicationContext扩展了很多功能,比如事件、国际化等。

beanfactory和factorybean的区别:(https://blog.csdn.net/wangbiao007/article/details/53183764)
beanfactory是Bean的工厂,顶层核心接口,只负责生产bean,ApplicationContext会提供一系列人性化服务(如国际化、加载Bean定义、监听器等等)。

public interface FactoryBean<T> {  
    T getObject() throws Exception; // 返回的bean的类型就是泛型T  
    Class<?> getObjectType();  
    boolean isSingleton();  
} 

FactoryBean 是一个扩展点,我们自定义的类实现该接口重写getObject()方法,最终会调用 getObject()方法(返回值是Object,所以可以返回我们指定的对象),返回一个最终的bean。就把它当做一个改装车行,它可以改装你原本的bean。

源码过程参考图灵徐庶老师的笔记02-Spring-IoC源码.pdf。
ConfigurationClassPostProcessor该类实现了BeanFactoryPostProcessor , RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);在这里注册成bean定义。叫做创世纪类,用来解析配置类的,把自己生成BeanDefinition放在private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>中。

AutowiredAnnotationBeanPostProcessor该类也实现了BeanFactoryPostProcessor 。RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);在这里把自己注册成bean定义,也叫做创世纪类,用来解析@Autowired @Value等。

SpringIOC生成bean定义过程(参考第一节课课上图):
总结:1 加载很多创世纪类(ConfigurationClassPostProcessor(用来解析配置类的)、AutowiredAnnotationBeanPostProcessor(用来解析@Autowired @Value等)等创世纪类,它们都是BeanFactoryPostProcessor(bean工厂后置处理器)的子类 ) ;2 由创世纪类解析带有@Component等注解的配置类,生成BeanDefinition存放到DefaultListableBeanFactory类中的private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256)中去; 3 生成bean定义后,可以由BeanDefinitionRegistryPostProcessor等类修改BeanDefinition;4 调用BeanFactory的getBean方法开始生产bean,实例化对象、填充属性(解析@Autowired,@Value等注解);5 在bean的创建过程中,会有很多的扩展点进行扩展,都是调用BeanPostProcessor(bean后置处理器) 的某些方法进行扩展; 6 还会调用很多Aware类(比如BeanNameAware)的方法,再次对bean进行扩展;7 生成最终的bean放入DefaultSingletonBeanRegistry类中的 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); Map<beanname, bean实例>(map里面存的数据就是这个)。

第一步实例化BeanDefinitionReader接口的一个子类会加载很多创世纪类,这些创世纪类用来解析配置类、@Autowired 等注解的 --> 再由ClassPathBeanDefinitionScanner(xml的方式会在这里处理很多逻辑,注解的方式用到的代码很少,所以二者是稍微有点差别的。) BeanDefinition存放在DefaultListableBeanFactory类中的ConcurrentHashMap属性里面。BeanDefinitionMap中就有了一个很重要的创世纪类ConfigurationClassPostProcessor(bean工厂后置处理器的子类,带有注册功能的)。以上是在public AnnotationConfigApplicationContext(Class<?>… annotatedClasses) { this() }方法中完成的。
第二步,在register(annotatedClasses);去扫描指定包下的带有@Component等注解的类,把这些配置类也注册到BeanDefinitionMap中去,此时BeanDefinitionMap就有了解析配置类的ConfigurationClassPostProcessor等创世纪类(bean工厂后置处理器)和所有的配置类。

第三步,就到了refresh()方法了,调用一个很重要的方法
//5: 调用我们的bean工厂的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
这个方法就是用ConfigurationClassPostProcessor(bean工厂后置处理器的子类,带有注册功能的)去解析我们的配置类(配置类上有各种注解@component,@Configuration等等)。解析后就生成BeanDefinition,也是存放到BeanDefinitionMap(DefaultListableBeanFactory类中的一个属性,private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);)中去。

生成BeanDefinition之后,ApplicationContext提供了对BeanDefinition进行修改、再新增注册的功能,由BeanFactoryPostProcessor来修改BeanDefinition,由BeanDefinitionRegistryPostProcessor 来注册成新的BeanDefinition。接着调用BeanFactory 的getBean方法,实例化对象、填充属性(解析@Autowired,@Value等注解)、初始化(initMethod destroy,调用一堆的Aware)。在bean的创建过程中(这三个方法的前后),会有很多的扩展点进行扩展,都是调用BeanPostProcessor 及其子类的某些方法进行扩展。生产完成之后,把bean放在一个Map里面,DefaultSingletonBeanRegistry类中的 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); Map<beanname, bean实例>(map里面存的就是这个)。 二级缓存、三级缓存都在这个类里面。

 /**BeanFactory spring的顶层核心接口,使用了简单工厂模式,负责生产bean的
 * IOCbean工厂的顶级接口   定义一系列对bean的操作
 */
public interface BeanFactory {

}
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
	BeanDefinition也是spring的一个顶层核心接口,封装了生产bean的一切原料。主要是其子类AbstractBeanDefinition 
	中定义了很多属性。
}

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
		// BeanDefinitiond的子类,定义了很多属性在里面
		/**
	 * 用于保存bean组件的class对象
	 */
	@Nullable
	private volatile Object beanClass;
	/**
	 * bean的作用范围
	 * 默认是singleton,prototype
	 */
	@Nullable
	private String scope = SCOPE_DEFAULT;
}
public interface BeanDefinitionRegistry extends AliasRegistry {
// 将读取到的配置信息注册生成成BeanDefinition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;
}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** 用于保存原始的bean定义信息(没有被mearged) */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
}

BeanFactoryPostProcessor(bean工厂后置处理器)是一个扩展点,可以修改BeanDefinition,只需要我们自己定义一个类实现接口BeanFactoryPostProcessor,重写postProcessBeanFactory方法即可。扩展点都是属于ApplicationContext的,不属于BeanFactory,BeanDefinitionRegistryPostProcessor(bean定义注册后置处理器)也是一个扩展点,实现了BeanFactoryPostProcessor 。

// 扩展点  bean工厂后置处理器,用来修改BeanDefinition
public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
/**
    扩展点 bean定义注册后置处理器,用来注册成BeanDefinition
	BeanFactoryPostProcessor的子接口,用来注册成BeanDefinition的
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}
//  扩展点  bean的后置处理器,有多个实现类,在生成bean期间大约有9次调用它的实现类。可以参考fox老师的笔记
https://www.processon.com/view/link/5eafa609f346fb177ba8091f
public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	// 创建AOP动态代理就在这里,由子类AbstractAutoProxyCreator来重写这个方法
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}
try {
/**
 * <3> 初始化 bean 这个过程其实是调用 createBean() 方法,singletonFactory是函数式接口对象,该接口只有一个方法。
 * 这里会触发 sharedInstance = getSingleton(beanName, () -> {
 * 						try {
 * 							//进入创建bean的逻辑
 * 							return createBean(beanName, mbd, args);
 *                                                }
 * 						catch (BeansException ex) {
 * 							//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
 * 							destroySingleton(beanName);
 * 							throw ex;
 *                        }* 					});
 *  只触发方法createBean(beanName, mbd, args);相当于在singletonFactory.getObject()方法中调用createBean(beanName, mbd, args)方法
 */
singletonObject = singletonFactory.getObject();//先执行这个方法后再执行createBean(beanName, mbd, args);
newSingleton = true;
}

//创建单例bean
if (mbd.isSingleton()) {
/**
 * 把beanName 和一个singletonFactory 并且传入一个回调对象用于回调,并把bean标记为正在创建
 * 和上面的Object sharedInstance = getSingleton(beanName);不是同一个方法, 是方法重载了
 * 函数接口,lamanda表达式, 把一个方法作为一个参数传进去,
 */
sharedInstance = getSingleton(beanName, () -> {
	try {
		//进入创建bean的逻辑
		return createBean(beanName, mbd, args);
	}
	catch (BeansException ex) {
		//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
		destroySingleton(beanName);
		throw ex;
	}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

加了@Configuration注解,spring才会给该类做cglib动态代理,否则就不做。
不做的话,类中的方法即使加了@Bean注解,方法中如果有new User()的话,就会每次都创建一个新对象。 @Configuration 、 @Bean这两个注解都加,新建的对象才受ioc容器控制,才是单例的,否则就不受ioc容器控制,对象就是多例的。
两个类上都有@Component(“car”),会直接报错;如果一个类上是@Component(“car”),另一个类上是@Configuration,方法里面有 @Bean (“car”),这种情况就不会报错,因为@Bean是后面才加载的,会覆盖前面那个@Component(“car”)的bean。

//5: 调用我们的bean工厂的后置处理器. 1. 会在此将class扫描成beanDefinition 2.bean工厂的后置处理器调用 解析配置类
invokeBeanFactoryPostProcessors(beanFactory);以下是该方法的调用链。。。。。。。。

public class ConfigurationClassPostProcesso
//真正的解析我们的配置类
parser.parse(candidates);

class ConfigurationClassParser
//解析我们的配置类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
//从我们的配置类上解析处ComponentScans的对象集合属性
Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
//把我们扫描出来的类变为bean定义的集合 真正的解析
Set scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

class ComponentScanAnnotationParser
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean(“useDefaultFilters”), this.environment, this.resourceLoader);// 这里发现了ClassPathBeanDefinitionScanner,跟最开始的那个对象并非同一个。
//真正的进行扫描解析
return scanner.doScan(StringUtils.toStringArray(basePackages));

public class ClassPathBeanDefinitionScanner
//找到候选的Components
Set candidates = findCandidateComponents(basePackage);
registerBeanDefinition(definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// DefaultListableBeanFactory 实现了BeanDefinitionRegistry
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// bean定义就放入到ConcurrentHashMap中去了
this.beanDefinitionMap.put(beanName, beanDefinition);

public class ClassPathScanningCandidateComponentProvider
return scanCandidateComponents(basePackage);
//把我们的包路径转为资源路径 cn/tulingxueyuan/MainConfig
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + ‘/’ + this.resourcePattern;

解决循环依赖:
总结:先去一级缓存中去拿,拿不到就去二级缓存去拿,再拿不到就去三级缓存中拿,三级缓存中是有动态代理对象的(也可能是原实例),会把这个动态代理对象存入二级缓存,再存入一级缓存,再删除二三级缓存。
没有出现循环依赖,是不会去二三级缓存中去拿bean的,直接在一级缓存中就能获取到bean了
// 一级缓存: 存的是完整的bean(成熟bean)或者是动态代理对象,该bean可能是AOP的一个切面,所以要存动态代理对象。
// 二级缓存: 存的是纯净bean,还来不及给bean的属性进行赋值的对象。是为了将成熟Bean和纯净Bean分离,避免读取到不完整得bean, 不存原实例
// 三级缓存 不存bean的实例,存的是一个函数式接口(即不会立即缓存单例对象,而是等待调用时再返回),函数式接口调用的是bean后置处理器。三级缓存更多的是用来解耦,代码更清晰,也可以说是用来解决AOP(这说法不完全准确)
创建bean的时候,在哪里创建动态代理? 有2个地方,第一个是没有循环依赖时,bean初始化之后,abstract class AbstractAutowireCapableBeanFactory//进行对象初始化操作(在这里可能生成代理对象)exposedObject = initializeBean(beanName, exposedObject, mbd);
第二个是出现了循环依赖时,会在bean实例化之后调用。

@Component
public class InstanceA implements IApi {
@Autowired
private InstanceB instanceB;
}
@Component
public class InstanceB implements IApi {
@Autowired
private InstanceA instanceA;
}
假如A类依赖B类,B类依赖A类;程序首先解析A的话, 会先把B放入一级缓存、再把A放入一级缓存。
读取到bean定义,开始创建bean,首先第一次调用递归方法getBean,再调用getSingleton去一级缓存拿,第一次会拿不到A,拿不到就返回null,给该bean A标记为正在创建,实例化A,把动态代理对象存入三级缓存,接下来就解析InstanceA类中的所有属性 ,拿到A类中属性private InstanceB instanceB; 继续解析InstanceB,第二次调用递归getBean解析方法。继续调用getSingleton,同样第一次去一级缓存中是获取不到的B,拿不到就返回null,给该bean B标记为正在创建,实例化B,
解析B类中的private InstanceA instanceA,第三次调用递归getBean解析方法(此时又再次解析A)。在getBean方法中继续调用getSingleton去一级缓存中获取,仍然没有获取到,但是发现bean A被标记为正在被创建,就会去二级缓存中拿,二级缓存暂时也没有,就从三级缓存中拿,三级缓存本来没有,但是调用了钩子函数factory.getObject()后,bean就有值了,并且是一个动态代理对象,会马上把这个动态代理对象放入二级缓存,此时因为bean有值了,方法结束,把这个bean返回,if(singleton!=null){
return singleton; },Object fileObject= getBean(name);此时fileObject就有值了,继续执行下面的代码 declaredField.set(instanceBean,fileObject),这里是给B类中的 InstanceA 属性赋值,该值是一个动态代理对象。由于递归完后A 还是原实例, 所以要从二级缓存中拿到动态代理对象赋值给原实例instanceBean。最后再把这个原实例instanceBean(动态代理对象)添加到一级缓存,并且还要删除掉二三级缓存。 以上是使用了AOP,AOP就需要动态代理对象,如果没有使用AOP的话, 就不用把动态代理对象赋值给原实例。

abstract class AbstractApplicationContext
//11: 实例化我们剩余的单实例bean.
finishBeanFactoryInitialization(beanFactory);
//实例化剩余的单实例bean
beanFactory.preInstantiateSingletons();

class DefaultListableBeanFactory
getBean(beanName);

abstract class AbstractBeanFactory
doGetBean

class DefaultSingletonBeanRegistry
// <5> 加入缓存中
addSingleton(beanName, singletonObject);// 加入一级缓存,删除二三级缓存。

abstract class AbstractAutowireCapableBeanFactory
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 把纯净态bean放入到三级缓存中去
// 把我们的早期对象包装成一个singletonFactory对象 该对象提供了一个getObject方法,该方法内部调用getEarlyBeanReference方法
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));// 三级缓存里面就是只缓存了一个单例bean工厂
//加入到三级缓存中,,,,,暴露早期对象用于解决循环依赖
this.singletonFactories.put(beanName, singletonFactory);
//属性赋值 给我们的属性进行赋值(调用set方法进行赋值)
populateBean(beanName, mbd, instanceWrapper);
/**
* 去缓存中获取到我们的对象 由于传递的allowEarlyReference 是false 要求只能在一级二级缓存中去获取
* 正常普通的bean(不存在循环依赖的bean) 创建的过程中,压根不会把三级缓存提升到二级缓存中
*/
Object earlySingletonReference = getSingleton(beanName, false);

//动态代理
abstract class AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
//创建我们的真正的代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
//真正的创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());

public class ProxyFactory extends ProxyCreatorSupport
public Object getProxy(@Nullable ClassLoader classLoader) {
//createAopProxy() 用来获取我们的代理工厂
return createAopProxy().getProxy(classLoader);
}

final class JdkDynamicAopProxy
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

老师课堂上某些问题的回答思路:
Spring并没有解决构造函数的循环依赖,因为会在实例化的时候一直无限循环下去。
Object instanceBean = beanClass.newInstance(); // 通过无参构造函数(老师写的代码,在spring-tl-beans项目中的com.tuling.circulardependencies.MainStart类中)
会在这里死循环,无法执行后面的代码,导致去缓存中实例都拿不到。

构造函数的循环依赖  
@Component
public class CircularDependencyA {  
 private CircularDependencyB circB;
 @Autowired
 public CircularDependencyA(CircularDependencyB circB) {
  this.circB = circB;
 }
}

@Component
public class CircularDependencyB {  
 private CircularDependencyA circA;
 @Autowired
 public CircularDependencyB(CircularDependencyA circA) {
  this.circA = circA;
 }
}

还有一些解决循环依赖的方法:https://www.jb51.net/article/185514.htm

多例是不能解决循环依赖的,因为多例bean是不会存到缓存(代码中显示为首先放入到三级缓存)当中去。

Spring监听器源码课
在对象进行初始化时,调用了invokeAwareMethods方法,此时只用到了3个Aware

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
//若我们的bean实现了XXXAware接口进行方法的回调
			invokeAwareMethods(beanName, bean);}
此时只用到了3Aware
private void invokeAwareMethods(final String beanName, final Object bean) {
		if (bean instanceof Aware) {
			//我们的bean实现了BeanNameAware
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			//实现了BeanClassLoaderAware接口
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			//实现了BeanFactoryAware
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}
注册了一个完整的ApplicationContextAwareProcessor 后置处理器用来处理ApplicationContextAware接口的回调方法

```java
/**
	 * 为我们的bean工厂填充内部属性
	 * @param beanFactory the BeanFactory to configure
	 */
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	//注册了一个完整的ApplicationContextAwareProcessor 后置处理器用来处理ApplicationContextAware接口的回调方法
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
	/**
		 *
		 * 忽略以下接口的bean的 接口函数方法。 在populateBean时
		 * 因为以下接口都有setXXX方法, 这些方法不特殊处理的话将会自动注入容器中的bean,
			一旦这里设置了忽略,spring不会把这些配置类自动注成bean。
		 * 这里只有对xml配置方式起效,对JavaConfig注解配置并不起效,
		 * 因为JavaConfig并不能设置忽略, @Service无法这样子设置忽略,
		 * JavaConfig只能通过Implements BeanFactoryPostProcessor  重写里面的postProcessBeanFactory方法来实现修改
		 */
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		/**
		 * 当注册了依赖解析后,例如当注册了对 BeanFactory.class 的解析依赖后,
		 * 当 bean 的属性注 入的时候, 一旦检测到属性为 BeanFactory 类型便会将 beanFactory   的实例注入进去。
		 * 知道为什么可以
		 * @Autowired
		 * ApplicationContext  applicationContext 
		 *  就是因为这里设置了
		 */
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);
}

ApplicationContextAwareProcessor类中有invokeAwareInterfaces这个方法调用了除去前面3个之外的很多的Aware
private void invokeAwareInterfaces(Object bean) {




```java
//属性赋值 给我们的属性进行赋值(调用set方法进行赋值)
populateBean(beanName, mbd, instanceWrapper);

一文告诉你Spring是如何利用“三级缓存“巧妙解决Bean的循环依赖问题的【享学Spring】(https://fangshixiang.blog.csdn.net/article/details/92801300)

springboot笔记整理

@SpringBootApplication --> @EnableAutoConfiguration --> @Import(AutoConfigurationImportSelector.class) 点击 AutoConfigurationImportSelector.class就进入到这个类,这个类实现了DeferredImportSelector接口(延时加载、分组),重写了方法public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)。

// 从META-INF/spring.factories中获得候选的自动配置类List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

任何一个springboot应用,都会引入Maven:org.springfraemwork.boot:spring-boot-autoconfigure:2.2.13RELEASE,而spring.factories文件就在该包下面。spring.factories文件是Key=Value形式,多个Value时使用,隔开,该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration

Auto Configuration Import Filters

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=
org.springframework.boot.autoconfigure.condition.OnBeanCondition,
org.springframework.boot.autoconfigure.condition.OnClassCondition,
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
matchIfMissing = true 如果没配也生效

会发现AopAutoConfiguration 中的内部类

@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {
	// 中的Advice.class飘红,所以该内部类AspectJAutoProxyingConfiguration 不生效}

要想生效就需要在pom.xml中添加对应的启动类后就能生效(Advice.class变成正常的颜色)

 <dependency>
     <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>

可以在类AspectJAutoProxyingConfiguration 中还能看到两个内部类,springboot默认使用Cglib代理

启动原理:
pom.xml中有一个插件

 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

fat jar(就是通过插件打出来的jar包,里面还包含了其他的jar包)这个插件在打包jar包时,生成了一个META-INF文件夹,这个文件夹里面有一个MANIFEST.MF文件,这个文件里面指定了Main-Class: org.springframework.boot.loader.JarLauncher,从而就指定了启动的主程序,这个类是一个类加载器,会去加载jar包中的jar文件(BOOT-INF下的classes目录和lib目录下的jar文件)。MANIFEST.MF中有这样的代码, Start-Class: com.alibaba.csp.sentinel.dashboard.DashboardApplication 是通过反射来启动这个类的。
看源码可以从这里面开始看SpringApplication.run(SpringbootmybatisplusApplication.class, args);
ConfigFileApplicationListener 有这么一个类,就可以看到加载yml配置文件的顺序

* <ul>
 * <li>file:./config/</li>
 * <li>file:./</li>
 * <li>classpath:config/</li>
 * <li>classpath:</li>
 * </ul>

在run方法中有一个比较重要的方法refreshContext(context);就是调用spring的refresh方法。

排除springboot内嵌的tomcat有两种方法, 一个是用<exclusions>,另一个是用<scopse>,详见徐庶老师的springboot启动原理源码课1:55:00.

spring源码中涉及到的一些设计模式
1 单例模式(看样子是循环依赖中用到)
在这里插入图片描述二、适配器模式
主要是在springmvc中的一个HandlerAdapter
三、代理模式
AOP用代理模式实现的,有JDK动态代理(需要实现接口)和CGLib代理(直接给字节码加代码)

四、工厂模式
1、aop中,代理的创建用了工厂模式
在这里插入图片描述
五、责任链模式(Filter 链)
六、简单工厂:BeanFactory,根据传入的一个唯一标志来getBean对象。
七、工厂方法:FacotryBean接口,一个子类A实现该接口,重写getObject()方法,返回的并不是A对象,返回的是一个B对象。

八、观察者模式(Spring 事件监听器)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值