1. Spring是什么
- Spring是一个针对bean的生命周期进行管理的轻量级容器框架,它同时也是SpringMVC,SpringBoot,SpringCloud等Spring开源框架的基石,Spring提出了IOC,AOP,DI等重要的概念(功能),本文主要介绍IOC(Inversion of Control)控制反转的概念
2.IOC基本介绍
在面向对象编程时我们通常通过new关键字来初始化一个bean对象并进行使用,而在Spring中我们将bean的信息和bean之间的依赖关系通过XML文件/注解配置好后,交给Spring来帮助我们初始化bean对象,并放入容器中进行统一管理。这样程序员可以更加关注如何使用对象完成业务逻辑。
这就引出了IOC控制反转的概念,程序员不再主动new一个对象,而是将控制权交给Spring容器,让它帮我们进行bean的配置,DI(Dependency Injection)是IOC的另一种叫法,就是通过配置bean对象之间的依赖关系,初始化bean对象注入到spring容器中
1. Spring容器的结构剖析
我们先通过XML文件配置的方式配置一个bean对象
<bean id="person01" class="ioc.beans.Person">
<property name="name" value="tom"/>
<property name="age" value="19"/>
<property name="gender" value="male"/>
</bean>
再通过创建容器得到配置到容器中的bean对象
@Test
public void getBean() {
//创建一个Spring容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
//获取容器中的bean对象
Person person01 = ioc.getBean("person01", Person.class);
System.out.println(person01);
}
我们通过Debug来看看Spring的容器结构 spring容器中的一个重要字段beanFactory维护了一个beanDefinitionMap字段 他的类型为ConcurrentHashMap,key作为bean的唯一标识id,value的类型为GenericBeanDefinition里面,存储了bean的信息(类型,懒加载,属性值等)
beanFactory中还有一个重要字段singletonObjects(单例池),在我们通过xml文件配置bean时默认为singleton(单例), spring就会把单例对象放入单例池中,这样我们通过getBean方法就可以获取到对应的bean
2. 获取Bean对象的三种方式
- 通过类型获取
该方式要求的前提是容器中只存在与该类型唯一匹配的bean对象,否则就会报错
Person person01 = ioc.getBean(Person.class);
- 通过id获取
该方式可以通过配置bean时设置的唯一标识id获取对应对象返回类型是Object(需要强转),如果没有设置则默认为 ioc.beans.Person#0 全类名#0(1,2…)
Object person01 = ioc.getBean("person01");
- 通过id和类型获取
Object person01 = ioc.getBean("person01", Person.class);
和第二种方式相同但是直接返回的是对应的类型不需要强转
3.基于XML文件配置Bean对象
spring底层在都是通过反射的方式创建一个bean对象,所以要确保bean类有无参构造器,和setter/构造方法
- 通过setter方法设置属性
spring通过反射调用无参构造器后 调用setter方法初始化对象的属性
<bean id="person01" class="ioc.beans.Person">
<property name="name" value="tom"/>
<property name="age" value="19"/>
<property name="gender" value="male"/>
</bean>
- 通过构造器设置属性
spring调用对应的有参构造器初始化属性
<bean id="person02" class="ioc.beans.Person">
<constructor-arg index="0" value="jack"/>
<constructor-arg index="1" value="19"/>
<constructor-arg index="2" value="male"/>
</bean>
- 通过p名称空间(setter方法)设置属性
<bean id="person03" class="ioc.beans.Person"
p:name="mary" p:age="3" p:gender="female" />
- 注入其他bean对象
将school中的dept引用指向id为dept的对象
<bean id="dept" class="ioc.beans.Dept"/>
<bean id="school" class="ioc.beans.School">
<property name="dept" ref="dept"/>
</bean>
- 注入内部bean对象
<bean id="school" class="ioc.beans.School">
<property name="dept">
<bean class="ioc.beans.Dept">
<property name="id" value="1"/>
</bean>
</property>
</bean>
- 注入集合/数组
<bean id="master" class="com.syf.spring.bean.Master">
<property name="name" value="master"/>
<property name="monsterName">
<array>
<value>m1</value>
<value>m2</value>
</array>
</property>
<property name="monsterList">
<list>
<ref bean="monster01"/>
<ref bean="monster02"/>
</list>
</property>
<property name="monsterMap">
<map>
<entry key="m1" value-ref="monster01"/>
<entry key="m2" value-ref="monster02"/>
<entry key="m3">
<bean class="com.syf.spring.bean.Monster">
<property name="name" value="m3"/>
<property name="age" value="3"/>
<property name="skill" value="s3"/>
</bean>
</entry>
</map>
</property>
<property name="properties">
<props>
<prop key="m1">s1</prop>
<prop key="m2">s2</prop>
</props>
</property>
</bean>
- 通过util名称空间创建list
这种方式可以实现对List属性的重用,即可以有多个bean对象引用该List
<util:list id="monsterList">
<ref bean="monster01"/>
<ref bean="monster02"/>
</util:list>
<!--引用util list-->
<property name="monsterList" ref="monsterList"/>
- 级联属性赋值
注入dept时并没有给它的属性初始化,而是在emp中初始化
<!--级联属性赋值-->
<bean id="dept" class="com.syf.spring.bean.Dept"/>
<bean class="com.syf.spring.bean.Emp" id="emp">
<property name="name" value="emp"/>
<property name="dept" ref="dept"/>
<property name="dept.name" value="no1"/>
</bean>
- bean信息的重用
通过设置parent重用person07的信息相当于一种继承关系
<bean id="person07" class="ioc.beans.Person">
<property name="name" value="tom"/>
<property name="age" value="19"/>
<property name="gender" value="male"/>
</bean>
<bean id="person08" class="ioc.beans.Person" parent="person07"/>
- bean的自动装配
根据byType实现自动装配
要求被装配的属性类型 在容器中唯一匹配与之相同类型的bean
<bean id="dao" class="ioc.beans.Dao"/>
<bean id="service" class="ioc.beans.Service" autowire="byType"/>
根据byName实现自动装配
根据setter方法 setXxx后面的 xxx 去匹配容器中的bean的id,匹配上就会装配
<bean id="dao" class="ioc.beans.Dao"/>
<bean id="service" class="ioc.beans.Service" autowire="byName"/>
4.通过bean工厂获取对象
1. 静态工厂获取
public class StaticFactory {
private static ConcurrentHashMap<String, Person> map;
static {
map = new ConcurrentHashMap<>();
map.put("person01", new Person("mike", 19, "male"));
map.put("person02", new Person("jack", 19, "male"));
}
public static Person getBean(String id) {
return map.get(id);
}
}
<bean id="person04" class="ioc.factory.StaticFactory" factory-method="getBean">
<constructor-arg index="0" value="person01"/>
</bean>
2.实例工厂获取
public class InstanceFactory {
private ConcurrentHashMap<String, Person> map;
{
map = new ConcurrentHashMap<>();
map.put("person01", new Person("mike", 19, "male"));
map.put("person02", new Person("jack", 19, "male"));
}
public Person getBean(String id) {
return map.get(id);
}
}
<bean class="ioc.factory.InstanceFactory" id="factory"/>
<bean id="person05" factory-bean="factory" factory-method="getBean">
<constructor-arg index="0" value="person01"/>
</bean>
3.实现FactoryBean接口获取
public class MyFactoryBean implements FactoryBean<Person> {
private String key;
private Map<String, Person> map;
{
map = new ConcurrentHashMap();
map.put("person01", new Person("mike", 19, "male"));
map.put("person02", new Person("mary", 19, "female"));
}
public void setKey(String key) {
this.key = key;
}
@Override
public Person getObject() throws Exception {
return map.get(key);
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="person06" class="ioc.factory.MyFactoryBean">
<property name="key" value="person02"/>
</bean>
5.基于注解配置bean
一.简介
此方法主要是配置项目开发中的组件 Controller Service Dao
1.@Component 表示当前注解标识的是一个组件
2.@Controller 表示当前注解标识的是一个控制器,通常用于 Servlet
3.@Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于 Service 类
4.@Repository 表示当前注解标识的是一个持久化层的类,通常用于 Dao 类
- 这些注解配置的都是单例bean对象,需要在XML文件中配置要自动扫描的包 还可以使用 * 通配符来指定比如
ioc.*表示扫描ioc下面所有的文件
<context:component-scan base-package="ioc.component"/>
- 表示只扫描符合pattern的类
<context:component-scan base-package="ioc.component" resource-pattern="User*.class"/>
- 排除(不扫描)某些注解
<context:component-scan base-package="ioc.component" resource-pattern="User*.class">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
4.指定扫描某些注解
use-default-filters=“false” 指不再使用默认的过滤机制(全部扫描)
<context:component-scan base-package="ioc.component" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
二.注解的自动装配
两种注解都可以实现自动装配 @AutoWired @Resource
1.@AutoWired自动装配机制
- 在spring容器中查找待装配组件的类型,如果有唯一匹配则自动装配
- 如果有多个匹配,则按照待装配的组件的属性名作为id 进行查找匹配,找到就自动装配,找不到就抛异常
- @Resource自动装配机制
@Resource有两个属性name 和 type,分别是根据byName和byType策略进行装配
- 如果不指定name或者type则默认根据byName策略进行装配,即根据属性名作为id在容器中进行匹配如果没有匹配上,则根据byType策略装配
- 如果指定name,则根据name作为id查找容器中的bean,如果设置的id在容器中不存在,则编译会报错
三.泛型依赖注入
为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的 注入机制
public abstract class BaseDao<T> {
public abstract void save();
}
@Repository
public class MemberDao extends BaseDao<MemberDao>{
@Override
public void save() {
System.out.println("memberDao save() method");
}
}
public abstract class BaseService<T> {
@Resource
private BaseDao<T> baseDao;
public void save() {
baseDao.save();
}
}
@Service
public class MemberService extends BaseService<Member>{
}
6.Bean对象的生命周期
在spring容器中注入的bean默认为单例(singleton), 即配置完bean之后,容器之后创建一个bean对象。默认为非懒加载(lazy-init),即在启动容器时就创建了bean对象
如果我们希望每次getBean都得到一个新的bean对象可以声明scope=“prototype”进行配置
bean对象配置初始化方法和销毁方法
init-method=“init” destroy-method=“destroy”
<bean id="person01" class="ioc.beans.Person"
init-method="init" destroy-method="destroy">
<property name="name" value="tom"/>
<property name="age" value="19"/>
<property name="gender" value="male"/>
</bean>
bean对象的生命周期
- 创建对象调用无参构造器
- 调用setter方法
- 后置处理器
- 初始化方法
- 后置处理器
- 容器关闭 销毁方法