一、体系结构图
二、7大核心模块
Spring框架由7个定义良好的模块(组件)组成,各个模块可以独立存在,也可以联合使用。
(1)Spring Core:核心容器提供了Spring的基本功能。核心容器的核心功能是用Ioc容器来管理类的依赖关系.Spring采用的模式是调用者不理会被调用者的实例的创建,由Spring容器负责被调用者实例的创建和维护,需要时注入给调用者。这是目前最优秀的解耦模式。利用了反射机制
(2)Spring AOP:Spring的AOP模块提供了面向切面编程的支持。SpringAOP采用的是纯Java实现。Spring AOP采用基于代理的AOP实现方案,AOP代理由Ioc容器负责生成、管理,依赖关系也一并由Ioc容器管理,尽管如此,Spring Ioc容器并不依赖于AOP,这样我们可以自由选择是否使用AOP。
(3)Spring ORM:提供了与多个第三方持久层框架的良好整合。
(4)Spring DAO: Spring进一步简化DAO开发步骤,能以一致的方式使用数据库访问技术,用统一的方式调用事务管理,避免具体的实现侵入业务逻辑层的代码中。
(5)Spring Context:它是一个配置文件,为Spring提供上下文信息,提供了框架式的对象访问方法。Context为Spring提供了一些服务支持,如对国际化(i18n)、电子邮件、校验和调度功能。
(6)Spring Web:提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IoC容器初始化和针对Web的applicationContext.
(7)Spring MVC:提供了Web应用的MVC实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和web form之间。并且,还可以借助Spring框架的其他特性
三、什么是FactoryBean?
spring框架也称为BeanFactory
Bean工厂 (作用:创建Bean)
Spring中共有两种Bean:1.普通Bean, 2.FactoryBean (工厂Bean)
一般情况下,Spring通过反射机制利用<bean>
的class属性指定实现类实例化Bean,即getBean("<baen></bean>标签中的id值")
,在某些情况下,实例化Bean过程比较复杂,
如果按照传统的方式,则需要在<bean>
中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。
Spring为此提供了一个org.springframework.bean.factory.FactoryBean
的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。
FactoryBean
接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。
它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>
的形式
四、Bean的创建方式有哪些?(普通Bean和工厂Bean)
1.普通Bean(两种方式),默认是单例模式(反射实现,此时要求该bean类拥有一个无参数构造器)
User.java
1).方式一:
<bean id="u1" class="cn.qf.spring.pojo.User">
<property name="userName" value="崔宸"></property>
</bean>
【说明】id是Bean的名称
- a)在IOC容器中必须是唯一的;
- b)若id没有指定,Spring会自动将全系nag定型为类名作为Bean的名字;
- c)id可以指定多个名字,名字之间可用逗号、分号、或空格分隔。
2).方拾二:
① 使用工厂对象的非静态方法,先创建工厂类的对象:
factory-bean="factory"
工厂对象的名字
factory-method="createUser"
工厂对象的方法名字
在工厂类的非静态方法中,给对象初始化赋值
测试结果:
② 使用工厂对象的静态方法:可以直接创建Bean对象
在工厂类的静态方法中,给对象初始化赋值
结果:
2.工厂Bean (实现 FactoryBean接口)
接口中的三个方法:
-
public Object getObject() throws Exception
-
public Class getObjectType()
-
public boolean isSingleton()
如果设置返回值为true,就是单例模式applicationContext.xml
POJO实体类,实现FactoryBean接口
结果:
可以在getObject()方法中加入自己的业务逻辑
BeanFactory和FactoryBean的区别?
区别:BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似
五、XMLBeanFactory ?
最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory
,例如:XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用.
当执行User cuichen = (User)factory.getBean("u1");
时,给创建出来的bean对象初始化赋值
六、ApplicationContext通常的实现是什么?
ClassPathXmlApplicationContext
:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。(常用)
FileSystemXmlApplicationContext
:此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。(参数必须是文件的全路径名!例如:D:\wangyiyun\CloudMusic\locales\.........
)
WebXmlApplicationContext
:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
七、 你怎样定义类的作用域?( 单例Bean是非线程安全的)
当定义一个 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定 义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次 使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。
八、解释Spring支持的几种bean的作用域。
Spring框架支持以下五种bean的作用域:
- singleton : bean在每个Spring ioc 容器中只有一个实例。
- prototype:一个bean的定义可以有多个实例。
- request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
- global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
缺省的Spring bean 的作用域是Singleton.
示例:
在配置文件中将作用域修改为singleton(单例模式)
<bean id="studentService" class="com.doaoao.impl.StudentServiceImpl" scope="singleton"/>
在配置文件中将作用域修改为 prototype(原型模式)每次使用getBean时都会创建
<bean id="studentService" class="com.doaoao.impl.StudentServiceImpl" scope="prototype"/>
九、 Spring框架中的单例bean是线程安全的吗?
不,Spring框架中的单例bean不是线程安全的。
十、Bean的生命周期※※※4
1、定义阶段(读取到配置文件后就会创建) --> 2、初始化阶段–> 3、调用阶段(getBean()) --> 4、销毁阶段: ApplicationContext销毁时
(1)bean创建
在配置文件里面用<bean></bean>
来进行定义,
或者注解进行定义。例:UserDaoImpl.java中
@Repository("userDao")
创建名字为userDao的bean
(2)bean初始化(赋值)
有两种方式初始化:
- A.在配置文件中通过指定init-method属性来完成
在Bean的类中实现一个初始化Bean属性的方法,如init(),如:
public class HelloWorld{
public String msg=null;
public Date date=null;
public void init() {
msg=”HelloWorld”;
date=new Date();
}
……
}
然后,在配置文件中设置init-mothod属性:
<bean id=”HelloWorld” class=”com.pqf.beans.HelloWorld” init-mothod=”init” >
</bean>
-
B.实现org.springframwork.beans.factory.InitializingBean接口
Bean实现InitializingBean接口,并且增加 afterPropertiesSet() 方法:
public class HelloWorld implement InitializingBean {
public String msg=null;
public Date date=null;public void afterPropertiesSet() {
msg=”向全世界问好!”;
date=new Date();
}
……
}
那么,当这个Bean的所有属性被Spring的BeanFactory设置完后,会自动调用afterPropertiesSet()方法对 Bean进行初始化,于是,配置文件就不用指定 init-method属性了。
(3)bean调用
有三种方式可以得到bean实例,并进行调用
- 1、使用BeanWrapper
HelloWorld hw=new HelloWorld();
BeanWrapper bw=new BeanWrapperImpl(hw);
bw.setPropertyvalue(”msg”,”HelloWorld”);
system.out.println(bw.getPropertyCalue(”msg”));
- 2、使用BeanFactory
InputStream is=new FileInputStream(”config.xml”);
XmlBeanFactory factory=new XmlBeanFactory(is);
HelloWorld hw=(HelloWorld) factory.getBean(”HelloWorld”);
system.out.println(hw.getMsg());
- 3、使用ApplicationConttext (常用)
ApplicationContext ac = new ClassPathXmlApplicationContext("config.xml");
HelloWorld hw=(HelloWorld) actx.getBean(”HelloWorld”);
System.out.println(hw.getMsg());
(4)bean销毁
销毁有两种方式
- A.使用配置文件指定的destroy-method属性
与初始化属性 init-methods类似,在Bean的类中实现一个撤销Bean的方法,然后在配置文件中通过 destory-method指定,那么当bean销毁时,Spring将自动调用指定的销毁方法。 - B.实现org.springframwork.bean.factory.DisposeableBean接口
如果实现了DisposebleBean接口,那么Spring将自动调用bean中的Destory方法进行销毁,所以,Bean中必须提供 Destory方法。
十一、在StudentServiceImpl类中创建两个方法 init 和 destroy
package com.doaoao.impl;
import com.doaoao.service.StudentService;
public class StudentServiceImpl implements StudentService {
@Override
public void testDao() {
System.out.println(“Hello World”);
}
public StudentServiceImpl(){
System.out.println(“执行 StudentService的构造方法”);
}
public void init(){
System.out.println("执行初始化方法");
}
public void destroy(){
System.out.println("执行销毁方法");
}
}
在配置文件中添加两个属性 init-method 和 destroy-method 分别指定初始化方法和销毁方法
<bean id="studentService" class="com.doaoao.impl.StudentServiceImpl" init-method="init" destroy-method="destroy"/>
十二、spring的三种装配方式:(推荐使用@Autowired自动装配给属性赋值)
1、在 XML 文件中显式配置
2、通过注解装配 Bean
package pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = “student1”)
public class Student {
@Value(“1”) int id;
@Value(“student_name_1”) String name; // getter and setter
}
缺点:对于 @ComponentScan 注解,它只是扫描所在包的 Java 类,但是更多的时候我们希望的是可以扫描我们指定的类
3、自动装配——@Autowired(推荐!!!)※※※※※※※※※
上面提到的两个弊端之一就是没有办法注入对象,通过自动装配我们将解决这个问题。
所谓自动装配技术是一种由 Spring 自己发现对应的 Bean,自动完成装配工作的方式,它会应用到一个十分常用的注解 @Autowired
上,这个时候 Spring 会根据类型去寻找定义的 Bean 然后将其注入。
例:UserServiceImpl.java
package cn.qf.spring.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.qf.spring.dao.UserDao;
import cn.qf.spring.pojo.User;
import cn.qf.spring.service.UserService;
//@Service 只能用在service层中
@Service("userService")
public class UserServiceImpl implements UserService{
//声明UserDao类型的属性
//为dao属性注入值
//@Autowired 是spring内的注解,根据属性的类型查找对应的Bean,找到后进行赋值
@Autowired
private UserDao dao;
public boolean addUser(User user) {
int ret = dao.addUser(user);
if (ret==1) {
return true;
}else {
return false;
}
}
}
十二、获取Bean的方式
方法一:
ApplicationContext 的主要实现类:
1.ClassPathXmlApplicationContext
:从 类路径下加载配置文件(常用)
2.FileSystemXmlApplicationContext
: 从文件系统中加载配置文件
3.ConfigurableApplicationContext
: 扩展于 ApplicationContext,新增加两个主要方法:refresh()
和 close(), 让 ApplicationContext 具有启动、刷新和关闭上下文的能力
4.ApplicationContext
:在初始化上下文时就实例化所有单例的 Bean
方法二:通过Spring提供的utils类获取ApplicationContext对象
方法三:继承自抽象类ApplicationObjectSupport
方法四:继承自抽象类WebApplicationObjectSupport
方法五:实现接口ApplicationContextAware
方法六:通过Spring提供的ContextLoader
十三、BeanFactory和ApplicationContext的区别?
BeanFactory 接口
这是一个用来访问 Spring 容器的 root 接口,要访问 Spring 容器,我们将使用 Spring 依赖注入功能,使用 BeanFactory 接口和它的子接口
特性:
通常情况,BeanFactory 的实现是使用懒加载的方式,这意味着 beans 只有在我们通过 getBean() 方法直接调用它们时才进行实例化
实现 BeanFactory 最常用的 API 是 XMLBeanFactory
这里是如何通过 BeanFactory 获取一个 bean 的例子:
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
public class HelloWorldApp{
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory (new ClassPathResource("beans.xml"));
User obj = (User) factory.getBean("gufeng");
obj.getMessage();
}
}
ApplicationContext 接口
ApplicationContext 是 Spring 应用程序中的中央接口,用于向应用程序提供配置信息
它继承了 BeanFactory 接口,所以 ApplicationContext 包含 BeanFactory 的所有功能以及更多功能!它的主要功能是支持大型的业务应用的创建
ApplicationContext的三个实现类:
a、ClassPathXmlApplication:把上下文文件当成类路径资源
b、FileSystemXmlApplication:从文件系统中的XML文件载入上下文定义信息
c、XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息
总结:
spring的两种容器:
- a、BeanFactoy
- b、ApplicationContext应用上下文
ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。但是也有一些限制情形,比如移动应用内存消耗比较严苛,在那些情景中,使用更轻量级的 BeanFactory 是更合理的。然而,在大多数企业级的应用中,ApplicationContext 是你的首选。
BeanFactory:
BeanFactory懒加载,在启动的时候不会去实例化Bean,只有从容器中拿Bean的时候
getBean()
才会去实例化;
ApplicationContext:
ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
十四、spring DI 中注入值的方式都有哪些?
1)、设值注入 通过setXXX()给属性注入值
2)、构造注入 通过构造方法给属性赋值
3)、p命名空间注入
<!-- p命名空间注入 -->
<bean id="haohao" class="cn.cc.spring.pojo.User"
p:id="1001" p:userCode="cuichen" p:userName="崔宸">
</bean>
十五、Spring 框架中都用到了哪些设计模式?
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被自动更新,如Spring中listener的实现–ApplicationListener。
十六.Spring Ioc容器
Spring的IoC容器就是一个实现了BeanFactory接口的可实例化类。事实上,Spring提供了两种不同的容器:一种是最基本的BeanFactory,另一种是扩展的ApplicationContext。BeanFactory 仅提供了最基本的依赖注入支持,而 ApplicationContext 则扩展了BeanFactory ,提供了更多的额外功能。二者对Bean的初始化也有很大区别。BeanFactory当需要调用时读取配置信息,生成某个类的实例。如果读入的Bean配置正确,则其他的配置中有错误也不会影响程序的运行。而ApplicationContext 在初始化时就把 xml 的配置信息读入内存,对 XML 文件进行检验,如果配置文件没有错误,就创建所有的Bean ,直接为应用程序服务。相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
ApplicationContext会利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而BeanFactory需要在代码中通过手工调用addBeanPostProcessor()方法进行注册。
Bean装配实际上就是让容器知道程序中都有哪些Bean,可以通过以下两种方式实现:
配置文件(最为常用,体现了依赖注入DI的思想)
编程方式(写的过程中向BeanFactory去注册)
作用:
- BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。
- ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:
十七.有一个对象A,其中引用了对象B,对象B其中引用了对象C,对象C其中又引用了对象A,这个情况在IOC容器中是怎么加载的呢?(Spring的循环依赖问题)
IOC容器加载对象时有一个Map结构的初始化池,正在初始化的对象会放到池子中。Spring使用依赖注入的形式去实现依赖翻转,注入的方式有构造函数注入和set方法注入。如果选择构造函数注入的话,首先加载A对象,把A对象放入池子中,然后发现A对象中有B对象的引用,于是递归循环地去加载B对象,把B对象放入池子中,然后递归循环加载C对象,把C对象放入池子中,然后发现C对象中有A对象的引用,递归加载A对象,然后发现A对象已经在池子中了,这时候因为Spring默认是单例模式的,C对象要引用这个A对象,然而A对象还没有初始化完成,无法使用,于是就会抛出异常。
如果我们选择set方式注入,A对象在加载时放入池中,然后加载完毕从池中取出,这是通过set去注入B对象,B进入池中,加载完毕,B出池。然后通过set给B注入C的引用,C加载入池,加载完毕,C出池。然后给C对象注入A的引用,这时由于A对象已经构建完毕,就可以注入到C中了,这样就不会发生异常。