- 概述
- (一)IOC原理
- (二)IOC的优缺点
- (三)IOC实现原理
- (四)BeanFactory与ApplicationContext
- (五)@ComponentScan
- (六)@Component
- (七)@Configuration
- (九)@Bean, @Component 区别
- (十)@Component和@Configuration作为配置类实例化@Bean的差别
- (十一)往IOC 容器中添加组件的方式
- (十二)@Value+@PropertySource给组件赋值
- (十三)@AutoWired自动装配和@Qualifier
- (十四)注入static修饰的类变量
- (十五)Spring Bean初始化/销毁
- (十六)@Required
- (十七)@PostConstruct
- (十八)@DependsOn
概述
IOC重要思想是:依赖倒置原则(Dependency Inversion Principle )
把自己new对象的方式交给Spring容器去实例化,这就是控制反转思想
容器概念
在每个框架的中都有一个容器的概念,所谓的容器就是将常用的服务,封装起来,然后,用户只需要遵循一定的规则,就可以达到统一、灵活、安全、方便、快速的目的
具有依赖注入功能的容器,负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖
IOC负责管理你的任意对象,并结合你对对象的描述进行初始化和加强.
比如,对于一个用注解@Controller声明的对象,Spring会认为这个对象是Web Controller ,如果这个对象里面有方法有@RequestMapping注解,则会将客户端发起的HTTP请求转化成java方法调用.
(一)IOC原理
解析xml配置,获取bean class地址,使用Java的反射机制,进行实例化对象,然后把对象返回.
Spring的IOC实现原理就是工厂模式加反射机制,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才被决定到底是哪一种对象。把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象。
(二)IOC的优缺点
优点
实现组件之间的解耦,提高程序的灵活性和可维护性
缺点
对象 生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高
(三)IOC实现原理
Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
Bean缓存池:HashMap实现
Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化 Bean 并建立 Bean 之间的依赖关系。 Spring 的 IoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载等高级服务。
BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;
ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合我们都直接使用 ApplicationContext 而非底层的 BeanFactory。
BeanDefinitionRegistry: Spring 配置文件中每一个节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册 BeanDefinition 对象的方法。
BeanFactory 接口位于类结构树的顶端 ,它最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的 Bean,BeanFactory 的功能通过其他的接口得到不断扩展:
ListableBeanFactory:该接口定义了访问容器中 Bean 基本信息的若干方法,如查看Bean 的个数、获取某一类型 Bean 的配置名、查看容器中是否包括某一 Bean 等方法;
HierarchicalBeanFactory:父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器; 通过 HierarchicalBeanFactory 接口, Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。Spring 使用父子容器实现了很多功能,比如在 Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。
ConfigurableBeanFactory:是一个重要的接口,增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法;
AutowireCapableBeanFactory:定义了将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法;
SingletonBeanRegistry:定义了允许在运行期间向容器注册单实例 Bean 的方法;
(四)BeanFactory与ApplicationContext
beanFactory主要是面对与 spring 框架的基础设施,面对 spring 自己。而 Applicationcontex 主要面对与 spring 使用的开发者。日常开发程序员基本都会使用 Applicationcontex 并非 beanFactory 。
1. BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。
2. ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:a. 国际化支持b. 资源访问:Resource rs = ctx. getResource(“classpath:config.properties”), “file:c:/config.properties”
c. 事件传递:通过实现ApplicationContextAware接口
详细说明,两者对比区别:
https://www.yuque.com/docs/share/bf1e302d-2098-45fb-84a5-47c59b7e745f?#
应用上下文(ApplicationContext)
AnnotationConfigApplicationContext:从一个或多个基于 Java 的配置类中加载 Spring 应用上下文。
AnnotationConfigWebApplicationContext:从一个或多个基于 Java 的配置类中加载 Spring Web 应用上下文。
ClassPathXmlApplicationContext:从类路径下的一个或多个 XML 配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。
FileSystemXmlapplicationcontext:从文件系统下的一个或多个 XML 配置文件中加载上下文定义。
XmlWebApplicationContext:从 Web 应用下的一个或多个 XML 配置文件中加载上下文定义。
有了这些上下文之后,我们可以调用上下文的 getBean() 方法从 Spring 容器中获取 bean。
(五)@ComponentScan
配置扫描包注解
如果不指定,默认从声明@ComponentScan所在类的package进行扫描。正因为如此,SpringBoot的启动类都默认在src/main/java下。
如果配置了就只扫描配置的路径,默认扫描启动类路径下的就会失效,需要注意这一点.
@ComponentScan(basePackages = {“com.lanmili.core.util”,“com.lanmili.rest.*”})
排除的话用excludeFilters属性 , 包含的话,可以用includeFilters
注解包含属性:
basePackages:指定多个包名进行扫描basePackageClasses:对指定的类和接口所属的包进行扫excludeFilters:指定不扫描的过滤器includeFilters:指定扫描的过滤器lazyInit:是否对注册扫描的bean设置为懒加载nameGenerator:为扫描到的bean自动命名resourcePattern:控制可用于扫描的类文件scopedProxy:指定代理是否应该被扫描scopeResolver:指定扫描bean的范围useDefaultFilters:是否开启对@Component,@Repository,@Service,@Controller的类进行检测
1.@ComponentScan.Filter
a)注解形式的FilterType.ANNOTATION @Controller @Service @Repository @Compent
b)指定类型的 FilterType.ASSIGNABLETYPE @ComponentScan.Filter(type =
FilterType.ASSIGNABLE_TYPE,value = {TulingService.class})
c)aspectj类型的 FilterType.ASPECTJ(不常用)
d)正则表达式的 FilterType.REGEX(不常用)
e)自定义的 FilterType.CUSTOM
自定义类型案例参考: 案例:ZJJ_Spring2019/10/12 9:26:16_xa9fz
public enum FilterType { //注解形式 比如@Controller @Service @Repository @Compent ANNOTATION, //指定的类型 ASSIGNABLE_TYPE, //aspectJ形式的 ASPECTJ, //正则表达式的 REGEX, //自定义的 CUSTOM } |
---|
(六)@Component
Spring框架在进行扫描spring.xml 的context标签的时候,会实例化一个扫描器,递归扫描base-package路径下的所有类,判断这些所有类是否有@Component注解,如果有注解就封装BeanDefinition对象.
@Component 作用就是标记这个类,给这个类封装成BeanDefinition对象, 也就是加入到Spring容器里面,但是需要配置@ComponentScan注解
自定义Component注解
默认的@Service 和@Component如果你觉得不好用的话,你可以自定义注解,被自定义注解标注的类会加入到Spring容器里面去
@MyService public class MyAnnoService { 参考:案例:ZJJSpring2020/02/07 2:13:44_qguex |
---|
(七)@Configuration
从Spring3.0,@Configuration用于定义配置类,相当于Spring配置了一个xml配置文件,
配置类里面的@Bean注解在方法上,声明当前方法的返回值为一个Bean.
注意:@Configuration注解的配置类有如下要求:
@Configuration标注在类上,相当于这个类就是一个xml文件,@Bean注解就相当于里面的每一个bean
作用为:配置spring容器(应用上下文)
@Configuation等价于
@Bean等价于
@ComponentScan等价于
一句话概括就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
从定义来看, @Configuration 注解本质上还是 @Component,因此
@Configuration 标记的类必须符合下面的要求:
@Configuration不可以是final类型(没法动态代理);
@Configuration不可以是匿名类;
嵌套的configuration必须是静态类。
配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,
配置类必须是非本地的(即不能在方法中声明,不能是 private)。
任何嵌套配置类都必须声明为static。
@Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。
加载过程
Spring 容器在启动时,会加载默认的一些 PostPRocessor,其中就有 ConfigurationClassPostProcessor,这个后置处理程序专门处理带有 @Configuration 注解的类,这个程序会在 bean 定义加载完成后,在 bean 初始化前进行处理。主要处理的过程就是使用 cglib 动态代理增强类,而且是对其中带有 @Bean 注解的方法进行处理。
我们在 @Configuration 注解定义的 bean 方法中可以直接调用方法,不需要 @Autowired 注入后使用。
(九)@Bean, @Component 区别
@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。(引用第三方类库中的类装配到Spring容器就需要使用@Bean来实现了)
两者的目的是一样的,都是注册bean到Spring容器中。
@Component(@Controller、@Service、@Repository)通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
而@Bean注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
列子:
@Controller //在这里用Component,Controller,Service,Repository都可以起到相同的作用。 @RequestMapping(″/web/controller1″) public class WebController { ….. } |
---|
而@Bean的用途则更加灵活
当我们引用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现,而且还可以根据情况进行更灵活的装配bean
@Bean public OneService getService(status) { case (status) { when 1: return new serviceImpl1(); when 2: return new serviceImpl2(); when 3: return new serviceImpl3(); } } |
---|
总结:@Component和@Bean都是用来注册Bean并装配到Spring容器中,但是Bean比Component的自定义性更强。可以实现一些Component实现不了的自定义加载类。
在@Component类中使用方法或字段时不会使用CGLIB增强(及不使用代理类:调用任何方法,使用任何变量,拿到的是原始对象,后面会有例子解释)。而在@Configuration类中使用方法或字段时则使用CGLIB创造协作对象(及使用代理:拿到的是代理对象);当调用@Bean注解的方法时它不是普通的Java语义,而是从容器中拿到的由Spring生命周期管理、被Spring代理甚至依赖于其他Bean的对象引用。在@Component中调用@Bean注解的方法和字段则是普通的Java语义,不经过CGLIB处理。
(十)@Component和@Configuration作为配置类实例化@Bean的差别
@Component 里面的bean对象不是Spring容器里面的对象.
@Configuration 里面的bean对象是Spring容器里面的对象
默认情况下是 @Configuration是随容器启动开始加载的,始终存在的单例模式。 @Component是使用一次即实例化一次
参考:
https://www.yuque.com/docs/share/49d99d09-f7e3-42c7-9508-29a1dcb96e18?#
(十一)往IOC 容器中添加组件的方式
案例:ZJJ_Spring2019/10/12_10:24:02_cby1u |
---|
1.:通过@CompentScan +@Controller @Service @Respository @compent
适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中。
2.:通过@Bean的方式来导入组件(适用于导入第三方组件的类)
3.:通过@Import来导入组件 (导入组件的id为全类名路径)
@import使用场景是SpringBoot自动装配,
@Configuration @Import(value = {Person.class, Car.class}) public class MainConfig { } |
---|
通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径)
public class TulingImportSelector implements ImportSelector { /可以获取导入类的注解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{“com.tuling.testimport.compent.Dog”}; } } @Configuration @Import(value = {Person.class, Car.class, TulingImportSelector.class}) public class MainConfig { } |
---|
通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称)
public class TulingBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //创建一个bean定义对象 RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class); //把bean定义对象导入到容器中 registry.registerBeanDefinition(“cat”,rootBeanDefinition); } } @Configuration //@Import(value = {Person.class, Car.class}) //@Import(value = {Person.class, Car.class, TulingImportSelector.class}) @Import(value = {Person.class, Car.class, TulingImportSelector.class, TulingBeanDefinitionRegister.class}) public class MainConfig { } |
---|
4. 通过实现FacotryBean接口来实现注册组件
使用场景是复杂的初始化,就是Bean初始化需要设置很多很多的组件,比较大的就建议用这种方式去配置,典型的例子就是SqlSessionFactoryBean
public class CarFactoryBean implements FactoryBean /返回bean的对象 @Override public Car getObject() throws Exception { return new Car(); } /返回bean的类型 @Override public Class<?> getObjectType() { return Car.class; } /是否为单利 @Override public boolean isSingleton() { return true; } } |
---|
1.initMethod和destroyMethod方式
initMethod和destroyMethod方式实现Bean的初始化和销毁
@Configuration @ComponentScan(basePackages = “F_bean的生命周期”) public class MainConfig { @Scope(value = “prototype”) @Bean(initMethod = “init”, destroyMethod = “destroy”)//指定类的init方法和destroy方法 public Car car() { return new Car(); } |
---|
针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法
针对多实例bean(多例模式)的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理(多例模式是GC垃圾回收器管理的,不是IOC管理的).
2.InitializingBean和DisposableBean
案例:ZJJSpring_2020/02/15 3:54:59_dwn4p |
---|
如果需要在一个类实例化以后去做一些事情,那么就可以借助InitializingBean这个接口来完成,比如在bean实例化需要做xml解析,或者资源的开启释放,加载预热缓存等等,都可以用InitializingBean这个接口来实现.
通过InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。
关于 InitializingBean的具体看
https://mp.weixin.qq.com/s?__biz=MzI5NTYwNDQxNA==&mid=2247484595&idx=4&sn=d8de987a336a08333c7fef423289c175&chksm=ec505d62db27d47480967e2249db20f22479ec7ac1ab61a2bf7ec80018de03a6339b6c74b617&scene=7&key=34c4aa20c67724dd4a68a71c8e465811169ac1bf709249a547b4ae2d58914e00287a1d836dc6106e7f8348faba734fce94bec00c1c5d5a486ecdbdafef5cf0a3b19d0377a93183644b649f3b53911acd&ascene=0&uin=MTQwMjAyODM3MA%3D%3D&devicetype=Windows+7&version=62070158&lang=zh_CN&exportkey=A8q3Ea45yuIiFB1HyJjMIcs%3D&pass_ticket=xlBM9daG3yq8iOb%2FIUpOZUh6Ytdj%2BUZ0rPH5F%2BpDs1Lu6ao81UIDGF1ldr9J98mz
@Component public class Person implements InitializingBean, DisposableBean { public Person() { System.out.println(“Person的构造方法”); } @Override public void destroy() throws Exception { System.out.println(“DisposableBean的destroy()方法 “); } / 该方法在 BeanFactory 设置完了所有属性之后被调用 该方法允许 bean 实例设置了所有 bean 属性时执行初始化工作,如果该过程出现了错误则需要抛出异常 */ @Override public void afterPropertiesSet() throws Exception { System.out.println(“InitializingBean的 afterPropertiesSet方法”); } }** |
---|
3.bean实例化的时候加载东西 @PostConstruct 和@ProDestory
案例:ZJJSpringBoot_2020/02/06 5:03:06_pq7hc |
---|
@PostConstruct用来标记是在Bean实例化之后加载完依赖注入后执行这个方法。用来修饰一个非静态的void()方法,也就是Spring容器启动时就执行,多用于一些全局配置、数据字典之类的加载.同时只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。PreDestroy()方法在destroy()方法执行执行之后执
@PostConstruct总体概述
1. 要在依赖加载后,对象使用前执行,而且只执行一次,原因在上面已经说了。
2. 所有支持依赖注入的类都要支持此方法。
3. 文档中说一个类只能有一个方法加此注解,但实际测试中,我在一个类中多个方法加了此注解,并没有报错,而且都执行了,我用的是 Spring Boot 框架。
@PostConstruct注释规则
1. 除了拦截器这个特殊情况以外,其他情况都不允许有参数,否则spring框架会报IllegalStateException;而且返回值要是void,但实际也可以有返回值,至少不会报错,只会忽略
2. 方法随便你用什么权限来修饰,public、protected、private都可以,反正功能是由反射来实现3. 方法不可以是static的,但可以是final的所以,综上所述,在spring项目中,在一个bean的初始化过程中,方法执行先后顺序为:
Constructor > @Autowired > @PostConstruct先执行完构造方法,再注入依赖,最后执行初始化操作,所以这个注解就避免了一些需要在构造方法里使用依赖组件的尴尬。
@PostConstruct实现原理
参考:
https://www.yuque.com/docs/share/68f9d4e9-e2e7-4e5a-b857-4fa6e46dfd4c?#
@PreDestroy 和 @PostConstruct差不多
被@PreDestroy修饰的方法会在Bean销毁之前执行被这个注解修饰的方法
4.BeanPostProcessor后置处理器
通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程
使用场景:
用来修改Bean的一些属性的,可以在postProcessBeforeInitialization()方法里面进行修改
postProcessBeforeInitialization 在init方法之前调用
postProcessAfterInitialization 在init方法之后调用
/ 后置处理器 在bean调用初始化方法前后进行调用 BeanPostProcessor左右:在bean的初始化前后进行调用 */ @Component public class TulingBeanPostProcessor implements BeanPostProcessor { / 在初始化之前 @param bean __ @param beanName __ * @return * @throws BeansException / @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(“TulingBeanPostProcessor…postProcessBeforeInitialization:” + beanName); return bean; } /** 在初始化之后 @param bean __ * @param beanName __ * @return * @throws BeansException / @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(“TulingBeanPostProcessor…postProcessAfterInitialization:” + beanName); *return bean; } |
---|
BeanPostProcessor的执行时机
populateBean(beanName, mbd, instanceWrapper) initializeBean{ applyBeanPostProcessorsBeforeInitialization() invokeInitMethods{ isInitializingBean.afterPropertiesSet 自定义的init方法 } applyBeanPostProcessorsAfterInitialization()方法 } |
---|
(十二)@Value+@PropertySource给组件赋值
案例:ZJJ_Spring2019/10/12_12:14:31_glj4b |
---|
@Value(“${person.lastName}”) private String lastName; |
---|
@PropertySource(value = {“classpath:person.properties”}) //导入属性文件注解 |
---|
EmbeddedValueResolverAware解析配置文件的值
EmbeddedValueResolverAware是内嵌的值解析器
案例:ZJJSpring2019/10/1213:29:241469a @Configuration @PropertySource(value = {“classpath:ds.properties”}) public class MainConfig implements EmbeddedValueResolverAware { private String jdbcUrl; private String classDriver; / 解析配置文件的值,可以给形参赋值 @param _resolver */ @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.jdbcUrl = resolver.resolveStringValue(“${ds.jdbcUrl}”); this.classDriver = resolver.resolveStringValue(“${ds.classDriver}”**); } |
---|
(十三)@AutoWired自动装配和@Qualifier
如果在一个类里面,不管是属性注入还是构造注入或者是方法注入,都会触发需要依赖的这个引用数据类型的getBean的操作.getBean第一次是实例化对象,后面大部分是从IOC容器缓存里面获取.
自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照属性名称来进行装配.
@Autowired
private TulingDao tulingDao;
比如,我容器中有二个TulingDao类型的组件 一个叫tulingDao 一个叫tulingDao2那么我们通过@AutoWired 来修饰的属性名称时tulingDao,那么拿就加载容器的tulingDao组件,若属性名称为tulignDao2 那么他就加载的时tulingDao2组件
在两个TulingDao类型的组件中假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier(“tulingDao”)来指定装配的组件或者在配置类上的@Bean加上@Primary注解
@Autowired @Qualifier(“tulingDao”) //这样装备的就是tulingDao private TulingDao tulingDao2 |
---|
假设我们容器中即没有tulingDao 和tulingDao2,那么在装配的时候就会抛出异常
No qualifying bean of type ‘com.tuling.testautowired.TulingDao’ available
若我们想不抛异常 ,我们需要指定 required为false的时候可以了
@Autowired(required = false) @Qualifier(“tulingDao”) private TulingDao tulingDao2; |
---|
标注在set方法上
标注set方法上的话,作用的对象是这个set方法的形参TulingLog tulingLog
然后set方法内部就会对成员类进行赋值操作.
@Autowired public void setTulingLog(TulingLog tulingLog) { this.tulingLog = tulingLog; } |
---|
标注在构造方法上
标注构造方法上的话,作用的对象是这个构造方法的形参TulingLog tulingLog
然后构造方法内部就会对成员类进行赋值操作.
@Autowired public TulingAspect(TulingLog tulingLog) { this.tulingLog = tulingLog; } |
---|
标注在配置类上的入参中(可以不写)
@Bean public TulingAspect tulingAspect(@Autowired TulingLog tulingLog) { TulingAspect tulingAspect = new TulingAspect(tulingLog); return tulingAspect; } |
---|
可以给Map里面进行自动注入,被注入的对象都需要实现泛型的接口(EntStrategy)
@Component public class DefaultStrategy implements EntStrategy { @Component public class EntAStrategy implements EntStrategy { 上面是别的类 @Component public class EntStrategyHolder { // 关键功能 Spring 会自动将 EntStrategy 接口的类注入到这个Map中,Map的key是bean的名字,默认是首字母小写. @Autowired private Map public EntStrategy getBy(String entNum) { return entStrategyMap.get(entNum); } } |
---|
注入到List里面
使用上和上面注入到Map里面是一样的,被注入的值需要实现EntStrategy接口
@Autowired private List |
---|
@Qualifier
https://blog.csdn.net/qq_41489540/article/details/81056918详细介绍
作用:在自动按照类型注入的基础之上,再按照bean的id注入。
属性:
value:用于指定bean的id。
@Qualifier(“名字”):和@Autowired配合使用,可以根据名字注入byName,
注意:
在给类成员注入时,它不能独立使用。必须配合@Autowired如果没有就报错)
但是给方法参数注入时,它可以独立使用(待会再讲)
@Profile根据运行时环境选择装配
@Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效
@Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活
没有标志为@Profile的bean 不管在什么环境都可以被激活
案例:ZJJ_Spring2019/10/12_13:21:05_qgcaq |
---|
@Bean @Profile(value = “dev”) //如果是dev环境就装配bean public DataSource devDs() { return buliderDataSource(new DruidDataSource()); } @Bean @Profile(value = “prod”) // 如果是prod环境就装配bean public DataSource prodDs() { return buliderDataSource(new DruidDataSource()); } |
---|
获取容器环境,同时设置容器环境
public static void main(String[] args) { / AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);/ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); // ctx.getEnvironment().setActiveProfiles(“test”,”dev”);//配置运行时环境,在开发的时候一般设置一个.不会设置两个 ctx.getEnvironment().setActiveProfiles(“test”); ctx.register(MainConfig.class); //注册一个或多个要处理的带annotated的类 ctx.refresh(); //返回静态指定的ApplicationListeners列表 printBeanName(ctx); } |
---|
优先选择的 Bean
我们都知道面向接口编程,就像我们 MVC 三层结构中 Controller 中注入一个 Service, 而这个 Service 的接口存在多个实现类时,Spring 会报 NoUniqueBeanDefinitionException
异常。因为 Spring 无法自主的去选择一个合适的实现类。这时我们需要给 Spring 提供帮助,让他能够找到合适的首选(primary)的 Bean。
@Component @Primary public class IceCream implements Dessert { … } |
---|
(十四)注入static修饰的类变量
@Configuration public class CeuiUtils { @Autowired /任务管理,可以做任务的查询的/ private TaskService _taskService; private static TaskService taskService; @PostConstruct //这个注解是重点 public void init() { this.taskService = _taskService; } public static void ceui() { System.out.println(“——-“ + taskService); // 这里能打印出来地址值 } } |
---|
(十五)Spring Bean初始化/销毁
日常开发过程有时需要在应用启动之后加载某些资源,或者在应用关闭之前释放资源。Spring 框架提供相关功能,围绕 Spring Bean 生命周期,可以在 Bean 创建过程初始化资源,以及销毁 Bean 过程释放资源。Spring 提供多种不同的方式初始化/销毁 Bean,如果同时使用这几种方式,Spring 如何处理这几者之间的顺序?
参考:
https://www.yuque.com/docs/share/f3cd8664-1134-493b-bdfb-7b9c4633653f?#
(十六)@Required
在生产规模的应用程序中,IoC容器中可能会有数百或数千个bean,并且它们之间的依赖关系通常非常复杂。setter注入的一个缺点是你很难检查是否已经设置了所有必需的属性。使用“ dependency-check”属性
除了使用验证依赖项之外dependency-check,您还可以使用@Required注释来检查是否设置了值non-null。
https://www.yuque.com/docs/share/7f5a3158-8ee0-441e-a5dd-3b8c581fc7e1?#
(十七)@PostConstruct
该注解可以实现在运行工程时,自动运行该注解下的方法,指的是在项目启动的时候执行这个方法,也可以理解为在spring容器启动的时候执行,可作为一些数据的常规化加载,比如数据字典之类的。
被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行
也就是加载顺序
服务器加载Servlet -> servlet 构造函数的加载 -> postConstruct ->init(init是在service 中的初始化方法. 创建service 时发生的事件.) ->Service->destory->predestory->服务器卸载serlvet
那么问题:spring中Constructor、@Autowired、@PostConstruct的顺序Constructor >> @Autowired >> @PostConstruct
依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象p与对象a,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowired注解,那么@Autowired注入是发生在A的构造方法执行完之后的。
@PostConstruct应用场景:
如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
@PostConstructor 和 afterPropertiesSet的功能是一样的.
(十八)@DependsOn
@DependsOn注解可以配置Spring IoC容器在初始化一个Bean之前,先初始化其他的Bean对象。