一、什么是Spring
Spring是一个IOC和AOP框架容器
具体描述:
轻量级:
轻量级体现在Spring是非侵入式的,基于Spring开发的项目不一定要依赖于Spring的API
- 依赖注入
- 面向切面编程
容器:
Spring管理所有对象的生命周期及其依赖的主入
框架:
Spring通过使用一些简单的组件组合成一个复杂的应用(通过注解和配置文件组合这些对象)
一站式
Spring支持兼容其他优秀的第三方框架,并且自身也提供了一整套框架
Spring特性
- 通过IOC实现了类之间的解耦、简化了开发
- 通过AOP对各个业务逻辑与非业务逻辑代码进行分离解耦
- 自带了测试功能模块方便测试
- 兼容其他优秀框架
- 通过引入事务模块就可以很方便实现事务的支持和操作
核心Jar包
- Spring-beans-X.X.X.RELEASE.jar:SpringIOC功能模块
- Spring-context-X.X.X.RELEASE.jar:Spring上下文模块
- Spring-core-X.X.X.RELEASE.jar
- Spring-expression-X.X.X.RELEASE.jar
二、IOC
由SpringIOC容器管理类之间的依赖关系,实现类之间的解耦(我认为都是将依赖关系降低到聚合)
1.底层原理
- XML解析:类信息配置在XML中,由容器解析,加载、注册到容器中
- 工厂模式:容器创建对象的方式是基于抽象工厂模式进行设计,由工厂(容器)统一管理bean的创建
- 反射:bean在容器中是通过反射进行实例化
2.IOC实现方式(容器创建方式)
- BeanFactory:实现了懒加载
- ApplicationContext:非懒加载,在容器刷新过程的末尾部分完成了所以单例bean对象的实例化,同时ApplicationContext是BeanFactory的子类实现,对BeanFactory进行了拓展增强。
开发过程中我们一般通过两种方式实例化容器加载配置,而根据配置方式的不同我们实现容器的方式也不同
下面是根据配置方式的不同我们使用了不同的容器类
- XML配置方式:我们将配置信息放在配置文件中
- ClassPathXMLApplicationContext:根据相对路径获取配置文件进行容器初始化
- FileSystemXMLApplicationContext:根据配置文件绝对路径获取配置文件进行容器初始化
- 完全注解开发:我们以配置类的形式完成配置
- AnnotationConfigApplicationContext:根据配置类进行容器初始化,传入的是配置类的类对象
3.Bean配置(容器中管理的Bean从何处而来)
除去bean本身自带的一些为我们提供服务的后置处理器类以外,其他的我们自定义的类我们都需要自己配置,下面介绍几种配置方式。
3.1 XML的Bean标签配置
<bean id="beanName" class="classPath" />
其中每个bean在容器中都有一个对应的BeanId标识,它们被存放一个HashMap中,而这个HashMap的具体实现我们能够在容器的父类中的DefaultListableBeanFactory类中,通过<String,BeanDefinition>的形式进行存储,其中的String类型就是我们的id,而后面的BeanDefinition是我们配置中bean所对应的类的一个包装类,是我们所以bean在容器中的一个存储形式。
标签的属性和子标签
属性:
- id:bean的id名称
- class:全限定类名
- scope:类的作用域,默认为sigleton
- autowire:是否自动装配
子标签:
- <property>:用于setter注入
- name:成员变量名
- value:值
- ref:引入的bean的id
- <constructor-arg>:用于构造方法注入,我们容器中用于使用的是反射机制来实例化对象,所以默认要调用无参构造
- name:成员变量名
- value:值
- ref:引入的bean的id
同时我们还可以通过引入p空间来简化我们属性的注入
3.2 XML的扫描器+注解配置
xml配置文件中定义包扫描器
类上添加注解
<context:component-scan base-package="packagePath"></context:component-scan>
@Component
class beanA{}
容器实例化时,会通过我们在配置文件中配置的包扫描器去扫描对应包下所有类。只要类上存在@Component、@Service、@Repository、@Controller注解,就会被加载到我们的容器中
3.3 配置类的@Bean配置
@Configuration
public class Config{
@Bean
public A a(){
return new A();
}
}
Class A{}
在这个方式中我们可以看到我们的Config上添加了@Configuration注解,代表着这个类是一个注解类,该类等同于一个配置文件,只是是一个类的形式出现。
其中我们通过@Bean配置Bean类,其中方法的返回类型相当于我们Bean的类型,默认方法名是我们bean的id,也可以在@Bean标签中通过参数显式修改
3.4 配置类的扫描器注解+注解配置
配置类中引入扫描器注解来指定需要扫描的包
类上添加注解
@Configuration
@ComponentScan("packagePath")
public class Config{
@Bean
public A a(){
return new A();
}
}
@Component
class beanA{}
3.5 FactoryBean
通过实现FactoryBean接口,返回我们需要的Bean,但是FacrotyBean也是需要以前四种方式配置起来才能使用。
public class MyFactoryBean implements FactoryBean<A>{
@Override
public Object getObject() throws Exception{
return new A();
}
@Override
public Class<?> getObjectType(){
return A.class;
}
@Override
public boolean isSingleton(){
return false;
}
}
class A{}
3.6 Bean的属性注入的类型
以Student类和IDCard类为例,完成各种类型值的注入
package com.xmlSpring.beans;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Student {
private String name;
private Integer age;
private IDCard idCard = new IDCard();
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private String[] array;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public IDCard getIdCard() {
return idCard;
}
public void setIdCard(IDCard idCard) {
this.idCard = idCard;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public String[] getArray() {
return array;
}
public void setArray(String[] array) {
this.array = array;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard +
", list=" + list +
", set=" + set +
", map=" + map +
", array=" + Arrays.toString(array) +
'}';
}
}
package com.xmlSpring.beans;
public class IDCard {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "IDCard{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}
<bean id="student" class="com.xmlSpring.beans.Student">
<!--注入NULL-->
<property name="name">
<null/>
</property>
<!---->
<property name="age" value="33"></property>
<!--级联注入(需要该对象非空)-->
<property name="idCard.id" value="111"></property>
<property name="idCard.name" value="张三"></property>
<!--注入array-->
<property name="array">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
<!--注入list-->
<property name="list">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<!--注入set-->
<property name="set">
<set>
<value>1</value>
<value>2</value>
</set>
</property>
<!--注入map-->
<property name="map">
<map>
<entry key="1" value="1"/>
<entry key="2" value="2"/>
</map>
</property>
</bean>
上述集合标签中如果需要引入其他对象作为参数,就将value改为ref
4.Bean的生命周期
- 通过构造器创建实例
- 类成员完成属性注入
- bean的后置处理器
- 调用Bean的初始化方法
- bean的后置处理器
- 使用bean
- 销毁bean
5.Bean的后置处理器BeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return null;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return null;
}
}
6. Bean的作用域
- sinleton:单例(默认)
- prototype:多例
- session:在各自的session内保持单例
- request:在以此请求中保持单例
7.获取外部文件
<!--import outside properties file-->
<context:property-placeholder location="classpath:datasource.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--get properties from proerties file by '${}' expression-->
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
8.bean属性的自动装配
我们前面的属性注入是通过手动注入实现注入的,现在我们使用自动注入完成属性注入
<bean id="student2" class="com.xmlSpring.beans.Student" autowire="byType"/>
<bean id="card" class="com.xmlSpring.beans.IDCard">
<property name="id" value="111"/>
<property name="name" value="aaa"/>
</bean>
当我们需要获取student2时,容器会为我们自动获取容器内的IDCard类注入到我们的成员变量中
同时我们的autowire可以设为byName,只要我们bean的id和属性名一致就会为其完成属性注入
基于注解的自动装配
- @Resource:根据类型和名字完成自动注入
- @Autowire:根据类型注入
- @Qualifer:根据名字完成注入
9.扫描组件
我们前面在通过注解配置Bean的时候使用到了扫描器,它的作用是扫描目标包中的类的注解,完成注解装配
同时,我们的扫描组件中还能设置一些包含和排除某些组件的属性
<!--关闭默认全部扫描-->
<context:component-scan base-package="com" use-default-filters="false">
<!--扫描包含Controller注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--不扫描包含Component注解的类-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
下面是注解式的扫描组件配置
@ComponentScan(
value = "com.annotationSpring.beans",
includeFilters = {@Filter(type = FilterType.ANNOTATION,value = Controller.class)},
excludeFilters = {@Filter(type = FilterType.ANNOTATION,value = Component.class)}
)