轻松了解Spring中的控制反转和依赖注入

点击上方 "程序员小乐"关注公众号, 星标或置顶一起成长

每天早上8点20分, 第一时间与你相约

每日英文

When you have something you really love but it causes you pain, God is just testing you to see if you are strong enough to hold it.

当你真正喜欢一样东西,但它又给你带来伤害的时候,其实这是老天在考验你是否足够坚持。

每日掏心话

距离因拥抱而拉近,天气因拥抱而温暖,呼吸而拥抱而急促,心跳因拥抱而猛烈,空气因拥抱而凝固,时间因拥抱而停止。


来自:请叫我红领巾 | 责编:乐乐

链接:cnblogs.com/xxzhuang/p/5948902.html

640?wx_fmt=jpeg

程序员小乐(ID:study_tech)第 631 次推文   图片来自网络

   正文   


我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达170平方米,重达30吨,到现如今的个人笔记本,事物更加轻量功能却更加丰富,这是事物发展过程中的一个趋势,在技术领域中同样也是如此。

企业级JavaBean(Enterprise JavaBean,EJB)在创建之初是非常成功,但是时间一久人们便开始追逐更加方便更加简易和轻量级的技术框架实现,于是Spring就应运而生,并且Spring一直开始不断地涉及到其他领域(这里就不再多详谈了),而Spring的精髓当中就包括控制反转和依赖注入。

浅谈控制反转(Inversion of Control,IOC)

我们首先先来了解一下控制二字,也就是在控制“正”转的情况下,在任何一个有请求作用的系统当中,至少需要有两个类互相配合工作,在一个入口类下使用new关键字创建另一个类的对象实例。

这就好比在面向对象编程的思想下,“我“充当一个入口类,在这个入口类中,我每次吃饭的时候都要买一双一次性筷子(每一次使用都要new一次),在这样的关系下,是”我“(即调用者)每次都要”主动“去买一次性筷子(另一个类)。我对筷子说你老老实实的过来我的手上,是我控制了筷子,那好,在这种控制正转的关系下,放在现实生活当中,肯定是不现实的,而且人是懒惰的,他总会去创造出更加方便自己生活的想法。

更确切的做法是,买一双普通的筷子(非一次性),把他放在一个容器当中(在Spring中叫做IOC容器),你需要使用的时候就对容器说:IOC我想要用筷子(向容器发出请求),接着筷子就会”注入“到的手上,而在这个过程当中,你不再是控制方,反而演变成一名请求者(虽然本身还是调用者),依赖于容器给予你资源,控制权坐落到了容器身上,于是这就是人们俗称的控制反转。

初涉依赖注入(Dependency Injection) 

 

同样接着上面的例子,在控制反转的统一下,筷子是怎么来到我的手上(即我们是如何获得请求的类),这就是一个依赖注入的过程。

640?wx_fmt=png

再谈IOC与DI

设计原则中好莱坞原则描述到,“别找我们,我们找你”,百度百科上对这点描述是“不要给我们打电话,我们会给你打电话(don‘t call us, we‘ll call you)”这是著名的好莱坞原则。

在好莱坞,把简历递交给演艺公司后就只有回家等待。由演艺公司对整个娱乐项的完全控制,演员只能被动式的接受公司的差使,在需要的环节中,完成自己的演出。

这一点完美的体现了在IOC身上,IOC所注重的是设计思想上,从一个常规的创建对象的做法,即new一个对象,转变成向IOC容器递交”简历“,被动的等待IOC容器返回资源给你。控制反转即指的是”演艺公司控制演员“,而说到依赖,则是“演员需要公司混饭”,我们所需求的对象,需要依赖容器来获得,这个过程即是依赖注入。本质上IOC和DI是同一思想下不同维度的表现。

代码实现

既然说在控制反转中获取资源的过程叫做依赖注入,那么这里代码实现也是专注于依赖注入。依赖注入有3种方式,分别为构造注入,设置注入,接口注入。

1.接口注入:在接口中定义要注入的信息,并通过接口来完成注入。(Spring不支持这种注入方式--不支持的原因是--Spring声称其是非入侵式的《离开这个框架也能活》,如果使用接口注入的话,就违背了这一原则),这里不做代码实现讲解。

2.setter注入:我们先脱离Spring来实现setter注入,分两步,第一步我们先看看在常规的做法下类之间的关系,第二步使用IOC来进行设计类,对比一下之间的差别。

在常规的做法下

public class UserDao{
  public void addUser(String username)
{
  System.out.println("添加用户:"+username);  
}  
}

public class UserMessage {
  String username="demo";
  UserDao userDao;
  public UserMessage(){
  userDao=new UserDao();  
}      
  public void addUser(){
 userDao.addUser(username);  
}  
}

public class test{
 UserMessage userMessage=new UserMessage();
 userMessage.addUser();
}

这上面的代码存在一个缺陷,由于在UserMessage内部创建了UseDao对象,这就造成了两个类之间的耦合度较高,当项目经理想要用另外一个Dao类的方法来实现addUser的时候,这个时候我就需要修改User Message的代码,重新new另一个类。

如果系统中有一百个地方用到这个类,那我们这些地方都要全部修改,出现Bug的概率将大大增加,然后过了一阵子,项目经理又说我想换回以前的Dao类了……

下面我们看看IOC设计的实现

public interface UserDao{
 void addUser(String username);      
}

public class UserDaoImpl implements UserDao{
   @Override    
   public void addUser(String username)
{
   System.out.println("添加用户:"+username);  
}  
}

public class UserMessage{    
   
   private UserDao userDao;    
   
   //使用设值方式赋值    
   public void setUserDao(UserDao userDao) {    
       this.userDao = userDao;    
   }    
   public void addUser(String userName, String password) {    
     
       userDao.addUser(userName);    
   }    
}

public class test{
   public static void main(String[] args) {
       UserDao userDao =new UserDaoImpl();
       UserMessage userMessage=new UserMessage();
       userMessage.setUserDao(userDao);
   }
}

我们仔细观察这里的做法, UserDao userDao=new UserDaoImpl()做法,这里就不得不提到了多态,即父类可以引用子类的方法,在这里形成的一个效果就是降低了User Message和UserDao的耦合度。

因为UserMessage和UserDao之间仅存在做什么事(addUser方法)的联系,User Message并不了解UserDaoImpl的实现方式。当我们需要换另一种UserDao的实现类的话,我们只需要修改test类下的第一行的代码就可以了,UserMessage内部并不需要修改。

再想想,读者可能会说不对啊,你说的控制反转和依赖注入需要向容器请求资源,这个容器并没有在上面体现出来啊,下面我们就讲解一下Spring 中是如何做到注入的。

<!-- 使用spring管理对象的创建,还有对象的依赖关系 -->    
<bean id="userManager" class="scau.zzf.service.UserMessage">    
       <!-- (1)UserMessageImpl使用了userDao,Ioc是自动创建相应的UserDao实现,都是由容器管理-->    
       <!-- (2)在UserMessageImpl中提供构造函数,让spring将UserDao实现注入(DI)过来 -->    
       <!-- (3)让spring管理我们对象的创建和依赖关系,必须将依赖关系配置到spring的核心配置文件中 -->    
       <property name="userDao" ref="UserDao"></property>   
   <!-- 构造注入 -->
   <!-- <constructor-arg ref="userDao"/>  --> 
   </bean>   
<bean id="UserDao" class="scau.zzf.Dao.UserDao">
   
</bean>

首先我们需要装配Bean,即在Spring容器中将Bean进行配置后,然后返回Bean对象实例。我们可以通过XmlBeanFactory读取我们xml文件,从而获取相关的Bean信息。

public class test {
   public static void main(String[] args) throws Exception {
   BeanFactory factory=new XmlBeanFactory(new FileSystemResource("src/appllication.xml"));
       UserMessage userMessage=(UserMessage)factory.getBean("UserMessage");
       userMessage.add("德玛西亚");

   }
}

在实际应用当中,我们并不会手动去读取Xml中的信息或者加载配置文件,Spring底层已经帮我做好了这些,也就是在实际应用当中,我们就只是需要发送一个请求而已,当然了解这么一个过程还是很有必要的。

面再简单讲解一下如何通过注解来实现注入。

 
                                                  

@Configuration的作用是使整个类成为一个配置类,@Bean注解会告诉Spring这个注解下的方法将会返回一个对象,这个对象要注册为Spring应用上下文的Bean。在默认情况下,Spring的Bean都是单例的。

也就是在上面的例子当中,无论我们使用多少次getUserDao(),结果返回的对象至始至终都是相同的。关于JavaConfig的配置进一步相关说明,读者可以前往笔者的另一篇文章《更加优雅的配置SSH》中进行参考。

640?wx_fmt=png

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

欢迎各位读者加入程序员小乐技术群,在公众号后台回复“加群”或者“学习”即可。

猜你还想看

关于如何实现一个 TCC 分布式事务框架的一点思考

中美互联网科技公司对比图!你有的,我们也有!

教你用Python将图片转化为字符画!附源代码

太牛逼了,居然有人将各大编程语言绘成了一部编年史!

如何用几个简单的命令改善你的Linux安全?

嘿,你在看吗640?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值