Spring
Spring
标签(空格分隔): Spring
Spring通过配置文件去描述
Bean
和Bean
之间的依赖关系, 利用Java
的反射功能实例化Bean
并建立Bean
之间的依赖关系.Spring IoC
在完成这些底层工作的基础之上还提供了Bean
的实例缓存, 生命周期管理, Bean代理实例, 事件发布, 资源装载等高级服务.
- 实例缓存: 对于 单例模式的
Bean
,Spring IoC
在将其实例化之后会进行缓存, 以供下次使用, 当我们向容器索取Bean
的时候, 第一步就是从缓存中读取, 如果没有的话 才回去创建新的实例. - 生命周期管理: 可以配置单例模式的
init-method
@PostConstruct
(构造函数之后) 和destory-method
@PreDestory
(销毁之前). Bean
代理实例: 通过代理模式给实例添加额外功能.- 事件发布: 给某个事件 加上一个监听器, 以对事件的不同结果, 实行不同的操作.
- 资源装载: 装载类外资源.
生命周期管理
Bean工厂(
com.springframework.beans.factory.BeanFactory
): 是Spring框架中最核心的接口, 它提供了高级IoC的配置机制. BeanFactory使管理不同类型的Java对象成为可能.
应用上下文(
com.springframework.context.ApplicationContext
):建立在BeanFactory
基础之上提供了更多面向引用的功能, 它提供了国际化支持和框架事件体系, 更加易于创建实际应用.
我们一般称BeanFactory
为IoC
容器, 而称ApplicationContext
为应用上下文或者Spring容器.
BeanFactory是Spring框架的基础设置, 面向Spring本身; ApplicationContext面向使用Spring框架的开发者, 几乎所有的应用场合都可以直接使用ApplicationContext
.
Spring框架是工厂, 而被创建的类对象本身也可能是一个工厂, 这就形成了创建工厂的工厂.
BeanFactory
BeanFactory是类的通用工厂. 他可以创建并管理各种类的对象, 这些类就是POJO, Spring称这些被创建并管理的对象为Bean.
1. 初始化
我们在Spring配置文件中创建Bean People, 然后通过BeanFactory加载配置文件, 启动IoC容器.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people" class="net.deniro.springBoot.spring4.IoC.People"
p:name="deniro"
p:age="25"
/>
</beans>
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("classpath:beans.xml");
System.out.println("getURL:" + resource.getURL());
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
System.out.println("Bean 工厂已完成初始化");
People people = factory.getBean("people", People.class);
System.out.println("People 已被创建");
System.out.println(people.toString());
ApplicationContext
ApplicationContext由BeanFactory派生而来, 提供了很多的实际应用的功能. 在BeanFactory中, 很多功能需要编程实现, 而在ApplicationContext中则可以通过配置的方式去实现解耦.
//从类路径加载配置文件
// 提供一组配置文件, Spring IoC在资源装配的时候, 会将其进行整合.
ApplicationContext context3 = new ClassPathXmlApplicationContext(new
String[]{"beans.xml", "beans2.xml"});
//从文件系统加载配置文件
ApplicationContext context2 = new FileSystemXmlApplicationContext("d:/beans.xml");
Spring支持注解配置, 主要功能来自于Spring的一个名为JavaConfig
的子项目, 目前JavaConfig
已经升级为Spring核心框架的一部分, 一个标注@Configuration
注解的POJO既可以为Spring
提供所需的Bean
配置信息.
@Configuration//表示这个类包含配置信息
public class Beans {
//定义 Bean
@Bean(name = "people")
public People build() {
People people = new People();
people.setAge(25);
people.setName("deniro");
return people;
}
}
ApplicationContext context = new AnnotationConfigApplicationContext(Beans.class);
People people = context.getBean("people", People.class);
Assert.assertNotNull(people);
Bean的生命周期
- Spring Bean生命周期简单易懂. 在一个
bean
实例被初始的时候, 需要执行一系列的操作以达到可用的状态. 同样的, 当一个类不再被调用的时候需要执行相关的析构函数, 并将其从容器中移除. Spring bean factory
负责管理在Spring
容器中被创建的bean
的生命周期. Bean的生命周期由两组回调函数组成.- 初始化之后调用的毁掉方法.
- 销毁之前调用的毁掉方法.
- Spring框架提供了以下四种方式来管理bean的生命周期事件 :
- 配置文件中提供构造类中
init-method
和destory-method
的方法名, 用于构造函数之后,和析构函数之前的调用. - 注解方式的情况下在类中的方法上加
@PostConstruct
和@PreDestory
注解. - InitializingBean 和 DisposableBean 回调接口
- 针对特殊行为的其他 Aware 接口
Spring IoC 如何实现
Bean工厂(
com.springframework.beans.factory.BeanFactory
): 是Spring框架中最核心的接口, 它提供了高级IoC的配置机制. BeanFactory使管理不同类型的Java对象成为可能.
应用上下文(
com.springframework.context.ApplicationContext
):建立在BeanFactory
基础之上提供了更多面向引用的功能, 它提供了国际化支持和框架事件体系, 更加易于创建实际应用.
我们一般称BeanFactory
为IoC
容器, 而称ApplicationContext
为应用上下文或者Spring容器.
BeanFactory是Spring框架的基础设置, 面向Spring本身; ApplicationContext面向使用Spring框架的开发者, 几乎所有的应用场合都可以直接使用ApplicationContext
.
Spring框架是工厂, 而被创建的类对象本身也可能是一个工厂, 这就形成了创建工厂的工厂.
Spring AOP
有了IOC其中CGLIB
解决了静态代理和动态代理对类的接口的依赖性和功能依赖性的问题之后, 使得将所有类(非final修饰的类)都实现代理模式成为可能(CHLIB是基于继承的方式做的动态代理). 所以IOC是AOP的基础.
面向切面编程, 在我们的应用中, 经常需要做一些事情, 但是这些事情与核心业务无关, 比如要记录所有update的执行时间, 操作人信息等等 到日志. 通过 AOP就可以在不修改原有代码的情况下完成需求.
AOP思想: 基于动态代理把各个事务单独抽取出来, 然后通过配置去寻找他的前置,后置,异常等方法.
实际上就是通过 动态代理设计模式的动态代理方法, 使用 InvocationHandler传入类加载器和实现的接口 以 获取代理运行的被代理类的函数的方法名和传入的参数, 然后通过反射创建被代理类完成其功能, 并在其前和其后加入其它功能.
JDK动态代理类和委托类需要实现同一个接口, 也就是说只有实现了某个接口的类可以使用Java动态代理机制. 但是, 事实上使用中并不是遇到的所有类都会有实现接口. 因此, 对于没有实现接口的类, 就不能使用该机制. CGLIB则可以实现对类的动态代理.
Spring事务实现方式
编程式事务指的是通过编码方式实现事务, 即类似于JDBC编程实现事务管理.
声明式事务管理又有两种实现方式:
- 基于xml配置文件的方式
- 另一个是在业务方法上进行
@Transaction
注解, 将事务规则应用到业务逻辑中.
划分处理单元IOC
通过IOC将事务的各种配置放到IOC容器之中.
由于Spring解决的问题是对单个数据库进行局部事务处理的, 具体的实现首先用Spring中的IOC划分了事务处理单元. 并且将对事务的各种配置放到了IOC容器中(设置事务管理器, 设置事务的传播特性以及隔离机制).
AOP拦截需要进行事务处理的类
Spring事务处理模块是通过AOP功能来实现声明式事务处理的, 具体操作(比如事务实行的配置和读取, 事务对象的抽象), 用TransactionProxyFactoryBean
接口来使用AOP功能, 生成proxy代理对象. 通过TransactionProxyFactoryBean
完成对代理方法的拦截, 将事务的处理功能编织到拦截的方法中. 读取IOC容器事务配置属性, 转化为Spring事务处理需要的内部数据结构,转化为TransactionAttribute
表示的数据对象.
SpringMVC 运行流程
SpringMVC
将所有的请求都交给DispatcherServlet
.DispatcherServlet
查询一个或多个HandlerMapping
, 找到处理请求的Controller
.DispatcherServlet
将请求提交到目标Controller
.Controller
进行业务逻辑处理之后, 会返回一个ModelAndView
.DispatcherServlet
查询一个或多个ViewResolver
视图解析器, 找到ModelAndView
对象指定的视图对象.- 视图对象负责渲染并返回给客户端.
SpringMVC的启动流程
<!-- 分发所有的HTTP 请求和响应. -->
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-mvc*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
-
web.xml
中为SpringMVC
的Servlet
配置了load-on-startup
, 然后程序启动的时候会初始化SpringMVC
, 在HTTPServlet
中配置的contextConfigLocation
属性到Servlet
中, 然后在FrameWorkServlet
中创建了WebApplicationContext
,DispatcherServlet
根据contextConfigLocation
配置的classpath
下的xml
文件初始化了SpringMVC
总的组件. -
load-on-startup
: 大于等于0的时候,表示容器在启动的时候就加载并初始化这个Servlet; 小于0或者不设置的时候, 表示容器在该Servlet被选择使用的时候才回去加载.
SpringMVC启动过程大致分为两个阶段:
- ContextLoaderListener初始化, 实例化IOC容器, 并将此容器注册到ServletContext中.
- DispatcherServlet初始化, 建立自己的上下文, 也注册到ServletContext中.
Spring单例实现原理.
我们实现的单例,不论是懒汉,饿汉或者是双重加锁且进制代码重排, 都需要在定义类的时候对其单例模式进行定义, 但是在Spring中却并没有这样做.
- Spring对Bean实例的创建时用单例注册表的方式实现的, 这个注册表的缓存是
ConcurrentHashMap
对象, 在我们需要实例的时候, 去向IOC索取, 如果发现缓存中不存在的话就去实例化该对象, 然后放入缓存并返回. 如果缓存有的话就直接返回. - 通过这种方式实现了Spring的单例模式, 并且减少了因为频繁的创建和销毁实例付出的代价.
单例模式 / 原型模式
单例模式: 确保每个类只有一个实例, 而且自行实例化并向整个系统提供这个实例.
当多用户同时请求一个服务的时候, 容器会给每一个请求分配一个线程, 这时多个线程会并发执行该请求对应的业务逻辑(成员方法). 重要的是, 如果这时候逻辑处理中有对该单例属性的修改(有状态Bean), 则必须考虑线程同步问题.
同步机制的比较
ThreadLocal和线程同步机制相比有什么优势呢?
- ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题.
- 同步机制: 通过对象的锁机制保证同一时间只有一个线程访问变量, 这时候变量是多个线程共享的, 使用同步机制要求程序员缜密的分析什么时候对变量进行读写, 什么时候需要锁定某个对象, 什么时候释放对象锁等复杂的问题, 程序设计和编写的难度比较大.
- ThreadLocal: 从另一个角度去避免上述的困难并且解决并发访问, ThreadLocal会为每一个线程提供一个独立的变量副本, 从而隔离了多个线程对数据的访问冲突. 因为每一个线程都有自己的变量副本, 从而也就没必要对改变量进行同步了. ThreadLocal提供了线程安全的共享对象, 在编写多线程代码的时候, 可以把不安全的对象封装进ThreadLocal.
- 在算法常用的角度来说就是 同步机制 以牺牲时间复杂度的代价降低空间复杂度. ThreadLocal以牺牲空间复杂度的代价降低时间复杂度.
Spring使用ThreadLocal解决线程安全问题. 按上述只有无状态的Bean才可以在多线程情况下共享. 但是在Spring中绝大部分的Bean都可以声明为Singleton, 这是因为Spring对一些 有状态的Bean使用了ThreadLocal进行处理.
原型模式的使用情况: 自己生命的有状态的Bean在高并发的情况下, 可以将其声明为Prototype.
这样在基本情况下我们使用 单例模式就可以了 , 对于我们声明的一些 有状态Bean, 对其
@Scope(ConfigurableFactory.SCOPE_PROTOTYPE)
装饰.