Spring Core 学习 IOC容器与DI深入理解
前言
Spring Core:
Spring 的核心功能:IOC容器,解决对象创建及依赖关系。
Spring的核心思想之一:Inversion of Control,控制反转 IOC,对象的创建交给外部容器来完成,这个就称之为控制反转。
- Spring 使用控制反转来实现对象不需要再程序中写死
- 控制反转解决问题处理问题【把对象交给别人来创建】
对象与对象之间的依赖关系通过 dependency injection 依赖注入解决
- Spring 使用依赖注入来实现对象直接的依赖关系
- 在创建完对象之后,对象的依赖关系处理即是依赖注入
??无论是创建对象、处理对象之间的依赖关系、对象创建的时间还是要创建对象的数量,我们都是在Spring 为我们提供的IOC 容器上配置对象的信息即可。IOC的思想最核心的地方在于,资源不由要使用资源的双方管理,而是由不使用资源的第三方来管理,一是可以实现资源的集中管理,二是可以降低使用资源双方的依赖程度,低耦合。
一、Core模块入门
1. 新建Spring项目
- 使用IDEA创建一个maven项目,在pom.xml中引入对应的jar包
??commons-logging-1.1.3.jar 日志、spring-beans-5.2.21.RELEASE.jar bean节点、spring-context-5.2.21.RELEASE.jar spring上下文节点、spring-core-5.2.21.RELEASE.jar spring核心功能、spring-expression-5.2.21.RELEASE.jar spring表达式
- 准备Spring的配置文件按
??在resources 目录下创建spring的配置文件,可以直接选择idea 中的选项
- 编写启动类
??在java 文件夹下新建启动类Application ,可以通过两种方式得到Spring的 IOC 容器对象:1通过Bean 工厂,BeanFactory;2通过Resource 获取BeanFactory (常用)
2. IOC容器与对象创建
- 获取到了IOC容器,通过IOC容器配置对象
在Spring 中总体来看, 可以通过四种方式来配置对象
- 使用XML 文件来配置
- 使用注解来配置
- 使用JavaConfig 来配置
- groovy 脚本DSL
??准备一个User类,先不指定构造方法,在xml中配置
<bean id="user" class="User"></bean>
通过IOC容器得到无参构造方法对象完成 Java中几种常用的创建对象方法有:无参构造器创建、有参构造器创建、工厂模式创建(静态方法、非静态方法)
public User(String id, String username) {
this.id = id;
this.username = username;
}
<bean id="user" class="User">
<constructor-arg index="0" name="id" type="java.lang.String" value="01"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" value="zhoukang"></constructor-arg>
</bean>
<bean id="person"></bean>
<bean id="user" class="User">
<constructor-arg index="0" name="id" type="java.lang.String" value="01"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" ref="person"></constructor-arg>
</bean>
静态工厂方法创建对象
public class Factory {
public static User getBean() {
return new User("1", "zhoukang");
}
}
<bean id="user" class="Factory" factory-method="getBean"></bean>
非静态工厂方法创建对象
public class Factory {
public User getBean() {
return new User("1", "zhoukang");
}
}
<bean id="factory" class="Factory">
<bean id="user" class="User" factory-bean="factory" factory-method="getBean"></bean>
3. 使用注解的方式来配置 bean 与 IOC 容器
- 注解使用xml 配置的方式
<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
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:component-scan base-package="aa"></context:component-scan>
实体类测试,分别在持久层、业务逻辑层、控制层新建了三个类,互相引用,最后看IOC 容器中是否含有该类
@Repository
public class UserDao {
public void save() {
System.out.println("UserDao : save");
}
}
@Service
public class UserService {
@Resource(name = "userDao")
private UserDao userDao;
public void save() {
userDao.save();
}
}
@Controller
public class UserAction {
@Resource(name = "userService")
private UserService userService;
public String execute() {
userService.save();
return "";
}
}
- 注解使用启动扫描器的方式
@Configuration
@ComponentScan("aa")
public class AppConfig {
}
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(aa.AppConfig.class);
创建对象以及处理对象之间的依赖关系, 相关的注解有:
- @ComponentScan 扫描器, 可指定扫描包、类型
- @Configuration 表明该类为配置类
- @Component 指定把一个对象加入IOC 容器, @Name 也考研实现相同的效果(不常用)
- @Repository 作用同 @Component, 在持久层使用
- @Service 作用同 @Component, 在业务逻辑层使用
- @Controller 作用同 @Component, 在控制层使用
- @Resouce 依赖关系, 如果@Resouce 不指定值, 则会根据类型来查找对象, 如果指定了就会根据名字来查找
持久层、业务层、控制层
- 持久层也叫作DAO 层, 主要负责诶数据库做CRUD 操作, 并把这些数据库操作提供给业务层做处理.
- 业务层也叫作Service 层, 主要工作是负责组装持久层的数据操作, 把需要的数据封装在实体类中, 并提供最终控制层想要的数据.
- 控制层也叫作Controller 层, 主要工作是负责根据传递cookie 参数, session 参数或者前端传过来的参数, 并把这些参数传递给业务层封装好的数据操作, 并把持久层传递过来的数据进行序列化后传递给前端页面. Controller和Service的区别是:Controller负责具体的业务模块流程的控制;Service层负责业务模块的逻辑应用设计
- 实体层
- 视图层
二、Bean对象的创建细节
对象写死的问题已经解决,IOC 容器就是控制反转创建对象的
1. scope 属性
指定scope 属性,IOC 容器就知道创建对象的时候该对象是单例还是多例的了,不指定的话默认单例。
<bean id="userDao" class="aa.UserDao" scope="singleton"></bean>
<bean id="userDao" class="aa.UserDao" scope="prototype"></bean>
2. lazy-init 懒加载,init-method 初始化方法
lazy-init 属性只对singleton 单例的对象有效,lazy-init 默认为false,前面说了单例模式下的bean 会在IOC 容器创建好之前完成创建,有时候我们想要使用这个bean 的时候他才去创建,将lazy-init 设置为true 即可。
<bean id="userDao" class="aa.UserDao" scope="singleton" lazy-init="true"></bean>
如果想要对象在创建之后执行某个方法,配置init-method 即可,IOC 容器销毁后会执行的方法用destroy-method
<bean id="userDao" class="aa.UserDao" scope="singleton" lazy-init="true" init-method="initMethod" destroy-method="desMethod"></bean>
三、Spring 依赖注入
对象之间的依赖关系,说白了就是给对象上的属性赋值。因为对象上有其他对象的变量,就形成了依赖。Spring中给属性赋值的方法有:
- 通过构造函数
- 通过set方法给属性注入值
- p名称空间
- 自动装配
- 注解
1. 准备环境
准备三个类,UserAction 、UserService 、UserDao ,三个类中互相依赖。
public class Dao {
public void save() {
System.out.println("UserDao : save");
}
}
public class Service {
public void save() {
userDao.save();
}
}
public class Action {
public String execute() {
userService.save();
return "";
}
}
2. 通过构造函数
前面也介绍过通过构造函数传值,这里不再赘述。
<bean id="user" class="User">
<constructor-arg index="0" name="id" type="java.lang.String" value="01"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" ref="person"></constructor-arg>
</bean>
3. 通过set方法给属性注入值(目前项目中使用的就是这种方式)
测试service 层和dao 层的依赖关系就好了,在service 层通过set 方法来把userDao 注入到UserService 中。
<bean id="dao" class="bb.Dao"></bean>
<bean id="service" class="bb.Service">
<property name="dao" ref="dao"></property>
</bean>
public class Service {
public void setDao(Dao dao) {
this.dao = dao;
System.out.println(dao);
}
|
内部bean的方式:在实际场景中看你们会看到引用类型并不是使用ref 的形式,而是内部bean 的方式,二者的效果是一致的。
<bean id="service" class="bb.Service">
<property name="dao">
<bean id="dao" class="bb.Dao" />
</property>
</bean>
4. p名称空间注入属性值
P名称空间就是set方法的一种优化,减少了配置的工作量
<bean id="service" class="bb.Service" P:dao-ref="dao"></bean>
5. 自动装配 AutoWire
Spring 中提供了自动装配的功能,能够非常简化我们的配置。自动装配默认是不打开的,自动装配根据常用的可分为
<bean id="dao" name="dao" class="bb.Dao"></bean>
<bean id="service" class="bb.Service" autowire="byName"></bean>
<bean id="dao" name="dao" class="bb.Dao"></bean>
<bean id="service" class="bb.Service" autowire="byName"></bean>
无论是采用那种自动装配方式,当找到对应的对象时就会自动装配进我们的bean 中,调set 方法,当找不到对应的bean 是就不会注入。 当多个对象需要自动装配时,可以在xml 配置信息中指定默认的自动装配的方法
6. 使用注解来实现自动装配
@Autowired 注解可以实现自动装配的功能
- 可以在构造器上修饰
- 可以在setter 方法上修饰
- 来自java 的
@inject 的和 @AutoWired 有相同的功能
@Service
public class UserService {
private UserDao userDao;
public void setUserDao (UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.save();
}
}
userDao 没有注入进来,将// @Autowired 注释取消,实现自动装配
希望该文章对大家有所帮助!
|