Spring学习( 二 ):IOC控制反转和DI依赖注入
(一)、IOC的概念
一句话:把创建对象的权利交给了工厂,原来通过new 来创建,自己可以控制,如果通过Factory创建就把创建去权交出去了。
(二)、首先我们先介绍下ApplicationContext和它相关的类和接口
1、顶级接口
从图中可以看出BeanFactory
是Spring容器的顶级接口,主要实现类有三个。
2、实现类
ClassPathXmlApplicationContext(最重要):配置文件的相对路径
FileSystemXmlApplicationContext:绝对路径
AnnotationConfigApplicationContext:基于注解
(三)、BeanFactory和ApplicationContext的区别
1、功能不同
BeanFactory:最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。
ApplicationContext:国际化、访问资源,如URL和文件(ResourceLoader)、AOP(拦截器)。
2、装载Bean的时机不同
BeanFactory是在第一次调用getBean的时候创建bean,ApplicationContext是在加载完配置就读取进去了,也可以在Bean配置lazy-init=true设为懒加载。(2种都是默认单例模式)
(四)、bean的三种创建方式
1、调用构造器创建Bean
2、调用实例工厂方法创建Bean
3、调用静态工厂方法创建Bean
下面逐个介绍
1、调用构造器创建Bean
注意点
默认是无参构造函数创建Bean,如果Bean中存在有参构造函数,没写无参构造且使用默认xml配置,那么程序会报错。(没没找到默认的无参构造方法)
要想不出错,直接加个无参构造,但是如果Bean是再JAR包中,我们无法修改代码,或者说我们想把Jar包中每个方法返回值(类)加载到Spring容器中,这时候就要用下面2中方法来获取Bean了。
xml配置
<bean id="userService" class="com.service.impl.UserServiceImpl" />
Bean对象
public class UserServiceImpl implements UserService {
@Override
public String getUser(String name) {
return "我是:" + name;
}
}
2、调用实例工厂方法创建Bean
应用场景:我们想把Jar包中每个方法返回值(类)加载到Spring容器中
xml配置
<bean id="instanceFactory" class="com.factory.InstanceFactory"></bean>
<bean id="userService" factory-bean="instanceFactory" factory-method="getUserservice" />
工厂Bean对象
/**
* @author wangquan
* @date 2020/12/11
* 模拟一个工厂类,我们想把Jar包中每个方法返回值(类)加载到Spring容器中
*/
public class InstanceFactory {
public UserServiceImpl getUserservice(){
return new UserServiceImpl();
}
}
3、调用静态工厂方法创建Bean
xml配置
<bean id="userService" class="com.factory.StaticFactory" factory-method="getUserservice" />
静态工厂Bean对象
public class StaticFactory {
public static UserServiceImpl getUserservice(){
return new UserServiceImpl();
}
}
(五)、bean的作用域
目前Bean的作用域有以下6种
作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。 |
application | 限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。 |
global-session | 全局session,集群下的session,如果不是集群环境,和session没去别。 |
详情看这篇文章
(六)、bean的生命周期
总的说下面8步
1、实例化Bean(通过构造方法或工厂创建)
2、注入属性(依赖注入)
3、执行实现Aware接口的方法,如实现了BeanNameAware接口,调用setBeanName()方法传递Bean的ID。
4、将Bean传递给Bean的前置处理器postProcessBeforeInitialization
5、调用Bean的初始化方法。
6、将Bean传递给Bean的后置处理器postProcessAfterInitialization
7、使用Bean
8、容器关闭前调用destroy方法。(如果作用域为prototype,不会调用,而是由垃圾回收器销毁)
代码演示
1、Student类实现了BeanNameAware接口,重写setBeanName方法
public class Student implements BeanNameAware {
private String name;
public Student(){
System.out.println("1、调用构造方法初始化Bena");
}
@Override
public void setBeanName(String name) {
System.out.println("3、BeanNameAware设置BeanName:" + name);
}
public void setName(String name) {
System.out.println("2、设置对象属性setName()..");
this.name = name;
}
/**
* Bean的使用
*/
public void study() {
System.out.println("7、Student这个Bean:使用,调用study()方法,此时我的名字:" + this.name);
}
public void initMethod(){
System.out.println("5、设置Bean的初始化方法..");
}
public void destroyMethod(){
System.out.println("8、设置Bean的销毁方法..");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
2、MyBeanPostProcessor实现BeanPostProcessor,重写前置和后置方法
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("4、对初始化之前的Bean进行处理,此时我的名字:" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Student stu = null;
String xmlBeanName = "student";
System.out.println("6、对初始化之后的Bean进行处理,将Bean的成员变量的值修改了");
if(xmlBeanName.equals(beanName) || bean instanceof Student) {
stu = (Student) bean;
stu.setName("李四");
}
return stu;
}
}
3、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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.bean.Student" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="张三"/>
</bean>
<!-- 配置bean的后置处理器,不需要id,IoC容器自动识别是一个BeanPostProcessor -->
<bean class="com.MyBeanPostProcessor"></bean>
</beans>
4、测试类
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student) context.getBean("student");
student.study();
context.close();
5、打印结果
(七)、依赖注入
1、什么是依赖注入
一个类要用到另一个类,我们只需要到spring配置文件配置,让spring为我们提供,如MySQL的配置注入到连接类里面,我们可以直接拿到类,而不需要额外配置。
2、依赖注入类型和方式
能注入的类型
- 基本类型和String
- 其他bean类型(配置文件配置的bean或注解配置过的)
- 复杂类型 / 集合类型
注入的方式
- 构造方法注入
- set注入
- 注解注入
3、注入方式
1、构造方法注入
Bean:Student
public class Student {
private String name;
private Integer age;
private Date birthday;
public Student(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public void getStudent(){
System.out.println(this.name+" : " + age + " : " + birthday);
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.bean.Student">
<constructor-arg name="name" value="12"/>
<constructor-arg name="age" value="12"/>
<constructor-arg name="birthday" ref="date"/>
</bean>
<bean id="date" class="java.util.Date"></bean>
</beans>
测试类
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student) context.getBean("student");
student.getStudent();
优势
在获取Bean,注入操作必须的,否则无法创建Bean
劣势
改变了Bean的实例化方式,再创建对象的时候,如果用不到这些数据,也必须提供。
2、get方法注入
Bean:Student
public class Student {
private String name;
private Integer age;
private Date birthday;
// 省略set方法。。。
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.bean.Student">
<property name="name" value="张三" />
<property name="age" value="33" />
<property name="birthday" ref="date" />
</bean>
<bean id="date" class="java.util.Date"></bean>
</beans>
测试类
和上面一样
复杂数据类型的注入
Bean
public class Student {
private String[] array;
private List<String> lists;
private Set<String> sets;
private Map<String,String> map;
private Properties properties;
// 省略set方法。。。
@Override
public String toString() {
return "Student{" +
"array=" + Arrays.toString(array) +
", lists=" + lists +
", sets=" + sets +
", map=" + map +
", properties=" + properties +
'}';
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.bean.Student">
<property name="array" >
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="strings">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="sets">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="map">
<map>
<entry key="aaa" value="bbb"/>
<entry key="ccc" value="ddd"/>
<entry key="eee" value="fff"/>
</map>
</property>
<property name="properties">
<props>
<prop key="aaa">bbb</prop>
<prop key="ccc">ccc</prop>
<prop key="eee">ddd</prop>
</props>
</property>
</bean>
</beans>
测试结果
只要记住一个list一个map即可
发现互用也可以。
3、注解注入
1、找到对应的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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--找到对应的XML头,打开包扫描-->
<context:component-scan base-package="com"/>
</beans>
2、把要注入到spring容器的类加上注解
@Repository
public class UserDapImpl implements UserDao {
@Service("userService")
public class UserServiceImpl implements UserService {