Spring的依赖注入和控制反转——概念介绍
依赖注入
介绍控制反转之前需要介绍依赖注入,所谓依赖注入,就是把类中需要new的对象,改成类的属性,并且为该属性设置get、set方法,主程序如果需要调用类的话,要在主程序中set这个属性
也就是说,把类从new中解放出来,把初始化实例的工作交给调用它的程序
还是刚刚那个例子,NewDao不变,改变NewService类,将所需的newDao类改成自己的属性,并且为其设置get、set方法
public class NewsService {
// NewsDao newsDao = new NewsDao();
//=======这里是添加的内容
private NewsDao newsDao;
public NewsDao getNewsDao() {
return newsDao;
}
public void setNewsDao(NewsDao newsDao) {
this.newsDao = newsDao;
}
//=======这里是添加的内容
public void save() {
System.out.println("NewsService保存操作");
newsDao.save();
}
}
在主程序中这样测试:
public static void main(String[] args) {
// NewsService newsService =new NewsService();
// newsService.save();
//======这是新的测试方法
NewsService newsService =new NewsService();
NewsDao newsDao = new NewsDao();
newsService.setNewsDao(newsDao);
newsService.save();
}
这样的话,就把new一个newsDao的工作从newService转移给了测试类。
测试类所做的工作就叫做依赖注入,即把newService所需的实例,通过set方法注入给newService
控制反转
那么什么叫控制反转呢?
回顾一下,我们为了依赖注入做了什么工作呢
- 把newsDao改成newsService的私有属性,并且为其生成set和get方法
- 在测试类中为newsService使用set方法注入NewsDao的实例
- 使用newsService.save()方法
在使用依赖注入之前我们是怎么做的呢?
- 在NewsService中new一个newsDao
- 在NewsService里的save方法中使用newsDao
- 在测试类中直接使用newsService.save()方法
也就是其实我们只做了一件事,就是在测试方法中给newsService实例注入newsDao实例,其实就是做了依赖注入这件事
在用了依赖注入这个方法后,本来应该由NewsService实例化NewsDao,变成了由测试方法实例化NewsDao。也就是,本来应该由被调用者自己完成创建私有属性的工作,变成了由调用者创建被调用者所需的属性,这就叫控制反转
这样做的好处是什么呢?在面向接口编程的情况下,可以实现解耦
举个例子,现在有一个接口InterNews,它有一个save()方法
public interface InterNews{
void save();
}
这个接口有两个实现类NewsDao1和NewsDao2
public class NewDao1 implement interNews{
@Override
public void save(){
System.out.println("NewsDao1:第一种保存方式");
}
}
public class NewDao2 implement interNews{
@Override
public void save(){
System.out.println("NewsDao2:第一种保存方式");
}
}
但是业务类并不关心到底是哪个实现类,总之我要调用传进来的实现类的save方法,因此我所使用的是InterNews接口进行编程。如果我们使用依赖注入的方法,那么NewsService类如下:
public class NewsService{
private InterNews interNews;
public InterNews getInterNews() {
return interNews;
}
public void setInterNews(InterNews interNews) {
this.interNews = interNews;
}
public void save(){
//我不关心到底是InterNews的哪个是实现类,总之我要调用它的save方法
interNews.save();
}
}
那么无论我要调用NewsDao1的save方法还是NewsDao2的方法都很轻易,只需要给newsService注入不同的实现类即可
public static void main(String[] args) {
NewsService newsService = new NewsService();
//调用第一种实现的save
if(我想调用第一种实现){
newsService.setInterNews(new NewsDao1);
newsService.save();
}
if(我想调用第二种实现){
//调用第二种实现的save
newsService.setInterNews(new NewsDao1);
newsService.save();
}
}
但是如果我们不采用依赖注入那么NewsServcie和InterNews的实现类耦合度就很高
如果不采用依赖注入,如果我们想使用第一种实现:
public class NewsService{
private InterNews interNews=new NewsDao1;
public void save(){
interNews.save();
}
}
这时候测试类很简单,并不需要依赖注入:
public static void main(String[] args) {
NewsService newsService = new NewsService();
newsService.save();
}
但是如果我们想使用第二种实现的话,就得修改NewsService类的方法,将实例改成
public class NewsService{
private InterNews interNews=new NewsDao2;
public void save(){
interNews.save();
}
}
可以看到,这样InterNews的是实现类和NewsService的实现类就明显增强了许多
下一节介绍Spring怎么实现依赖注入和控制反转