Spring之IOC
1.IOC的核心概念
IOC(Inversion of Control) 控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
Spring技术对IOC思想进行了实现
- Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部"
- IOC容器负责对象的创建,初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean
在IOC的基础上,又衍生出了一种思想:DI(Dependency Injection) 依赖注入
- 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
使用Spring的IOC的目的
使用IOC以达到充分解耦的状态
- 使用IOC容器管理bean(IOC)
- 在IOC容器内将有依赖关系的bean进行关系绑定(DI)
最终效果:
- 使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
2.IOC快速上手
1.导入Spring的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2.定义Spring管理的类
一般来说Spring管理的类是业务层(Service)或者是数据访问层(DAO)
示例代码:
public interface UserService {
}
public class UserServiceimpl implements UserService {
}
public interface UserDAO(){
}
3.创建Spring核心配置文件,并将Spring管理的类配置成为bean
<?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的bean
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl"></bean>
</beans>
注意:bean定义时id属性要唯一,在同一上下文中不能重复。
4.初始化IOC容器(Spring容器),通过容器获取bean
//加载配置文件得到上下文对象,也就是容器对象
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取资源
UserService userService = applicationContext.getBean("userService", UserService.class);
3.bean的配置
1.bean的基本配置
定义bean也就是定义Spring核心容器管理的对象
属性列表 | 描述 |
---|---|
id | bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一 |
class | bean的类型,即配置的bean的全路径名 |
示例:
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" />
2.bean别名配置
使用bean的name属性配置别名,可以定义多个,使用逗号(,)分号(;)空格( )分割
示例:
<bean name="userService,userServiceImpl" class="com.maoge.service.impl.UserServiceimpl" />
注意:
- 获取bean无论是id还是name获取,如果无法获取到,将抛出NoSuchBeanDefinitionException
3.bean作用范围配置
使用scope属性定义bean的作用范围
-
singleton:单例(默认)
-
prototype:非单例
示例:
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" scope="prototype" />
Spring的bean默认设置成单例的原因:
- 提高了性能
- 减少JVM垃圾回收
- 可以快速获取到bean
缺点:在并发的环境下可能会出现线程安全问题
4.bean实例化
1.构造方法实例化bean
-
提供可访问的构造方法
public class UserServiceimpl implements UserService { public UserServiceimpl() { System.out.println("执行了构造方法"); } @Override public void save() { System.out.println("save hello..."); } }
-
配置
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" />
2.静态工厂实例化bean
-
创建静态工厂
public class UserServiceFactory { public static UserService getUserService(){ return new UserServiceimpl(); } }
-
配置
<bean id="userService" class="com.maoge.BeanFactory.UserServiceFactory" factory-method="getUserService"/>
3.实例工厂实例化bean
-
实例工厂
public class UserServiceFactory(){ public UserService getUserService(){ return new UserServiceImpl(); } }
-
配置
<bean id="userServiceFactory" class="com.maoge.BeanFactory.UserServiceFactory"/> <bean id="userService" factory-bean="userServiceFactory" factory-method="getUserService"/>
4.通过FactoryBean实例化bean
-
FactoryBean
public class UserServiceFactoryBean implements FactoryBean<UserService> { @Override public UserService getObject() throws Exception { return new UserServiceimpl(); } @Override public Class<?> getObjectType() { return UserService.class; } }
-
配置
<bean id="userService" class="com.maoge.BeanFactory.UserServiceFactoryBean"/>
5.bean生命周期
bean的生命周期:bean从创建到销毁的整个过程
1.bean生命周期控制
而我们可以通过一些方法来控制bean的生命周期
方式一(定义方法):
-
配置生命周期控制方法:
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" init-method="init" destroy-method="destroy" />
//bean初始化对应的操作 public void init(){ System.out.println("bean初始化"); } //bean销毁前对应的操作 public void destroy(){ System.out.println("bean销毁"); }
方式二(接口控制):
-
实现InitialzingBean,DisposableBean接口
public class UserServiceimpl implements UserService, InitializingBean, DisposableBean { @Override public void afterPropertiesSet() throws Exception { } @Override public void destroy() throws Exception { } }
2.bean生命周期流程
-
初始化容器
- 创建对象(内存分配)
- 执行构造方法
- 执行属性注入(set操作)
- 执行bean初始化方法
-
使用bean
- 执行业务操作
-
关闭/销毁容器
- 执行bean销毁方法(容器关闭前触发bean的销毁)
关闭容器方式:
-
手工关闭容器
ConfigurableApplicationContext接口 close() 操作 -
注册关闭钩子,在虚拟机退出前关闭容器再退出虚拟机
ConfigurableApplicationContext接口registerShutdownHook()操作
4.DI(依赖注入)
1.依赖注入方式
setter注入
-
引用类型注入
-
在bean中定义引用类型属性并提供可访问的set方法
public class UserServiceimpl implements UserService { private UserDAO userDAO; public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } }
-
配置中使用property标签ref属性注入引用类型对象
<bean id="userDAO" class="com.maoge.dao.UserDAOimpl"/> <bean id="userService" class="com.maoge.service.impl.UserServiceimpl" > <property name="userDAO" ref="userDAO"/> </bean>
-
-
简单类型注入
-
在bean中定义简单类型属性并提供可访问的set方法
public class UserServiceimpl implements UserService { private int username; public void setUsername(int username) { this.username = username; } }
-
配置中使用 property 标签 value 属性注入简单类型数据
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" > <property name="username" value="maoge"/> </bean>
-
构造器注入
-
引用类型注入
-
在bean中定义引用类型属性并提供可访问的构造方法
public class UserServiceimpl implements UserService { private UserDAO userDAO; public UserServiceimpl(UserDAO userDAO){ this.userDAO=userDAO; } }
-
配置中使用 constructor-arg 标签 ref 属性注入引用类型对象
<bean id="userDAO" class="com.maoge.dao.UserDAOimpl"/> <bean id="userService" class="com.maoge.service.impl.UserServiceimpl" > <constructor-arg name="userDAO" ref="userDAO"/> </bean>
-
-
参数适配
-
配置中使用 constructor-arg 标签 type 属性设置按形参类型注入
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" > <constructor-arg type="java.lang.String" value="maoge"/> <constructor-arg type="int" value="1"/> </bean>
-
配置中使用 constructor-arg 标签 index 属性设置按形参位置注入
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" > <constructor-arg index="0" value="maoge"/> <constructor-arg index="1" value="1"/> </bean>
-
2.依赖自动装配
配置中使用 bean 标签的 autowire 属性设置自动装配的类型
自动装配的规则:
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean
- 自动装配优先级低于setter注入与构造器注入,同时出现自动装配失效
示例:
<!--根据类型自动注入 -->
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" autowire="byType" />
<!--根据名字自动注入 -->
<bean id="userService" class="com.maoge.service.impl.UserServiceimpl" autowire="byName" />
3.其他类型注入
-
数组对象注入
<property name="array"> <array> <value>毛哥</value> <value>李哥</value> <value>王哥</value> </array> </property>
-
List对象注入
<property name="list"> <list> <value>毛哥</value> <value>李哥</value> <value>王哥</value> </list> </property>
-
Set对象注入
<property name="set"> <set> <value>毛哥</value> <value>李哥</value> <value>王哥</value> </set> </property>
-
Map对象注入
<property name="map"> <map> <entry key="country" value="中国"/> <entry key="province" value="湖南省"/> <entry key="city" value="长沙"/> </map> </property>
-
Properties对象注入
<property name="property"> <props> <prop key="country">中国</prop> <prop key="university">湖南师范大学</prop> <prop key="city">湖南长沙</prop> </props> </property>
5.IOC容器
- BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext
- FIleSystemXmlApplicationContext
1.创建容器
-
方式一:类路径加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
-
文件路径加载配置文件
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
-
加载多个配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans1.xml","beans2.xml");
2.获取bean
-
方式一:使用bean名称获取
UserService userService = applicationContext.getBean("userService");
-
方式二:使用bean名称获取并指定类型
UserService userService = applicationContext.getBean("userService", UserService.class);
-
方式三:使用bean类型获取
UserService userService = applicationContext.getBean(UserService.class);
6.注解式开发(简化开发)
1.Java类代替Spring核心配置文件
-
@Configuration 注解用于设定当前类为配置类
-
@ComponentScan 注解用于设定扫描路径(多个路径用数组格式)
示例:
@Configuration @ComponentScan("com.maoge.service","com.maoge.dao") public class SpringConfig { }
2.注解式bean管理
1.定义bean
-
Spring提供 @Component 注解的三个衍生注解
- @Controller:表现层bean定义
- @Service:业务层bean定义
- @Repository:数据层bean定义
-
示例:
@Controller public class UserController { } @Service public class UserServiceimpl implements UseService(){ } @Repository public interface UserDAO { }
2.bean管理
bean作用范围
-
@Scope(“singleton”) 单例模式
-
@Scope(“prototype”) 多例模式
示例:
@Repository @Scope("singleton") public class UserDAOimpl implements UserDAO { }
bean生命周期控制
-
使用 @PostConstruct、@PreDstroy 定义bean生命周期
示例:
@Repository @Scope("singleton") public class UserDAOimpl implements UserDAO { @PostConstruct public void init(){ } @PreDestroy public void destroy(){ } }
bean依赖注入
-
使用 @Autowired 注解开启自动装配
示例:
@Service public class UserServiceimpl implements UserService{ @Autowired private UserDAO userDAO; }
注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法
-
使用 @Qualifier 注解指定名称装配bean
示例:
@Service public class UserServiceimpl implements UserService{ @Autowired @Qualifier("userDAO") private UserDAO userDAO; }
注意:@Qualifier 注解无法单独使用,必须配合 @Autowired 注解使用
-
使用 @Value 实现简单类型注入
示例:
@Service public class UserServiceimpl implements UserService{ @Value("maoge") private String username; @Value("18") private int age; }
第三方bean管理
- 使用 @bean 配置第三方bean
示例:管理druid数据源
@Configuration
public class SpringConfig{
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource=new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///cloud-user");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
return druidDataSource;
}
}
扩展:使用独立配置类管理第三方bean
-
方式一:导入式
public class JdbcConfig { @Bean public DataSource dataSource(){ DruidDataSource druidDataSource=new DruidDataSource(); //相关配置 return druidDataSource; } } //使用 @Import 注解手动加入配置类到核心配置(多个数据用数组格式) @Configuration @Import(JdbcConfig.class) public class SpringConfig{ }
-
方式二:扫描式
@Configuration public class JdbcConfig { @Bean public DataSource dataSource(){ DruidDataSource druidDataSource=new DruidDataSource(); //相关配置 return druidDataSource; } } //使用 @ComponentScan 注解手动加入配置类到核心配置(多个数据用数组格式) @Configuration @ComponentScan({"com.maoge.config","com.maoge.service","com.maoge.dao"}) public class SpringConfig{ }
第三方bean依赖注入
-
简单类型依赖注入 @Value 注解
public class JdbcConfig { @Value("${driverClass}") public String driverClass; @Value("${url}") public String url; @Value("${user}") public String username; @Value("${password}") public String password; @Bean public DataSource dataSource(){ DruidDataSource druidDataSource=new DruidDataSource(); druidDataSource.setDriverClassName(driverClass); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }
-
引用类型依赖注入
//引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象 @Bean public DataSource dataSource(UserService userService){ System.out.println(userService); DruidDataSource druidDataSource=new DruidDataSource(); //属性设置 return druidDataSource; }