CodeGym /Java 博客 /随机的 /Spring for lazy people Foundation,基本概念,以及代码示例。第2部分
John Squirrels
第 41 级
San Francisco

Spring for lazy people Foundation,基本概念,以及代码示例。第2部分

已在 随机的 群组中发布
上一篇文章中,我简单解释了什么是Spring,什么是bean和context。现在是时候尝试一下了。我将使用 IntelliJ IDEA Enterprise Edition 来完成它。但我的所有示例也应该适用于免费的 IntelliJ IDEA 社区版。在屏幕截图中,如果您看到我有一些您没有的窗口,请不要担心 — 这对这个项目并不重要 :) Spring for lazy people Foundation,基本概念,以及代码示例。 第 2 - 1 部分首先,创建一个空的 Maven 项目。我在此链接的文章中展示了如何执行此操作。阅读“现在是我们将 Maven 项目转换为 Web 项目的时候了。 ”——之后,文章展示了如何创建 Web 项目,但我们现在不需要它。在src/main/java文件夹,创建一个包(在我的例子中,我称之为 " en.codegym.info.fatfaggy.animals。你可以随意调用它。只是不要忘记在所有正确的地方用你的包名替换我的包名。现在创建类并添加Main一个方法

public static void main(String[] args) {
    ...
}
之后,打开 pom.xml 文件并添加该dependencies部分。现在转到Maven 存储库并找到最新稳定版本的Spring 上下文。将我们找到的内容放入该dependencies部分。我在另一篇 CodeGym 文章中更详细地描述了这个过程(请参阅标题为“在 Maven 中连接依赖项”的部分)。然后 Maven 自己会找到并下载必要的依赖项。最后,您应该得到这样的结果: Spring for lazy people Foundation,基本概念,以及代码示例。 第 2 - 2 部分在左侧的窗口中,您可以看到带有包和Main类的项目结构。中间的窗口显示了 pom.xml 如何寻找我。我还添加了一个属性部分。此部分告诉 Maven 我在源文件中使用的 Java 版本以及要编译的版本。这只是为了让 IDEA 不会警告我我正在使用旧版本的 Java。这是可选的 :) 右边的窗口清楚地表明,即使我们只连接了 spring-context 模块,它也会自动拉入 spring-core、spring-beans、spring-aop 和 spring-expression 模块。我们本可以分别连接每个模块,在 pom.xml 文件中为每个模块写出一个明确版本的依赖关系,但现在我们对它们的现状很满意。现在创建entities包并在其中创建 3 个类:Cat, Dog, Parrot。让我们给每只动物起个名字(private String name- 你可以在那里硬编码一些值)。getter/setter 是公开的。现在我们继续讨论Main类和main()方法,我们这样写:

public static void main(String[] args) {
	// Create an empty Spring context that will look for its own beans based on the annotations in the specified package
	ApplicationContext context = 
		new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.entities");

	Cat cat = context.getBean(Cat.class);
	Dog dog = (Dog) context.getBean("dog");
	Parrot parrot = context.getBean("parrot-polly", Parrot.class);

	System.out.println(cat.getName());
	System.out.println(dog.getName());
	System.out.println(parrot.getName());
}
首先,我们创建一个上下文对象,告诉构造函数要在哪个包中查找 bean。换句话说,Spring 将遍历这个包并尝试找到标有特殊注释的类,表明它们是 beans。然后它创建这些类的对象并将它们放在上下文中。之后,我们从这个上下文中得到一只猫。我们调用上下文对象给我们一个bean(对象),指明我们想要的对象的类(顺便说一句,我们也可以指定接口,而不仅仅是类)。之后,Spring 返回一个请求类的对象,然后我们将其保存在一个变量中。接下来,我们要求 Spring 为我们提供一个名为“dog”的 bean。当 Spring 创建一个Dogobject,它给对象一个标准名称(除非创建的 bean 已经明确分配了一个名称),这是类名,但首字母小写。在本例中,我们的类名为Dog,因此 bean 的名称为“dog”。如果我们BufferedReader在那里需要一个对象,那么 Spring 会将其命名为“bufferedReader”。而且因为 Java 不能 100% 确定我们想要哪个类,它返回一个Object对象,然后我们手动将其转换为所需的类型,即Dog. 明确指示类的选项更方便。第三个选项是通过类名和 bean 名获取 bean。上下文可能有一个类的多个 bean。为了指示我们需要的特定 bean,我们指示它的名称。因为我们在这里也明确指出了类,所以我们不再需要执行强制转换。 重要的!如果Spring找到了几个符合我们要求的bean,那么它就不能确定给我们哪个bean,所以会抛出异常。因此,为避免这种情况,您应该尝试尽可能具体地告诉 Spring 您需要哪个 bean。如果 Spring 搜索其上下文,但未能找到符合我们要求的单个 bean,那么它也会抛出异常。最后,我们简单地显示我们动物的名字来验证我们真的得到了我们需要的对象。但是,如果我们现在运行该程序,我们会发现 Spring 不高兴 — 它无法在其上下文中找到我们需要的动物。这是因为它还没有创建这些 bean。正如我之前所说,当 Spring 扫描类时,它会查找自己的 Spring 注释。如果 Spring 没有找到这些注解,那么它就不会 不要认为这些类对应于它需要创建的 beans。解决这个问题只需要添加@Component我们每个动物类前面的注释。

@Component
public class Cat {
	private String name = "Oscar";
	...
}
但还有更多。如果我们需要显式地告诉 Spring 这个类的 bean 应该有一个特定的名称,我们在注释后的括号中指明名称。例如,要告诉 Spring 将名称“ parrot-polly”赋予鹦鹉 bean,这是我们将在方法中用来获取这只鹦鹉的名称main,我们应该这样做:

@Component("parrot-polly")
public class Parrot {
	private String name = "Polly";
	...
}
这就是自动配置 的重点。您编写您的类,用必要的注释标记它们,并告诉 Spring 包含您的类的包。这是框架将运行以查找注释和创建这些类的对象的包。顺便说一句,Spring 不仅会查找@Component注解,还会查找继承此注解的所有其他注解。比如 , @Controller, @RestController, @Service,@Repository等等,我们会在以后的文章中介绍。现在我们将尝试使用基于 Java 的配置来做同样的事情。首先,删除@Component我们班级的注释。为了使事情更具挑战性,假设我们没有编写这些类,所以我们不能轻易修改它们,这意味着我们不能添加注释。就像这些类被打包在某个库中一样。在这种情况下,我们无法编辑这些类以使其被 Spring 识别。但是我们需要这些类的对象!这里我们需要基于 Java 的配置来创建对象。首先,创建一个名称类似于configs. 在这个包中,创建一个普通的 Java 类,如MyConfig,并用@Configuration注解标记它。

@Configuration
public class MyConfig {
}
现在我们需要调整main()方法,改变我们创建上下文的方式。我们可以明确指出哪个类具有我们的配置:

ApplicationContext context =
	new AnnotationConfigApplicationContext(MyConfig.class);
如果我们有几个创建 bean 的不同类,并且我们想同时连接其中的几个,我们只需将它们全部显示在那里,用逗号分隔:

ApplicationContext context =
	new AnnotationConfigApplicationContext(MyConfig.class, MyAnotherConfig.class);
如果我们有太多它们并且我们想同时连接它们,那么我们只需指出它们包含的包的名称:

ApplicationContext context =
	new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.configs");
在这种情况下,Spring 将遍历包并找到所有标有@Configuration注解的类。好吧,如果我们有一个非常大的程序,其中配置分为不同的包,那么我们只需指出包含配置的包名称的逗号分隔列表:

ApplicationContext context =
	new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.database.configs",
		"en.codegym.info.fatfaggy.animals.root.configs",
		"en.codegym.info.fatfaggy.animals.web.configs");
或者它们都通用的包的名称:

ApplicationContext context =
	new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals");
您可以随心所欲地进行操作,但在我看来,第一个选项(仅指示具有配置的类)最适合我们的程序。在创建上下文时,Spring 会查找标有@Configuration注解的类,并将创建这些类的自己的对象。它尝试调用标有@Bean注解的方法,这意味着这些方法返回 Spring 将添加到上下文中的 beans(对象)。现在,我们将使用基于 Java 的配置为类中的猫、狗和鹦鹉创建 bean。这很简单:

@Bean
public Cat getCat() {
	return new Cat();
}
在这里,我们手动创建我们的猫并将其交给 Spring,然后 Spring 将我们的对象保存在它的上下文中。由于我们没有显式地为我们的 bean 命名,Spring 将为其命名为与方法名称相同的名称。在我们的例子中,猫豆将被称为“ getCat”。但是因为我们使用类而不是名称来在方法中获取 cat bean main,所以 bean 的名称对我们来说并不重要。同样,创建一个 dog bean,请记住 Spring 会将方法名称赋予该 bean。要显式命名我们的鹦鹉 bean,我们只需在注释后的括号中指明它的名称@Bean

@Bean("parrot-polly")
public Object weNeedMoreParrots() {
	return new Parrot();
}
如您所见,我在这里指定了一个Object返回类型并为该方法指定了一个任意名称。这不会影响 bean 的名称,因为我们在这里明确指定了名称。不过,最好还是指示一个或多或少有意义的返回值和方法名称。这样做只是为了在一年后重新打开项目时帮自己一个忙。:) 现在考虑我们需要一个 bean 来创建另一个 bean的情况。例如,假设我们希望 cat bean 中的猫的名字是鹦鹉的名字加上字符串“-killer”。没问题!

@Bean
public Cat getCat(Parrot parrot) {
	Cat cat = new Cat();
	cat.setName(parrot.getName() + "-killer");
	return cat;
}
这里Spring会看到为了创建这个bean,框架需要传入之前创建的parrot bean。因此,它将安排必要的方法调用链:首先,调用创建鹦鹉的方法,然后框架将新的鹦鹉传递给创建猫的方法。这就是依赖注入发挥作用的地方:Spring 本身将所需的鹦鹉 bean 传递给我们的方法。如果 IDEA 对变量不满意parrot,请不要忘记将鹦鹉创建方法的返回类型从 更改ObjectParrot。此外,基于 Java 的配置让您可以运行任何 Java 代码在你的 bean 创建方法中。你真的可以做任何事情:创建其他辅助对象,调用任何其他方法,甚至是那些没有用 Spring 注释标记的方法,创建循环,布尔条件——任何想到的!这在自动配置中并非都是可能的,而在 XML 配置中更是如此。 现在让我们考虑一个更有趣的问题。多态性和接口 :) 我们将创建一个WeekDay接口并创建 7 个实现该接口的类:Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday。我们将为接口提供一个String getWeekDayName()方法,该方法将返回相应类的星期几的名称。换句话说,Monday类将返回“Monday" 等。在启动应用程序时,假设我们的任务是将对应于星期几的 bean 放入上下文中。不是所有实现该接口的类的 bean — 只有我们需要的一个 bean。您WeekDay可以这样做:

@Bean
public WeekDay getDay() {
	DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
	switch (dayOfWeek) {
		case MONDAY: return new Monday();
		case TUESDAY: return new Tuesday();
		case WEDNESDAY: return new Wednesday();
		case THURSDAY: return new Thursday();
		case FRIDAY: return new Friday();
		case SATURDAY: return new Saturday();
		default: return new Sunday();
	}
}
这里的返回类型是我们的接口。该方法返回实现该接口的类之一的真实对象,具体取决于星期几。现在我们可以在方法中执行以下操作main()

WeekDay weekDay = context.getBean(WeekDay.class);
System.out.println("Today is " + weekDay.getWeekDayName() + "!");
对我来说,程序告诉我今天是星期天 :) 我相信如果我明天运行程序,上下文将包含一个完全不同的对象。请注意,我们只是使用接口获取 bean:context.getBean(WeekDay.class)。Spring 将在其上下文中搜索实现该接口的 bean,并将其返回。然后事实证明我们的WeekDay变量以 Sunday 对象结束,并且当我们使用这个变量时,熟悉的多态性概念适用。:) 现在简单介绍一下组合方法,其中一些 bean 由 Spring 自动创建,一些通过扫描包中带有注释的类创建@Component,另一些则通过基于 Java 的配置创建。当我们考虑这一点时,我们将返回到原始版本,其中CatDogParrot类标有@Component注释。假设我们想通过让 Spring自动扫描包来为我们的动物创建 bean entities,但我们也想创建一个带有星期几的 bean,就像我们刚刚所做的那样。您需要做的就是@ComponentScan在类级别添加注释MyConfig,我们在创建上下文时指出main(),并在括号中指出需要扫描的包并自动创建必要类的bean:

@Configuration
@ComponentScan("en.codegym.info.fatfaggy.animals.entities")
public class MyConfig {
	@Bean
	public WeekDay getDay() {
		DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
		switch (dayOfWeek) {
			case MONDAY: return new Monday();
			case TUESDAY: return new Tuesday();
			case WEDNESDAY: return new Wednesday();
			case THURSDAY: return new Thursday();
			case FRIDAY: return new Friday();
			case SATURDAY: return new Saturday();
			default: return new Sunday();
		}
	}
}
创建上下文时,Spring 发现它需要处理该类MyConfig。它进入该类并发现它需要扫描“ en.codegym.info.fatfaggy.animals.entities”包并创建这些类的 bean,然后执行该类MyConfiggetDay()方法并将一个WeekDaybean 添加到上下文中。在该main()方法中,我们现在可以访问所需的所有 bean:动物对象和带有星期几的 bean。如果您需要让 Spring 也获取一些 XML 配置文件,您可以自己进行网络搜索以找到解释:) 总结:
  • 尝试使用自动配置
  • 自动配置时,指明需要创建bean的类所在的包名
  • 这些类标有@Component注解
  • Spring遍历所有这些类,创建对象,并将它们放入上下文中;
  • 如果由于某种原因自动配置不适合我们,我们使用基于 Java 的配置
  • 在这种情况下,我们创建一个普通的 Java 类,其方法返回我们需要的对象。@Configuration如果我们要扫描整个包而不是在创建上下文时用配置指示特定类,我们会用注解标记这个类
  • 此类返回 bean 的方法用注解@Bean标记
  • 如果我们想在使用基于 Java 的配置时启用自动扫描,我们使用注解 @ComponentScan
如果这篇文章完全令人困惑,请尝试在几天后阅读它。或者,如果您处于 CodeGym 的早期级别之一,那么您学习 Spring 可能有点早。当您对自己的 Java 编程技能更有信心时,可以随时返回本文。如果一切都清楚,那么您可以尝试将您的一些宠物项目转换为 Spring :) 如果有些事情很清楚而有些事情不清楚,那么请发表评论 :) 如果我做错了,请告诉我您的建议和批评在某处或写了一些废话:) 在下一篇文章中,我们将突然进入spring-web-mvc并使用 Spring 制作一个简单的 Web 应用程序。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION