七. Service
1. 数据与逻辑分离
之前我们讲面向对象设计,都是把数据和逻辑放在一起,这是理想情况。
现实情况是,把对象分为两类,一类专门存数据,一类专门执行逻辑
存数据的就是一个 Java Bean
存逻辑的叫做 XxxService
2. 控制反转
一直以来,都是我们自己用 new 关键字配合构造方法来创建对象,但我们现在用的是 Spring 框架,可以把一些创建对象的活交给 Spring 框架去做。
那么 Spring 框架怎么创建对象呢?它主要是配合一些注解来完成对象的创建,例如,我们一直在用的 @Controller 注解,当 Spring 程序运行时,它会检查这些类上有没有加一些特殊注解,例如它发现这个类上加了 @Controller 注解,框架就知道,该由框架来创建这个 CalculatorController 对象,默认只会创建一个。
这样的注解还有不少,我们现在需要掌握的有 @Controller 算一个,还有一个是 @Service,试试把这些 Service 类的创建交给 Spring 吧:
@Service
class CalculatorService0 implements Cal {public String[] cal(Calculator c) {}public String[][] details(Calculator c) {}
}@Service
class CalculatorService1 implements Cal {public String[] cal(Calculator c) {}public String[][] details(Calculator c) {}
}@Service
class CalculatorService2 implements Cal {public String[] cal(Calculator c) {}public String[][] details(Calculator c) {}
}
- @Service 的作用就是告诉 Spring,这个类以后要创建对象的话,不归程序员管了啊,归 Spring 管
- 其实 @Controller 的作用也是类似的,控制器对象虽然我们没 new,实际由 Spring 来创建了
把对象的创建权交给 Spring 来完成,对象的创建权被交出去,这称之为控制反转
3. 依赖注入
那么我们的代码里怎么拿到 Spring 创建的对象呢?
@Controller
public class CalController {// ...@Autowiredprivate Cal[] calArray;// ...}
这儿又要引入一个相关的名词:依赖注入
比如说,这里的 控制器 需要 service 才能工作,就可以说控制器对象依赖于 service 对象,缺了这些依赖对象行吗?不行吧。怎么找到这些依赖对象呢?如果是框架帮你找到这些依赖对象,按一定规则提供给你,就称之为依赖注入
怎么让框架帮你找到这些依赖对象呢?答案是 @Autowired
在 Cal[] 数组上添加 @Autowired 即可,它是根据类型去问 Spring 要对象,Spring 中有很多对象,具体要哪个对象呢?答案是根据类型要
- Cal 表示,只要 Spring 创建的对象实现了 Cal 接口,就符合条件
- Cal[] 表示,要多个
- 最终的结果是找到 Spring 管理的 CalculatorService0、CalculatorService1、CalculatorService2 三个对象,并放入了 Cal[] 数组
4. 由Spring创建JavaBean
Spring 还可以根据请求中的多个查询参数,帮我们创建 JavaBean 数据对象
@Controller
public class CalController {// ...@Autowiredprivate Cal[] calArray;@RequestMapping("/cal")@ResponseBodyString[] cal(Calculator c, int type) {return calArray[type].cal(c);}@RequestMapping("/details")@ResponseBodyString[][] details(Calculator c, int type) {return calArray[type].details(c);}}
- 如果提供了无参构造,Spring 会优先用它来创建对象,并调用 setXXX 方法,对属性进行赋值
- 查询参数有 p,那么 Spring 会找一个名为 setP 的方法完成赋值
- 查询参数有 m,那么 Spring 会找一个名为 setM 的方法完成赋值
- 查询参数有 yr,那么 Spring 会找一个名为 setYr 的方法完成赋值
- 查询参数有 type,但 Calculator 对象没有 setType 方法,所以 type 并不会存入对象
- 如果没有无参构造,但提供了有参构造,Spring 会拿构造方法参数名与查询参数相对应,并完成赋值
- 我们的例子中,查询参数有 p,m,yr,构造方法参数名也叫 p,m,yr,根据对应关系完成赋值
注意
- 不是所有 JavaBean 对象都应该交给 Spring 创建,一般只有请求中的数据,才会这么做
5. 包结构约定
目前可以划分 3 个包
- controller 包,用来存放控制器类
- service 包,用来存放业务逻辑类
- dto 包,用来存放存数据的 JavaBean 类,dto 是 Data Transfer Object(数据传输对象)的缩写
最后要注意一下入口类的位置,必须放在 service, controller 这几个包的上一层,为什么呢?
这个入口类,它还肩负了一个职责,查找 @Service, @Controller 等注解的类,然后创建对象。它查找的范围是在这个类的当前 package 之内,因此如果 service,controller 等包如果不在这个 package 内,那么会查找不到