一、什么是Spring IOC?
1、IOC不是一种技术而是一种设计思想,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
2、在传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;
3、何为控制反转?
传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;
因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
二、IOC能做什么
1.IOC控制反转
IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。而Spring提供了IOC容器来帮我们生成所需要的对象。也就是说在我们原先的对象中有用到其他对象的地方Spring会帮我们来注入。不用我们再去考虑这些问题。
2.DI(依赖注入)
spring提供了三种方式来依赖注入,有构造方法注入,setter方法注入以及接口注入。使用构造方法注入需要注意的一点就是要避免循环依赖。所谓的循环依赖指的就是在A对象的构造方法中Spring要注入B,而在B对象中Spring要注入A。这个时候会形成一个闭环因为Spring不知道该先注入哪一个接着会抛出异常。而Spring建议的处理方式是说如果遇到这种情况的话就改用Setter方式注入。
而spring就是通过反射来实现注入的。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”。
谁依赖谁:应用程序依赖于IoC容器;
为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
谁注入谁:IoC容器注入应用程序某个对象,应用程序依赖的对象;
注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据)
三、底层原理
ioc的底层原理是工厂设计模式即通过工厂获取实例对象,实现代码的解耦,好了,话不多说,通过实例来理解:
首先创建一个实体类User(使用lombok实现实体类的方法)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String username;
private String password;
}
创建接口UserMapper和类UserMapperImpl
public interface UserMapper {
User selectByUser(User user);
Integer addUser(User user);
}
public class UserMapperImpl implements UserMapper{
@Override
public User selectByUser(User user) {
System.out.println("select * from user where username="+user.getUsername()+"and password = "+user.getPassword());
return user;
}
@Override
public Integer addUser(User user) {
System.out.println("insert into user(username,password) values("+user+")");
return 1;
}
}
创建接口UserService
public interface UserService {
Boolean login(User user);
Boolean register(User user);
}
在未使用spring之前,我们书写类UserServiceImpl时,如下:
public class UserServiceImpl implements UserService{
private UserMapper userMapper = new UserMapperImpl();
@Override
public Boolean login(User user) {
User usr = userMapper.selectByUser(user);
return usr == null;
}
@Override
public Boolean register(User user) {
Integer line = userMapper.addUser(user);
return line>0;
}
}
此时使用new创建对象,是硬编码,不利于代码维护且耦合性高
当我们使用工厂对象时:
1、创建配置文件:applicationContext.properties
userService=cn.kgc.spring.basic.UserServiceImpl
userMapper=cn.kgc.spring.basic.UserMapperImpl
2、创建工厂对象:BeanFactory
/**
*工厂创建对象如何解耦
* 存在原因: 使用 new关键词创建对象
*
*创建对象的方式:
* 1. new 调用构造方法
* 2. 反射
* 1.获取类对象 Class clazz
* 获取类对象的方式:
* 1. 类名.class
* 2. Class.forName("全类名") 推荐 更灵活
* 3.对象.getClass()
*
* 配合使用 配置文件 properties 实现进一步的解耦
*/
public class BeanFactory {
private static ResourceBundle rb;
static {
rb=ResourceBundle.getBundle("applicationContext");
String userService = rb.getString("userService");
}
public static UserService getUserServiceImpl(){
UserService userService=null;
try {
// 类的全类名 需要读取配置文件
Class<?> clazz = Class.forName(rb.getString("userService"));
userService = (UserService)clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return userService;
}
public static UserMapper getUserMapperImpl(){
UserMapper userMapper=null;
try {
Class<?> clazz = Class.forName(rb.getString("userMapper"));
userMapper = (UserMapper)clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return userMapper;
}
}
此时的UserServiceImpl可以改写为:
public class UserServiceImpl implements UserService{
private UserMapper userMapper = BeanFactory.getUserMapperImpl();
@Override
public Boolean login(User user) {
User usr = userMapper.selectByUser(user);
return usr == null;
}
@Override
public Boolean register(User user) {
Integer line = userMapper.addUser(user);
return line>0;
}
}
进一步,将BeanFactory改写:创建NewBeanFactory
public class NewBeanFactory {
private static ResourceBundle rb;
static {
// 加载配置文件
rb=ResourceBundle.getBundle("applicationContext");
}
public static Object getBean(String key){
Object obj = null;
try {
// 类的全类名 需要读取配置文件
Class<?> clazz = Class.forName(rb.getString(key));
obj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
// 泛型 方法上 使用泛型
public static <T> T getBean(String key,Class<T> tClass){
T cast = null;
try {
// 类的全类名 需要读取配置文件
Class<?> clazz = Class.forName(rb.getString(key));
Object obj = clazz.newInstance();
cast = tClass.cast(obj);
} catch (Exception e) {
e.printStackTrace();
}
return cast;
}
}
此时的UserServiceImpl可以改写为:
public class UserServiceImpl implements UserService{
private UserMapper userMapper = (UserMapper)NewBeanFactory.getBean("userMapper");
@Override
public Boolean login(User user) {
User usr = userMapper.selectByUser(user);
return usr == null;
}
@Override
public Boolean register(User user) {
Integer line = userMapper.addUser(user);
return line>0;
}
}
看到这里,相信你对oc的底层原理已经有了初步的认识,接下来用Spring IOC
1、引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
2、创建配置文件:applicationContext.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">
<!-- 使用标签声明 需要托管的对象-->
<bean id="userMapper" class="cn.kgc.spring.basic.UserMapperImpl"></bean>
<bean id="userMapper2" class="cn.kgc.spring.basic.UserMapperImpl2"></bean>
<!--
项目中 entity包装的类 一般不会纳入spring容器的管理 项目中的组件( controller service mapper ) 会放入容器管理
原因: 实体类对应是数据库中的表 实体类的重要作用是用于封装持久化层的数据 持久层框架 会自动完成对象的创建和数据的封装
所以 一般在项目中不会将 实体类放入spring容器
-->
<bean id="user" class="cn.kgc.spring.basic.User"></bean>
</beans>
3、创建test文件:TestSpring
@Test
public void test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationComtext.xml");
// ac 类似 NewBeanFactory
UserMapper userMapper = (UserMapper)ac.getBean("userMapper");
User user = new User("tom", "123");
userMapper.addUser(user);
userMapper.selectByUser(user);
}
public void test2(){ // 如何通过spring容器获取指定对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// ac 类似 NewBeanFactory
// 根据 class 类型获取对象 前提: 配置文件中只能有一个指定类型的对象
UserMapper userMapper = ac.getBean(UserMapper.class);
User user = new User("tom", "1234",null);
userMapper.addUser(user);
userMapper.selectByUser(user);
}
@Test
public void test3(){ // 如何通过spring容器获取指定对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取容器中管理bean的个数
int beanDefinitionCount = ac.getBeanDefinitionCount();
System.out.println("beanDefinitionCount = " + beanDefinitionCount);
// 获取配置文件中所有bean标签中的id 值
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
String[] beanNamesForType = ac.getBeanNamesForType(UserMapper.class);
for (String s : beanNamesForType) {
System.out.println("s = " + s);
}
boolean b = ac.containsBean("usr");
System.out.println("user = " + b);
}