spring控制反转(IoC)实现过程
提示:下文中所有地方提到的实例对象
和 bean对象
等价于 accountService = new AccountServiceImp();
一、示例代码准备
- POJO
- Account
package com.roy.demo;
import java.io.Serializable;
//pojo
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
//无参构造......略
//有参构造......略
//getter()/setter()/toStirng()......略
}
- service层接口
- AccountService
package com.roy.demo.service;
import com.roy.demo.Account;
import java.util.List;
//service层的接口
public interface AccountService {
List<Account> findAllAccount();
}
- service层接口的实现类
- AccountServiceImpl
package com.roy.demo.service.impl;
import com.roy.demo.Account;
import com.roy.demo.service.AccountService;
import java.util.List;
//接口的实现类
public class AccountServiceImpl implements AccountService {
@Override
public List<Account> findAllAccount() {
//dao层代码略
return accountDao.findAllAccount();
}
业务场景描述:
- 客户端发送请求——>服务端(WEB)接受请求并调用servcie及dao实现请求
- web层要调用service的功能方法,必须要拿到service的实例对象,即:AccountService accountService = new AccountServiceImpl();
- 使用accountService.findAllAccount();才可以调用service层方法
二、实现业务的原始方案
- web层调用service层实现查询所有账户功能,代码实现:
//web层调用service层
public class AccountServlet {
private AccountService *accountService* = new AccountServiceImpl();
public void findAllAccount() {
accountService.findAllAccount();
}
}
原始方案存在的问题:
- 真实项目中service层中不可能只有AccountService一个接口实现,可能还会有:UserService、OrderService等。
- web层要想实现所有的功能就必须new AccountServiceImpl(),这就使web层对service层的代码产生了编译期依赖,因为如果没有accountService,web就完成不了功能。
- 下面的改进方案就帮我们解决了编译期依赖。
三、改进方案
改进方案一:
- 使用工厂模式和反射机制,从工厂中获取一个Bean对象
package com.roy.demo;
public class BeanFactory {
private static Object accountService;
public static Object getBean() {
try {
Class<?> clazz = Class.forName("com.roy.service.impl.AccountServiceImpl");
accountService = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return accountService;
}
}
当存在上面一个BeanFactory的类时,我们只需要提供一个com.roy.service.impl.AccountServiceImpl
就可以通过BeanFactory.getBean();
拿到sercive的对象,而不需要我们自己去new;
仍然存在的问题:
- 我们仍需自己手写
com.roy.service.impl.AccountServiceImpl
,带来了硬编码问题
- 改进方案二解决了硬编码问题
改进方案二:
- 使用配置文件
beans.properties
解决硬编码问题
// 格式:[key=value]
accountService=com.roy.service.impl.AccountServiceImpl
userService=com.roy.service.impl.AccountServiceImpl
orderService=com.roy.service.impl.AccountServiceImpl
- BeanFactory改进代码
package com.roy.demo;
import java.io.InputStream;
import java.util.Properties;
import java.util.PropertyResourceBundle;
public class BeanFactory {
public static Object getBean(String key) throws Exception{
// 读取配置文件
InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.properties");
Properties properties = new Properties();
properties.load(is);
// getProperty("userService")就可以得到className = "com.roy.service.impl.AccountServiceImpl"
String className = properties.getProperty(key);
Class<?> clazz = Class.forName(className);
// 得到 UserService的实例对象 bean
Object bean = clazz.newInstance();
return bean;
}
}
- 此时获取bean对象的方式变为传入一个key:
AccountServcie accountService = BeanFactory.getBean("accountService"); 其它bean对象获取方式相同
- 改进方案二已经又了springIoC(控制反转的雏形)
spring 控制反转实现分析:
- springIoC可以理解成一个容器,它帮我们管理和创建我们所需要的依赖对象,比如:上面的accountService、userService等等。简单的画个图理解一下:
四、Spring控制反转代码完善
package com.roy.demo;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory {
// 创建一个‘容器’存放实例对象【可以想象成就是一个Spring的IoC‘容器’】
private static Map<String,Object> beans = new HashMap<String, Object>();
static {
// 读取配置文件
try {
InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.properties");
Properties properties = new Properties();
properties.load(is);
Enumeration<?> enumeration = properties.propertyNames();
// 循环每个key
if (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
// 获取每个key的value得到的就是全限定类名
String className = properties.getProperty(key);
Class<?> clazz = Class.forName(className);
// 通过反射的到className对应的实例对象
Object bean = clazz.newInstance();
// 将对象丢到map‘容器’中
beans.put(key,bean);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBean(String key) {
// 从map‘容器’中找到key对应的实例对象,并返回给调用者
return beans.get(key);
}
}
五、总结
- 控制反转的理解:
- Spring将想象成一个容器,IoC控制反转就是将我们主动创建对象反转成被动接受【Spring容器帮我们创建了】
- 自始至终我们都要提供
accountService=com.roy.service.impl.AccountServiceImpl
,Spring IoC的实现原理使用到了反射机制,Class.forName("com.roy.service.impl.AccountServiceImpl");
- 之前我们自己写的代码是从配置文件中获取
com.roy.service.impl.AccountServiceImpl
; - 要想spring帮我们管理和创建实例对象,也是需要告诉spring
com.roy.service.impl.AccountServiceImpl
;告诉的方式就是在spring的核心配置文件中使用标签:<bean id="accountService" class="com.roy.service.impl.AccountServiceImpl"/>
END