-
- Spring 框架概述
- Spring是个容器,(2)用于降低代码间的耦合度,(3)根据不同的代码它采用的IOC和AOP两种技术进行解耦合。
Spring是于2003年兴起的一个轻量级的java开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring的核心是控制反转(IOC)和面向切面编程(AOP)。简单来说,Spring是一个分层的javaSE/EE full-stack(一站式)轻量级开源框架。
Spring的主要作用就是为代码”解耦”,降低代码间的耦合度。
根据功能的不同,可以将一个系统中的代码分为主业务逻辑和系统级业务逻辑两类。
Spring根据代码的功能特点,将降低耦合度的方式分为两类:IOC和AOP。IOC使得主要业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由Spring容器统一管理,自动“注入”。而AOP使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了。而是由Spring容器统一完成 ”织入”。
-
- Spring体系结构
Spring由20多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、Web 、面向切面编程(AOP Aspects)、应用服务器设备管理(Instrumentation)、消息发送(Messaging)、核心容器(Core container)和测试(Test)。
-
- Spring的下载
-
- Spring的特点
- 非侵入式
- Spring的特点
所谓非侵入式是指,Sping框架的API不会在业务逻辑上出现,即业务逻辑是POJO。由于业务逻辑中没有Spring的API,所以业务逻辑可以从Spring框架快速的移植到其他框架。
POJO:Plain Old Java Object(最普通最老的java对象)
-
-
- 容器
-
Spring作为一个容器,可以管理对象的生命周期、对象与对象之间的依赖关系。可以通过配置文件,来定义对象,以及设置与其他对象的依赖关系。
-
-
- IOC
-
控制反转(Inersion of Control),即创建被调用者的实例不是由调用者完成,而是由Spring容器完成,并注入调用者。
-
-
- AOP
-
面向切面编程(AOP,Aspect Orient Peogramming),是一种编程思想,是面向对象编程OOP的补充。很多框架都实现了对AOP编程思想的实现。Spring也提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行开发。
可以把日志、安全、事务管理等服务理解成一个”切面”。
2.1 Spring与IOC
控制反转(IOC,Inversion of Control),是一个概念,是一种思想,指将传统上由程序代码直接控制的对象调用权交给容器,通过容器来实现对象的装配和管理,控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。
IOC是一个概念,是一种思想,其实现方式多种多样,当前比较流行的实现方式由两种,依赖注入和依赖查找。
依赖查找:Dependency lookup,DL,容器日工回调接口和上下文环境给组件,程序代码则需要提供集体的查找方式,比较典型的是依赖于JNDI服务接口(Java Naming and Directory Interface)的查找。
依赖注入:Dependency Injection,DI,程序代码不做定位查询,这些工作由容器自行完成。
依赖注入DI是指程序运行过程中,若需要调用另一个对象协助时,无需在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
Sping的依赖注入对调用者与被调用者几乎没有任何要求,完全支持POJO之间依赖关系的系统。
依赖注入时目前最优秀的解释方式,依赖注入让Spring的Bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起的。
2.2 IOC的使用
XML文件的配置,对bean进行注册
创建容器对象,加载Sping配置文件(三种方式)
动态代码块
- 动态代码块执行之前已经分配内存并创建对象
- 一个空对象(如:Object obj = new Object(),里面没有成员变量)在堆内存中占8个字节
- 引用型的变量占的字节数量是不确定的,跟机器寻址有关
BeanFactory容器和ApplicationContext的区别:
- 这两个容器对于其中bean的创建时机不同
- ApplicationContext:容器在进行初始化的时候,会将其中的 所有Bean对象进行创建
- 缺点:占用系统资源(内存,cpu)
- 优点:响应速度块
3、BeanFactory:在执行getBean()方法的时候,即获取的时候创建的对象
-
- 缺点:相对来说响应速度慢
- 优点:不多占用系统资源
2.3 Bean的装配
Bean的装配,即Bean对象的创建,容器根据代码要求创建Bean对象后再传递给代码的过程,称为Bean的装配。
2.3.1 默认装配方式
代码通过getBean()方式容器获取指定的Bean实例,容器首先会调用Bean类的无参构造器,创建空值的实例对象。
是要给出有参构造器,就需要写出无参构造器。(因为底层使用反射机制)
2.3.2 动态工厂Bean
有些时候,项目中需要通过工厂类来创建Bean实例,而不能向前面例子中似的,直接由Spring容器来装配Bean实例。使用工厂模式创建Bean实例,就会使工厂类与要创建的Bean类耦合到一起。
- 将动态工厂Bean作为普通Bean使用
在配置文件中注册过动态工厂Bean后,测试类直接通过getBean()获取到工厂对象,再由工厂对象调用其响应方法创建相应的目标对象,配置文件中无需注册目标对象的Bean,因为目标对象的创建不由Spring容器来管理。XML文件配置:
工厂类中的返回SomeServiceImpl的对象:
对于类中的静态方法,一般是用:类名.方法名();
当使用:对象.方法名();的时候,无论这个对象是new过的还是null的都会被装成:类名.方法名(); 多以当使用一个null对象调用静态方法的时候是不会报空指针异常的。
(2)静态工厂bean的使用
使用静态方法返回实现类的对象。就是把上面的getSomeService方法改成static的了。
2.3.3 容器中Bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean的实例化,还可以通过scope属性,为Bean指定特定的作用域,Spring支持5中作用域。
- singleton:单例模式。即在整个Spring容器中,使用singleton定义的Bean将是单例的,只有一个实例,默认值。
- Prototype:原型模式。即每次使用getBean方法获取的同一个<bean/>的实例都是一个新的实例。
- Request:对于每次HTTP请求,都将会产生一个不同的Bean实例。
- Session:对于每个不同的HTTP session,都将产生一个不同的Bean实例。
- 对于scope的值request、session与global session,只有在web应用中使用Spring时该作用域才有效。
- 对于scope为singleton的单例模式。该Bean是在容器被创建的时候即被装配好了。
- 对于scope为prototype的原型模式。Bean实例是在代码中使用该Bean实例时才进行装配的。
2.3.4 Bean后处理器
Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化的时候,均会自动执行类的两个方法。由于该Bean是由其他Bean自动调用执行的,不是程序员手工调用的,因此Bean无需id属性。
需要做的是,在Bean后处理器类方法中,只要对Bean类与Bean类中的方法进行判断,就可以实现对指定的Bean的指定方法功能扩展与增强。方法返回的Bean对象,即是增过的对象。
代码中需要自定义Bean后处理器类,该类就实现了接口BeanPostProcessor的类,该接口中包含两个方法,分别在目标Bean初始化完毕之前与之后执行,它们返回值为:功能被扩展或增强后的Bean对象。
使用动态代理对类中的方法返回值进行增强:
2.3.5 定制Bean的生命始末
可以为Bean定制初始化后的生命行为,也可以为Bean定制销毁前的生命行为。
首先,这些方法需要在Bean类中事先定义好:是方法名随意放入public void方法。
2.3.6 Bean的生命周期
Bean的生命周期有11个时间点。
- 实现类的无参构造器
- 实现类的setter方法,在bean中注入
- 实现类实现了BeanNameAware接口,执行重写方法setBeanName(),获取BeanName的方法
- 实现类实现了BeanFactoryAware接口,执行重写方法setBeanFactory()。获取工厂对象
- 执行bean后处理器的before的方法
- 实现类实现接口InitializingBean,执行重写方法afterPropertiesSet(),表示属性设置完毕了
- 执行实现类的初始化方法,即上面所说的生命始末的开始
- 执行Bean后处理器的after
- 执行程序的主业务
- 实现类实现DisposableBean接口,重写方法detory()方法,表示销毁之前。这里不是真正的销毁,销毁是由spring容器管理的。
- Spring容器关闭,执行生命始末的末尾方法
2.4 基于XML的DI
2.4.1 注入分类
Bean 实例在调用无参构造器创建了空值对象后,就要对bean对象的属性进行初始化。初始化是由容器自动完成的。称为注入,根据注入方式的不同,常用的有两类:设值注入、构造注入。
还有另一种,实现特定接口注入,由于这种方式采用浸入式编程,污染了代码,所以几乎不用。
- 设值注入
设值注入是指,通过setter方法传入被调用者的实例,这种注入方式简单、直观,因而在Spring的依赖注入中大量使用。
(2)构造注入
构造注入是指,在构造调用者实例的同时,完成被调用者的实例化,使用构造器设置依赖关系。
2.4.2 集合属性注入
方法一配置XML,注入集合属性值:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mySchool" class="edu.sdut.di2.School" >
<property name="name" value="大学"/>
</bean>
<bean id="mySchool2" class="edu.sdut.di2.School" >
<property name="name" value="中学"/>
</bean>
<bean id="mySome" class="edu.sdut.di2.Some" >
<property name="schools">
<array>
<ref bean="mySchool"/>
<ref bean="mySchool2"/>
</array>
</property>
<property name="myStrs">
<array>
<value>中国</value>
<value>北京</value>
</array>
</property>
<property name="muList">
<list>
<value>List1</value>
<value>List2</value>
</list>
</property>
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="mobile" value="1234567"></entry>
<entry key="mobile" value="5555555"></entry>
</map>
</property>
<property name="MyPros">
<props>
<prop key="education">大学</prop>
<prop key="gender">man</prop>
</props>
</property>
</bean>
</beans>
下面这种简写方式只适合存储字符串的时候。
2.4.3 对于域属性的自动注入
对于域属性的注入,也可不在配置文件中显示的注入,可以通过为<bean/>标签设置autowire属性值,为域属性进行隐式自动注入,根据自动注入判断标准的不同,可以分为两种:
byName:根据名称自动注入
byType:根据类型自动注入
- byName方式自动注入
当配置文件中被调用者Bean的id值与代码中调用者Bean类型的属性名相同时,可使用byName方式,让容器自动将被调用者Bean注入给调用者Bean。容器是通过调用者的Bean类的属性名与配置文件的被调用者bean的id进行比较而实现自动注入的。
- byType方式自动注入
2.4.5 使用SPEL注入
SPEL,Spring Expression Language,即Spring EL表达语言。即,在Spring配置文件中为Bean的属性注入值时,可以直接使用SPEL表达式计算结果。SPEL表达式以 # 开头,后跟一对大括号。
用法:<bean id = “abc” value=”#{}”>
2.4.6 同类抽象bean
2.4.7 异步抽象类
2.4.8 为应用指定多个Spring配置文件
在实际应用里,随着应用规模的增加,系统中Bean数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生。提高配置文件的可读性和可维护性,可以将Spring配置文件分解成多个配置文件。
- 平等关系的配置文件
将配置文件分解为地位平等的多个配置文件,并将所有配置文件的路径定义为一个String数组,将其作为容器初始化参数出现。其将与可变参数构造器匹配。
- 包含关系的配置文件
在xml配置文件中使用标签<import resource=”其他xml文件的全名”>
如果其他的文件包路径下,需要加”classpath:”
如:<import resource=”classpath:其他xml文件的全名”>
如果有多个import的话也可以使用*通配符,这时需要注意通配符匹配的xml文件不能包含自己,否则会稻城递归。
2.5 基于注解的DI
对于DI使用注解,将不再需要在Spring配置文件中声明Bean实例。Spring中使用注解,需要在原有Spring运行环境基础上再做一些改变,即:
- 导入AOP的jar包,因为注解的后台实现用到了AOP编程
- 需要更换配置头文件,即添加相应的约束
在xml中配置
持久化类中实现注解
/*
* 与@Component注解功能相同,但是意义不同的还有三个:
* @Repository:注解在Dao实现类上
* @Service:注解在Service实现类上
* @Controller:注解在SpringMVC的处理器上
*/
@Scope("prototype") //默认是singleton
@Component("mySchool") //组件,表示当前类被Spring容器所管理
public class School {
@Value("xxx大学") //对属性进行注入
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "School [name=" + name + "]";
}
}
2.5.1 对域属性使用注解注入
2.5.2 域属性注解@Resource
Spring提供了对JSR-250规范中定义@Resource标准注解的支持,@Resource注解即可以按名称匹配Bean,也可以按类型匹配Bean,使用该注解,要求JDK必须是6以上。
- 按类型注入域属性
@Resource注解若不带任何参数,则会按照类型进行Bean的匹配注入。
- 按名称注入域属性
@Resource注解指定其name属性,则name的值即为按照名称进行匹配的Bean的id。
2.5.3 Bean的声明始末@PostConstruct 与 @PreDestory
在方法上使用@PostConstruct,与原来的init-method等效,在方法上使用@PreDestory,与destory-method等效。