为 Spring Framework 和 Spring Boot 构建代码
发布于 3 年前 作者 phuang 3996 次浏览 来自 分享

原标题:Spring认证|为 Spring Framework 和 Spring Boot 构建代码

如何为 Spring 框架构建心理框架

下面有一个很好的评论,提到原始标题Structuring Spring Boot Applications有点误导,因为该博客不打算谈论如何组织类型和包,而是考虑 Spring 如何与代码中的对象。下一段试图澄清这一点,但显然没有达到。无论如何,我已经更改了它,不幸的是这会破坏早期的链接。我对糟糕的标题和现在两天的链接断开感到抱歉。我希望它对每个人都有用,即使我显然可以用标题做得更好…

很难考虑构建应用程序。在更高级别有很多需要考虑的事情——它是批处理作业、Web 应用程序、消息应用程序等。框架——分别是 Spring Batch、Spring Webflux、Spring Integration——将指导这些决策。除此之外,还有许多其他框架,它们是量身定制的,可帮助您为特定的垂直业务构建值得生产的东西。我们不会在这篇文章中探讨这些。相反,我想回答这个问题:我们如何构建我们的配置?

我不能给你一个固执的答案,希望在一篇文章中适合它,但我们可以讨论 2021 年 Spring Boot 应用程序中配置的技术维度。 更容易谈论如何构建你的 Java 对象以使其正常工作在控制反转 (IoC) 容器中。请记住,归根结底,Spring 是一大袋对象。它需要知道您希望如何安排您的对象——它们如何连接以及它们如何相互关联——以便为它们提供服务。例如,它可以在方法启动和停止时开始和提交事务。它可以创建 HTTP 端点,在请求到达时调用您的 Spring 控制器处理程序方法。它可以调用您的消息侦听器对象来响应来自 Apache Kafka 代理或 AWS SQS 或 RabbitMQ 或其他任何东西的新消息。

Spring 有一个对象的元模型——它有点像 Java 反射 API。它知道哪些类有注释。它知道哪些对象有构造函数。它知道给定对象依赖于哪些依赖项、哪些 bean 和哪种类型。你的工作是帮助它建立这个元模型来为你管理所有的对象。例如,如果它可以控制对象的创建,那么它也可以在创建对象之前更改对象的创建。

Spring 只能为您提供所有这些服务,前提是它知道对象是如何连接在一起的。所以这个想法是你给 Spring 普通的 Java 对象 (POJO),它会检测它们上的注释并使用这些注释来连接你的服务的行为。但是,当然,除非它控制 Java 对象的创建,否则它不能这样做。

在幕后,它通过创建一个 Java InvocationHandler(一个 JDK 代理)或者更常见的方法是使用 CGLIB 之类的东西来创建一个扩展 Java 类的新类。这个类是你的类的子类。所以,想象一下你有一个这样的类:

class CustomerService { private final JdbcTemplate template; CustomerService (JdbcTemplate jt) { this.JdbcTemplate = jt; } @Transactional public void updateCustomer ( long customerId, String name){ // … . }}复制

您希望 Spring 在每次调用该方法时自动启动和停止事务。为此,Spring 需要在调用方法之前和之后插入自身。在幕后,它会做这样的事情:

class SpringEnhancedCustomerService extends CustomerService {

// Spring provides a reference from the applicationContext of type JdbcTemplate

SpringEnhancedCustomerService (JdbcTemplate jt) {

super(JdbcTemplate ) ;

}

@Override

public void updateCustomer (long customerId, String name) {

// call Java code to start a JDBC transaction

super.updateCustomer(customerId, name);

// call Java code to stop a JDBC transaction

}}复制

在您的代码中,您可以注入对CustomerService. 你仍然会得到一个,但不是你创建的那个。相反,您将获得子类。正是这个魔术 - 你要一顶帽子,然后得到一顶带有兔子的帽子 - 让 Spring 如此强大。

因此,Spring 必须了解您的对象。有很多方法可以做到这一点。

一是你可以非常明确。在 Spring Boot 之前,您有两个标准选项:XML 和 Java 配置。然而,那是 2013 年及更早的时间。现在,我们不鼓励使用 XML,因此只剩下 Java 配置了。下面是一个例子:

@Configuration class ServiceConfiguration {

@Bean DataSource h2DataSource (){

return … ;

}

@Bean JdbcTemplate JdbcTemplate (DataSource ds) {

return new JdbcTemplate(ds);

}

@Bean CustomerService customerService (JdbcTemplate jdbcTemplate) {

return new CustomerService (jdbcTemplate);

}}复制

在这里,您正在创建三个对象并显式地将事物连接在一起。当 Spring 启动时,它会找到@Configuration类,调用所有用 注释的方法@Bean,将所有返回值存储在应用程序上下文中,并使它们可用于注入。如果该方法似乎需要参数,它会查找任何其他返回该类型值的方法并首先调用它。然后将该值作为参数注入到方法中。如果它已经为其他注入调用了该方法,它只会重用已经创建的实例。

这种方法受益于显式——关于你的对象如何连接的所有信息都在一个地方——配置类。但是,对于您创建的类,您在两个不同的位置拥有知识:类本身和配置类。

因此,您可以使用另一种更隐式的方法:组件扫描。在这种方法中,Spring 在类路径上查找具有构造型注释的类,例如@Component或@Controller。所有构造型注释最终都用@Component. @Component是最低、最无差别的注释。如果你看@Controller,它是用@Component. 如果你看@RestController,它是用@Controller. 有 3 位间接寻址,但是用 注释的类@RestController仍然至少像用 注释的类一样对待@Component。专门的注释增加了专门的处理,但它们仍然是 的专门化@Component,而不是它的替代品。

因此,我们可能会认为CustomerService在配置类中定义和配置它很烦人。毕竟,如果 Spring 只知道类,它肯定可以自己弄清楚其余的关系吗?它可以查看构造函数并看到,要构造 的实例CustomerService,它需要对 的引用JdbcTemplate,该引用已在别处定义。

所以,这就是组件扫描的作用。您可以向类添加@Service另一个带有@Component,注释@Bean的构造型注释,然后删除配置类中的方法。Spring 将自动创建服务,它将提供所需的依赖项。它还将对类进行子类化以提供这些服务。

我们正在取得进展,移除更多样板。但是DataSource和JdbcTemplate呢?您需要它们,但您肯定不必每次都重新创建它们吗?这就是 Spring Boot 的洞察力。在创建类或调用方法之前,它使用@Condition注释来装饰用注释的类@Component@Configuration评估测试@Bean。这些测试可以在环境中寻找线索。例如,假设您有 H2 - 类路径上的嵌入式 SQL 数据库。并且您spring-jdbc在包含JdbcTemplate该类的类路径上拥有该库。它可以使用测试来测试类路径上是否存在这些类,并推断出您想要一个嵌入式 SQLDataSource并且您想要一个JdbcTemplate与新生成的SQL连接的实例DataSource. 它有自己的配置来为你提供这些 bean。现在,您可以@Configuration完全放弃课程!Spring Boot 提供了两个 bean,并基于构造型注释隐含了另一个 bean。

我们已经研究了 Spring IoC 容器的基本动机,并且我们已经研究了 IoC 容器如何工作以帮助满足框架提出的承诺。

我们确实可以走得更远,探索面向方面的编程 (AOP)、自动配置等等,但这意味着提供一个心理框架来理解何时应用哪种配置,以便您可以专注于使工作软件安全快速地投入生产的重要工作。

回到顶部