文章目录
- Spring IoC
- 一、IoC概述
- 1、概述
- 2、IoC原理
- 3、IoC作用
- 4、Spring Bean
- 二、Spring容器 相关接口
- 1、BeanFactory
- 2、ApplicationContext
- 1)HierarchicalBeanFactory
- 2)ListableBeanFactory
- 3)EnvironmentCapable
- 4)ApplicationEventPublisher
- 5)MessageSource
- 6)ResourcePatternResolver
- 3、ApplicationContext 的实现
- 4、BeanFactory & ApplicationContext
- 三、Bean 的声明方式
- 1、XML的bean标签
- 1)构造函数方式
- 2)静态工厂方式
- 3)实例工厂方式
- 2、@Component
- 1)配合 @ComponentScan
- 2)@Component 的扩展
- 3、@Bean
- 1)搭配 @Configuration
- 2)搭配 @Component
- 3)搭配 ApplicationContext
- 4、FactoryBean
- 5、SmartFactoryBean
- 6、registerBean 方法
- 四、Bean 的作用域
- 1、bean的作用域
- 2、@Scope 注解
- 3、扩展 @Scope 注解
- 4、单例bean & 单例模式
- 5、单例池
- 6、bean的线程安全问题
- 五、依赖注入
- 1、手动注入 - xml方式(了解)
- 1)set方法
- 2)构造方法
- 2、自动注入 - xml方式(了解)
- 1)set方法
- 2)构造方法
- 3、自动注入 - @Autowired
- 1)基本使用
- 2)搭配 @Qualifier
- 3)三种注入方式
- 【1】Filed变量注入
- 【2】Setter方法注入
- 【3】构造器注入(推荐)
- 4)构造器注入 案例分析
- 【1】单个构造
- 【2】空参构造 + 有参构造
- 【3】多个有参构造
- 【4】多个 @Autowired(false)
- 5)注意事项
- 4、自动注入 - @Resource
- 1)基本使用
- 2)注意事项
- 5、自动注入 - @Bean注解
- 1)NO(主要关注这个)
- 【1】同一配置类
- 【2】不同配置类
- 2)BY_NAME(了解)
- 3)BY_TYPE(了解)
- 6、自动注入 - @RequiredArgsConstructor
- 1)基本功能
- 2)注入功能
- 六、Bean 的生命周期
- 1、Bean的创建
- 2、Bean的生命周期
- 3、案例演示
Spring IoC
一、IoC概述
1、概述
IoC 全称为 Inversion of Control,即 控制反转,是一种设计思想,而不是一个具体的技术实现。
- 控制:控制对象的创建和销毁
- 反转:将对象的控制权(创建和销毁)交给IoC容器(主动new对象 -> 到IoC容器中取)
2、IoC原理
IoC(Inversion of Control)的原理基于两个重要的概念:依赖注入(Dependency Injection)和控制反转(Inversion of Control)。
-
依赖注入(Dependency Injection):
依赖注入是 IoC 的一个重要实现方式,它通过外部传递依赖对象的方式来实现组件之间的依赖关系。
简单来说,就是将一个对象所依赖的其他对象,通过构造函数、属性或者方法参数的方式注入到该对象中。这样,对象不需要自己创建和管理依赖对象,而是由外部容器负责注入依赖,从而实现了依赖关系的解耦。
-
控制反转(Inversion of Control):
控制反转是 IoC 的核心概念,它将程序的控制权从应用程序代码中反转到容器或者框架中。
传统的程序设计中,应用程序代码通常负责管理对象的创建、依赖关系的维护等,而在 IoC 中,这些控制权被转移到了外部容器或者框架中,由它们负责创建和管理对象,管理对象之间的依赖关系,并在需要时将对象注入到需要依赖的组件中。
3、IoC作用
IoC(Inversion of Control)即控制反转,是一种软件设计思想,它将控制权从应用程序代码中转移至框架或容器,使得框架或容器负责控制程序的流程和对象的创建、销毁等行为。IoC 的作用主要体现在以下几个方面:
-
降低耦合性(Decoupling):
将组件之间的依赖关系从代码中解耦,通过将依赖关系交由容器管理,组件不再直接依赖于特定的实现类,而是依赖于抽象接口或者规范。这样,当需要替换某个实现类时,只需要修改配置文件或者注解,而不需要修改代码,从而降低了组件之间的耦合性。
-
简化代码(Simplifying Code):
通过 IoC 容器管理对象的生命周期和依赖关系,可以减少开发人员编写大量的样板代码,例如对象的创建、初始化、销毁等操作,使得代码更加简洁、清晰,并且易于维护。
-
集中管理(Centralized Management):
IoC 容器可以集中管理应用程序中的所有组件,包括对象的创建、依赖关系的管理、配置信息的管理等,从而提高了系统的可管理性和可维护性。
-
提高灵活性(Improving Flexibility):
由于依赖关系和配置信息被集中管理,可以通过修改配置文件或者注解来实现不同的配置,从而使得系统更加灵活,能够快速适应不同的需求和变化。
-
提高可测试性(Improving Testability):
由于依赖关系被注入到组件中,可以更方便地进行单元测试和集成测试,通过替换模拟对象或者配置不同的依赖关系,可以轻松地对组件进行测试,提高了系统的可测试性。
总的来说,IoC 通过将控制权从应用程序代码中转移至框架或容器,降低了组件之间的耦合性,简化了代码,集中了管理,提高了灵活性和可测试性,是一种强大的软件设计思想,被广泛应用于各种软件开发项目中。
4、Spring Bean
简单来说,Spring Bean 就是那些被 IoC 容器所管理的对象。
二、Spring容器 相关接口
1、BeanFactory
BeanFactory
是最顶层的容器接口,只提供了获取Bean相关的一些方法。
public interface BeanFactory {// 对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象String FACTORY_BEAN_PREFIX = "&";// 根据bean的名字,在IOC容器中得到bean实例,Object getBean(String name) throws BeansException;// 根据bean的名字,在IOC容器中得到bean实例,args:显式参数(必须为非单例模式)Object getBean(String name, Object... args) throws BeansException;// 根据bean的名字获得对象,并转换为Class类型(可以不用进行类型转换)<T> T getBean(String name, Class<T> requiredType);// 根据bean的类型获得对象(必须是拥有唯一实现类)<T> T getBean(Class<T> requiredType) throws BeansException;// 根据bean的类型获得对象,args:显式参数<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;// 判断IOC容器是否有这个名字的beanboolean containsBean(String name);// 判断这个bean是不是单例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 判断这个bean是不是多例 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 获取bean实例的Class类型 Class<?> getType(String name) throws NoSuchBeanDefinitionException;// 这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来 String[] getAliases(String name);
}
2、ApplicationContext
ApplicationContext
是更高级的容器接口,继承了许多其他接口,提供了更多的功能。
- 获取 Environment(EnvironmentCapable)
- getBean 的扩展(ListableBeanFactory)
- 事件的发布与监听机制(ApplicationEventPublisher)
- 支持父子容器(HierarchicalBeanFactory)
- 国际化(MessageSource)
- 事件的发布与监听机制(ApplicationEventPublisher)
- 资源访问(ResourcePatternResolver)
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {@NullableString getId();String getApplicationName();String getDisplayName();long getStartupDate();@NullableApplicationContext getParent();// 用于进行手动的 Bean 注入和依赖注入AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;}
1)HierarchicalBeanFactory
HierarchicalBeanFactory
用于表示具有层次结构的 BeanFactory 容器。
-
支持父子容器的关系,一个 BeanFactory 可以有一个或多个父容器。
子容器可以访问父容器中定义的 Bean,但父容器不能访问子容器中定义的 Bean。
-
如果子容器中不存在某个 Bean,则会委托父容器进行查找。
public interface HierarchicalBeanFactory extends BeanFactory {@NullableBeanFactory getParentBeanFactory();boolean containsLocalBean(String name);
}
2)ListableBeanFactory
ListableBeanFactory
扩展了 BeanFactory 接口,提供了额外的功能来 获取 Bean 及其相关信息
public interface ListableBeanFactory extends BeanFactory {// 获取 Bean 的定义信息boolean containsBeanDefinition(String beanName);int getBeanDefinitionCount();String[] getBeanDefinitionNames();// 通过 类型 获取 BeanString[] getBeanNamesForType(ResolvableType type);String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit);String[] getBeanNamesForType(@Nullable Class<?> type);String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);// 通过 注解 获取BeanString[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);// 获取 <beanName, type><T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;// 获取 <beanName, annotation>Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;// 获取bean的注解@Nullable<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)throws NoSuchBeanDefinitionException;
}
3)EnvironmentCapable
EnvironmentCapable
接口用于获取 Environment
实例
public interface EnvironmentCapable {Environment getEnvironment();
}
Environment
用于表示运行时环境(例如配置文件、系统属性、环境变量等)的抽象,提供了访问应用程序配置信息的统一接口。
public interface Environment extends PropertyResolver {// 返回激活profile名称的数组String[] getActiveProfiles();// 返回默认profile名称的数组String[] getDefaultProfiles();// environment是否支持给定profileboolean acceptsProfiles(Profiles profiles);@Deprecated // 弃用boolean acceptsProfiles(String... profiles);
}
public interface PropertyResolver {// 检查某个属性是否存在boolean containsProperty(String key);// 获取指定属性的值(不存在则返回null)String getProperty(String key);// 获取指定属性的值(不存在则返回默认值)String getProperty(String key, String defaultValue);// 获取指定属性的值,可以指定要返回对象的类型(不存在则返回null)<T> T getProperty(String key, Class<T> targetType);// 获取指定属性的值,可以指定要返回对象的类型(不存在则返回默认值)<T> T getProperty(String key, Class<T> targetType, T defaultValue);// 获取指定属性的值(不存在则抛出异常)String getRequiredProperty(String key) throws IllegalStateException;<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;String resolvePlaceholders(String text);String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
4)ApplicationEventPublisher
ApplicationEventPublisher
用于发布应用程序事件并通知所有已注册的事件监听器。
@FunctionalInterface
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {publishEvent((Object) event);}void publishEvent(Object event);
}
5)MessageSource
MessageSource
是 Spring Framework 中用于国际化支持的接口之一。
- 它提供了一种统一的方式来访问应用程序中的消息资源,支持多语言和多地区的国际化功能。
public interface MessageSource {String getMessage(String code, Object[] args, String defaultMessage, Locale locale);String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
6)ResourcePatternResolver
ResourcePatternResolver
加载和访问应用程序中的资源,支持通配符和模式匹配,可以方便地加载多个资源。
public interface ResourcePatternResolver extends ResourceLoader {String CLASSPATH_ALL_URL_PREFIX = "classpath*:";Resource[] getResources(String var1) throws IOException;
}
public interface ResourceLoader {String CLASSPATH_URL_PREFIX = "classpath:";Resource getResource(String var1);@NullableClassLoader getClassLoader();
}
3、ApplicationContext 的实现
ApplicationContext
负责创建和管理应用程序中的所有 Bean 对象,其实现类的区别仅仅在于如何加载配置
AnnotationConfigApplicationContext
:使用「注解方式」加载配置ClassPathXmlApplicationContext
:从「资源根目录」下加载配置FileSystemXmlApplicationContext
:从「文件路径下」加载配置
4、BeanFactory & ApplicationContext
从接口来看:
BeanFactory
:Spring容器的顶层接口,只提供了获取Bean相关的一些方法。ApplicationContext
:更高级的Spring容器接口,继承了许多其他接口,提供了更多的功能。
从单例bean的加载顺序来看:
BeanFactory
:延迟加载(什么时候使用,什么时候创建)ApplicationContext
:饥饿加载(创建Spring容器时,就创建bean,不管是否使用)
注意:以上只针对单例bean(什么是单例bean请看「Bean的作用域」),多例bean
【案例演示】
<!-- load.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="loadBean" class="com.spring.ioc.demo.LoadBean"/></beans>
public class LoadBeanTest {public static void main(String[] args) {System.out.println("======ApplicationContext容器开始创建=====");ApplicationContext applicationContext = new ClassPathXmlApplicationContext("load.xml");System.out.println("======ApplicationContext容器创建完成=====");applicationContext.getBean("loadBean");System.out.println(" ");System.out.println("======BeanFactory容器开始创建=====");Resource resource = new ClassPathResource("load.xml");BeanFactory beanFactory = new XmlBeanFactory(resource);System.out.println("======BeanFactory容器创建完成=====");beanFactory.getBean("loadBean");}}
运行结果如下所示:
======ApplicationContext容器开始创建=====
LoadBean初始化
======ApplicationContext容器创建完成===========BeanFactory容器开始创建=====
======BeanFactory容器创建完成=====
LoadBean初始化
三、Bean 的声明方式
1、XML的bean标签
在resource
目录下,定义要声明的bean的xml文件
<!-- spring.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="account" class="com.xxx.spring.pojo.Account"/></beans>
通过 ClassPathXmlApplicationContext
,读取 xml文件,注册bean
public class ApplicationContextTest {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");Account account = (Account) context.getBean("account");}
}
1)构造函数方式
# 说明使用空参构造创建对象,并装配到IOC容器中(默认方式)
# 注意被实例化的Bean中必须要有空参构造函数,否则会创建失败。
# 场景当各个bean的业务逻辑相互比较独立时,或者与外界关联较少时可以使用。
【xml配置】
<!--空参构造实例化-->
<bean id="account" class="com.xxx.spring.pojo.Account"/>
2)静态工厂方式
# 说明使用静态工厂中的静态方法创建对象,并装配到IOC容器中。
# 场景1. 可以统一管理多个bean,本身不被Spring管理。 2. 当各个bean在 创建之前 需要相同的初始化处理时,可以使用静态factory方法进行统一的处理。
【静态工厂】
public class StaticFactory {public static Account createAccount(){System.out.println("静态工厂构建1!");return new Account();}public static User createUser(){System.out.println("静态工厂构建2!");return new User();}
}
【xml配置】
<!--静态工厂实例化-->
<bean id="accountStatic" class="com.xxx.spring.factory.StaticFactory" factory-method="createAccount"/>
<bean id="user"class="com.xxx.spring.factory.StaticFactory"factory-method="createUser"/>
id
:指定 bean 的 id,用于从容器中获取对象class
:静态工厂类的全限定名factory-method
:指定创建对象的静态方法
3)实例工厂方式
# 说明使用工厂中的实例方法创建对象,并装配到IOC容器中。
# 注意1. 先要将实例工厂类作为一个bean装配到spring容器中。2. 然后再引用工厂bean,调用里面的非静态方法获取bean,并装配到spring的IOC容器中。
# 场景1. 可以统一管理多个bean,本身也被Spring管理。 2. 将实例factory也作为业务bean控制,常用于集成其他框架bean创建管理配置类
【实例工厂】
public class InstanceFactory {public Account createAccount(){System.out.println("实例工厂构建!");return new Account();}public User createUser(){System.out.println("实例工厂构建!");return new User();}
}
【xml配置】
<!--实例工厂实例化-->
<bean id="instanceFactory" class="com.xxx.spring.factory.InstanceFactory"/>
<bean id="account" factory-bean="instanceFactory" factory-method="createAccount"/>
<bean id="user" factory-bean="instanceFactory" factory-method="createUser"/>
id
:指定 bean 的 id,用于从容器中获取对象class
:实例工厂类的全限定名factory-bean
:指定实例工厂类的 bean 的 idfactory-method
:指定创建对象的实例方法
2、@Component
@Component
注解:表明该类会作为组件类,并告知Spring要为这个类创建 bean。
Spring容器中的所有的bean都会指定一个ID,默认是 将类名的第一个字母变为小写。
/*** 可以为bean设置不一样的ID*/
@Component("pc")
public class Computer {}
注意:组件扫描默认是不启用的,需要配置 @ComponentScan 注解开启。
1)配合 @ComponentScan
@ComponentScan
注解:开启组件扫描,查找带有@Component
注解的类并为其创建一个bean。
Spring会扫描@ComponentScan
指定的包下的组件。默认是 @ComponentScan
标记的 配置类所在的包 及 这个包下的所有子包
/*** 指定扫描的基础包(单个) - 通过value属性中指明包的名称*/
@Configuration
@ComponentScan("package")
public class BeanConfig {}
/*** 指定不同的基础包(多个) - 通过basePackages属性中指明包的名称*/
@Configuration
@ComponentScan(basePackages = {"package1","package2"})
public class BeanConfig {}
如果觉得指定包是类型不安全(not type-safe)的,也可以指定包中所包含的类或接口:
/*** 这些类或接口所在的包,将会作为组件扫描的基础包*/
@Configuration
@ComponentScan(basePackageClasses = {ClassA.class, ClassB.class, InterfaceA.class})
public class BeanConfig {}
2)@Component 的扩展
@Repository
、@Service
、@Controller
这三个注解上都加了@Component
,可以起到一样的效果。
@Repository
:对应 持久层,主要用于数据库相关操作。@Service
:对应 服务层,主要涉及一些复杂的逻辑,需要用到 持久层。@Controller
:对应 控制层,主要用于接受用户请求并调用 服务层 返回数据给前端页面。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {@AliasFor(annotation = Component.class)String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {@AliasFor(annotation = Component.class)String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {@AliasFor(annotation = Component.class)String value() default "";
}
3、@Bean
1)搭配 @Configuration
@Configuration
public class Config {@Beanpublic User user() {return new User();}
}
2)搭配 @Component
@Component
public class Config {@Beanpublic User user() {return new User();}
}
3)搭配 ApplicationContext
public class Config {@Beanpublic User user() {return new User();}
}
AnnotationConfigApplicationContext
读取 Config 对象,作用和 @Configuration 一致。
public class ApplicationContextTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);User user = (User) context.getBean("user");}
}
4、FactoryBean
/*** 实现FactoryBean后,会生成2个bean对象* 1、beanName = factoryBeanImpl ------ getObject()返回的类 FactoryBeanObject* 2、beanName = &factoryBeanImpl ------ FactoryBean的实现类 FactoryBeanImpl*/
@Component
public class FactoryBeanImpl implements FactoryBean<FactoryBeanObject> {/*** 在`getBean`的时候才会调用(懒加载)*/@Overridepublic FactoryBeanObject getObject() {return new FactoryBeanObject();}@Overridepublic Class<?> getObjectType() {return FactoryBeanObject.class;}}
如果 类A 实现了 FactoryBean 接口,那么 类A 就变成了 “A工厂”。
- 根据 a 获得的实际上是调用 类A 的
getObject()
方法返回的对象, 而不是 “A工厂”自己。
5、SmartFactoryBean
/*** SmartFactoryBean 和 FactoryBean 类似,区别在于:* FactoryBean 的 `getObject()` 不是在Spring启动时调用的,是在getBean的时候调用的,有点类似于懒加载。* 而 SmartFactoryBean 可以设置是否在Spring容器启动时就调用`getObject()`方法。*/
@Component
public class SmartFactoryBeanImpl implements SmartFactoryBean<SmartFactoryBeanObject> {@Overridepublic SmartFactoryBeanObject getObject() {System.out.println("调用了SmartFactoryBeanImpl的getObject()");return new SmartFactoryBeanObject();}@Overridepublic Class<?> getObjectType() {return SmartFactoryBeanObject.class;}/*** 是否在Spring容器启动时就调用getObject()方法*/@Overridepublic boolean isEagerInit() {return false;}}
6、registerBean 方法
/*** 通过 ApplicationContext 的 registerBean方法 注册bean*/
public class DefineByRegisterBean {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);context.registerBean(User.class, new Supplier<User>() {@Overridepublic User get() {// 可以定义bean对象的属性User user = new User();user.setName("zs");return user;}});context.refresh();User user = (User) context.getBean("user");}
}
四、Bean 的作用域
1、bean的作用域
Spring官方文档中给出的bean的scope有五种
value取值 | value描述 |
---|---|
singleton | 单例,Spring容器只会创建该bean的唯一实例【默认】 |
prototype | 多例,每个请求都会创建一个新的实例 |
request | 每个 http request 都会创建一个新的实例【仅在 WebApplicationContext 中有效】 |
session | 每个 http session 都会创建一个新的实例【仅在 WebApplicationContext 中有效】 |
application | 全局的 http session 中,容器只会创建该bean的唯一实例【仅在 WebApplicationContext 中有效】 |
在BeanDefinition
中只有两种scope
,就是singleton
和prototype
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; // singletonString SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // prototype
}
其余三种scope
,都在WebApplicationContext
中
package org.springframework.web.context;public interface WebApplicationContext extends ApplicationContext {String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";String SCOPE_REQUEST = "request";String SCOPE_SESSION = "session";String SCOPE_APPLICATION = "application";String SERVLET_CONTEXT_BEAN_NAME = "servletContext";String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";@NullableServletContext getServletContext();
}
2、@Scope 注解
@Scope
注解加在类上方,可以设置bean的作用域
@Configuration
public class ScopeConfig {/*** 默认情况是单例bean*/@Bean("singletonBean")public SingletonBean singletonBean() {return new SingletonBean();}/*** 多例bean需要加上 @Scope("prototype")*/@Bean("prototypeBean")@Scope("prototype")public PrototypeBean prototypeBean() {return new PrototypeBean();}}
public class ScopeTest {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);System.out.println(context.getBean("singletonBean"));System.out.println(context.getBean("singletonBean"));System.out.println(context.getBean("prototypeBean"));System.out.println(context.getBean("prototypeBean"));}
}
com.dmeo.spring.ioc.scope.model.SingletonBean@1ffe63b9
com.dmeo.spring.ioc.scope.model.SingletonBean@1ffe63b9
com.dmeo.spring.ioc.scope.model.PrototypeBean@6ac13091
com.dmeo.spring.ioc.scope.model.PrototypeBean@5e316c74
3、扩展 @Scope 注解
可以自定义注解,加上@Scope("prototype")
,也能实现多例bean。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Scope("prototype")
public @interface CustomPrototype {}
@Configuration
public class ScopeConfig {/*** 使用自定义注解声明多例bean*/@Bean("customPrototypeBean")@CustomPrototypepublic PrototypeBean customPrototypeBean() {return new PrototypeBean();}}
public class ScopeTest {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);System.out.println(context.getBean("customPrototypeBean"));System.out.println(context.getBean("customPrototypeBean"));}
}
com.dmeo.spring.ioc.scope.model.PrototypeBean@1e802ef9
com.dmeo.spring.ioc.scope.model.PrototypeBean@2b6faea6
4、单例bean & 单例模式
单例bean 和 单例模式 是有区别的:
- 单例模式:一个类只有一个实例
- 单例bean:一个beanName只会获取到一个实例(但是同一个类可能会有多个实例)
5、单例池
单例池:一个存放单例bean的Map集合
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
单例bean的获取:
- 根据 beanName,到
singletonObjects
单例池 中获取 bean。- 如果获取到了,直接返回
- 如果获取不到
- 调用
singletonFactory
的createBean
方法,创建单例bean - 创建成功以后,存入
singletonObjects
单例池,并将引用返回。
- 调用
6、bean的线程安全问题
- 对于多例bean来说,每次请求都会生成一个新的bean,是线程安全的
- 对于单例bean来说:
- 如果bean是无状态的(无实例变量,不会保存数据),那么bean是线程安全的
- 如果bean是有状态的(有实例变量,有数据存储功能),那么bean不是线程安全的
那么如何解决单例bean存在的线程安全问题呢?
- 改变作用域:
- 非web应用,在对应的类名上加上注解
@Scope("prototype")
,改成多例bean。 - web应用,在对应的类名上加上注解
@Scope("request")
,也可以达到一样的效果。
- 非web应用,在对应的类名上加上注解
- 不改变作用域:
- 尽量避免定义可变的成员变量。(不太现实)
- 将需要的可变成员变量保存在
ThreadLocal
中(推荐的一种方式)。
实际应用中,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。
五、依赖注入
依赖注入:Dependency Injection(简称DI注入)。
- 作用:建立bean与bean之间的关系,能够让相互协作的软件组件保持松散耦合。
- 前提:Spring管理的对象
- 理解:以解耦的方式,给IOC容器中的bean属性赋值,简称DI注入
Spring中,对象无需自己查找或创建与其所关联的其他对象。容器负责把需要相互协作的对象引用赋予各个对象。
创建应用对象之间协作关系的行为 通常称为 装配(wiring),这也是 依赖注入(DI)的本质。
1、手动注入 - xml方式(了解)
1)set方法
<bean name="orderService" class="com.xxx.service.OrderService"/><bean name="userService" class="com.xxx.service.UserService"><property name="orderService" ref="orderService"/>
</bean>
public class OrderService {private UserService userService;// 底层通过set方法注入,必须要有set方法,否则会报错public void setUserService(UserService userService) {this.userService = userService;}}
2)构造方法
<bean name="orderService" class="com.xxx.service.OrderService"/><bean name="userService" class="com.xxx.service.UserService"><constructor-arg index="0" ref="orderService"/>
</bean>
public class OrderService {private UserService userService;public OrderService(UserService userService) {this.userService = userService;}
}
2、自动注入 - xml方式(了解)
1)set方法
<bean name="orderService" class="com.xxx.service.OrderService"/>
<bean name="userService" class="com.xxx.service.UserService" autowired="byType"/>
<bean name="orderService" class="com.xxx.service.OrderService"/>
<bean name="userService" class="com.xxx.service.UserService" autowired="byName"/>
public class OrderService {private UserService userService;// 通过set方法注入,必须要有set方法,否则会报错(byName是根据set方法名set后面的部分)public void setUserService(UserService userService) {this.userService = userService;}}
2)构造方法
<bean name="orderService" class="com.xxx.service.OrderService"/>
<bean name="userService" class="com.xxx.service.UserService" autowired="constructor"/>
public class OrderService {private UserService userService;public OrderService(UserService userService) {this.userService = userService;}
}
3、自动注入 - @Autowired
1)基本使用
/*** 依赖注入之 @Autowired* 先byType* 1)根据type有一个 -> 直接注入* 2)根据type有多个 -> 再根据name筛选* 再byName* 1)根据name有一个 -> 直接注入* 2)根据name没找到 -> 项目启动就会报错 NoUniqueBeanDefinitionException(type有多个,name不存在)* 3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException** required属性(默认为true)* 1)required=false -> 允许找不到bean注入,值为null* 2)required=true -> 必须找到bean注入*/
@Component
public class InjectByAutowired {/*** 先根据type -> 找到多个 -> 再根据name -> 找到一个 -> 注入blue*/@Autowiredprivate Color blue;/*** 启动就报错 NoUniqueBeanDefinitionException(和上面的区别是,有blue,但是没有green)*/
// @Autowired
// private Color green;/*** 先根据type* 1)required = true --> 抛错* 2)required = false --> 不抛错,noBeanObject = null*/@Autowired(required = false)private NoBeanObject noBeanObject;public void testInject() {blue.printName();System.out.println("noBeanObject = " + noBeanObject); // null}}
2)搭配 @Qualifier
type有多个,name不存在 导致启动报错的情况,通过 @Qualifier
可以解决
@Component
public class InjectWithQualifier {/*** 不加 @Qualifier -> 启动就报错 NoUniqueBeanDefinitionException(type有多个,name不存在)* 加了 @Qualifier -> 从多个Color中选择red注入*/@Autowired@Qualifier("red")private Color green;public void testInject() {green.printName();}}
3)三种注入方式
【1】Filed变量注入
@Component
public class OrderService {@Autowiredprivate DependencyA dependencyA;@Autowiredprivate DependencyB dependencyB;@Autowiredprivate DependencyC dependencyC;
}
【2】Setter方法注入
@Component
public class OrderService {// 待注入的属性private DependencyA dependencyA;private DependencyB dependencyB;private DependencyC dependencyC;// 参数是 要注入的对象@Autowiredpublic void setDependencyA(DependencyA dependencyA) {this.dependencyA = dependencyA;}@Autowiredpublic void setDependencyB(DependencyB dependencyB) {this.dependencyB = dependencyB;}@Autowiredpublic void setDependencyC(DependencyC dependencyC) {this.dependencyC = dependencyC;}
}
【3】构造器注入(推荐)
@Component
public class OrderService {// 待注入的属性private DependencyA dependencyA;private DependencyB dependencyB;private DependencyC dependencyC;// 参数是 要注入的对象@Autowiredpublic OrderService(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {this.dependencyA = dependencyA;this.dependencyB = dependencyB;this.dependencyC = dependencyC;}
}
4)构造器注入 案例分析
【1】单个构造
在 Spring4.3 之后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入(可省略@Autowired)
/*** 使用空参构造实例化*/
@Component
public class UserService {private User user;public UserService() {}}
/*** 使用有参构造实例化*/
@Component
public class UserService {private User user;public UserService(User user) {this.user = user;}}
【2】空参构造 + 有参构造
/*** 空参构造 + 有参构造* ① 没有@Autowired --> 默认使用空参构造(不会注入InjectService)* ② 一个@Autowired --> 使用@Autowired标记的构造方法* ③ 多个@Autowired --> 报错:Invalid autowire-marked constructor ... * Found constructor with 'required' Autowired annotation already*/
@Component
public class ManyConstruction1 {public InjectService injectService;public ManyConstruction1() {}public ManyConstruction1(InjectService injectService) {this.injectService = injectService;}}
【3】多个有参构造
/*** 多个有参构造* ① 没有@Autowired --> 报错:No default constructor found; * nested exception is java.lang.NoSuchMethodException: * com.demo.spring.ioc.inject.construct.ManyConstruction2.<init>()* ② 一个@Autowired --> 使用@Autowired标记的构造方法* ③ 多个@Autowired --> 报错:Invalid autowire-marked constructor ... * Found constructor with 'required' Autowired annotation already*/
@Component
public class ManyConstruction2 {public InjectService injectService;public InjectService2 injectService2;@Autowiredpublic ManyConstruction2(InjectService injectService) {this.injectService = injectService;}public ManyConstruction2(InjectService injectService1, InjectService2 injectService2) {this.injectService = injectService1;this.injectService2 = injectService2;}}
【4】多个 @Autowired(false)
/*** 如果多个@Autowired都声明了required = false,不会报错* ① 优先使用参数最多的构造方法* ② 如果参数多的构造方法的参数bean不存在/找不到 --> 使用参数第二多的构造方法(以此类推)* ③ 如果存在参数一样多的构造方法 --> 使用声明在上面的构造方法*/
@Component
public class ManyConstruction3 {public InjectService injectService;public InjectService2 injectService2;public NoBeanObject noBeanObject;public ManyConstruction3() {}@Autowired(required = false)public ManyConstruction3(InjectService injectService) {System.out.println("使用了1个参数的构造方法1");this.injectService = injectService;}@Autowired(required = false)public ManyConstruction3(InjectService2 injectService2) {System.out.println("使用了1个参数的构造方法2");this.injectService2 = injectService2;}@Autowired(required = false)public ManyConstruction3(InjectService injectService, InjectService2 injectService2) {System.out.println("使用了2个参数的构造方法");this.injectService = injectService;this.injectService2 = injectService2;}@Autowired(required = false)public ManyConstruction3(InjectService injectService, InjectService2 injectService2, NoBeanObject noBeanObject) {System.out.println("使用了3个参数的构造方法");this.injectService = injectService;this.injectService2 = injectService2;this.noBeanObject = noBeanObject;}}
5)注意事项
父类中 @Autowired 修饰的会被注入
public class BaseService {// 是否会被注入??@Autowiredpublic UserService userService;}@Component
public class OrderService extends BaseService {private void test() {System.out.println(userService); // 会被注入!}}
static修饰的属性不会被注入
@Component
public class OrderService {// 是否会被注入??@Autowiredprivate static UserService userService;private void test() {System.out.println(userService); // null 不会被注入}}
4、自动注入 - @Resource
1)基本使用
/*** 依赖注入之 @Resource* 1. 指定name(根据name)* 1)根据name有一个 -> 直接注入* 2)根据name没找到 -> 项目启动就会报错 NoSuchBeanDefinitionException* 3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException** 2. 指定type(先根据type,再根据name)* 1)根据type有一个 -> 直接注入* 2)根据type没找到 -> 抛错* 3)根据type有多个 -> 再根据name找** 3. 都不指定(先根据name,再根据type)* 1)根据name有一个 -> 直接注入* 2)根据name没找到 -> 再根据type找 * -> 根据type有一个 -> 直接注入* -> 根据type有多个 -> 抛错 NoUniqueBeanDefinitionException* 3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException*/
@Component
public class InjectByResource {/*** 先根据name -> 注入的是Red*/@Resource(name = "red")private Color blue;/*** 先根据type -> 找到多个 -> 再根据name -> 注入white*/@Resource(type = Color.class)private Color white;/*** 默认先根据name -> 找不到 -> 再根据type -> 找到多个 -> 抛错 NoUniqueBeanDefinitionException*/
// @Resource
// private Color green;/*** 默认先根据name -> 找不到 -> 再根据type -> 找到一个 -> 注入oneBeanObject*/@Resourceprivate OneBeanObject oneBeanObj;public void testInject() {blue.printName();white.printName();}}
2)注意事项
@Resource
注解不是Spring提供的,是由JDK提供的(javax.annotation.Resource
),但是解析是由Spring负责的。
5、自动注入 - @Bean注解
可以看到,@Bean
的autowire
属性已经被打上了@Deprecated
,这里重点关注default Autowire.NO,其他的做一个了解。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {@DeprecatedAutowire autowire() default Autowire.NO;
}
public enum Autowire {/*** Constant that indicates no autowiring at all.*/NO(AutowireCapableBeanFactory.AUTOWIRE_NO),/*** Constant that indicates autowiring bean properties by name.*/BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),/*** Constant that indicates autowiring bean properties by type.*/BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);// ...
}
1)NO(主要关注这个)
autowire 这个属性已经过期了,所以我们主要关注 default Autowire.NO(也就是不指定该属性),其余两个了解一下即可。
【1】同一配置类
同一配置类中,可以直接引用依赖对象的@Bean方法进行注入(必须在同一配置类中)
@Configuration
public class Config {/*** orderService()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,* 确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。*/@Beanpublic OrderService orderService() {return new OrderService();}@Beanpublic UserService userService() {return new UserService(orderService());}}
注意:看起来 UserService 对应的bean 是通过调用 orderService() 得到的,但情况并非完全如此。
默认情况下,@Configuration标记的Config是代理对象,因此调用的 orderService() 是代理类调用的
- 如果 OrderService 对应的bean不存在,会创建对应的bean
- 如果 OrderService 对应的bean已存在,会直接返回已存在的bean
也就是说,每次调用 orderService() 方法,得到的都是同一个bean
【2】不同配置类
不同配置类中,可以通过@Bean修饰方法的参数进行注入(不需要在统一配置类也可以)
@Configuration
public class Config1 {@Beanpublic OrderService orderService() {return new OrderService();}
}
@Configuration
public class Config2 {// 创建UserService的bean时,会自动装配一个OrderService@Beanpublic UserService userService(OrderService orderService) {return new UserService(orderService);}
}
这样在构建UserService的bean时,Spring会找到OrderService的bean,并传入@Bean方法构建UserService的bean。
2)BY_NAME(了解)
@Configuration
public class BeanInjectConfig {/*** autowire = Autowire.BY_NAME 根据set方法注入(name 根据 setXxx 的 Xxx)* 1、没有set方法 -> 注入失败(属性为null,不报错)* 2、根据name没找到 -> 注入失败(属性为null,不报错)* 3、根据name找到了 -> 注入成功*/@Bean(autowire = Autowire.BY_NAME)public BeanInjectService beanInjectService() {return new BeanInjectService();}}
public class BeanInjectService {private OneBeanObject oneBeanObject;private NoBeanObject noBeanObject;private Color color;/*** 根据name找到了 -> 注入成功*/public void setOneBeanObject(OneBeanObject oneBeanObject) {this.oneBeanObject = oneBeanObject;}/*** 根据name没找到 -> 注入失败(属性为null,不报错)---> 存在bean,但是set后面的name不匹配*/// public void setOneBeanObject123(OneBeanObject oneBeanObject) {// this.oneBeanObject = oneBeanObject;// }/*** 根据name没找到 -> 注入失败(属性为null,不报错)---> 本来就没有bean*/public void setNoBeanObject(NoBeanObject noBeanObject) {this.noBeanObject = noBeanObject;}/*** 根据name没找到 -> 注入失败(属性为null,不报错)---> 存在bean,但是set后面的name不匹配*/public void setColor(Color color) {this.color = color;}/*** 根据name找到了 -> 注入成功*/
// public void setBlue(Color color) {
// this.color = color;
// }}
3)BY_TYPE(了解)
@Configuration
public class BeanInjectConfig {/*** autowire = Autowire.BY_TYPE 根据方法的参数类型注入(必须是set开头的方法)* 1、没有set开头的方法 -> 注入失败(属性为null,不报错)* 2、根据type没找到 -> 注入失败(属性为null,不报错)* 3、根据type找到一个 -> 注入成功* 4、根据type找到多个 -> 报错 NoUniqueBeanDefinitionException*/@Bean(autowire = Autowire.BY_TYPE)public BeanInjectService beanInjectService() {return new BeanInjectService();}}
public class BeanInjectService {private OneBeanObject oneBeanObject;private NoBeanObject noBeanObject;private Color color;/*** 根据type找到一个 -> 注入成功(必须是set开头的方法)*/public void set123(OneBeanObject oneBeanObject) {this.oneBeanObject = oneBeanObject;}/*** 根据type没找到 -> 注入失败(属性为null,不报错)---> 本来就没有bean*/public void setNoBeanObject(NoBeanObject noBeanObject) {this.noBeanObject = noBeanObject;}/*** 根据type找到一个 -> 注入成功(必须是set开头的方法)*/public void setBlue(Blue color) {this.color = color;}/*** 根据type找到多个 -> 报错 NoUniqueBeanDefinitionException*/
// public void setColor(Color color) {
// this.color = color;
// }}
6、自动注入 - @RequiredArgsConstructor
@RequiredArgsConstructor
是 Lombok
提供的一个注解
- 当一个类中包含
final
或@NonNull
注解修饰的字段时,这个注解会为该类自动生成一个包含这些字段的构造函数。
1)基本功能
@RequiredArgsConstructor
public class Person {@NonNullprivate String name;private final Integer age;private String address;
}
上面的代码使用 @RequiredArgsConstructor 注解后,Lombok 会自动生成以下代码:
public class Person {private @NonNull String name;private final Integer age;private String address;public Person(final @NonNull String name, final Integer age) {if (name == null) {throw new NullPointerException("name is marked non-null but is null");} else {this.name = name;this.age = age;}}
}
2)注入功能
在 Spring4.3 之后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入(可省略@Autowired)
因此,@RequiredArgsConstructor
可以实现构造器注入,而且可以避免冗余的构造方法。
@RequiredArgsConstructor
@Component
public class OrderService {// 待注入的属性private DependencyA dependencyA;private DependencyB dependencyB;private DependencyC dependencyC;
}
注意,使用@RequiredArgsConstructor
注入时,最好别再写其他构造方法,避免造成影响(参考「构造器注入 案例分析」)
六、Bean 的生命周期
1、Bean的创建
单例bean:只会创建一次
BeanFactory
:延迟加载(什么时候使用,什么时候创建)ApplicationContext
:饥饿加载(创建Spring容器时,就创建bean,不管是否使用)
多例bean:每次使用都创建一个新的bean
2、Bean的生命周期
Bean在Spring容器中从创建到销毁经历了若干个阶段:
# 实例化1. Spring对bean进行实例化推断构造方法/工厂方法,基于 BeanDefinition 对bean进行实例化,实例化的Instance封装在 BeanWrapper 对象中。# 属性填充2. Spring将值和依赖的bean填充到bean对应的属性中根据 BeanDefinition 获取 PropertyValues,填充属性到 BeanWrapper 中的 Instance。# 初始化3. 如果bean实现了 BeanNameAware 接口-> Spring会调用 setBeanName(String beanName) 方法4. 如果bean实现了 BeanClassLoaderAware 接口-> Spring会调用 setBeanClassLoader(ClassLoader classLoader) 方法4. 如果bean实现了 BeanFactoryAware 接口-> Spring会调用 setBeanFactory(BeanFactory beanFactory) 方法5. 如果bean实现了 ApplicationContextAware 接口-> Spring会调用 setApplicationContext(ApplicationContext applicationContext) 方法6. 如果bean实现了 BeanPostProcessor 接口-> Spring会调用 postProcessBeforeInitialization(Object bean, String beanName) 方法7. 如果bean实现了 InitializingBean 接口-> Spring会调用 afterPropertiesSet() 方法8. 如果bean定义时声明了初始化方法 initMethod()-> Spring会执行 自定义的初始化方法 initMethod()9. 如果bean实现了 BeanPostProcessor 接口-> Spring会调用 postProcessAfterInitialization(Object bean, String beanName) 方法# 销毁10. 如果bean实现了DisposableBean接口-> Spring会调用 destroy() 方法,进行bean的销毁。11. 如果bean定义时声明了销毁方法 destroyMethod()-> Spring会执行 自定义的销毁方法 destroyMethod()
3、案例演示
@Configuration
public class CycleConfig {/*** 指定 自定义初始化 & 自定义销毁 方法*/@Bean(initMethod = "customInit", destroyMethod = "customDestroy")public BeanCycle beanCycle() {return new BeanCycle();}}
/*** Bean的生命周期*/
public class BeanLifecycle implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,ApplicationContextAware, BeanPostProcessor, InitializingBean, DisposableBean {public BeanLifecycle() {System.out.println("执行 构造方法");}@Overridepublic void setBeanName(String name) {System.out.println("执行 BeanNameAware 的 setBeanName 方法");}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("执行 BeanClassLoaderAware 的 setBeanClassLoader 方法");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("执行 BeanFactoryAware 的 setBeanFactory 方法");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("执行 ApplicationContextAware 的 setApplicationContext 方法");}@PostConstructvoid postConstruct() {System.out.println("执行 @PostConstruct 标记的方法");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// System.out.println("执行 BeanPostProcessor 的 postProcessBeforeInitialization 方法");return bean;}@Overridepublic void afterPropertiesSet() {System.out.println("执行 InitializingBean 的 afterPropertiesSet 方法");}public void customInit() {System.out.println("执行 自定义的初始化方法 customInit");}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// System.out.println("执行 BeanPostProcessor 的 postProcessAfterInitialization 方法");return bean;}@PreDestroyvoid preDestroy() {System.out.println("执行 @PreDestroy 标记的方法");}@Overridepublic void destroy() {System.out.println("执行 DisposableBean 的 destroy 方法");}public void customDestroy() {System.out.println("执行 自定义的销毁方法 customDestroy");}}
// 启动服务执行 构造方法
执行 BeanNameAware 的 setBeanName 方法
执行 BeanClassLoaderAware 的 setBeanClassLoader 方法
执行 BeanFactoryAware 的 setBeanFactory 方法
执行 ApplicationContextAware 的 setApplicationContext 方法
执行 @PostConstruct 标记的方法
执行 InitializingBean 的 afterPropertiesSet 方法
执行 自定义的初始化方法 customInit// 关闭服务执行 @PreDestroy 标记的方法
执行 DisposableBean 的 destroy 方法
执行 自定义的销毁方法 customDestroy