Spring Stereotype Annotations(每日一解系列)

2020-03-30 · 树下魅狐 · · 本文共3,168个字,预计阅读需要15分钟。

序言

在本章节中,我会集中介绍Spring框架中的构造型注解(Stereotype)。在Spring框架中,构造型注解主要有四个:

  • @Component: 一个通用的组件注解;
  • @Controller : 该注解表示视图层的一个控制器组件;
  • @Service : 该注解表示业务逻辑层的一个组件;
  • @Repository : 该注解表示数据持久层的一个组件。

那么,何为Stereotype呢?下面是Spring官方给出的解释:

Annotations denoting the roles of types or methods in the overall architecture (at a conceptual, rather than implementation, level).
表示类型或方法在整个体系结构中的作用的注释(是一种概念,而非具体的实现)

这句话怎么理解呢(上面的话看着云里雾里的)?简单来解释(我的理解):

比如说提到“基建狂魔”这个词,你会想到中国,辛勤劳动的工人,高效率等等;提到中国人,你会想到黄皮肤,黑头发,吃苦耐劳,团结一致等等。你首先在脑海里面会有一个基本的概念,而不是具体的细节,工人是怎么造桥修路,怎么施工;中国人每个人的升高体重是多少等等,这些细节上的东西首先不会去考虑。

你可以简单的理解为从宏观上是个什么概念,以最简单的逻辑去却分他们。@Component就表示一个普通的Spring组件,@Controller就表示Spring MVC的控制器,@Service就表示一个业务逻辑组件,@Repository表示一个数据持久化组件。

1.组件

简单来说,组件就是将具有相同业务领域或具有同等类别的方法归集到一起,统一向外提供服务(调用)。这和我们的日常工作很相似,我们会根据各种业务将员工分成不同的工作小组,部门等,好处就是统一协调,统一管理。在前面的章节中已经介绍到,@Controller,@Service@Repository这三个注解是@Component的特例(或特殊情况)。就好比介绍一个人说:“他是某某公司的职工”,至于是什么部门,什么岗位没有提及。你可以简单的理解为@Component@Controller@Service@Repository这三个注解的基类。

特别提示:

虽然说组件可以在应用程序中的任何地方出现,但这些注解只能注释在具体的类上,不能注释在接口上。虽然说他们是构造型的注解,但也是基于完整事物进行描述,不能用于描述整体的某个部分。另外,你可以将组件注解应用到抽象类上,抽象类是对完整事物的“艺术加工”,本质上还是完整的。需要仔细品味整体与部分的关系。

2. 综合演示

接下来的内容中,我会通过一个完整的模拟项目,来演示上述注解的使用。在模拟项目中,需要对书籍信息进行增删改查操作,为此,我们需要做如下的准备:

  • Book:需要一个书籍实体来承载信息
  • BookController : 需要一个视图控制器控制前端操作
  • BookService : 需要一个业务逻辑组件来处理前端请求
  • BookRepository: 需要一个持久化组件向数据库读写数据
  • H2:需要一个数据库存放数据

在模拟项目中,还会引入一些新的注解,如@Entity@Table@Id@Column等。我将基于Spring Boot 2.0版本并使用Maven来构建模拟项目。

2.1 项目构建

你可以通过Spring initializr来构建项目,或者使用STS(Spring Tool Suite)/IntelliJ IDEA提供的插件来构建项目。下面是Spring initializr官网:

本案例中,我使用IntelliJ IDEA来构建项目。

点击下一步之后,一此选择项目所需要的依赖包:Spring Web ,Spring Data JPA和H2 Database。除此之外,我还添加了Lombok插件,该插件可以快速生成getter和setter等方法。如下:

2.2 Book实体

按照分层架构的指导思想,我们创建一个entity包,并在包下编写Book.java文件的代码,内容如下:

Book.java

@Entity
@Table(name = "t_book")
@Getter
@Setter
public class Book implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "name",columnDefinition = "varchar(128) not null")
    private String name;

    @Column(name = "author",columnDefinition = "varchar(64) not null")
    private String author;

    @Column(name = "summary",columnDefinition = "varchar(255) default ''")
    private String summary;
}

在该文件中,使用了一些新的注解,不太了解的同学可以在头条搜索“Java持久化注解”或者“JPA持久化注解“,”Hibernate持久化注解”以及“Lombok注解”了解更多详细内容。

Book实体很简单,提供了书籍的ID,名称,作者以及简介。

2.3 Book Repository

在本示例中,使用了Spring Data JPA依赖,它可以快速地为我们提供数据增删查改操作能力,不需要再去处理繁重的数据库链接,编写SQL语句,执行数据库查询,获取结果集,值映射等操作。

BookRepository.java

public interface BookRepository extends JpaRepository<Book,Integer> {
}

BookRepository.java为一个接口,并继承JpaRepository类。前面提到,组件注解不能作用在接口上,那@Repository注解在哪儿呢?当我们自定义的持久化类继承JpaRepository接口后,除自定义的实现接口实现之外,其余的将使用JpaRepository默认的实现。在Spring Data JPA中,JpaRepositoryImplementation类继承了JpaRepository接口,在其实现类SimpleJpaReposiroty中使用了@Repository注解。

JpaRepositoryImplementation.java

public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>...{
    //...
}

SimpleJpaRepository.java

@Repository            //持久化组件注解
@Transactional(
    readOnly = true
)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
    //...
}

提示:

如果你想自定义持久化接口的实现类,则需要将@Repository注解加上,也可以让你的自定义持久化实现类继承SimpleJpaRepository类,则不需要再使用@Repository注解。

2.4 Book Service

接下来,我们需要定义一个Book Service的接口,并提供相关的实现类并在实现类上使用@Service注解。在接口中,我们提供增删查改四个方法。

BookService.java

public interface BookService {
    Book add(Book book);
    boolean delete(Integer id);
    Book findById(Integer id);
    Book update(Book book);
    List<Book> findAll();
}

BookServiceImpl.java

@Service("bookService")
public class BookServiceImpl implements BookService {

    private final BookRepository bookRepository;

    public BookServiceImpl(BookRepository bookRepository){
        this.bookRepository = bookRepository;
    }

    //省略其他....
}

在BookServiceImpl.java文件中,使用@Service对类进行了注解,并定义了Bean的名称为“bookService”,此外,采用了基于构造器的注入方式将持久化类BookRepository注入到了BookServiceImpl类中。前面章节提到,当类中只有一个构造器时,可以不使用@Autowired注解,Spring会根据默认的构造器完成自动装配和依赖注入。

2.5 Book Controller

为了能够快速演示整个项目,在控制器中引入了一个新的注解:@RestController。该注解是@Controller@ResponseBody的组合组件,用于直接将业务层的计算结果直接范围到前端页面中。

RestController.java

@Controller
@ResponseBody
public @interface RestController {
 //省略...
}

接下来,在controller包中定义BookController,并通过属性的方式注入BookService,然后提供对书籍的增删查改操作。

BookController.java

@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @GetMapping
    public ResponseEntity<Object> books(){
        return ResponseEntity.ok().body(bookService.findAll());
    }

    @PostMapping
    public ResponseEntity<Object> add(@RequestBody Book book){
        return ResponseEntity.ok().body(bookService.add(book));
    }

    @PutMapping
    public ResponseEntity<Object> update(@RequestBody Book book){
        return ResponseEntity.ok().body(bookService.update(book));
    }

    @DeleteMapping("/{id:\\d+}")
    public ResponseEntity<Object> delete(@PathVariable("id")Integer id){
        if(bookService.delete(id)){
            return ResponseEntity.ok().build();
        }else{
            return ResponseEntity.badRequest().build();
        }
    }

}

到此,模拟项目的三层(Dao Layer,Service Layer,Controller Layer)已经全部编码完成。接下来,将在application.yml文件中配置H2数据库。关于H2数据库的更多信息,感兴趣的同学可以在头条搜搜“H2数据库”。

H2数据库配置

application.yml

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password: demo
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: update
  h2:
    console:
      enabled: true
      path: /h2-console

在此配置中,定义了数据库的URL地址,用户名和密码,另外还定义了数据库的方言以及H2数据的控制台访问地址(默认就是/h2-console)。

3. 启动应用程序

完成上诉所有配置后,启动模拟项目,待项目启动完成后,首先测试H2数据库是否可用。

3.1 测试H2数据库

在浏览器中输入http://localhost:8080/h2-console ,访问H2数据库控制台页面。

在登录页面中,填写刚刚在配置文件中定义的数据库URL地址,用户名和密码,点击“Connect”按钮登录H2数据库,成功连接上H2数据库后,你将进入下面的页面:

在此页面中,你可以看到,我们的T_BOOK表已经创建成功。接下来,我们将测试BookController的各项功能,并观察H2数据库中的T_BOOK表是否有变化。

3.3 测试

当前,我们的应用中没有任何的数据,接下来,将以POST请求的方式调用http://localhost:8080/books 接口添加一本书的信息到应用中。为了方便测试,在本案例中使用了Postman软件。

在执行请求前,需要设置Headers的content-type为application/json,然后再body选项中填写如下信息(格式为JSON)。

{
    "name":"book1",
    "author":"author1",
    "summary":"author1`s book and book name is book1"
}

点击”Send”按钮,发送请求。可以看到,服务端成功返回数据:

{
    "id": 3,
    "name": "book1",
    "author": "author1",
    "summary": "author1`s book and book name is book1"
}

最后,我们在H2控制台执行查询语句,看数据是否入库:

可以看到,数据已经成功保存到H2数据库中。

最后,我将通过一个短片,来演示整个测试过程:

总结

在本章节中,讲解了Spring框架中的Stereotype 注解以及相关的注意事项,同时给出了一个完整的模拟项目,加深对Spring框架中Stereotype注解的理解。章节中所涉及到的源代码已经上传到Github,感兴趣的朋友可以到GitHub官网进行下载。