前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >猿蜕变系列2——一文搞懂spring的花式DI

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

作者头像
山旮旯的胖子
发布2020-07-28 16:55:14
2540
发布2020-07-28 16:55:14
举报
文章被收录于专栏:猿人工厂猿人工厂

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

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

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

代码语言:javascript
复制
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);
}
 
代码语言:javascript
复制
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接口和其实现类:

代码语言:javascript
复制
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方法:

代码语言:javascript
复制
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中增加配置:

代码语言:javascript
复制
<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">

代码语言:javascript
复制
<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">

创建测试方法:

代码语言:javascript
复制
       @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,添加构造方法

代码语言:javascript
复制
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属性和构造方法中的参数顺序一致。

代码语言:javascript
复制
   <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类

代码语言:javascript
复制
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和其实现类

代码语言:javascript
复制
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;
       }
      
      
 
}
代码语言:javascript
复制
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;
       }
   
   
}  
代码语言:javascript
复制
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();
       }
}

编写测试用例:

代码语言:javascript
复制
@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感受下效果。

代码语言:javascript
复制
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;
       }
      
      
 
}
 
代码语言:javascript
复制
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;
       }
      
      
 
}
代码语言:javascript
复制
 
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的那么属性,就只会按照属性名称完成装配。我们看个小例子:

代码语言:javascript
复制
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 用于注解对象被销毁前被调用的方法。

我们看下例子:

代码语言:javascript
复制
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。

代码语言:javascript
复制
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:

代码语言:javascript
复制
<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引入新的配置文件即可。

代码语言:javascript
复制
  <import resource="ApplicationContext2.xml"/>
  
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿人工厂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档