Spring的IOC思想
spring的IOC
概述:
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
应用场景分析:
以前我们想要使用一个对象的时候,就会进行如下的创建
User user =new User();
咋一看觉得没什么问题,其实这种创建对象的过程需要在堆中进行一系列复杂的操作,其中包括检查资源、开辟空间等等一系列操作。其实当一个项目中的对象达到一定数量的时候,就会在一定程度上特别的影响效率。
此时我们引用spring的思想来做
我们先将对象放到容器当中,对象由容器来控制(控制)
当我们需要此对象的时候,从容器中获取(调用getBean方法):(反转)
综上而言: 可得IOC的控制反转
扩展:
对象从容器中获取的疑问:
运用Spring的思想,当我们要使用对象的时候,我们从容器获取对象,那对象是怎么来的呢?
有人会想,是不是也是在容器中通过new的方式创建对象获取对象,这样不就类似于我们平常普通创建对象一样,在堆中进行进行一系列复杂的操作得到对象。
解惑: 其实不是,我们在容器中获取对象的方式是通过反射的方式,而且在配置文件中配置的Bean使用的是 单列模式,永远使用最初的创建的那一个对象。如果是我们自己普通new,将会大大浪费空间和资源。
Bean标签范围配置
scope:指对象的作用范围,取值如下:
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
1)当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
<bean id="userDao" class="com.duanping.dao.impl.UserDaoImpl" scope="singleton"></bean>
2)当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
<bean id="userDao" class="com.duanping.dao.impl.UserDaoImpl" scope="prototype"></bean>
Bean生命周期配置
init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称
Bean实例化的三种方式
- 使用无参构造方法实例化
原理:它会默认根据无参构造方法来创建对象,如果该bean中没有默认的无参构造函数,将会创建失败
<bean id="userDao" class="com.duanping.dao.impl.UserDaoImpl"></bean>
- 工厂静态方法实例化
工厂的静态方法返回Bean实例
public class StaticNew {
public static UserDao getUserDao(){
return new UserDaoImpl();
}
}
<!--
工厂静态方法实例化
-->
<bean id="userDao1" class="com.duanping.staticspring.StaticNew" factory-method="getUserDao"></bean>
- 工厂实例方法实例化
工厂的非静态方法返回Bean实例
public class StaticNew {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
<!--
工厂实例方法实例化
-->
<bean id="userDao2" class="com.duanping.staticspring.StaticNew"></bean>
<bean id="userDao3" factory-bean="userDao2" factory-method="getUserDao"></bean>
Bean的依赖注入
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取
Bean的依赖注入方式
- set方法
在UserServiceImpl中添加setUserDao方法
public class UserServiceImpl implements UserService {
/*
* private UserDao userDao;
* 如果普通的就这样使用会出现空指针异常,对象默认是不会创建的,默认为null
* jvm虚拟机等着执行回收的操作
*
* 这个时候就需要用到依赖注入
*
* */
private UserDao userDao;
//完成依赖注入,必须要有set方法
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
public void save() {
userDao.save();
}
}
配置Spring容器调用set方法进行注入
<bean id="userDao" class="com.duanping.dao.impl.UserDaoImpl" ></bean>
<!--普通的依赖注入方式 这是通过set方法进行注入-->
<bean id="userService" class="com.duanping.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
- 构造方法
创建有参构造
public class UserServiceImpl implements UserService {
private UserDao userDao;
//通过有参构造完成依赖注入
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl() {
}
public void save() {
userDao.save();
}
}
<!--普通的依赖注入方式 这是通过有参构造进行注入-->
<bean id="userService2" class="com.duanping.service.impl.UserServiceImpl" >
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
- set方法:P命名空间注入
P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入P命名空间:
xmlns:p="http://www.springframework.org/schema/p"
其次,需要修改注入方式
<!--使用p标签的方式进行依赖注入-->
<bean id="userService1" class="com.duanping.service.impl.UserServiceImpl" p:userDao-ref="userDao">
</bean>
Bean的依赖注入的数据类型
注入数据的三种数据类型 :普通数据类型、引用数据类型、集合数据类型
在接下来的讲解中,引用数据类型可以参照上面的来看,接下来就不多讲了。我们来看普通数据类型和集合数据类型
各种类型注入前的准备:
public class UserDaoImpl implements UserDao {
private String name;
private int num;
private List<User> list;
private Map<String, User> map;
private Properties properties;
public void init(){
System.out.println("初始化方法。。。。。");
}
public void setName(String name) {
this.name = name;
}
public void setNum(int num) {
this.num = num;
}
public void setList(List<User> list) {
this.list = list;
}
public void setMap(Map<String, User> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void save() {
System.out.println("name="+name);
System.out.println("num="+num);
System.out.println("list="+list);
System.out.println("map="+map);
System.out.println("properties="+properties);
}
}
在applicationContext.xml中完成各项依赖注入的配置:
<bean id="user1" class="com.duanping.pojo.User">
<property name="userName" value="zhangsan"></property>
<property name="password" value="7777"></property>
</bean>
<bean id="user2" class="com.duanping.pojo.User">
<property name="userName" value="lisi"></property>
<property name="password" value="8888"></property>
</bean>
<bean id="userDao" class="com.duanping.dao.impl.UserDaoImpl">
<!--普通数据类型的注入-->
<property name="name" value="savior"></property>
<property name="num" value="8888"></property>
<!--list集合 List<User> list 的注入-->
<property name="list">
<list>
<bean class="com.duanping.pojo.User"></bean>
<ref bean="user1"></ref>
<ref bean="user2"></ref>
</list>
</property>
<!--Map集合 Map<String, User> map 的注入-->
<property name="map">
<map>
<entry key="user1" value-ref="user1"></entry>
<entry key="user2" value-ref="user2"></entry>
</map>
</property>
<!-- Properties properties 的注入-->
<property name="properties">
<!--注意下方的特殊之处,对应的value值是直接写在标签内部的-->
<props>
<prop key="propKey001">propValue001</prop>
<prop key="propKey002">propValue002</prop>
</props>
</property>
</bean>
引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
其他的配置文件可以命名为applicationContext-xxx.xml
在spring主配置文件中通过import标签进行加载
<import resource="applicationContext-xxx.xml"/>
ApplicationContext的概述
ApplicationContext的实质
applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象
ApplicationContext的实现类
- ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种
- FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
- AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
getBean()方法
其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。
当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错
代码展示:
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = (UserService) applicationContext.getBean("userService");
UserService userService2 = applicationContext.getBean(UserService.class);