1. Spring 是什么? 及其特点.
Spring 框架是一个开源的轻量级容器框架. 主要有三大特点:
(1) 容器. Spring 框架是一个容器, 能够管理项目中的所有对象.
(2) IoC(控制反转).Spring 将创建对象的方式反转了, 从程序员自己创建反转给了程序.
DI(依赖注入) 需要有 IoC 环境, 在 Spring 创建 Bean 对象时, 动态的将依赖对象注入到 Bean 对象中去. 依赖注入和控制反转最大的好处就是解耦合.
(3) AOP(面向切面). 面向切面编程, 简而言之, 就是将纵向重复的代码横向抽取出来. Spring 框架应用了面向切面的思想, 主要体现在为容器中管理的对象生成动态代理对象.
2. applicationContext & BeanFactory 区
BeanFactory
BeanFactory 是一个接口, 用来实例化, 配置和管理 Bean 对象, 里面有一个 getBean()方法获取 Bean 对象.
使用 BeanFactory(拿到 Bean 对象) 三种方法:
方式一: 根据 id 拿, 可以确定
方式二: 也是根据 id 和 Class 实例, 可以确定(不需要强转)
方式三: 根据类型, 现在有两个类型相等, 无法确定(报错)
- // 第一步: 拿到资源配置文件
- Resource resource = new ClassPathResource("applicationContext.xml");
- // 第二步: 拿到核心对象 BeanFactory
- BeanFactory factory = new XmlBeanFactory(resource);
- // 通过 BeanFactory 获取 Bean 对象的三种方式:
- // 第一种: 通过 xml 文件中配置的 id 获取对象
- Object bean1 = factory.getBean("ud");
- ((UserDaoImpl)bean1).add();
- // 第二种: 通过 xml 文件中配置的 id 及类的字节码对象 获取对象
- UserDaoImpl bean2 = factory.getBean("ud",UserDaoImpl.class);
- bean2.add();
- // 第三种: 通过类的字节码对象 获取对象.
- UserDaoImpl bean3 = factory.getBean(UserDaoImpl.class);
- bean3.add(); // 依赖注入(DI)/ 控制反转(IoC)
- // 三种方式拿到的 Bena 对象在内存中的地址都相同
- System.out.println(bean1==bean2 && bean2==bean3);//true
以上三种方式是 spring 框架底层拿对象的方式, 我们不会去用这些方式去直接获取 bean 对象
获取 spring 核心对象的首选方式:
- ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
- // 通过该对象的 getBean()获取具体的 bean 对象
- UserDaoImpl bean4 = ac.getBean("ud",UserDaoImpl.class);
- UserDaoImpl bean5 = ac.getBean("ud",UserDaoImpl.class);
- System.out.println(bean4 == bean5); //true
区别:
1. ApplicationContext 是 BeanFactory 的子类, 拥有更多的功能与方法. 比如: 提供了文本信息解析工具, 包括 I18N(国际化)的支持; 提供了载入文件资源的通用方法; 提供了发送事件的功能.
2. ApplicationContext, 它是在容器启动时, 一次性创建了所有的 Bean. 这样, 在容器启动时, 我们就可以发现 Spring 中存在的配置错误, 这样有利于检查所依赖属性是否注入.
而 BeanFactroy 采用的是延迟加载形式来注入 Bean 的, 即只有在使用到某个 Bean 时(调用 getBean()), 才对该 Bean 进行加载实例化.
3. BeanFactory 和 ApplicationContext 都支持 BeanPostProcessor,BeanFactoryPostProcessor 的使用, 但两者之间的区别是: BeanFactory 需要手动注册, 而 ApplicationContext 则是自动注册.
扩展: 我们在使用 ApplicationContext 的时候, 可以通过配置让它也变成与 BeanFactory 一样的懒加载:
配置一: 让所有 Bean 都变成懒加载
- <beans xmlns="http://www.springframework.org/schema/beans" .... default-lazy-init="true">
- <bean id="myBean" class="cn.itsource._01_hello.MyBean">
- </bean>
- </beans>
配置二: 让其中一个 Bean 变成懒加载
<bean id="myBean" class="cn.itsource._01_hello.MyBean"lazy-init="true"></bean>
3. 请解释 Spring Bean 的生命周期?
先简单了解:
生命周期属性(了解)--- 初始化和销毁
(1)配置一个方法作为生命周期初始化方法, spring 会在对象创建之后立刻调用 init-method
(2)配置一个方法作为生命周期的销毁方法, spring 容器在关闭并销毁所有容器中的对象之前调用 destory-method
- <bean init-method="init" destory-method="destory">
- </bean>
- 对应注解为 @PostConstruct
- <bean name="hello" class="完整类名">
- </bean>
- 对应注解为 @PreDestory
Spring 中 Bean 的管理是其最基本的功能, 根据下面的图来了解 Spring 中 Bean 的生命周期:
解说:
(1)BeanFactoryPostProcessor 的 postProcessorBeanFactory()方法: 若某个 IoC 容器内添加了实现了 BeanFactoryPostProcessor 接口的实现类 Bean, 那么在该容器中实例化任何其他 Bean 之前可以回调该 Bean 中的 postPrcessorBeanFactory()方法来对 Bean 的配置元数据进行更改, 比如从 xml 配置文件中获取到的配置信息.
(2)Bean 的实例化: 对于 BeanFactory 容器, 当客户向容器请求一个尚未初始化的 bean 时, 或初始化 bean 的时候需要注入另一个尚未初始化的依赖时, 容器就会调用 createBean 进行实例化. 对于 ApplicationContext 容器, 当容器启动结束后, 通过获取 BeanDefinition 对象中的信息, 实例化所有的 bean.
(3)Bean 属性注入: 实例化后的对象被封装在 BeanWrapper 对象中, 紧接着, Spring 根据 BeanDefinition 中的信息 以及 通过 BeanWrapper 提供的设置属性的接口完成依赖注入.
(4)BeanNameAware 的 setBeanName()方法: 如果某个 Bean 实现了 BeanNameAware 接口, 那么 Spring 将会将 Bean 实例的 ID 传递给 setBeanName()方法, 在 Bean 类中新增一个 beanName 字段, 并实现 setBeanName()方法.
(5)BeanFactoryAware 的 setBeanFactory()方法: 如果某个 Bean 实现了 BeanFactoryAware 接口, 那么 Spring 将会将创建 Bean 的 BeanFactory 传递给 setBeanFactory()方法, 在 Bean 类中新增了一个 beanFactory 字段用来保存 BeanFactory 的值, 并实现 setBeanFactory()方法.
(6)ApplicationContextAware 的 setApplicationContext()方法: 如果某个 Bean 实现了 ApplicationContextAware 接口, 那么 Spring 将会将该 Bean 所在的上下文环境 ApplicationContext 传递给 setApplicationContext()方法, 在 Bean 类中新增一个 ApplicationContext 字段用来保存 ApplicationContext 的值, 并实现 setApplicationContext()方法.
(7)BeanPostProcessor 预初始化方法: 如果某个 IoC 容器中增加的实现 BeanPostProcessor 接口的实现类 Bean, 那么在该容器中实例化 Bean 之后, 执行初始化之前会调用 BeanPostProcessor 中的 postProcessBeforeInitialization()方法执行预初始化处理.
(8)InitializingBean 的 afterPropertiesSet()方法: 如果 Bean 实现了 InitializingBean 接口, 那么 Bean 在实例化完成后将会执行接口中的 afterPropertiesSet()方法来进行初始化.
(9)自定义的 inti-method 指定的方法: 如果配置文件中使用 init-method 属性指定了初始化方法, 那么 Bean 在实例化完成后将会调用该属性指定的初始化方法进行 Bean 的初始化.
(10)BeanPostProcessor 初始化后方法: 如果某个 IoC 容器中增加的实现 BeanPostProcessor 接口的实现类 Bean, 那么在该容器中实例化 Bean 之后并且完成初始化调用后执行该接口中的 postProcessorAfterInitialization()方法进行初始化后处理.
(11)使用 Bean: 此时有关 Bean 的所有准备工作均已完成, Bean 可以被程序使用了, 它们将会一直驻留在应用上下文中, 直到该上下文环境被销毁.
(12)DisposableBean 的 destory()方法: 如果 Bean 实现了 DisposableBean 接口, Spring 将会在 Bean 实例销毁之前调用该接口的 destory()方法, 来完成一些销毁之前的处理工作.
(13)自定义的 destory-method 指定的方法: 如果在配置文件中使用 destory-method 指定了销毁方法, 那么在 Bean 实例销毁之前会调用该指定的方法完成一些销毁之前的处理工作.
4. 解释 Spring 支持的几种 bean 的作用域.
Spring 容器中的 bean 可以分为 5 个范围: scope 属性
(1)singleton: 默认, 每个容器中只有一个 bean 的实例, 单例的模式由 BeanFactory 自身来维护.
(2)prototype: 多例原型: 被标识为多例的对象, 每次在获得才会被创建, 每次创建都是新的对象.
(3)request:web 环境下, 对象与 request 生命周期一致 .
(4)session:Web 环境下, 对象与 session 生命周期一致.
(5)global-session: 全局作用域, global-session 和 Portlet 应用相关. 当你的应用部署在 Portlet 容器中工作时, 它包含很多 portlet. 如果你想要声明让所有的 portlet 共用全局的存储变量的话, 那么这全局变量需要存储在 global-session 中. 全局作用域与 Servlet 中的 session 作用域效果相同.
总结: 绝大多数情况下, 使用单例 singleton(默认值), 但是在与 struts 整合时候, 务必要用 prototype 多例, 因为 struts2 在每次请求都会创建一个新的 Action, 若为单例, 在多请求情况下, 每个请求找找 spring 拿的都是同一个 action.
5. Spring 框架中的单例 Beans 是线程安全的么?
Spring 框架并没有对单例 bean 进行任何多线程的封装处理. 关于单例 bean 的线程安全和并发问题需要开发者自行去搞定. 但实际上, 大部分的 Spring bean 并没有可变的状态(比如 Serview 类和 DAO 类), 所以在某种程度上说 Spring 的单例 bean 是线程安全的. 如果你的 bean 有多种状态的话(比如 View Model 对象), 就需要自行保证线程安全. 最浅显的解决办法就是将多态 bean 的作用域由 "singleton" 变更为 "prototype".
6,Spring 如何处理线程并发问题?
在一般情况下, 只有无状态的 Bean 才可以在多线程环境下共享, 在 Spring 中, 绝大部分 Bean 都可以声明为 singleton 作用域, 因为 Spring 对一些 Bean 中非线程安全状态采用 ThreadLocal 进行处理, 解决线程安全问题.
ThreadLocal 和线程同步机制都是为了解决多线程中相同变量的访问冲突问题. 同步机制采用了 "时间换空间" 的方式, 仅提供一份变量, 不同的线程在访问前需要获取锁, 没获得锁的线程则需要排队. 而 ThreadLocal 采用了 "空间换时间" 的方式.
ThreadLocal 会为每一个线程提供一个独立的变量副本, 从而隔离了多个线程对数据的访问冲突. 因为每一个线程都拥有自己的变量副本, 从而也就没有必要对该变量进行同步了. ThreadLocal 提供了线程安全的共享对象, 在编写多线程代码时, 可以把不安全的变量封装进 ThreadLocal.
7. 注解将对象注册到 Spring 容器当中, 有几种注解方式? 它们有什么区别吗?
4 种. 配置在类上方的注解分别是:@Component() 普通的注解: 用于三层外的 bean 的配置. @Service() 业务层 . @Controller() Web 层中使用 . @Respository()// 持久层.
Spring 框架最早出现的只有 @Component()注解, 但如果所有的对象都使用同一个注解, 很难区分对象究竟属于哪一层架构. 基于此, Spring 又推出了 @Service(),@Controller(),@Respository()三种注解, 用于区分对象属于哪一层架构. 但 4 种注解方式从功能上来说没有任何区别.
8. 如何用注解的方式来完成属性注入?
1. 在需要使用注解进行的时候, 需要在配置文件中进行 开启注解扫描的配置
- <!-- 开启注解的扫描 扫描 com.gs 下所有的包 -->
- <context:component-scan base-package="com.gs" />
- <!-- 开启 Spring 的注解的扫描:@Controller @Service @Repository @Component 等 . 默认存在 -->
- <context:annotation-config />
2. 按类型分可以分为值类型注入和引用类型注入.
值类型注入可以通过 @Value()注解来完成, 该注解既可以声明在属性上, 也可以声明在方法上, 建议声明在方法上, 但是更多的人会声明在属性上, 因为更方便.
引用类型注入可以通过三种注解方式来完成:
配置在属性上的注解
@Resource javax 包下的一个注解, 功能更加强大点
@Autowired
@Qualifier 属性注入时通知 spring 容器注入何种类型的对象 (告诉 spring 容器对象的 id(name)) 注意: 在使用注解的方式下 及 一个接口下有多个实现类的时候 结合 @Autowird 一起使用
9. Spring 框架中, 什么注解可以用来指定对象的作用范围? 和怎么导入其他 spring 配置文件
答:@Scope(scopeName="singleton").
模块化配置, 即分模块配置(导入其他 spring 配置文件)
- <beans>
- <import resource="spring 配置文件的全路径名" />
- </beans>
10,Spring 的自动装配.
Spring 中 bean 有三种装配机制, 分别是: 1. 在 xml 中显示配置, 2. 在 java 中显示配置, 3. 隐式的 bean 发现机制和自动装配.
set 注入和构造注入有时在做配置时比较麻烦. 所以框架为了提高开发效率, 提供自动装配功能, 简化配置.
Spring 自动化装配可以借助 @Autowired 属性实现, 以下是自动装配 @Autowired 属性的六个值的简要介绍:
(1)no: 默认的方式是不进行自动装配的, 通过手工设置 ref 属性来进行装配 bean.
(2)byName: 通过 bean 的名称进行自动装配, 如果一个 bean 的 property 与另一 bean 的 name 相同, 就进行自动装配.
(3)byType: 通过参数的数据类型进行自动装配.
缺点: 如果存在多个相同类型的 bean 对象, 会出错.
如果属性为单一类型的数据, 那么查找到多个关联对象会发生错误.
如果属性为数组或集合 (泛型) 类型, 那么查找到多个关联对象不会发生异常.
(4)constructor: 利用构造函数进行装配, 并且构造函数的参数通过 byType 进行装配.
(5)autodetect: 自动探测, 如果有构造方法, 通过 construct 的方式自动装配, 否则使用 byType 的方式自动装配.
(6)default : 表示默认采用上一级标签的自动装配的取值. 如果存在多个配置文件的话, 那么每一个配置文件的自动装配方式都是独立的.
11.spring 中 有哪些依赖注入方式?
1. 使用属性的 setter 方法注入 , 这是最常用的方式.
属性注入要求 Bean 提供一个默认的无参的构造函数, 并为需要注入的属性提供对应的 Setter 方法. Spring 先调用 Bean 的默认构造函数实例化 Bean 对象, 然后通过反射的方式调用 Setter 方法注入属性值.
看下简单的例子:
待续.......
12.Spring 框架中都用到了哪些设计模式?
(1)工厂模式: BeanFactory 就是简单工厂模式的体现, 用来创建对象的实例;
(2)单例模式: Bean 默认为单例模式.
(3)代理模式: Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术;
(4)模板方法: 用来解决代码重复的问题. 比如. RestTemplate, JmsTemplate, JpaTemplate.
(5)观察者模式: 定义对象键一种一对多的依赖关系, 当一个对象的状态发生改变时, 所有依赖于它的对象都会得到通知被制动更新, 如 Spring 中 listener 的实现 --ApplicationListener.
参考文献: https://www.cnblogs.com/V1haoge/p/6106456.html
https://www.cnblogs.com/xiaoxi/p/5865330.html
来源: http://www.bubuko.com/infodetail-2985598.html