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

上一章节我们讲解了Spring IOC中 Bean的装配过程,本章节我们主要讲解Spring的依赖注入方式。spring的花式DI,配置,看这个就够了。

猿思考是一个原创系列文章,帮助你从一个小白快速掌握基础知识,很多基础知识,在于思考的变通,更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取

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"/>  

我建了一个群,群里有很多高手,欢迎大家入群探讨。

猜你喜欢

转载自blog.csdn.net/hzldds2019/article/details/106046547
今日推荐