Spring概览

Spring 是分层的 full-stack(全栈)轻量级开源框架,以 IoC 和 AOP 为内核,提供了展现层 SpringMVC 和业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,已经成为使用最多的 Java EE 企业应用开源框架。

我们经常说的 Spring 其实指的是Spring Framework(spring 框架)。

Spring 的优势 方便解耦,简化开发(通过Spring提供的IoC容器实现), AOP编程的支持,声明式事务的支持,方便程序的测试,方便集成各种优秀框架,降低JavaEE API的使用难度,源码是经典的 Java 学习范例。

Spring 的核心结构:Spring是一个分层非常清晰并且依赖关系、职责定位非常明确的轻量级框架

主要包括几个大模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Container模块和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了一个令人愉悦的融合了现有解决方案的零侵入的轻量级框架。

Spring 的核心思想:IOC和AOP不是spring提出的,在spring之前就已经存在,只不过更偏向于理论化,spring在技术层次把这两个思想做了非常好的实现(Java)

什么是IoC?
IoC Inversion of Control (控制反转/反转控制),注意它是一个技术思想,不是一个技术实现描述的事情:Java开发领域对象的创建,管理的问题传统开发方式:比如类A依赖于类B,往往会在类A中new一个B的对象
IoC思想下开发方式:我们不用自己去new对象了,而是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使用哪个对象,去问IoC容器要即可我们丧失了一个权利(创建、管理对象的权利),得到了一个福利(不用考虑对象的创建、管理等一系列事情)为什么叫做控制反转?控制:指的是对象创建(实例化、管理)的权利反转:控制权交给外部环境了(spring框架、IoC容器)

IoC解决对象之间的耦合问题

OC和DI描述的是同一件事情,只不过角度不一样罢了

AOP是OOP的延续,从OOP说起

oop是一种垂直纵向继承体系

OOP三大特征:封装、继承和多态oop是一种垂直继承体系OOP编程思想可以解决大多数的代码重复问题,但是有一些情况是处理不了的,比如下面的在顶级父类Animal中的多个方法中相同位置出现了重复代码,OOP就解决不了

横切逻辑代码横切逻辑代码存在什么问题:横切代码重复问题横切逻辑代码和业务代码混杂在一起,代码臃肿,维护不方便AOP出场,AOP独辟蹊径提出横向抽取机制,将横切逻辑代码和业务逻辑代码分析代码拆分容易,那么如何在不改变原有业务逻辑的情况下,悄无声息的把横切逻辑代码应用到原有的业务逻辑中,达到和原来一样的效果,这个是比较难的

AOP在解决什么问题:在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复

为什么叫做面向切面编程:

「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以面向横切逻辑

「面」:横切逻辑代码往往要影响的是很多个方法,每一个方法都如同一个点,多个点构成面,有一个面的概念在里面

Spring IOC 应用
BeanFactory与ApplicationContext区别:BeanFactory是Spring框架中IoC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能的。通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等等

启动 IoC 容器的方式:
Java环境下启动IoC容器,ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)FileSystemXmlApplicationContext:从磁盘路径上加载配置文件AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
Web环境下启动IoC容器:从xml启动容器
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener></web-app>

从配置类启动容器:
纯xml模式
实例化Bean的三种方式
方式一:使用无参构造函数
方式二:使用静态方法创建
方式三:使用实例化方法创建
Bean的X及生命周期
作用范围的改变
在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它支持配置的方式改变作用范围。作用范围官方提供的说明如下图
在上图中提供的这些选项中,我们实际开发中用到最多的作用范围就是singleton(单例模式)和prototype(原型模式,也叫多例模式)。配置方式参考下面的代码:<beanid="userService"class="com.lagou.factory.BeanFactory"factory-method="getTransferService"></bean><beanid="beanFactory"class="com.lagou.factory.instancemethod.BeanFactory"></bean><beanid="transferService"factory-bean="beanFactory"factory-method="getTransferService"></bean><beanid="transferService"class="com.lagou.service.impl.TransferServiceImpl"scope="singleton"></bean>

单例模式:singleton对象出生:当创建容器时,对象就被创建了。对象活着:只要容器在,对象一直活着。对象死亡:当销毁容器时,对象就被销毁了。一句话总结:单例模式的bean对象生命周期与容器相同。多例模式:prototype对象出生:当使用对象时,创建新的对象实例。对象活着:只要对象在使用中,就一直活着。对象死亡:当对象⻓时间不用时,被java的垃圾回收器回收了。一句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。

Bean标签属性在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的一个对象。换句话说,如果一个对象想让spring管理,在XML的配置中都需要使用此标签配置,Bean标签的属性如下:id属性:用于给bean提供一个唯一标识。在一个标签内部,标识必须唯一。class属性:用于指定创建Bean对象的全限定类名。name属性:用于给bean提供一个或多个名称。多个名称用空格分隔。factory-bean属性:用于指定创建当前bean对象的工厂bean的唯一标识。当指定了此属性之后,class属性失效。factory-method属性:用于指定创建当前bean对象的工厂方法,如配合factory-bean属性使用,则class属性失效。如配合class属性使用,则方法必须是static的。scope属性:用于指定bean对象的作用范围。通常情况下就是singleton。当要用到多例模式时,可以配置为prototype。init-method属性:用于指定bean对象的初始化方法,此方法会在bean对象装配后调用。必须是一个无参方法。destory-method属性:用于指定bean对象的销毁方法,此方法会在bean对象销毁前执行。它只能为scope是singleton时起作用。

DI 依赖注入的xml配置依赖注入分类按照注入的方式分类构造函数注入:顾名思义,就是利用带参构造函数实现对类成员的数据赋值。set方法注入:它是通过类成员的set方法实现数据的注入。(使用最多的)按照注入的数据类型分类基本类型和String注入的数据类型是基本类型或者是字符串类型的数据。其他Bean类型
注入的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。复杂类型(集合类型)注入的数据类型是Aarry,List,Set,Map,Properties中的一种类型。依赖注入的配置实现之构造函数注入顾名思义,就是利用构造函数实现对类成员的赋值。它的使用要求是,类中提供的构造函数参数个数必须和配置的参数个数一致,且数据类型匹配。同时需要注意的是,当没有无参构造时,则必须提供构造函数参数的注入,否则Spring框架会报错。

在使用构造函数注入时,涉及的标签是constructor-arg,该标签有如下属性:name:用于给构造函数中指定名称的参数赋值。index:用于给构造函数中指定索引位置的参数赋值。value:用于指定基本类型或者String类型的数据。ref:用于指定其他Bean类型的数据。写的是其他bean的唯一标识。依赖注入的配置实现之set方法注入顾名思义,就是利用字段的set方法实现赋值的注入方式。此种方式在实际开发中是使用最多的注入方式。

在使用set方法注入时,需要使用property标签,该标签属性如下:name:指定注入时调用的set方法名称。(注:不包含set这三个字母,druid连接池指定属性名称)value:指定注入的数据。它支持基本类型和String类型。ref:指定注入的数据。它支持其他bean类型。写的是其他bean的唯一标识。复杂数据类型注入首先,解释一下复杂类型数据,它指的是集合类型数据。集合分为两类,一类是List结构(数组结构),一类是Map接口(键值对)。接下来就是注入的方式的选择,只能在构造函数和set方法中选择,我们的示例选用set方法注入。

在List结构的集合数据注入时,array,list,set这三个标签通用,另外注值的value标签内部可以直接写值,也可以使用bean标签配置一个对象,或者用ref标签引用一个已经配合的bean的唯一标识。在Map结构的集合数据注入时,map标签使用entry子标签实现数据注入,entry标签可以使用key和value属性指定存入map中的数据。使用value-ref属性指定已经配置好的bean的引用。同时entry标签中也可以使用ref标签,但是不能使用bean标签。而property标签中不能使用ref或者bean标签引用对象

xml与注解相结合模式
纯注解模式

@Configuration 注解,表名当前类是一个配置类@ComponentScan 注解,替代 context:component-scanpublicclassTransferServiceImpl {@Autowired@Qualifier(name="jdbcAccountDaoImpl") privateAccountDaoaccountDao;}publicclassTransferService {@ResourceprivateAccountDaoaccountDao;@Resource(name="studentDao") privateStudentDaostudentDao;@Resource(type="TeacherDao") privateTeacherDaoteacherDao;@Resource(name="manDao",type="ManDao") privateManDaomanDao;} <dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version></dependency>
@PropertySource,引入外部属性配置文件@Import 引入其他配置类@Value 对变量赋值,可以直接赋值,也可以使用 ${} 读取资源配置文件中的信息@Bean 将方法返回对象加入 SpringIOC 容器

Spring IOC高级特性

1.lazy-init 默认为false,意思就是立即加载,起服务的时候就会把所有的 singleton bean提前实例化。
<bean>标签里有个lazy-init属性可配置当前这个bean为延迟加载,当第一次向容器getBean的时候才会实例化。比如两个bean,A依赖B,A配置的是立即加载,B配置的是延迟加载,但实际上,B在A实例化的时候也会实例化。这也符合当第一次向容器getBean的时候 实例化的规则。
配置全局懒加载,可以在<beans>标签内,配置default-lazy-init
配置成懒加载这么做的好处有:
开启延迟加载可以提高容器启动运转效率,但实际上现有硬件水平基础上这个优点并不明显。主要是对于不常用的bean,应该设置为懒加载,什么时候用什么时候实例化,没必要一开始就占用资源。
2.FactoryBean和BeanFactory
BeanFactory:就是创建Bean的工厂,它就是最顶级的一个接口,负责管理和生产Bean的一个工厂,具体使用它下面的子接口类型,比如ApplicationContext
factoryBean:spring中有两种类型的Bean,一种是普通bean,另外一种就是工厂Bean,即FactoryBean,这种Bean可以生产某一类型的Bean实例然后返回给我们,说白了,就是利用这种FactoryBean可以自定义Bean的创建过程。可以让我们完成比较复杂的bean的创 建。
实现过程举例:定义一个实现了FactoryBean接口的类。在getObject方法下具体实现Bean的创建过程。然后将此类配置到xml中bean标签。然后当调用ApplicationContext.getBean("companyBean")就会返回咱自定义的Bean实例。
注意:假如不是要获取自定义的bean实例,而是要获取这个自定义的factoryBean的具体实现类,则这样写:ApplicationContext.getBean("&companyBean");
后置处理器:
其实就是spring提供的扩展接口。分别是BeanPostProcessor和BeanFactoryPostProcessor
BeanPostProcessor:针对Bean级别的处理,可以具体到某一个Benn,注意:此时Bean的实例化已经完成,但是生命周期并未结束。处理是发生在 spring容器的实例化和依赖注入之后。
该接口定义了两个方法,分别在Bean的初始化之前或者Bean的初始化之后执行,具体这个初始化方法指的是什么,类似于我们我们在定义Bean的时候,init-method所指定的方法。
定义一个类实现BeanPostProcessor,默认会对所有的bean进行后置处理,如果要对某个具体的Bean进行后置处理,则根据参数判断即可。
BeanFactoryPostProcessor:BeanFactory级别的处理,针对的是整个Bean工厂进行处理:
这个接口只提供了一个方法,方法参数为:ConfigurableListableBeanFactory,该参数类型定义了一些方法;其中有个getBeanDefinition的方法,我们可以通过此方法找到我们定义的BeanDefinition,这个BeanDefinition中的属性类似于我们<bean>标签中的属性,所以当我们拿到这个BeanDefinition的时候,就可以手动修改bean标签中的属性值。
BeanDefinition:相当于spring解析<bean>标签后生成的一个javaBean,而这个javaBean就是BeanDefinition。
注意:调用BeanFactoryPostProcessor方法时,这时候bean还没有实例化,此时bean刚被解析为BeanDefinition.
Spring IOC源码深度剖析 未完待续。。。

推荐阅读更多精彩内容