Spring知识总结

本人小白一个,不能保证博客中内容都准确,如果博客中有错误的地方,望各位多多指教,请指正。欢迎找我一起讨论

 

Spring知识总结

     1、Spring中IOC的加载过程

 

 

 

 

 

                   IOC的加载过程其实就是配置的类去创建成一个bean的过程。

                     将一个类创建成一个bean,我们通常是怎么做的呢?

                                 ①配置类 (通过XML或者注解)

                                 ②加载Spring容器(ClassPathXmlApplicationContext  、AnnotationConfigApplicationContext )

                                               ClassPathXmlApplicationContext:读取XML配置文件

                                               AnnotationConfigApplicationContext :读取注解

                                         不管是通过上面的哪种方式,都是将类的一些信息读取到 IOC 容器,存入BeanDefinition

                                 ③getBean()

                     下面以注解的方式讲解IOC的加载过程:

                                 ①  new AnnotationConfigApplicationContext()

                                               

                                               (1)this(): new了三个对象:DefaultListableBeanFactory 、AnnotatedBeanDefinitionReader 、ClassPathBeanDefinitionScanner

                                                                            DefaultListableBeanFactory 是整个bean加载的核心部分,是Spring注册及加载bean的默认实现

                                                                            AnnotatedBeanDefinitionReader 用来加载class类型的配置,将配置的bean定义信息一个一个的注册到BeanDefinition,一个类对应一个BeanDefinition

                                                                            ClassPathBeanDefinitionScanner 扫描@ComponentScan("路径") 设置了路径下的文件/类,将@Component、@Controller、@Service、@Repository标记的类的定义信息一个一个的注册到BeanDefinition,一个类对应一个BeanDefinition

                                                   父类 GenericApplicationContext

                                                                  

                                                   当前类 AnnotationConfigApplicationContext

                                                                 

                                               (2)register(componentClasses):使用AnnotatedBeanDefinitionReader将配置的类注册到BeanDefinition

                                                                

                                               (3)refresh()

     2、bean的生命周期(单例bean)

                     简单的说就是:  实例化(相当于new,开内存空间。属性值为默认值)--->初始化(set注入,给属性赋值)--->销毁

                     稍微详细点说就是:对着下面这张图一顿bb,具体怎么bb,自行脑补。

                                  

                                 1、实例化bean对象

                                 2、给bean对象填充属性

                                 3、检查一大堆xxxAware接口,通过重写它们的set方法,获取相对应的容器对象,设置相关的依赖

                                                普通对象(你自定义的  你写的bean标签的东西)  

                                                容器对象(容器内部自己使用的对象)

                                                

                                               那么问题来了,通过实现XxxAware接口,自定义设置的相关依赖,是什么时候注入进去的呢?又是怎么注入进去的呢?

                                                           在调用 initializeBean()初始化的时候,在该方法内调用的 invokeAwareMethods()方法将图片上面设置的依赖注入进去的

                                                                AbstractAutowireCapableBeanFactory#initializeBean()初始化方法部分代码:

                                                                

                                                               AbstractAutowireCapableBeanFactory#invokeAwareMethods()方法:

                                                                

                                                         这里仅仅是注入了BeanName、classLoader、beanFactory,那么其他的在哪里注入的呢?

                                                            在调用 initializeBean()初始化的时候,在该方法内调用的 postProcessBeforeInitialization()方法将其他设置的依赖注入进去的

                                                               AbstractAutowireCapableBeanFactory#postProcessBeforeInitialization()方法部分代码:

                                                               

                                                              ApplicationContextAwareProcessor#invokeAwareInterface()方法:

                                                             

                                 4、调用bean前置处理器。   applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)   

                                 5、如果用户自定义了init方法,调用用户自定义的init()方法 。   invokeInitMethods(beanName, wrappedBean, mbd)   

                                             在<bean>标签中 init-method 属性设置的方法

                                 6、调用bean的后置处理器。    applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)  

                                             在此处进行了AOP增强

                                 7、bean初始化结束,创建好的bean被放入单例池中

                                 8、注册必要的Destruction相关的回调接口  

                                 9、如果用户自定义了destroy方法,就调用用户自定义的destroy方法

                                             在<bean>标签中 destroy-method 属性设置的方法

                                 10、bean销毁,就这样结束了自己的一生

 

     3、bean的循环依赖问题

              1、什么是循环依赖?

                             循环依赖就是引用依赖,也就是两个或多个bean相互持有对方,如下图,Aservice中引用了Bservice,而Bservice中又引用的Aservice。此时Aservice实例的创建需要先有Bservice,而Bservice实例的创建又需要先有Aservice,此时就自然形成了一个环状结构。

                                                    

              2、怎么解决循环依赖?

                              以上面图中代码为例:

                             1)、构造器循环依赖(单例)--------- spring解决不了

                                               Spring容器创建Aservice的实例,首先会去“正在创建bean池(Set<String>  singletonsCurrentlyInCreation”中查找当前bean是否在创建,如果没发现,就往下执行,而此时构造器参数需要Bservice的实例,但是此时没有Bservice,将Aservice标识符放到“正在创建bean池”,去创建Bservice的实例。

                                              Spring容器创建Bservice的实例,首先会去“正在创建bean池(Set<String>  singletonsCurrentlyInCreation”中查找当前bean是否在创建,如果没发现,就往下执行,而此时构造器参数需要Aservice的实例,但是此时没有Aservice,将Bservice标识符放到“正在创建bean池”,去创建Aservice的实例。

                                               Spring容器创建Aservice的实例,首先会去“正在创建bean池(Set<String>  singletonsCurrentlyInCreation”中查找当前bean是否在创建,而此时在“正在创建bean池”发现了Aservice标识符,表示了循环依赖,于是抛出BeanCurrentlyInCreationException异常。对于创建完毕的Bean将从singletonsCurrentlyInCreation中清除掉。

                                                             

                                               由于源码中是先创建了bean的实例(实例化),才将这个bean提前曝光,也就是将bean添加到 Map<String, ObjectFactory<?>> singletonFactories中,所以构造方法的循环依赖Spring解决不了。

                                                       为什么叫提前曝光呢?

                                                             因为这个缓存中的bean是一个未进行赋值的bean,仅仅是一个引用。

                                                                 具体代码看下面的图,都是在 AbstractAutowireCapableBeanFactory 类中,图一和图三在同一个方法中(doCreateBean方法),图二是图一中调用的createBeanInstance()方法也是就在这个方法中实例化了bean,所以结合下面三个图中的源码,可以看出spring容器是先实例化bean之后才将bean提前曝光的,所以构造依赖解决不了。

                                                             

                                                             

                                                             

 

                             2)、setter构造循环依赖(单例)

                                      Spring提供了三级缓存来解决循环依赖。

                                             DefaultSingletonBeanRegistry类中 :

                                                   一级缓存(单例池):  Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

                                                   二级缓存: Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

                                                   三级缓存: Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

 

                                               Spring容器创建Aservice的实例,首先会去“正在创建bean池(Set<String>  singletonsCurrentlyInCreation”中查找当前bean是否在创建,如果没发现,就往下执行,将Aservice标识符放到“正在创建bean池”中,根据无参构造创建实例bean,提前暴露到三级缓存singletonFactories 中,然后进行setter注入Bservice。

                                                Spring容器创建Bservice的实例,首先会去“正在创建bean池(Set<String>  singletonsCurrentlyInCreation”中查找当前bean是否在创建,如果没发现,就往下执行,将Bservice标识符放到“正在创建bean池”中,根据无参构造创建实例bean,提前暴露到三级缓存singletonFactories 中,然后进行setter注入Aservice,在注入Aservice的时候,由于Aservice已经提前暴露在了三级缓存singletonFactories 中,此时Bservice就可以去三级缓存singletonFactories 中拿到Aservice的bean实例,进行setter注入Aservice。

                                                最后完成Aervice对Bservice的注入。

                                                           AbstractAutowireCapableBeanFactory#doCreateBean()

                                                          

                                                          DefaultSingletonBeanRegistry#addSingletonFactory()

                                                          

                                                         

                                                          AbstractBeanFactory#doGetBean()

                                                          

                                                          DefaultSingletonBeanRegistry#getSingleton()

                                                          

                                                                     ①先从singletonObjects(一级缓存)中获取实例,如果可以获取到则直接返回singletonObject实例。
                                                                     ②如果从singletonObjects(一级缓存)中获取不对实例,再从earlySingletonObjects(二级缓存)中获取实例,如果可以获取到则直接返回singletonObject实例。
                                                                     ③如果从earlySingletonObjects(二级缓存)中获取不对实例,则从singletonFactories(三级缓存)中获取singletonFactory,如果获取到则调用getObject方法创建实例,把创建好的实例放到earlySingletonObjects(二级缓存)中,并且从singletonFactories(三级缓存)删除singletonFactory实例,然后返回singletonObject实例。
                                                                     ④如果从singletonObjects、earlySingletonObjects和singletonFactories中都获取不到实例,则singletonObject对象为空。

 

                             3)、原型模式下的循环依赖(spring解决不了)

                                          在创建bean的源码中直接判断 如果是原型模式下的循环依赖,直接抛出异常。

                                                AbstractBeanFactory#doGetBean()

                                                

              3、经过你的描述,二级缓存也可以解决循环依赖,那为什么还要用三级缓存呢?

                              上面描述的,就相当于, Aservice  a = new Aservice();  Bservice b = new Bservice(); b.a = a; a.b=b; ,但是此时Bservice中填充的Aservice是原始对象,也就是说此时Aservice的属性是没有进行赋值的,但好在Aservice是单例的,再Bservice创建之后,Aservice会给它的属性赋值。

                              再想一个问题,如果Aservice需要进行AOP增强,那么此时放入单例池中的对象,应该是Aservice的代理对象,然而此时Bservice填充它自己的Aservice属性时,却是填充的是Aservice的原始对象,那么这样就有问题了,此时怎么将Aservice的代理对象填充给Bservice的Aservice属性呢?

                              此时就应该想想,我能不能AOP提前呢,能不能在Aservice创建的时候,就提前AOP生成代理对象放入一个map中,然后Bservice在注入它的Aservice属性时直接将去Map中去取Aservice的代理对象,然后注入进行。正常情况下,对象的AOP增强应该是在属性赋完值之后,在调用 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName) 方法的时候进行Aop增强,然后由于此时Aservice出现了循环依赖,所以需要提前AOP生成代理对象。那么此时问题又来了,我应该怎么判断Aservice出现了循环依赖呢?

                              在Bservice的创建过程中,需要对它的属性Aservice进行注入,那么就会去“正在创建bean池(Set<String>  singletonsCurrentlyInCreation”中找,Aservice是否正在创建,如果此时Aservice正在创建,那么就说明此时出现了循环依赖,所以此时就可以让Aservice提前进行Aop增强,然后得到Aservice的代理对象。

                              此时有这样一个场景,现在在Aservice,Bservice的基础上,加了一个Cservice,此时这个Cservice与Aservice相互依赖,也就是说,Aservice中有个Cservice的属性,Cservice中有个Aservice的属性,那么此时在Cservice的创建过程中,跟Bservice创建过程一样,也会去“正在创建bean池”中Aservice是否正在创建,发现Aservice正在创建,此时就出现了循环依赖,此时Aservice又会去进行Aop增强,然后得到一个代理对象。那么此时就有问题了呀!Bservice的创建的时候得到一个Aservice的代理对象,Cservice的创建的时候又得到一个Aservice的代理对象,此时有两个不同的代理对象,此时问题就大发了呀!所以说这个问题该怎么解决呢?怎么才能让这两个代理对象是同一个代理对象呢?

                              所以此时,在Bservice的创建的时候得到一个Aservice的代理对象的时候就得那一个Map存在,当Cservice的创建的时候出现循环依赖的时候,就先去这个Map中去找,如果能找到就直接拿过来用,如果找不到,再去让Aservice进行Aopp增强。此时这个Map就是真正的二级缓存。代理对象中包含原始对象。此时问题又来了,这个Map中的代理对象能直接放入单例池(也就是一级缓存)中吗?

                              不行,因为此时的代理对象是个半成品,属性还没赋值呢,如果直接丢到单例池中,丢到单例池中去了,就说明此时这个bean已经初始化完成了,是一个完整的bean,然而此时如果直接丢进去,就相当于丢进去一个半成品,到时候别的bean拿到的都是Aservice的半成品就有问题了呀。那么问题来了,这个Map中的代理对象什么时候丢入一级缓存单例池中?--------在属性赋值完成之后,去二级缓存中拿对象,将该对象丢入单例池中。

                              Spring的AOP中target目标对象是bean的原始对象,也就是说,如果Aservice想要进行AOP,就需要拿到Aservice的原始对象,所以此时就需要一个Map来存,然后这个Map(三级缓存)存的不仅仅是原始对象,存的是一个lambda表达式,这个lambda表达式中包含了原始对象,看下图:

                                              

                                              

                              此时Bservice、Cservice创建过程中如果出现了循环依赖,就会去找三级缓存,然后就会执行三级缓存中的lambda表达式,在执行lambda表达式的过程中就会判断是否需要进行AOP增强,此时如果需要Aop增强,就会进行AOP增强得到代理对象,不需要Aop增强,就会得到原始对象,不管执行lambda表达式得到的是什么对象,都会将这个对象存入二级缓存。

                               Aservice的创建过程中,当bean属性值全部赋值之后,怎么判断Aservice是否已经进行了AOP增强了呢?是谁进行了判断呢?是在spring源码中判断的还是AOP插件中判断的呢?由于我们要使用Aop增强,就需要添加@EnableAspectJAutoProxy注解插件,所以肯定是在插件中判断的,在AbstractAutoProxyCreator类中有两个方法。

                                            

                                            

                               插件中为什么正常进行AOP的方法(postProcessAfterInitialization),如果提前进行了AOP,返回的是原始对象呢?因为源码中回去缓存中拿对象,不用它的返回对象。如果出现了循环依赖进行了提前AOP,那么源码中调用了getSingleton()方法,会去缓存中拿对象,此时拿到的是原始对象还是代理对象就看这个bean是否需要进行AOP。

              4、多线程情况下,怎么避免获取到不完整的bean?

                               第一个bean还没有被创建完, 第二个bean就开始了. 这是典型的并发问题。这种情况下第一想法不就是加锁嘛。

                               spring源码中也是加锁:

                                      第一处:在getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法中,在创建bean的时候,加了一把锁,锁的是一级缓存,保证只创建一个实例。

                                                         

                                      第二处:在getSingleton(String beanName, boolean allowEarlyReference)方法中,在从三级缓存中拿数据的时候,加了一把锁,因为要保证要保证二级缓存和三级缓存的原子性,也就是二级缓存的添加 和 市三级缓存的删除 整体的原子性。

                                                        

 

              5、Spring不支持扫描接口:

                              比如以下代码:

                                        

                           错误信息:No bean named 'userMapper' availale     ======> NoSuch BeanDefinition Exception 

                           

                          ========> 这个错误可能出现在哪里?

                                   1)配有配置这个bean 

                                    2) 容器没有识别出来

 

                         ==========>原因就是容器识别不出来,此时@Component标识的是接口,spring不支持扫描接口。

                        ===========》能解决spring不能扫描接口的问题吗 ?

                                在   ClassPathScanningCandidateComponentProvider#isCandidateComponent方法中加下面代码,就可以让容器扫描接口,

                                 

                                但是会报错,以为容器处理不了接口

                                 

                                 那怎么才能让容器能处理呢?

                                         通过UserMapper的BeanDefinition中beanClass的值,给他一个实例对象不就Ok了嘛,

                               

                             但是这个  UserMapper  是一个接口,怎么给它弄个实例对象???

                                       使用动态代理技术给它一个动态代理对象就OK啦,但是这个代理对象怎么创建,又怎么给它呢?

                                                此时就需要用到 FactoryBean 接口了,spring实例化 FactoryBean 的实现类的时候,会调用   getObject( ) ,最终会生成一个 getObject( )方法中创建的对象而不是 FactoryBean 实现类的对象。

                 

               此时,通过以上方法就是让spring扫描接口了,并生成接口的代理对象,就可以通过getBean()获取到。

            

 

                 =======》想想上面这样实现有什么缺点?

                          ①修改了spring底层的源代码(ClassPathScanningCandidateComponentProvider#isCandidateComponent方法中加了一段代码)

                         ②改变的Bean的定义属性BD,不灵活,每个接口都得改,来一个改一个

                          ③ 每一个接口都得写一个对应的FactoryBean 生成代理对象 ,如果接口一多,人都傻了

               =======》上面这些缺点怎么解决呢?

                   改spring源码肯定 是不现实的,那要怎么办才能在ClassPathScanningCandidateComponentProvider#isCandidateComponent方法中加一段代码呢?

                      自定义一个类去继承它,由于它还有个子类,不能去掉子类的功能,所以继承它的子类ClassPathBeanDefinitionScanner,再去重写它的这个方法

/**
 * @author 啊涛
 * @version 1.0
 * @date 2021/1/4 11:48
 */
//@Component// 这里不能加 @Component 这个注解 不然会报错 , 鬼知道什么原因
public class ClassPathScanningCandidateComponentProviderGrandson extends ClassPathBeanDefinitionScanner {
	
	public ClassPathScanningCandidateComponentProviderGrandson( BeanDefinitionRegistry registry ) {
		super(registry);
	}
	
	@Override
	protected boolean isCandidateComponent( AnnotatedBeanDefinition beanDefinition ) {
		//在这个方法中加上这个if判断就可以让容器支持扫描你配置的接口,但是会报错,容器处理不了
		return beanDefinition.getMetadata( ).isInterface( );
	}
	
	@Override
	protected Set< BeanDefinitionHolder > doScan( String... basePackages ) {
		return super.doScan( basePackages );
	}
}

               ===》但是此时有个问题,你这个类怎么加入到spring中,让spring知道你这个类是干嘛的呢?

                    使用用@Component注解标识这个类, 肯定是不行滴,这样标识它就是个普通的bean,起不了作用 ,那怎么办?

              =======》    自定义一个注解  EnableMapperScanner  用来导入你自定义的注册类,以及指定扫描哪个路径

                     自定义一个注册类 AtaoImportBeanDefinitionRegistrar ,在里面创获取 EnableMapperScanner 注解指定的路径,创建上面那个类的ClassPathScanningCandidateComponentProviderGrandson对象,通过它取开启可以扫描接口,并且扫描给定路径下的所有bean定义。 解决了上面说的①的缺点。

                    然后修改bean定义的属性,beanDefinition.setBeanClass( UserMapperFactoryBean.class ),这里的bean都是接口类型。此处通过遍历直接就处理了给定路径下所有的bean定义,解决了上面说的②出现的缺点。

                    然后通过 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue( beanClassName );  指定使用哪个构造方式,只有直接把全类名传过去,可以在FactoryBean中,你传什么类型就生成什么类型的代理对象,就解决了上面说的③的缺点。

代码如下:

/**
 * @author 啊涛
 * @version 1.0
 * @date 2021/1/4 14:51
 */
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
@Import( value = AtaoImportBeanDefinitionRegistrar.class )
public @interface EnableMapperScanner {
	String basePackage();
}

 


/**
 * @author 啊涛
 * @version 1.0
 * @date 2021/1/4 14:52
 */
//ImportBeanDefinitionRegistrar  解析bean定义的接口
// 使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成bean
public class AtaoImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata , BeanDefinitionRegistry registry ) {
		AnnotationAttributes attributes = ( AnnotationAttributes ) importingClassMetadata.getAnnotationAttributes( EnableMapperScanner.class.getName( ) );
	   //配置了EnableMapperScanner注解
		if ( attributes == null ){
			return;
		}
		String basePackage = attributes.getString( "basePackage" );
		//扫描bean定义
		ClassPathScanningCandidateComponentProviderGrandson mapperScanner = new ClassPathScanningCandidateComponentProviderGrandson( registry );
		mapperScanner.addIncludeFilter( new TypeFilter( ) {
			@Override
			public boolean match( MetadataReader metadataReader , MetadataReaderFactory metadataReaderFactory ) throws IOException {
				return true;
			}
		} );
		//此时扫描出来的bean定义  是一个一个的接口   //我们指定的interface1包下的接口类型bean定义
		//批量导入bean定义(接口类型)
		//扫描bean定义对象还是接口类型     doScan方法就是 给我一个路径,我给你路径下所有bean定义
		Set< BeanDefinitionHolder > scannedBds = mapperScanner.doScan( basePackage );
		for ( BeanDefinitionHolder bdh : scannedBds ) {
			//获取bean定义
			GenericBeanDefinition beanDefinition = ( GenericBeanDefinition ) bdh.getBeanDefinition( );
			//拿到bean定义中的接口的Class字符串
			String beanClassName = beanDefinition.getBeanClassName( );
			System.out.println("原生接口的Class类型 " + beanClassName );
			//如果不进行beanClass修改,会去实例化接口,就报错,所以此处需要对beanClass进行修改
			beanDefinition.setBeanClass( UserMapperFactoryBean.class );
			beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
			// 这行代码就是控制调用哪个构造函数
			beanDefinition.getConstructorArgumentValues().addGenericArgumentValue( beanClassName );
		}
	}
}

 

 

 

 


/**
 * @author 啊涛
 * @version 1.0
 * @date 2021/1/3 20:42
 */
@SuppressWarnings("rawtypes")
//这里可以不用将这个类注入到spring , 运行的时候,spring应该会扫描到吧  不懂  不用加@Component
public class UserMapperFactoryBean<T>  implements FactoryBean<T> {
	private Class<T>  targetClass;
	public UserMapperFactoryBean( Class<T>  targetClass ) {
		System.out.println("targetClass  ------------------ " +targetClass);
		this.targetClass = targetClass;
	}
	@Override
	@SuppressWarnings("unchecked")
	public T getObject( ) throws Exception {
		return (T) Proxy.newProxyInstance( targetClass.getClassLoader( ) , new Class[]{ targetClass } , new InvocationHandler( ) {
			@Override
			public Object invoke( Object proxy , Method method , Object[] args ) throws Throwable {
				return null;
			}
		} );
	}

	@Override
	public Class< ? > getObjectType( ) {
		return targetClass;
	}

}

测试代码:

/**
 * @author 啊涛
 * @version 1.0
 * @date 2020/12/22 9:49
 */
@ComponentScan("com.dt")
@EnableMapperScanner( basePackage = "com.dt.test.ioc.interface1")
public class NewBeanTest {

	public static void main( String[] args ) {
		ApplicationContext ac = new AnnotationConfigApplicationContext( NewBeanTest.class );
		System.out.println( ac.getBean( "userMapper").getClass() );

	}
}

 

              6、Spring有 多个构造方法  怎么识别 用哪一个 呢?

                       默认是无参构造 

                       如果你不想用无参构造,而是让它走你自己的写的有参构造 ,  你在你写的有参构造方法上 加 @Autowred注解  ,就不会走无参,而是走你写的有参

                       如果类中 没有无参构造,但是有两个有参构造 ,此时运行会报错,因为spring不知道走哪个有参构造

                                     spring 怎么走有参构造呢 ?有参构造的参数 怎么来的 ? 先byType  再 byName  
                                                 如果直接byName去拿,可以会造成类型不匹配

 

              7、 @Autowired底层大概是什么样的 ?

                          @Autowired工作原理 :

                               先 byType ,   剔除掉 autocandidate  = false的  
           
                               看有没有  @Qualifier(名字 )   ,有的话,直接去找这个名字的bean

                               看 有没有 @Primary注解       -- -  主bean   ,有的话,直接去找这个名字的bean
 
                               看 有没有配置bean的优先级  @Priority(1) 注解 设置优先级 ,值越大越高  ,有的话,直接去找优先级最高的bean

                                再 byName 找到这个值  
   
                               再通过反射 赋值

 

                           注意:

                               @Resource 标识  方法时  ,会根据方法的名字来找   ,比如 方法名是 setUser , 会根据 user 去找

                              @Autowired 标识方法时,会根据方法中形参的类型找,在根据形参的 名字找 

              8、@Resource 底层大概是什么样的 ?

                           @Resource  工作原理 :

                              1、指定的name ,就根据 name去找 ,找不到就报错
             
                              2、 没指定 name,会根据 属性的 名字, 去map中找,判断存不存在这个名字的key 
                     
                                                    存在的话,就直接拿出value

                                                   不存在的话,就会按类型找  ,但是按类型找,找到多个就会报错,它不知道选哪个

 

                           注意:

                               @Resource 标识  方法时  ,会根据方法的名字来找   ,比如 方法名是 setUser , 会根据 user 去找

                              @Autowired 标识方法时,会根据方法中形参的类型找,在根据形参的 名字找 

 

              9、spring中的单例bean  不等同于 单例模式

                       单例bean 指的的是  根据bean的名字 getBean的时候,只有一个,也就是说,根据bean的名字来拿bean只能拿到一个 , 只要名字相同,拿到的bean就是一样的

                              也就是 去  单例池中去拿   map < bean名字 ,bean对象>

 

              10、InitializingBean 接口

                                 InitializingBean 接口     有一个 afterPropertiesSet()方法      ======  相当于 xml 中的  init 配置  

                                         可以实现该接口  手动 来初始化 bean的属性值

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值