专栏首页猿人工厂猿蜕变系列2——一文搞懂spring的花式DI

猿蜕变系列2——一文搞懂spring的花式DI

看过之前的猿思考系列文章,相信你对java方面的基础有了一定的认识。经过之前的进化和思考的锻炼,也该是时候像模像样的做一些事情了。上一章节我们讲解了Spring IOC中 Bean的装配过程,本章节我们主要讲解Spring的依赖注入方式。

setter方式:Spring IOC容器通过调用对象的setter方法将依赖的对象注入,这种方式由于使用起来非常简单,所以使用的频率是最高的。我们用一个例子来说明下怎么去使用:

编写一个TravelRouteDao接口和其实现类:

package com.pz.study.frame.spring.dao;
 
import com.pz.study.frame.spring.domain.TravelRoute;
 
 
/**
 * 线路Service
 */
public interface TravelRouteDao {
 
 
    /**
     * 根据id查询
     * @param rid
     * @return
     */
    public TravelRoutefindTravelRouteById(String travelRouteId);
}
 
package com.pz.study.frame.spring.dao.impl;
 
import com.pz.study.frame.spring.dao.TravelRouteDao;
import com.pz.study.frame.spring.domain.TravelRoute;
 
public class TravelRouteDaoImpl implements TravelRouteDao {
      
 
       @Override
       public TravelRoute findTravelRouteById(StringtravelRouteId) {
              System.out.println("哈哈哈,是Dao我使用"+ travelRouteId+"操作数据");
              return  new TravelRoute();
       }
   
      
 
}

编写TravelRouteManager接口和其实现类:

package com.pz.study.frame.spring.manager;
 
import com.pz.study.frame.spring.domain.TravelRoute;
 
 
/**
 * 线路Service
 */
publicinterface TravelRouteManager {
 
 
    /**
     * 根据id查询
     * @param rid
     * @return
     */
    public TravelRoutefindTravelRouteById(String travelRouteId);
}

注意:在实现类中,使用TravelRouteDao做为一个成员变量,并创建setter方法:

package com.pz.study.frame.spring.manager.impl;
 
import com.pz.study.frame.spring.dao.TravelRouteDao;
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.manager.TravelRouteManager;
 
public class TravelRouteManagerImpl implements TravelRouteManager{
      
    private TravelRouteDao travelRouteDao;
   
   
       @Override
       public TravelRoutefindTravelRouteById(String travelRouteId) {
              System.out.println("哈哈哈,我在使用"+ travelRouteId+"的方式在调用程序噢");
              returntravelRouteDao.findTravelRouteById(travelRouteId);
       }
 
 
       public void setTravelRouteDao(TravelRouteDao travelRouteDao) {
              this.travelRouteDao = travelRouteDao;
       }
   
   
}

在配置文件ApplicationContext.xml中增加配置:

<bean id="travelRouteManager"class="com.pz.study.frame.spring.manager.impl.TravelRouteManagerImpl"/>
   
    <bean id="travelRouteDao" class="com.pz.study.frame.spring.dao.impl.TravelRouteDaoImpl"/>

注意:在xsd上增加配置:default-autowire="byName">

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:aop="http://www.springframework.org/schema/aop"
   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/aop
   http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName">

创建测试方法:

       @Test
       publicvoid testManager(){
             
 
              ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
               
               TravelRouteManagertravelRouteManager=(TravelRouteManager) applicationContext.getBean("travelRouteManager");
               travelRouteManager.findTravelRouteById("3333");
             
             
       }

在没有Spring或者其他IOC容器时,我们需要使用new关键字手去创建TravelRouteDaoImpl对象,使用SpringIOC容器后,容器替我们去创建了TravelRouteDaoImpl的对象它通过setter方法注入到TravelRouteManagerImpl中的travelRouteDao属性上面。

Constructure:构造注入是容器通过构造方法将实例化的对象进行注入。使用一个类嘛,不用过构造方法,怎么创建对象嘛。有些类没有无参构造方法,没它还真不行了。

修改之前的TravelRouteManagerImpl,添加构造方法

package com.pz.study.frame.spring.manager.impl;
 
import com.pz.study.frame.spring.dao.TravelRouteDao;
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.manager.TravelRouteManager;
 
public class TravelRouteManagerImpl implements TravelRouteManager{
      
      
      
    private TravelRouteDao travelRouteDao;
   
   
   
   
       public TravelRouteManagerImpl(TravelRouteDaotravelRouteDao) {
              this.travelRouteDao = travelRouteDao;
       }
 
 
       @Override
       public TravelRoutefindTravelRouteById(String travelRouteId) {
              System.out.println("哈哈哈,我在使用"+ travelRouteId+"的方式在调用程序噢");
              returntravelRouteDao.findTravelRouteById(travelRouteId);
       }
 
 
       publicvoid setTravelRouteDao(TravelRouteDao travelRouteDao) {
              this.travelRouteDao = travelRouteDao;
       }
   
   
}

修改applicationContext.xml文件,使用constructor-arg子标签,如果构造方法存在多个参数,就需要配置多个constructor-arg子标签,并且需要保证constructor-arg子标签中name属性和构造方法中的参数顺序一致。

   <bean id="travelRouteDao" class="com.pz.study.frame.spring.dao.impl.TravelRouteDaoImpl"/>
    
     <bean id="travelRouteManager" class="com.pz.study.frame.spring.manager.impl.TravelRouteManagerImpl">
    <constructor-arg name="travelRouteDao" ref="travelRouteDao"/>
       </bean>

注意:注释掉之前配置的travelRouteManager噢

随着annotation的流行,Spring也支持使用annotation的方式来完成依赖注入。下面主要讲解以下几个annotation的用法:

@Component、@Repository、@Service、@Controller 使用annotation的方式来实现依赖注入,就不需要在xml中配置bean了,不过在这之前需要增加一个扫描器:

<context:component-scan base-package="com.pz.study.frame.spring"/>

如果仅仅是使用依赖注入的功能,以上几个annotation在作用上是没什么差别的,但是如果Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用@Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释,Spring会把使用了以上注释的类纳入特有的容器管理方式。在后续的AOP方面,我们会讲到。

下面我们来使用下@Component、@Repository、@Service、@Controller这几个注解: 编写一个Controller类

package com.pz.study.frame.spring.controller;
 
import org.springframework.stereotype.Controller;
 
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.service.TravelRouteService;
 
@Controller("travelRouteController")
public class TravelRouteController {
      
       private TravelRouteService travelRouteService;
      
      
       public TravelRoute findTravelRouteById(StringtravelRouteId){
              returntravelRouteService.findTravelRouteById(travelRouteId);
       }
 
 
       publicvoid setTravelRouteService(TravelRouteServicetravelRouteService) {
              this.travelRouteService = travelRouteService;
       }
      
      
 
}
 

修改之前的TravelRouteService TravelRouteManager TravelRouteDao和其实现类

package com.pz.study.frame.spring.service.impl;
 
import org.springframework.stereotype.Service;
 
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.manager.TravelRouteManager;
import com.pz.study.frame.spring.service.TravelRouteService;
 
@Service(value="TravelRouteService")
publicclass TravelRouteServiceImpl implements TravelRouteService {
      
       private TravelRouteManager travelRouteManager;
      
       public TravelRouteServiceImpl(){
              System.out.println("TravelRouteServiceImpl被实例化了");
       }
 
       @Override
       public TravelRoutefindTravelRouteById(String travelRouteId) {
              returntravelRouteManager.findTravelRouteById(travelRouteId);
       }
   
      
       publicvoid init() {
           System.out.println("我是初始方法init我被执行了");
       }
 
       publicvoid destroy() {
           System.out.println("我是销毁方法destroy我被执行了");
       }
 
       publicvoid setTravelRouteManager(TravelRouteManagertravelRouteManager) {
              this.travelRouteManager = travelRouteManager;
       }
      
      
 
}
package com.pz.study.frame.spring.manager.impl;
 
import org.springframework.stereotype.Component;
 
import com.pz.study.frame.spring.dao.TravelRouteDao;
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.manager.TravelRouteManager;
 
@Component("travelRouteManager")
public class TravelRouteManagerImpl implements TravelRouteManager{
      
      
      
    private TravelRouteDao travelRouteDao;

       public  TravelRouteManagerImpl(TravelRouteDao travelRouteDao) {
              this.travelRouteDao = travelRouteDao;
       }
 
 
       @Override
       public TravelRoutefindTravelRouteById(String travelRouteId) {
              System.out.println("哈哈哈,我在使用"+ travelRouteId+"的方式在调用程序噢");
              returntravelRouteDao.findTravelRouteById(travelRouteId);
       }
 
 
       public void setTravelRouteDao(TravelRouteDao travelRouteDao){
              this.travelRouteDao = travelRouteDao;
       }
   
   
}  
package com.pz.study.frame.spring.dao.impl;
 
import org.springframework.stereotype.Repository;
 
import com.pz.study.frame.spring.dao.TravelRouteDao;
import com.pz.study.frame.spring.domain.TravelRoute;
 
 
@Repository("travelRouteDao")
publicclass TravelRouteDaoImpl implements TravelRouteDao {
      
 
       @Override
       public TravelRoutefindTravelRouteById(StringtravelRouteId) {
              System.out.println("哈哈哈,是Dao我使用"+ travelRouteId+"操作数据");
              return  new TravelRoute();
       }
}

编写测试用例:

@Test
       publicvoid testController(){
             
 
              ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
               
               TravelRouteController travelRouteController=(TravelRouteController)applicationContext.getBean("travelRouteController");
               travelRouteController.findTravelRouteById("3333");
             
             
       }

运行测试用例看看效果

@Autowired和@Qualifier注解:

上面的例子中使用到的annotation都是用于注解类的。我们对类的属性的依赖注入还是通过setter方法实现的。Spring IOC 也提供了annotation用于注解属性,代替属性的setter方法,完成依赖注入。我们可以使用annotation——@Autowired来代替属性的setter方法,@Autowired默认情况下先按类型到容器中查找被注释标记的bean完成自动装配,如果发现找不到匹配的类型,就会按照名称去查找。如果还是无法找到与被注释的bean,程序会抛出异常,也可以设置Autowired的required为false,这样即使找不到需要被注入的bean,程序也不会抛出异常,但是也会导致一个新问题,由于属性没有被注入,又使用了属性的方法,从而导致程序运行时的空指针问题,这就需要大家自己权衡了。如果同一个类型的bean被注入多次,那么就需要使用@Qualifier来标记到底使用哪一个bean了。被@Qualifier注释的属性,会按照bean 的id来获取。

我们看下例子:

修改下Controller和TravelRouteServiceImpl以及TravelRouteManagerImpl感受下效果。

package com.pz.study.frame.spring.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
 
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.service.TravelRouteService;
 
@Controller("travelRouteController")
publicclass TravelRouteController {
      
       @Autowired
       @Qualifier("travelRouteService")
       private TravelRouteService travelRouteService;
      
      
       public TravelRoute findTravelRouteById(StringtravelRouteId){
              returntravelRouteService.findTravelRouteById(travelRouteId);
       }
 
 
       public void setTravelRouteService(TravelRouteServicetravelRouteService) {
              this.travelRouteService = travelRouteService;
       }
      
      
 
}
 
package com.pz.study.frame.spring.service.impl;
 
import org.springframework.stereotype.Service;
 
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.manager.TravelRouteManager;
import com.pz.study.frame.spring.service.TravelRouteService;
 
@Service(value="TravelRouteService")
public class TravelRouteServiceImpl implements TravelRouteService {
      
       private TravelRouteManager travelRouteManager;
      
       public TravelRouteServiceImpl(){
              System.out.println("TravelRouteServiceImpl被实例化了");
       }
 
       @Override
       public TravelRoutefindTravelRouteById(String travelRouteId) {
              returntravelRouteManager.findTravelRouteById(travelRouteId);
       }
   
      
       public void init() {
           System.out.println("我是初始方法init我被执行了");
       }
 
       public void destroy() {
           System.out.println("我是销毁方法destroy我被执行了");
       }
 
       public void setTravelRouteManager(TravelRouteManagertravelRouteManager) {
              this.travelRouteManager = travelRouteManager;
       }
      
      
 
}
 
package com.pz.study.frame.spring.manager.impl;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import com.pz.study.frame.spring.dao.TravelRouteDao;
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.manager.TravelRouteManager;
 
@Component("travelRouteManager")
public class TravelRouteManagerImpl implements TravelRouteManager{
      
      
       @Autowired(required=false)
    private TravelRouteDao travelRouteDao;
   
   
   
   
       public TravelRouteManagerImpl(TravelRouteDao travelRouteDao) {
              this.travelRouteDao = travelRouteDao;
       }
 
 
       @Override
       public TravelRoutefindTravelRouteById(String travelRouteId) {
              System.out.println("哈哈哈,我在使用"+ travelRouteId+"的方式在调用程序噢");
              returntravelRouteDao.findTravelRouteById(travelRouteId);
       }
 
 
       public void setTravelRouteDao(TravelRouteDao travelRouteDao) {
              this.travelRouteDao = travelRouteDao;
       }
   
   
}

注意:例子为了保持兼容之前使用setter的方式,并没有去掉setter方法。大家可以注释掉setter方法,然后运行测试用例看效果。

@Resource不是Spring IOC提供的annotation,它是由javax.annotation包下提供的,属于JAVAEE规范的一部分,Spring为了支持JAVAEE也做了对应的实现。使用@Resource可以替代@Autowired完成依赖注入。@Resource默认按照名称进行装配,可以通过name属性来指定需要装配的属性,如果没有指定name属性,默认按属性名称进行查找完成装配,当查找不到与属性名称匹配的bean时,才按照类型进行装配。需要注意的是,一但指定了@Resource的那么属性,就只会按照属性名称完成装配。我们看个小例子:

package com.pz.study.frame.spring.service.impl;
 
import javax.annotation.Resource;
 
import org.springframework.stereotype.Service;
 
import com.pz.study.frame.spring.domain.TravelRoute;
importcom.pz.study.frame.spring.manager.TravelRouteManager;
importcom.pz.study.frame.spring.service.TravelRouteService;
 
@Service(value="TravelRouteService")
publicclass TravelRouteServiceImpl implements TravelRouteService {
      
       @Resource(name="travelRouteManager")
       private TravelRouteManager travelRouteManager;
      
       publicTravelRouteServiceImpl(){
              System.out.println("TravelRouteServiceImpl被实例化了");
       }
 
       @Override
       public TravelRoute findTravelRouteById(StringtravelRouteId) {
              returntravelRouteManager.findTravelRouteById(travelRouteId);
       }
   
      
       public void init() {
           System.out.println("我是初始方法init我被执行了");
       }
 
       public void destroy() {
           System.out.println("我是销毁方法destroy我被执行了");
       }
 
       public void setTravelRouteManager(TravelRouteManagertravelRouteManager) {
              this.travelRouteManager = travelRouteManager;
       }
      
      
 
}

运行测试用例,程序正常运行,说明已经完成装配。

使用注解实现初始化和销毁操作

@PostConstruct用于注解对象被实例化前被调用的方法。

@PreDestroy 用于注解对象被销毁前被调用的方法。

我们看下例子:

package com.pz.study.frame.spring.controller;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
 
import com.pz.study.frame.spring.domain.TravelRoute;
import com.pz.study.frame.spring.service.TravelRouteService;
 
@Controller("travelRouteController")
public class TravelRouteController {
      
      
      
      
       public TravelRouteController(){
              System.out.println("====TravelRouteController====被实例化了");
       }
 
       @Autowired
       @Qualifier("travelRouteService")
       private TravelRouteService travelRouteService;
      
      
       public TravelRoutefindTravelRouteById(String travelRouteId){
              returntravelRouteService.findTravelRouteById(travelRouteId);
       }
 
 
       public void setTravelRouteService(TravelRouteServicetravelRouteService) {
              this.travelRouteService = travelRouteService;
       }
      
      
       @PostConstruct
       public void before(){
              System.out.println("====TravelRouteController==before==被实例化之后才调用我");
       }
      
       @PreDestroy
       public void after(){
              System.out.println("====TravelRouteController==after==被销毁前才调用我");
       }
      
 
}

运行测试用例看下效果就知道了

@Scope注解 @Scope使用在类上用于指定bean的作用域,默认为singleton。

packagecom.pz.study.frame.spring.dao.impl;
 
importorg.springframework.context.annotation.Scope;
importorg.springframework.stereotype.Repository;
 
importcom.pz.study.frame.spring.dao.TravelRouteDao;
import com.pz.study.frame.spring.domain.TravelRoute;
 
 
@Repository("travelRouteDao")
@Scope("singleton")
publicclass TravelRouteDaoImpl implements TravelRouteDao {
      
 
       @Override
       public TravelRoutefindTravelRouteById(String travelRouteId) {
              System.out.println("哈哈哈,是Dao我使用"+ travelRouteId+"操作数据");
              return  new TravelRoute();
       }
   
      
 
}

关于使用注解还是XML做程序配置,其实都是为了配置程序运行时的信息,关于到底使用哪个更好,一直以来争论不断。注解的好处是,配置和代码在一起,一目了然,简单方便,注释是一种硬编码,修改后需要重新编译代码才能运行。

使用XML做程序配置,最大的好处是,配置发生变化后,无需编译代码,直接重启应用就可以了。这个好处在线上出现极端情况下,说实话比annotation好太多了。但是xml编写起来不如annotation直观,简洁。

如果同时使用了xml和annotation做配置,xml的配置的优先级高于注解,这样做的好处是,做到程序兼容,如果修改配置,只需要修改配置文件即可。但是程序看上去比较臃肿。

不过对于一个大型的有强大生命周期的项目而言,配置尽量清楚一些,可调整一些,打包编译上线需要你boss签字的,等打包签字的时间,事故等级都可能上升N个,不是大厂里混的,你是理解不了annotation其中的痛。

大家可能发现了,如果使用xml的方式配置文件,每增加一个bean就会编写一次配置文件。随着bean的增多,配置文件也会变得庞大无比,没有什么可读性,难以维护。Spring为了解决这个问题,是支持多个配置文件的,我们来看一个例子:

编写一个新的配置文件ApplicationContext2.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:aop="http://www.springframework.org/schema/aop"
   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/aop
   http://www.springframework.org/schema/aop/spring-aop.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName">
   
 
   
 
</beans>

修改ApplicationContext.xml引入新的配置文件即可。

  <import resource="ApplicationContext2.xml"/>
  

本文分享自微信公众号 - 猿人工厂(gh_deca5a88e287),作者:山旮旯的胖子

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 猿蜕变15——一文搞懂Spring AOP的正确姿势

    看过之前的蜕变系列文章,相信你对mybatis有了应用方面的认识。但是这些要完成你的蜕变还不够,考虑到大家的基础知识,我们继续回到spring的话题上来,我们一...

    山旮旯的胖子
  • 猿蜕变系列1——春天的故事

    看过之前的猿思考系列文章,相信你对java方面的基础有了一定的认识。经过之前的进化和思考的锻炼,也该是时候像模像样的做一些事情了。比如框架的学习。

    山旮旯的胖子
  • 猿实战01——vue后台前端框架搭建

    猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下...

    山旮旯的胖子
  • 猿蜕变15——一文搞懂Spring AOP的正确姿势

    看过之前的蜕变系列文章,相信你对mybatis有了应用方面的认识。但是这些要完成你的蜕变还不够,考虑到大家的基础知识,我们继续回到spring的话题上来,我们一...

    山旮旯的胖子
  • 单独设置UIView的坐标x或y或width或height

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

    用户1451823
  • 让AI给颜值打分?应该是最公正的裁判了!

    机器学习是不是很无聊,用来用去都是识别字体。能不能帮我找到颜值高的妹子,顺便提高一下姿势水平。 FaceRank 基于 TensorFlow CNN 模型,提供...

    AI研习社
  • HDU-1166 敌兵布阵 (树状数组模板题——单点更新,区间求和)

    _DIY
  • ABA式三声母域名xzx.com易主

    在域名圈,三声母域名凭借简短、含义多、实用性强等特点,吸引了众多企业的关注。近日,又曝出一枚三声母域名已易主,它就是:xzx.com。

    躲在树上的域小名
  • Exchange 2010 升级SP3

    1、了解先决条件 http://technet.microsoft.com/zh-CN/library/bb691354(v=exchg.141).aspx

    py3study
  • 开发 | 训练一个AI给颜值打分,公平公正!

    AI 科技评论按:本文作者灰灰,本文原载于作者的知乎专栏。授权转载。 机器学习是不是很无聊,用来用去都是识别字体。能不能帮我找到颜值高的妹子,顺便提高一下姿势水...

    AI科技评论

扫码关注云+社区

领取腾讯云代金券