揭秘Spring依赖注入和SpEL表达式
摘要:在本文中,我们深入探讨了Spring框架中的属性注入技术,包括setter注入、构造器注入、注解式属性注入,以及使用SpEL表达式进行属性注入。
本文分享自华为云社区《Spring高手之路3——揭秘Spring依赖注入和SpEL表达式》,作者:砖业洋__ 。
在本文中,我们深入探讨了Spring框架中的属性注入技术,包括setter注入、构造器注入、注解式属性注入,以及使用SpEL表达式进行属性注入。我们通过XML和注解两种方式,详细讲解了如何进行属性注入,并给出了完整的代码示例。无论你是Spring新手,还是有一定经验的开发者,本文都将帮助你理解并掌握Spring中的属性注入技术。
1. setter属性注入
1.1 使用XML进行setter方法注入
我们在前面的文章中已经使用过XML进行setter方法的属性注入了,下面让我们再来回顾一下:
<bean id="userSetter" class="com.example.demo.bean.User">
<property name="username" value="example-username-setter"/>
<property name="age" value="25"/>
</bean>
1.2 使用@Bean注解进行setter方法注入
我们在前面的文章中也学习过如何在bean创建时通过编程方式设置属性:
@Bean
public User user() {
User user = new User();
user.setUsername("example-username-anno-setter");
user.setAge(25);
return user;
}
1.3 setter方法注入完整代码示例
- 使用XML进行setter方法注入
首先,我们需要创建一个User类,并在其中包含username和age两个属性,以及相应的getter、setter方法和构造器。
public class User {
private String username;
private Integer age;
public User() {
}
// 为了节省篇幅,getter和setter方法省略......
@Override
public String toString() {
return "User{username='" + username + "', age=" + age + "}";
}
}
对于XML方式的setter注入和构造器注入,我们需要创建一个配置文件,比如叫applicationContext.xml。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- setter方法注入 -->
<bean id="userSetter" class="com.example.demo.bean.User">
<property name="username" value="example-username-setter"/>
<property name="age" value="25"/>
</bean>
</beans>
然后,我们需要创建一个DemoApplication类,使用ApplicationContext来加载配置文件并获取Bean:
import com.example.demo.bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User userSetter = (User) context.getBean("userSetter");
System.out.println(userSetter);
}
}
运行结果如下:
- 使用@Bean注解进行setter方法注入
我们需要创建一个配置类,例如叫AppConfig.java:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public User userSetter() {
User user = new User();
user.setUsername("example-username-anno-setter");
user.setAge(25);
return user;
}
}
使用@Bean注解来定义Bean。每个@Bean方法对应于XML配置中的一个<bean>元素。这个方法的名称就是Bean的id,方法的返回值就是Bean的类型
然后修改主程序,这里使用AnnotationConfigApplicationContext来创建Spring的应用上下文,并加载配置类。Spring会自动从配置类中获取所有的Bean定义,并创建相应的Bean实例。
package com.example.demo;
import com.example.demo.bean.User;
import com.example.demo.configuration.AppConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
User userSetter = (User) context.getBean("userSetter");
System.out.println(userSetter);
}
}
运行结果如下
注意:XML配置方式已经相对陈旧,而且在Spring Boot项目中,主流的做法是使用注解和Java配置方式。对于setter注入,有时会引发循环依赖的问题。在Spring中,可以使用构造器注入来避免这种情况,这里了解即可。
2. 构造器注入
setter注入是一种在对象被实例化之后(通过调用无参构造器创建实例)再通过setter方法注入依赖的方式。构造器注入则是在创建对象实例的时候就通过构造器参数来注入依赖。
为了演示构造器注入,我们需要给User添加一个全参数构造器:
public User(String username, Integer age) {
this.username = username;
this.age = age;
}
添加这个构造器后,Java不再提供默认的无参构造器,这会导致我们之前的<bean>标签创建时失败,因为它找不到默认的构造器。
2.1 使用XML进行构造器注入
我们可以在<bean>标签内部声明一个子标签:constructor-arg。它用于指定构造器的参数,来进行属性注入。constructor-arg标签的编写规则如下:
<bean id="userConstructor" class="com.example.demo.bean.User">
<constructor-arg index="0" value="example-username-constructor"/>
<constructor-arg index="1" value="25"/>
</bean>
index属性表示构造函数参数的位置,它的值是一个非负整数,其中0表示第一个参数,1表示第二个参数,以此类推。虽然value属性的值总是一个字符串,但是Spring会尝试将它转换为构造函数参数所需的类型。例如构造函数的第二个参数是int类型,那么Spring会尝试将字符串"25"转换为整数25。
使用index属性来指定构造函数参数的位置在大多数情况下是可以的,但是如果构造函数的参数数量或者顺序发生了改变,就可能会出错。另外一种更为可靠的方式是使用name属性来指定参数的名称,如:
<bean id="userConstructor" class="com.example.demo.bean.User">
<constructor-arg name="username" value="example-username-constructor"/>
<constructor-arg name="age" value="25"/>
</bean>
这样无论参数的顺序如何,只要参数名称不变,就不会出错。
2.2 使用@Bean注解进行构造器属性注入
在注解驱动的bean注册中,我们也可以直接使用编程方式赋值:
@Bean
public User user() {
return new User("example-username-anno-constructor", 25);
}
2.3 构造器注入的完整代码示例
- 使用XML进行构造器注入
首先,我们需要创建一个User类,并在其中包含username和age两个属性,以及相应的getter、setter方法和构造器。
public class User {
private String username;
private Integer age;
public User() {
}
public User(String username, Integer age) {
this.username = username;
this.age = age;
}
// 为了节省篇幅,getter和setter方法省略......
@Override
public String toString() {
return "User{username='" + username + "', age=" + age + "}";
}
}
对于XML方式的构造器注入,我们需要创建一个配置文件,比如叫applicationContext.xml,这里保留setter注入方便大家对比
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- setter方法注入 -->
<!-- setter方法注入 -->
<!-- <bean id="userSetter" class="com.example.demo.bean.User">-->
<!-- <property name="username" value="example-username-setter"/>-->
<!-- <property name="age" value="25"/>-->
<!-- </bean>-->
<!-- 构造器注入 -->
<bean id="userConstructor" class="com.example.demo.bean.User">
<constructor-arg name="username" value="example-username-constructor"/>
<constructor-arg name="age" value="25"/>
</bean>
</beans>
然后,我们需要创建一个DemoApplication类,使用ApplicationContext来加载配置文件并获取Bean:
package com.example.demo;
import com.example.demo.bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// User userSetter = (User) context.getBean("userSetter");
// System.out.println(userSetter);
User userConstructor = (User) context.getBean("userConstructor");
System.out.println(userConstructor);
}
}
运行结果如下:
- 使用@Bean注解进行构造器属性注入
我们需要创建一个配置类,例如叫AppConfig.java:
import com.example.demo.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
// @Bean
// public User userSetter() {
// User user = new User();
// user.setUsername("example-username-anno-setter");
// user.setAge(25);
// return user;
// }
@Bean
public User userConstructor() {
return new User("example-username-anno-constructor", 25);
}
}
同样,我们需要创建一个DemoApplication类,使用AnnotationConfigApplicationContext来加载配置类并获取Bean:
import com.example.demo.bean.User;
import com.example.demo.configuration.AppConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// User userSetter = (User) context.getBean("userSetter");
// System.out.println(userSetter);
User userConstructor = (User) context.getBean("userConstructor");
System.out.println(userConstructor);
}
}
运行结果:
注意:如果在类中同时使用构造器注入和setter注入,需要注意它们注入的顺序:先进行构造器注入,然后是setter注入。
3. 注解式属性注入
上面我们已经说过注解式的setter和构造器注入。我们又是如何处理那些通过@Component扫描而注册的bean的属性的呢?我们来仔细说说这个问题,同时展示如何在xml中进行相同的操作。
3.1 @Value注解式属性注入的应用
首先,让我们从最简单的属性注入方法:@Value开始。创建一个新的White类,并声明一些字段,但是这次我们不会设置setter方法:
@Component
public class White {
@Value("white-value-annotation")
private String title;
@Value("1")
private Integer rank;
@Override
public String toString() {
return "White{" + "title='" + title + '\'' + ", rank=" + rank + '}';
}
}
要实现注解式属性注入,我们可以直接在需要注入的字段上添加@Value注解:
@Value("white-value-annotation")
private String title;
@Value("1")
private Integer rank;
要注意的是,如果使用 @Value 注解来注入一个不存在的属性,那么应用程序会在启动时抛出异常。
然后,我们将通过组件扫描方式将这个White类扫描到IOC容器中,并将其取出并打印:
public class DemoApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(White.class);
White white = ctx.getBean(White.class);
System.out.println("Injected value : " + white);
}
}
运行main方法会看到White的字段已经成功注入:
Injected value : White{
title='white-value-annotation', rank=1}
3.2 引入外部配置文件@PropertySource
如果我们需要在Spring中使用properties文件,我们应该怎么办呢?Spring考虑到了这一点,并扩展了一个用于导入外部配置文件的注解:@PropertySource。
- 创建Bean和配置文件
创建一个新的Blue类,其结构与White类完全相同。然后在项目的resources目录下创建一个新的blue.properties文件,用于存储Blue类的属性配置:
blue.title=blue-value-properties
blue.rank=2
- 引入配置文件
使用@PropertySource注解将properties文件导入到配置类:
@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:blue.properties")
public class InjectValueConfiguration {
}
这个blue.properties文件是一个键值对的列表,Spring 将这些键值对加载到 Environment 中,我们可以通过 @Value 注解或者 Environment 类的方法来获取这些属性值。
@Value 注解和 Environment 类都可以用于读取 Spring 上下文中的属性值。这些属性值可能来自于多个不同的源,包括但不限于:
- Spring Boot 的默认配置文件(application.properties 或 application.yml)。
- 通过 @PropertySource 注解加载的属性文件。
- 系统环境变量。
- Java 系统属性(可以通过 -D 命令行参数设置)。
如果你想通过 @Value 注解来获取属性值,如下:
@Component
public class BlueConfig {
@Value("${blue.title}")
private String title;
@Value("${blue.rank}")
private int rank;
// getters and setters...
}
在 Spring 应用中使用 @PropertySource 注解来加载一个 .properties 文件时,这个文件中的所有配置项都会被读取,并存储在一个内部的 Map 结构中。这个 Map 的键是配置项的名称,值是配置项的值。Spring 中的一些内置配置项也会被添加到这个 Map 中。
当我们使用 ${...}` 占位符语法来引用一个配置项时,`Spring` 会查找这个 `Map`,取出与占位符名称相应的配置项的值。例如有一个配置项 `blue.title=blue-value-properties`,我们可以在代码中使用 `${blue.title} 占位符来引用这个配置项的值。
如果想通过 Environment 类的方法来获取属性值,可以像下面这样做:
@Component
public class SomeComponent {
@Autowired
private Environment env;
public void someMethod() {
String title = env.getProperty("blue.title");
int rank = Integer.parseInt(env.getProperty("blue.rank"));
// ...
}
}
在上述代码中,Environment 类的 getProperty 方法用于获取属性值。注意,getProperty 方法返回的是 String,所以如果属性是非字符串类型(如 int),则需要将获取的属性值转换为适当的类型。
注意:@PropertySource 无法加载 YAML 格式的文件,只能加载 properties 格式的文件。如果需要加载 YAML 格式的文件,而且使用的是 Spring Boot框架,那么可以使用@ConfigurationProperties或@Value注解。例如以下的YAML文件:
application.yml
appTest:
name: MyApp
version: 1.0.0
可以使用@ConfigurationProperties来加载这些属性:
@Configuration
@ConfigurationProperties(prefix = "appTest")
public class AppConfig {
private String name;
private String version;
// getters and setters...
}
@ConfigurationProperties注解主要用于指定配置属性的前缀,@ConfigurationProperties注解本身并不直接指定配置文件的位置, 而是由Spring Boot的自动配置机制处理的。
这样,name字段就会被自动绑定到appTest.name配置属性,version字段就会被自动绑定到appTest.version配置属性。
默认情况下,Spring Boot会在启动时自动加载src/main/resources目录下的application.properties或application.yml文件。我们可以通过设置spring.config.name和spring.config.location属性来改变默认的配置文件名或位置。
注意:@ConfigurationProperties注解需要配合@EnableConfigurationProperties注解或@Configuration注解使用,以确保Spring能够发现并处理这些注解。
或者,你也可以使用@Value注解来加载这些属性:
@Component
public class AppConfig {
@Value("${appTest.name}")
private String name;
@Value("${appTest.version}")
private String version;
// getters and setters...
}
- Blue类的属性注入
对于properties类型的属性,我们这里选择@Value注解和占位符来注入属性:
@Value("${blue.title}")
private String title;
@Value("${blue.rank}")
private Integer rank;
如果你熟悉jsp的el表达式,会发现这和它非常相似!
- 测试启动类
修改启动类,将配置类引入,然后取出并打印Blue:
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(InjectValueConfiguration.class);
Blue blue = ctx.getBean(Blue.class);
System.out.println("Properties value : " + blue);
}
运行main方法会看到控制台已经成功打印出了配置文件的属性:
Properties value : Blue{
title='blue-value-properties', rank=2}
3.3 在XML中引入外部配置文件
在xml中,我们可以和@Value相同的方式使用占位符:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
xmlns:context="http://www.springframework.org/schema/context">
<!-- 相当于注解中的 @PropertySource("classpath:blue.properties") -->
<context:property-placeholder location="classpath:blue.properties"/>
<bean class="com.example.demo.bean.Blue">
<property name="title" value="${blue.title}"/>
<property name="rank" value="${blue.rank}"/>
</bean>
</beans>
3.4 注解式属性注入完整代码示例
- @Value注解式属性注入的应用
创建White类:
package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class White {
@Value("white-value-annotation")
private String title;
@Value("1")
private Integer rank;
@Override
public String toString() {
return "White{" + "title='" + title + '\'' + ", rank=" + rank + '}';
}
}
创建启动类InjectValueAnnotationApplication:
package com.example.demo;
import com.example.demo.bean.White;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(White.class);
White white = ctx.getBean(White.class);
System.out.println("Injected value : " + white);
}
}
运行结果如下:
- 引入外部配置文件@PropertySource
创建Blue类和配置文件,没有setter和getter方法:
package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Blue {
@Value("${blue.title}")
private String title;
@Value("${blue.rank}")
private Integer rank;
@Override
public String toString() {
return "Blue{" + "title='" + title + '\'' + ", rank=" + rank + '}';
}
}
resources目录下的blue.properties文件:
blue.title=blue-value-properties
blue.rank=2
创建配置类InjectValueConfiguration:
package com.example.demo.configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:blue.properties")
public class InjectValueConfiguration {
}
修改启动类,引入配置类:
package com.example.demo;
import com.example.demo.bean.Blue;
import com.example.demo.configuration.InjectValueConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DemoApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(InjectValueConfiguration.class);
Blue blue = ctx.getBean(Blue.class);
System.out.println("Properties value : " + blue);
}
}
运行结果如下:
- 在xml中引入外部配置文件
在使用XML配置的情况下,我们需要创建一个XML文件来替代InjectValueConfiguration类,我们可以先注释掉InjectValueConfiguration类的所有内容
下面是相应的XML文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
xmlns:context="http://www.springframework.org/schema/context">
<!-- 相当于注解中的 @PropertySource("classpath:blue.properties") -->
<context:property-placeholder location="classpath:blue.properties"/>
<bean class="com.example.demo.bean.Blue">
<property name="title" value="${blue.title}"/>
<property name="rank" value="${blue.rank}"/>
</bean>
</beans>
在这里我们使用了context:property-placeholder标签来导入外部的properties文件,然后使用${...}占位符语法来引用配置文件中的属性值。这样无论是选择用注解方式还是XML方式,都可以方便地在Spring中使用外部配置文件。
这里还需要修改下Blue类,因为通过XML方法注入属性需要提供相应的setter方法,修改后的Blue类如下:
package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Blue {
@Value("${blue.title}")
private String title;
@Value("${blue.rank}")
private Integer rank;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
@Override
public String toString() {
return "Blue{" + "title='" + title + '\'' + ", rank=" + rank + '}';
}
}
然后,我们需要修改启动类,使用XmlApplicationContext代替AnnotationConfigApplicationContext:
package com.example.demo;
import com.example.demo.bean.Blue;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@ComponentScan("com.example")
public class DemoApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:injectValueContext.xml");
Blue blue = ctx.getBean(Blue.class);
System.out.println("Properties value : " + blue);
}
}
运行结果如下:
4. SpEL表达式
当我们谈到属性注入的时候,我们可能会遇到一些复杂的需求,例如我们需要引用另一个Bean的属性,或者我们需要动态处理某个属性值。这种需求无法通过使用${}的占位符方式实现,我们需要一个更强大的工具:SpEL表达式。
Spring Expression Language(SpEL)是从Spring框架 3.0开始支持的强大工具。SpEL不仅是Spring框架的重要组成部分,也可以独立使用。它的功能丰富,包括调用属性值、属性参数、方法调用、数组存储以及逻辑计算等。它与开源项目OGNL(Object-Graph Navigation Language)相似,但SpEL是Spring框架推出的,并默认内嵌在Spring框架中。
4.1 使用@Value注解和SpEL表达式实现属性注入
SpEL的表达式用#{}表示,花括号中就是我们要编写的表达式。
我们创建一个Bean,命名为Azure,同样地,我们声明属性name和priority,并提供getter和setter方法以及toString()方法。然后我们使用@Component注解标注它。
使用@Value配合SpEL完成属性注入,如下:
@Component
public class Azure {
@Value("#{'spel-for-azure'}")
private String name;
@Value("#{10}")
private Integer priority;
}
我们修改启动类,从IOC容器中获取Azure并打印,可以看到属性被成功注入:
Azure{
name='spel-for-azure', priority=10}
SpEL的功能远不止这些,它还可以获取IOC容器中其他Bean的属性,让我们来展示一下。
我们已经注册了Azure Bean,现在我们再创建一个Bean,命名为Emerald。我们按照上述方法对字段和方法进行声明,然后使用@Component注解标注。
我们希望name属性直接复制Azure的name属性,而priority属性则希望比Azure的priority属性大1,我们可以这样编写:
@Component
public class Emerald {
@Value("#{'copy of ' + azure.name}")
private String name;
@Value("#{azure.priority + 1}")
private Integer priority;
}
在Spring的SpEL中可以通过bean的名称访问到对应的bean,并通过.操作符访问bean的属性。在这个例子中,azure就是一个bean的名称,它对应的bean就是Azure类的实例。所以,azure.name就是访问Azure类实例的name属性。
如果你在一个不涉及Spring的环境中使用SpEL,这个特性是不会生效的。这是因为这个特性依赖于Spring的IoC容器。
我们修改启动类,测试运行,可以看到Azure的属性已经成功被复制:
use spel bean property : Emerald{
name='copy of spel-for-azure', priority=11}
SpEL表达式不仅可以引用对象的属性,还可以直接引用类的常量,以及调用对象的方法。下面我们通过示例进行演示。
我们新建一个Bean,命名为Ivory。我们按照上述方法初始化属性、toString()方法、注解。
假设我们有一个需求,让name取azure属性的前3个字符,priority取Integer的最大值。那么我们可以使用SpEL这样写:
@Component
public class Ivory {
@Value("#{azure.name.substring(0, 3)}")
private String name;
@Value("#{T(java.lang.Integer).MAX_VALUE}")
private Integer priority;
}
注意,直接引用类的属性,需要在类的全限定名外面使用T()包围。
我们修改启动类,测试运行,可以看到Ivory的属性已经是处理之后的值:
use spel methods : Ivory{
name='spe', priority=2147483647}
4.2 在XML中使用SpEL表达式实现属性注入:
<bean id="ivory" class="com.example.demo.bean.Ivory">
<property name="name" value="#{azure.name.substring(0, 3)}" />
<property name="priority" value="#{T(java.lang.Integer).MAX_VALUE}" />
</bean>
学习SpEL表达式不需要花费大量的精力,掌握基础的使用方法即可。
4.3 SpEL表达式属性注入完整代码示例
- 使用@Value注解和SpEL表达式实现属性注入
创建三个SpEL表达式属性注入的Bean:Azure.java、Emerald.java和Ivory.java。
Azure.java:
package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Azure {
@Value("#{'spel-for-azure'}")
private String name;
@Value("#{10}")
private Integer priority;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
@Override
public String toString() {
return "Azure{" +
"name='" + name + '\'' +
", priority=" + priority +
'}';
}
}
Emerald.java:
package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Emerald {
@Value("#{'copy of ' + azure.name}")
private String name;
@Value("#{azure.priority + 1}")
private Integer priority;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
@Override
public String toString() {
return "Emerald{" +
"name='" + name + '\'' +
", priority=" + priority +
'}';
}
}
Ivory.java:
package com.example.demo.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Ivory {
@Value("#{azure.name.substring(0, 3)}")
private String name;
@Value("#{T(java.lang.Integer).MAX_VALUE}")
private Integer priority;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
@Override
public String toString() {
return "Ivory{" +
"name='" + name + '\'' +
", priority=" + priority +
'}';
}
}
MyBean.java
@Component
public class MyBean {
@Autowired
private Azure azure;
@Autowired
private Emerald emerald;
@Autowired
private Ivory ivory;
public void init() {
System.out.println(azure);
System.out.println(emerald);
System.out.println(ivory);
}
}
MyBean是一个用于展示如何在Spring中通过SpEL表达式来注入属性的类,它聚合了三个对象Azure, Emerald和Ivory,并通过Spring的依赖注入机制将这三个对象注入到了MyBean类的实例中
主程序DemoApplication
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
MyBean myBean = applicationContext.getBean(MyBean.class);
myBean.init();
}
}
运行结果:
- 在XML中使用SpEL表达式实现属性注入
对于XML配置,Spring还支持在bean定义中使用SpEL。
首先,需要创建一个Spring XML配置文件,我们将其命名为app-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example" />
<bean id="azure" class="com.example.demo.bean.Azure">
<property name="name" value="#{
'spel-for-azure'}" />
<property name="priority" value="#{10}" />
</bean>
<bean id="emerald" class="com.example.demo.bean.Emerald">
<property name="name" value="#{
'copy of ' + azure.name}" />
<property name="priority" value="#{azure.priority + 1}" />
</bean>
<bean id="ivory" class="com.example.demo.bean.Ivory">
<property name="name" value="#{azure.name.substring(0, 3)}" />
<property name="priority" value="#{T(java.lang.Integer).MAX_VALUE}" />
</bean>
</beans>
注意:在XML中使用SpEL需要使用#{},而不是${}。
然后修改这3个Bean,如果是使用XML来配置Spring的Bean的话,那么在Java类中就不需要使用@Component注解了。因为XML配置文件已经明确地告诉Spring这些类是Spring Bean。
同样的,如果在XML文件中定义了Bean的属性值,那么在Java类中就不需要使用@Value注解来注入这些值了。因为XML配置文件已经明确地为这些属性赋了值。
Azure.java
package com.example.demo.bean;
public class Azure {
private String name;
private Integer priority;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
@Override
public String toString() {
return "Azure{" +
"name='" + name + '\'' +
", priority=" + priority +
'}';
}
}
Emerald.java
package com.example.demo.bean;
public class Emerald {
private String name;
private Integer priority;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
@Override
public String toString() {
return "Emerald{" +
"name='" + name + '\'' +
", priority=" + priority +
'}';
}
}
Ivory.java
package com.example.demo.bean;
public class Ivory {
private String name;
private Integer priority;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
@Override
public String toString() {
return "Ivory{" +
"name='" + name + '\'' +
", priority=" + priority +
'}';
}
}
然后需要在主程序中导入这个XML配置文件,这可以通过在主程序中添加@ImportResource注解实现:
package com.example.demo;
import com.example.demo.bean.MyBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
@ImportResource("classpath:app-config.xml")
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
MyBean myBean = applicationContext.getBean(MyBean.class);
myBean.init();
}
}
这样就可以在Spring的XML配置文件中使用SpEL了。
运行结果如下:
揭秘Spring依赖注入和SpEL表达式的更多相关文章
- 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析
目录 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGetBean AbstractAutowireC ...
- java后端开发三年!你还不了解Spring 依赖注入,凭什么给你涨薪
前言 前两天和一个同学吃饭的时候同学跟我说了一件事,说他公司有个做了两年的人向他提出要涨薪资,他就顺口问了一个问题关于spring依赖注入的,那个要求涨薪的同学居然被问懵了...事后回家想了想这一块确 ...
- Spring依赖注入(IOC)那些事
小菜使用Spring有几个月了,但是对于它的内部原理,却是一头雾水,这次借着工作中遇到的一个小问题,来总结一下Spring. Spring依赖注入的思想,就是把对象交由Spring容器管理,使用者只需 ...
- Spring依赖注入三种方式详解
在讲解Spring依赖注入之前的准备工作: 下载包含Spring的工具jar包的压缩包 解压缩下载下来的Spring压缩包文件 解压缩之后我们会看到libs文件夹下有许多jar包,而我们只需要其中的c ...
- Spring依赖注入:注解注入总结
更多11 spring 依赖注入 注解 java 注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.S ...
- Spring 依赖注入,在Main方法中取得Spring控制的实例
Spring依赖注入机制,在Main方法中通过读取配置文件,获取Spring注入的bean实例.这种应用在实训的时候,老师曾经说过这种方法,而且学Spring入门的时候都会先学会使用如何在普通的jav ...
- Spring依赖注入 --- 简单使用说明
Spring依赖注入 --- 简单使用说明 本文将对spring依赖注入的使用做简单的说明,enjoy your time! 1.使用Spring提供的依赖注入 对spring依赖注入的实现方法感兴趣 ...
- Spring依赖注入 --- 模拟实现
Spring依赖注入 --- 模拟实现 面向接口编程,又称面向抽象编程, 数据库如果发生更改,对应的数据访问层也应该改变多写几个实现,需要用谁的时候在service里new谁就可以了面向抽象编程的好处 ...
- Java Web系列:Spring依赖注入基础
一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是 ...
- Spring依赖注入的三种方式
看过几篇关于Spring依赖注入的文章,自己简单总结了一下,大概有三种方式: 1.自动装配 通过配置applicationContext.xml中的标签的default-autowire属性,或者标签 ...
随机推荐
- 可视化—AntV G6 紧凑树实现节点与边动态样式、超过X条展示更多等实用小功能
通过一段时间的使用和学习,对G6有了更一步的经验,这篇博文主要从以下几个小功能着手介绍,文章最后会给出完整的demo代码. 目录 1. 树图的基本布局和使用 2. 根据返回数据的属性不同,定制不一样的 ...
- Vue-router与hash与history区别
vue-router 基本使用 路由,其实就是指向的意思,当我点击页面上的home按钮时,页面中就要显示home的内容,如果点击页面上的about 按钮,页面中就要显示about 的内容.Home ...
- k8s集群进行删除并添加node节点
在已建立好的k8s集群中删除节点后,进行添加新的节点,可参考用于添加全新node节点,若新的node需要安装docker和k8s基础组件. 建立集群可以参考曾经的文章:CentOS8 搭建Kubern ...
- Java设计模式 —— 观察者模式
16 观察者模式 16.1 观察者模式概述 Observer Pattern: 定义对象之间的依赖关系(一对多),当一个对象的状态发生改变时,其关联的依赖对象均收到通知并自动更新. 观察者模式又称:发 ...
- Java设计模式 —— 装饰模式
12 装饰模式 12.1 装饰模式概述 Decorator Pattern: 动态地给一个对象增加一些额外的职责.提供一种比使用子类更加灵活的方案来扩展功能. 装饰模式是一种用于替代继承的技术,通过一 ...
- linux 给lvm磁盘扩容
目录 linux 给lvm磁盘扩容 扩容步骤 确认可用空间 创建新的物理卷 将物理卷添加到现有的卷组中 扩展逻辑卷 linux 给lvm磁盘扩容 早上到公司发现磁盘满了,挂载点是一个lvm 跟领导确认 ...
- 使用laravel开发微信公众的一个大坑,适合新手学习的laravel接入微信接口
最近使用laravel做微信公众号二次开发,发现网上能够参考的资料基本上很少,很多地方都讲的不够详细,致使许多新手采坑无数,所以这篇文章讲一下如何使用laravel接入微信接口,实现微信公众号二次开发 ...
- 用 Gaussian Process 建模 state-action 空间相关性,加速 Multi-Fidelity RL
目录 全文快读 1 intro 3 背景 4 method 4.1 model-based 算法:GP-VI-MFRL 4.2 model-free 算法:GPQ-MFRL 5 experiment ...
- Linux:管道命令与文本处理三剑客(grep、sed、awk)
1 管道命令(pipe)介绍 众所周知,bash命令执行的时候会输出信息,但有时这些信息必须要经过几次处理之后才能得到我们想要的格式,此时应该如何处置?这就牵涉到 管道命令(pipe) 了.管道命令使 ...
- 手写 HashSet的底层 和 迭代器
1 package Test.CollectionIterator; 2 import java.util.Iterator; 3 public class MyHashSet2<E> i ...