经过几天的理解,对IOC有了一些个人理解。
例如我们有一个需求要向mysql数据库中添加一条数据:
1、首先我们要有一个处理插入请求的SqlService类:
public class SqlService {
private MysqlDal sqlserverdal = new MysqlDal();
public void add() {
sqlserverdal.add();
}
}
2、在SqlService类中 我们需要对数据库进行操作的类MysqlDal :
public class MysqlDal {
public void add(){
System.out.println("在mysql数据库中插入了一条数据");
}
}
为了简化,我们将插入操作只定义为输出一句话“在数据库中插入了一条数据”
3、需要一个测试类来测试:
public class Test {
public static void main(String args[]) {
new SqlService().add();
}
}
运行程序 得到结果
从上图可以看到我们完成了预想的结果。
但是,我们突然接到通知说需求变了,原来是向Mysql数据库中插入数据,现在变成了向Oracle数据库中插入数据。 问题来了总得解决,所以我们就想怎么去解决这个问题。数据库变了 原来的MysqlDal类肯定不能用了,那我们就得建立一个向Oracle中插入数据的操作类:
public class OracleDal {
public void add(){
System.out.println("在oracle数据库中插入了一条数据");
}
}
由于数据库的类型变化了,我们在SqlService中的代码也要变化:
public class SqlService {
private OracleDal oracleDal = new OracleDal();
public void add() {
oracleDal.add();
}
}
改动完后再次运行得到结果:
终于通过改动,我们再次完成了需求,但是问题来了,如果今后数据库的类型还要改,是不是我们还得大费周章的修改代码?
显然,这不是一个良好的设计,组件之间高度耦合,可扩展性较差,它违背了DIP原则。高层模块SqlService 类不应该依赖于低层模块OracleDal ,MysqlDal ,两者应该依赖于抽象。那么我们是否可以通过IoC来优化代码呢?答案是肯定的。IOC有2种常见的实现方式:依赖注入和服务定位。其中,依赖注入使用最为广泛。下面我们将深入理解依赖注入(DI),并学会使用。
依赖注入(DI)
在上面的例子中,SqlService 依赖于OracleDal 或MysqlDal对象,并且MysqlDal和OracleDal对象的实例化是在SqlService内部实现的,这种方法并不可取。既然,不能在Order类内部直接绑定依赖关系,那么如何将SqlServerDal对象的引用传递给SqlService 类使用呢。
为了将向数据库中插入数据的操作抽象出来,我们定义一个接口,并且在接口中声明一个add方法:
public interface SqlAdd {
public void add();
}
我们让数据库操作类实现SqlAdd接口:
public class MysqlDal implements SqlAdd{
@Override
public void add() {
System.out.println("向mysql数据库中插入了一条数据");
}
}
public class OracleDal implements SqlAdd{
@Override
public void add() {
System.out.println("向Oracle数据库中插入了一条数据");
}
}
接下来对SqlService进行改造 声明一个SqlAdd接口的对象sqladd,通过调用sqladd的add方法来执行插入操作:
public class SqlService {
private SqlAdd sqlAdd;
public void add() {
sqlAdd.add();
}
}
到此为止我们对源码的改造只剩下最后一步:对声明的sqladd进行赋值。
赋值的过程就是依赖注入的过程,注入过程有三种:
1、构造函数注入:
我们通过使用SqlService的构造函数对sqlAdd进行注入
public class SqlService {
private SqlAdd sqlAdd;
public SqlService(SqlAdd sqlAdd) {
this.sqlAdd = sqlAdd;
}
public void add() {
sqlAdd.add();
}
}
在Test中我们传入一个数据库操作对象:
public class Test {
public static void main(String args[]) {
new SqlService(new MysqlDal()).add();
}
}
运行结果:
2、属性注入:
我们通过使用SqlService的set方法对sqlAdd进行注入
public class SqlService {
private SqlAdd sqlAdd;
public void setSqladd(SqlAdd sqlAdd) {
this.sqlAdd = sqlAdd;
}
public void add() {
sqlAdd.add();
}
}
在test中 我们通过sqlservice.setSqladd(new OracleDal());给sqlservice注入
public class Test {
public static void main(String args[]) {
SqlService sqlservice = new SqlService();
sqlservice.setSqladd(new OracleDal());
sqlservice.add();
}
}
运行结果如下:
IoC容器
前面所有的例子中,我们都是通过手动的方式来创建依赖对象,并将引用传递给被依赖模块。比如:
1 MysqlDal mysqldal = new MysqlDal (); //在外部创建依赖对象
2 new SqlService(new MysqlDal()); //通过构造函数注入依赖
对于大型项目来说,相互依赖的组件比较多。如果还用手动的方式,自己来创建和注入依赖的话,显然效率很低,而且往往还会出现不可控的场面。正因如此,IoC容器诞生了。IoC容器实际上是一个DI框架,它能简化我们的工作量。它包含以下几个功能:
动态创建、注入依赖对象。
管理对象生命周期。
映射依赖关系。
使用Spring进行 依赖注入:
1、传统XML bean标签进行配置
bean.mxl:
<?xml version="1.0" encoding="UTF-8"?>
<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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="sqlService" class="com.xm.test.SqlService">
<property name="sqlAdd" ref="mysqlDal" />
</bean>
<bean id="mysqlDal" class="com.xm.test.MysqlDal" />
</beans>
Test.java:
package com.xm.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String args[]) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
SqlService sqlservice = ctx.getBean("sqlService", SqlService.class);
sqlservice.add();
}
}
2、使用注解的方式进行配置:
使用注解可以减少代码量
bean.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 该 BeanPostProcessor 将自动对标注 @Autowired 的 Bean 进行注入 -->
<context:component-scan base-package="com.xm.test" />
</beans>
Test.java:
package com.xm.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String args[]) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
SqlService sqlservice = ctx.getBean("sqlService", SqlService.class);
sqlservice.add();
}
}
MysqlDal.java:
package com.xm.test;
import org.springframework.stereotype.Component;
@Component
public class MysqlDal implements SqlAdd {
@Override
public void add() {
System.out.println("向mysql中插入一条数据");
}
}
SqlService.java:
package com.xm.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SqlService {
@Autowired
private SqlAdd sqlAdd;
public void add() {
sqlAdd.add();
}
}
总结:Spring容器使我们不需要手动进行依赖注入,而是Spring容器自动调度。更加简化了依赖注入的方式。