GitHub:https://github.com/JDawnF/learning_note
目录
1、Spring 的配置方式
-
Bean 由 Spring IoC 容器实例化,配置,装配和管理。
-
Bean 是基于用户提供给 IoC 容器的配置元数据 Bean Definition 创建。
单纯从 Spring Framework 提供的方式,一共有三种:
1、XML 配置文件。
Bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签开头。例如:
<bean id="studentBean" class="org.edureka.firstSpring.StudentBean">
<property name="name" value="Edureka"></property>
</bean>
2、注解配置。
-
@Bean
注解扮演与<bean />
元素相同的角色。 -
@Configuration
类允许通过简单地调用同一个类中的其他@Bean
方法来定义 Bean 间依赖关系。 -
例如:
@Configuration public class StudentConfig { @Bean public StudentBean myStudent() { return new StudentBean(); } }
可以通过在相关的类,方法或字段声明上使用注解,将 Bean 配置为组件类本身,而不是使用 XML 来描述 Bean 装配。默认情况下,Spring 容器中未打开注解装配。因此,需要在使用它之前在 Spring 配置文件中启用它。例如:
<beans>
<context:annotation-config/>
<!-- bean definitions go here
或者通过下面这种方式,指定一个包:
<context:component-scan base-package="cn.gacl.java"/> -->
</beans>
3、Java Config 配置。
-
Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。
目前主要使用 Java Config 配置为主。当然,三种配置方式是可以混合使用的。例如说:
-
Dubbo 服务的配置,一般使用 XML 。
<dubbo:protocol name="dubbo" port="20884"></dubbo:protocol> <dubbo:application name="lehuan-search-service"/> <dubbo:registry address="zookeeper://192.168.98.135:2181"/> <!--相当于包扫描--> <dubbo:annotation package="com.lehuan.search.service.impl"/> <dubbo:provider delay="-1" timeout="10000"/>
-
Spring MVC 请求的配置,一般使用
@RequestMapping
注解。 -
Spring MVC 拦截器的配置,一般 Java Config 配置。
另外,现在已经是 Spring Boot 的天下,所以更加是 Java Config 配置为主。
2、Bean Scope作用域
Spring Bean 支持 5 种 Scope ,分别如下:
-
Singleton - 每个 Spring IoC 容器仅有一个单 Bean 实例。默认
Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个 Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是 Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式,配置为:
<bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>
-
Prototype - 每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例。
每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的 bean 使用 prototype 作用域,而对无状态的 bean 使用 singleton作用域。
-
Request - 每一次 HTTP 请求都会产生一个新的 Bean 实例,并且该 Bean 仅在当前 HTTP 请求内有效。
当前 Http 请求结束,该 bean实例也将会被销毁。
<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
-
Session - 每一个的 Session 都会产生一个新的 Bean 实例,同时该 Bean 仅在当前 HTTP Session 内有效。
-
Global session - 在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用 portlet context 时有效。(在Spring5中)
开发者是可以自定义 Bean Scope ,具体可参见 《Spring(10)—— Bean 作用范围(二)—— 自定义 Scope》
3、Bean的生命周期
-
通过构造器或工厂方法创建Bean实例
-
为Bean的属性设置值和对其它Bean的引用
-
调用Bean的初始化方法
-
Bean可以使用了
-
当容器关闭时,调用Bean的销毁方法
Spring Bean 的初始化流程如下:
实例化 Bean 对象
-
实例化一个 Bean,也就是我们常说的 new。Spring 容器根据配置中的 Bean Definition(定义)中实例化 Bean 对象。
Bean Definition 可以通过 XML,Java 注解或 Java Config 代码提供。
IOC 依赖注入:按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入。
setBeanName 实现:如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的 setBeanName(String name) 方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值
BeanFactoryAware 实现:如果这个 Bean 已经实现了 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory(BeanFactory beanFactory)
方法。setBeanFactory(BeanFactory)传递的是 Spring 工厂自身(可以用这个方式来获取其它 Bean, 只需在 Spring 配置文件中配置一个普通的 Bean 就可以)。
ApplicationContextAware 实现:如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用setApplicationContext(ApplicationContext)方法,传入 Spring 上下文(同样这个方式也 可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接 口,有更多的实现方法)
postProcessBeforeInitialization 接口实现-初始化预处理:如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用 作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应 用于内存或缓存技术。
init-method:如果 Bean 在 Spring 配置文件中配置了<bean />的 init-method 属性,会自动调用其配置的初始化方法。
postProcessAfterInitialization:如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessAfterInitialization(Object obj, String s)方法。 注:以上工作完成以后就可以应用这个 Bean 了,那这个 Bean 是一个 Singleton 的,所以一 般情况下我们调用同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中 也可以配置非 Singleton。
Destroy 过期自动清理阶段:当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用其实现的 destroy()方法;
destroy-method 自配置清理:
-
最后,如果这个 Bean 的 Spring 配置中配置了 <bean />的destroy-method 属性,会自动调用其配置的 销毁方法。
-
bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们你可以自己定制 初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。
<bean id="" class="" init-method="初始化方法" destroy-method="销毁方法">
4、内部Bean
只有将 Bean 仅用作另一个 Bean 的属性时,才能将 Bean 声明为内部 Bean。
-
为了定义 Bean,Spring 提供基于 XML 的配置元数据在
<property>
或<constructor-arg>
中提供了<bean>
元素的使用。 -
内部 Bean 总是匿名的,并且它们总是作为原型 Prototype 。
例如,假设我们有一个 Student 类,其中引用了 Person 类。这里我们将只创建一个 Person 类实例并在 Student 中使用它。示例代码如下:
// Student.java
public class Student {
private Person person;
// ... Setters and Getters
}
// Person.java
public class Person {
private String name;
private String address;
// ... Setters and Getters
}
<!-- bean.xml -->
<bean id=“StudentBean" class="com.edureka.Student">
<property name="person">
<!--This is inner bean -->
<bean class="com.edureka.Person">
<property name="name" value=“Scott"></property>
<property name="address" value=“Bangalore"></property>
</bean>
</property>
</bean>
可以理解为在一个bean标签中引用了另一个bean,并且这个内部bean是作为外部bean的属性存在的。
5、Spring 装配
当 Bean 在 Spring 容器中组合在一起时,它被称为装配或 Bean 装配。Spring 容器需要知道需要什么 Bean 以及容器应该如何使用依赖注入来将 Bean 绑定在一起,同时装配 Bean 。
装配,和上文提到的 DI 依赖注入,实际是一个东西。
自动装配
Spring 容器能够自动装配 Bean 。也就是说,可以通过检查 BeanFactory 的内容让 Spring 自动解析 Bean 的协作者。
自动装配的不同模式:
-
no - 这是默认设置,表示没有自动装配。应使用显式 Bean 引用进行装配。
-
byName - 它根据 Bean 的名称注入对象依赖项。它匹配并装配其属性与 XML 文件中由相同名称定义的 Bean 。
-
【最常用】byType - 它根据类型注入对象依赖项。如果属性的类型与 XML 文件中的一个 Bean 类型匹配,则匹配并装配属性。是@Autowired这个Spring注解默认的注入方式。
-
构造函数(constructor) - 它通过调用类的构造函数来注入依赖项。它有大量的参数。
-
autodetect - 首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配。
6、延迟加载
默认情况下,容器启动之后会将所有作用域为单例的 Bean 都创建好,但是有的业务场景我们并不需要它提前都创建好。此时,我们可以在Bean 中设置 lzay-init = "true"
。
-
这样,当容器启动之后,作用域为单例的 Bean ,就不在创建。
-
而是在获得该 Bean 时,才真正在创建加载。
7、单例 Bean 是否线程安全
Spring 框架并没有对单例 Bean 进行任何多线程的封装处理。
-
关于单例 Bean 的线程安全和并发问题,需要开发者自行去搞定。
-
并且,单例的线程安全问题,也不是 Spring 应该去关心的。Spring 应该做的是,提供根据配置,创建单例 Bean 或多例 Bean 的功能。
当然,但实际上,大部分的 Spring Bean 并没有可变的状态(比如Serview 类和 DAO 类),所以在某种程度上说 Spring 的单例 Bean 是线程安全的。
如果你的 Bean 有多种状态的话,就需要自行保证线程安全。最浅显的解决办法,就是将多态 Bean 的作用域( Scope )由 Singleton 变更为 Prototype 。
8、Spring解决循环依赖
参照:芋道源码