一.将类交给spring管理(IOC)
1.将类交给spring容器管理
如何将一个类交给spring容器进行管理呢?
方法很简单,只需要在applicationContext.xml中加入对应的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">
<!--将dao类加入到spring容器中进行管理-->
<!--name:bean的名称,class:该bean的实现类全名-->
<bean name="userDao" class="com.tledu.spring01.dao.UserDaoImpl"></bean>
</beans>
我们将加入到spring容器中的类叫做bean(豆子),他是容器的基本单位,交给了spring容器后,就可以通过容器去创建对象,这样就降低了耦合度,这个过程也叫做IOC,控制反转.
2.从spring容器中获取某个bean的实例
方法也很简单,如下:
//创建容器实例
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取容器中的实例
UserDao userDao = application.getBean("userDao",UserDaoImpl.class);
//也可以这样,getBean返回的是一个Object,所以需要强转一下,或者通过上面的方式传入一个Class类型的值
UserDao userDao = (UserDaoImpl)application.getBean("userDao");
二.bean的注入
1.什么是注入
一个都在说注入注入,啥叫个注入,注入简单来说就是将某个值赋值给某个bean的属性,其实就是给某个bean的属性赋值,再简单点说就是给某个类的属性赋值,但是赋值的方式不是通过new 关键字或者其他直接复制,而是通过spring来赋值的,还是来个简单的例子吧
远古时期写类的时候这么写:
public class User{
private String name = "123";//这是一个字符串
private GirlFriend gf = new GirlFriend();//这是一个女朋友对象
public void play(){}
}
现代人这么写:
public class User{
@Setter
private String name;//这是一个字符串
@Setter
private GirlFriend gf;//这是一个女朋友对象
public void play(){}
}
将上面这个类交给spring进行管理
<?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">
<!--将你的女朋友交给spring进行管理-->
<!--name:bean的名称,class:该bean的实现类全名-->
<bean name="girlFriend" class="com.tledu.spring01.dao.UserDaoImpl"></bean>
<bean name="user" class="包全名.User">
<!--在创建User对象的时候,给user对象中的属性赋值-->
<!--name:bean对应实现类属性的名称,ref:bean的名称-->
<property name="gf" ref="girlFriend"></property>
<property name="name" value="高富帅"></property>
</bean>
</beans>
2.通过property属性注入
package com.tledu.spring01.service;
import com.tledu.spring01.dao.UserDao;
import lombok.Setter;
/**
* @author Antg
* @date 2021/8/518:26
*/
public class UserServiceImpl1 implements UserService {
@Setter
private UserDao userDao;
@Override
public void testService() {
System.out.println("userService01执行了");
userDao.daoTest();
}
}
<!--通过property的方式注入-->
<bean name="userService1" class="com.tledu.spring01.service.UserServiceImpl01">
<!--name中的是该实现类对应属性的名称,ref中的是spring容器中某个bean的名称-->
<property name="userDao" ref="userDao"></property>
</bean>
注意:需要注入的属性一定要有setter方法
3.通过构造方法注入
package com.tledu.spring01.service;
import com.tledu.spring01.dao.UserDao;
/**
* @author Antg
* @date 2021/8/519:34
*/
public class UserServiceImpl2 implements UserService {
private UserDao userDao;
public UserServiceImpl2(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void testService() {
System.out.println("通过构造方法注入");
userDao.daoTest();
}
}
<!--通过构造方法的方式注入-->
<bean name="userService2" class="com.tledu.spring01.service.UserServiceImpl2">
<!--name中的是该实现类对应属性的名称,ref中的是spring容器中某个bean的名称-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
三.scope
1.概述
spring中scope是一个非常关键的概念,简单说就是对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。
目前,scope的取值有5种取值:
在Spring 2.0之前,有singleton和prototype两种;
在Spring 2.0之后,为支持web应用的ApplicationContext,增强另外三种:request,session和global session类型,它们只实用于web程序,通常是和XmlWebApplicationContext共同使用
以下仅先具体说明前两种
2.singleton
此取值时表明容器中创建时只存在一个实例,所有引用此bean都是单一实例。
也就是说创建对象是单例模式,并且如果不进行设置,默认就行单例
可以看到,两次从ioc容器获取,构造方法只执行了一次
3.prototype
spring容器在进行输出prototype的bean对象 时,会每次都重新生成一个新的对象给请求方,虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求 方之后,容器就不在拥有当前对象的引用,请求方需要自己负责当前对象后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回请求方该对象的一 个新的实例之后,就由这个对象“自生自灭”,最典型的体现就是spring与struts2进行整合时,要把action的scope改为 prototype。
简单来说,就是每次获取都创建一个新对象,并且这个对象的生命周期不归Spring管理
配置
<!--当scope=prototype时-->
<bean name="userService4" class="com.tledu.spring01.service.UserServiceImpl4" scope="prototype">
<!--name中的是该实现类对应属性的名称,ref中的是spring容器中某个bean的名称-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
可以看到,每次获取都会从新创建一个示例
4.request
request,session和global session类型只适用于web程序,通常是和XmlWebApplicationContext共同使用。
<bean id ="requestPrecessor" class="...RequestPrecessor" scope="request" />
Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,该对象的生命周期即告结束,如同java web中request的生命周期。当同时有100个HTTP请求进来的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,简单来讲,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。
5.session
对于web应用来说,放到session中最普遍的就是用户的登录信息,对于这种放到session中的信息,我们可以使用如下形式的制定scope为session:
<bean id ="userPreferences" class="...UserPreferences" scope="session" />
Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,比request scope的bean会存活更长的时间,其他的方面没区别,如同java web中session的生命周期。
6.global session
<bean id ="userPreferences" class="...UserPreferences" scope="globalsession" />
global session只有应用在基于porlet的web应用程序中才有意义,它映射到porlet的global范围的session,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待
四.集合的注入
类:
package com.tledu.spring01.service;
import lombok.Setter;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 测试集合的注入
*
* @author Antg
* @date 2021/8/520:27
*/
public class UserServiceImpl5 implements UserService {
@Setter
private List<String> list;
@Setter
private Set<String> set;
@Setter
private Map<String, String> map;
@Override
public void testService() {
System.out.println("=========List的注入========");
list.forEach(x -> System.out.println(x));
System.out.println("=========Set的注入========");
set.forEach(x -> System.out.println(x));
System.out.println("=========Map的注入========");
map.forEach((x, y) -> System.out.println(x + "--->" + y));
}
}
配置:
<!-- ############################### 集合的注入 ################################# -->
<bean name="userService5" class="com.tledu.spring01.service.UserServiceImpl5">
<property name="list">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
<value>ccc</value>
</list>
</property>
<property name="set">
<set>
<value>ddd</value>
<value>eee</value>
<value>fff</value>
<value>fff</value>
</set>
</property>
<property name="map">
<map>
<entry key="001" value="张三"></entry>
<entry key="001" value="李四"></entry>
<entry key="002" value="张三"></entry>
</map>
</property>
</bean>
五.自动装配
上面我们学习到了两种手动注入的方式,通过property注入和通过构造方法注入
接下来再来看两种更牛逼的自动注入autowire的两种方法 byName 和 byType
注意:以下两种注入方法都需要在类的对应的属性上加setter方法
1.autowire=byName
<!--使用byName-->
<bean name="userService6" class="com.tledu.spring01.service.UserServiceImpl6" autowire="byName"></bean>
byName是自动根据setter的属性名称去找,找到一个和ioc容器中名称相同的bean进行注入,如果找不到,就不赋值
2.autowire=byType
<!--使用byName-->
<bean name="userService6" class="com.tledu.spring01.service.UserServiceImpl6" autowire="byName"></bean>
byType是自动根据setter中属性的类型去找到ioc容器中对应的类型,如果中找到就返回,但是需要注意的是,如果在ioc容器中存在多个类型相同的bean,就会报错.
六.生命周期
类:
package com.tledu.spring01.service;
import com.tledu.spring01.dao.UserDao;
import lombok.Setter;
/**
* @author Antg
* @date 2021/8/522:08
*/
public class UserServiceImpl8 implements UserService {
@Setter
private UserDao userDao;
@Override
public void testService() {
System.out.println("测试生命周期的bean执行了");
userDao.daoTest();
}
public void init() {
System.out.println("UserServiceImpl8被初始化了");
}
public void destroy() {
System.out.println("UserServiceImpl8被销毁了");
}
}
配置:
<bean name="userService8" class="com.tledu.spring01.service.UserServiceImpl8" init-method="init"
destroy-method="destroy" autowire="byName">
</bean>
测试:
//测试singleton的生命周期
@Test
public void singletonTheLifeCycle() {
application.getBean("userService8", UserServiceImpl8.class).testService();
application.destroy();//销毁容器
}
执行结果:
总结:
init-method属性配置bean初始化的方法
destory-method属性配置bean被销毁的时候的方法
七.迟加载
我们知道,当容器创建的时候,就会将scope=singleton的bean创建出来,但是如果有太多这样的bean,那么就会导致程序启动很慢,所以引入了迟加载的概念
配置如下:
<bean name="userService9" class="com.tledu.spring01.service.UserServiceImpl9" autowire="byName"
lazy-init="true"></bean>
效果: