7.5.Spring框架底层分析

一、体系结构图

在这里插入图片描述
在这里插入图片描述


二、7大核心模块

Spring框架由7个定义良好的模块(组件)组成,各个模块可以独立存在,也可以联合使用。

(1)Spring Core:核心容器提供了Spring的基本功能。核心容器的核心功能是用Ioc容器来管理类的依赖关系Spring采用的模式是调用者不理会被调用者的实例的创建,由Spring容器负责被调用者实例的创建和维护,需要时注入给调用者。这是目前最优秀的解耦模式。利用了反射机制

(2)Spring AOPSpring的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去注册)

作用

  1. BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。
  2. 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中了,这样就不会发生异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值