猿蜕变系列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"/>
- CSS深入理解学习笔记之overflow
- Python爬虫实践——简单爬取我的博客
- Python爬虫入门(二)
- 在ASP.NET MVC5应用程序中快速接入QQ和新浪微博OAuth起步创建应用程序使用NUGET更新OWIN中间件启动SSL支持申请腾讯QQ的Oauth申请新浪微博的Oauth快速接入资源地址&源码
- 有趣的算法(六) ——Find-Union算法
- 有趣的算法(七) ——快速排序改进算法
- 编写你人生中第一个机器学习代码吧!
- 使用Octave来学习Machine Learning(二)
- RESTful API的十个最佳实践1. 使用名词而不是动词 2. Get方法和查询参数不应该改变资源状态3. 使用名词的复数形式 4. 为关系使用子资源 5. 使用HTTP头决定序列化格式 6. 使
- MYSQL数据库设计的一些小技巧[转载]
- 在ASP.NET 5应用程序中的跨域请求功能详解什么是“同域”添加CORS包在应用程序中配置CORSCORS策略选项跨域请求中的凭据设置先行请求的过期时间CORS是怎么样工作的先行请求
- 有趣的算法(八) ——红黑树插入算法
- 使用Donut Caching和Donut Hole Caching在ASP.NET MVC应用中缓存页面何时使用Donut CachingDonut Caching 的Nuget 包Donut Ho
- 有趣的算法(九) ——蛇形数组
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 悬挂引用是如何被Rust消灭的?
- python小例子(二)
- 面试题系列第1篇:说说==和equals的区别?你的回答可能是错误的
- django-URL转换器(四)
- 【猫狗数据集】加载保存的模型进行测试
- 【猫狗数据集】划分验证集并边训练边验证
- 【猫狗数据集】使用学习率衰减策略并边训练边测试
- 面试题系列第2篇:new String()创建几个对象?有你不知道的
- spring之整合struts2
- django-URL之include函数(五)
- springmvc之使用ModelAttribute避免不允许被修改的值更新时为空
- 【colab pytorch】使用tensorboard可视化
- django-URL别名的作用(六)
- springmvc之如何确定目标方法Pojo类型的参数?
- 【colab pytorch】使用tensorboardcolab可视化