控制反转Inverse Of Control的演变:
在之前的原生Javaweb项目的问题:
我们三层架构每一层之间的联系是这样的:
由GradeDao接口指向GradeDaoImpl
再由GradeService指向GradeServiceImpl
但是我们发现,如果这样写死在每一层的硬编码中的话,那么接口的存在就失去了意义
对象的创建是由这个上层的内部所决定
我们是希望每一层的联系最小化,但是又不能完全隔绝关系,组件就像组装的好的零件
相互之间的依赖降低到最小,比如这个daoImpl我需要替换,就根据实现的dao接口就可以有
MySQL实现,Oracle实现,PostgreSQL实现等等
对象的创建可以移交到外部,我们在内部设置setter注入即可,或者使用带参构造器
通过setter或者带参构造器,我们可以从本层的内部创建对象这个控制权力,移交到了外面
虽然这还是硬编码中
接下来我们就创建一个工厂类,由工厂实现对象的创建,而这个行为,非常像生产产品
一个个对象即我们的业务的产品
对象的注入交由工厂生产给予
但是我们是不是发现,工厂的产品还是固定的?然后再像Jdbc一样,
通过读取配置文件的形式解除耦合
首先是配置文件:bean.properties
例如这里我就配置另外一种实现
AaaDao = ioc.dao.AaaDaoImpl2
AaaService = ioc.service.AaaServiceImpl2
这里只是键值对简单的字符串
然后改造我们的BeanFactory
通过加载的时候读取配置文件直接生成对象放进这个map容器中
这个容器不就是bean的容器了吗,键对应的实现类名,值即对象
put放进去的key应该换value进去。。。我有点分不清
但这不是重点,我们下面获取对象的方法就是从容器里面获取
这就需要key作为参数:
再加上泛型,这样我们就不需要进行强转了
但是有个问题就是dao也是接口啊,接口指向了之后没办法在setter了,但是可以不设置接口处理
className应该换成叫beanName
现在我们通过配置文件的方式来获取对象,整个过程完全看不到一个new字。
总结:写的好烂,过程改了好几回
瞎扯的代码:
Bean工厂
package ioc; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * @author DaiZhiZhou * @file OA-Project * @create 2020-07-24 22:57 */ public class BeanFactory { private static Map<String,Object> beanContainer = new HashMap<>(); static { try { // InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); // 因为找不到配置文件就检查下路径的读取 String file = BeanFactory.class.getClassLoader().getResource("bean.properties").getFile(); System.out.println(file); InputStream inputStream = new FileInputStream(new File(file)); Properties properties = new Properties(); properties.load(inputStream); Enumeration<Object> keys = properties.keys(); while (keys.hasMoreElements()) { String key = (String)keys.nextElement(); String value = properties.getProperty(key); Class<?> classByValue = Class.forName(value); Object valueInstance = classByValue.newInstance(); beanContainer.put(key, valueInstance); } } catch (Exception e) { e.printStackTrace(); } } public static <Bean>Bean getBean(String className, Class<Bean> beanClass) { Object o = beanContainer.get(className); return (Bean)o; } }
properties:
AaaDao = ioc.dao.AaaDaoImpl2
AaaService = ioc.service.AaaServiceImpl2
测试类:假装是视图的Servlet
package ioc.servlet; import ioc.BeanFactory; import ioc.dao.AaaDao; import ioc.dao.AaaDaoImpl; import ioc.service.AaaService; import ioc.service.AaaServiceImpl; import ioc.service.AaaServiceImpl2; import org.junit.Test; /** * @author DaiZhiZhou * @file OA-Project * @create 2020-07-24 22:48 */ public class AaaServletFake { private AaaService aaaService; public void setAaaService(AaaService aaaService) { this.aaaService = aaaService; } @Test public void toAaaListPage() { setAaaService(new AaaServiceImpl(new AaaDaoImpl())); aaaService.getAaaList(); } @Test public void toAaaListPageByFactoryProduct() { //this.setAaaService(BeanFactory.getAaaService()); aaaService.getAaaList(); } @Test public void toAaaListPageByFactoryProductPropertyFileInject() { AaaDao aaaDao = BeanFactory.getBean("AaaDao", AaaDao.class); AaaServiceImpl2 aaaServiceImpl2 = BeanFactory.getBean("AaaService", AaaServiceImpl2.class); aaaServiceImpl2.setAaaDao(aaaDao); this.setAaaService(aaaServiceImpl2); this.aaaService.getAaaList(); } }
关于注入问题的再补充:
因为存在这种工厂方式的注入,所以Spring就也提供了对应的这种方式:
静态工厂获取 和 工厂实例方法获取:
详细见:
http://123.57.58.29/blog/1591625710261
静态工厂
<!-- 此种方式是: 使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器 id 属性:指定 bean 的 id,用于从容器中获取 class 属性:指定静态工厂的全限定类名 factory-method 属性:指定生产对象的静态方法 --> <bean id="accountService" class="com.tc51.factory.StaticFactory" factory-method="createAccountService"></bean>
实例工厂
<!--创建工厂对象--> <!--先通过实例工厂创建对象--> <bean id="bean3Factory" class="com.tc51.factory.Bean3Factory"></bean> <bean id="accountService" factory-bean="bean3Factory" factory-method="createAccountService"></bean>