Spring高手之路3——揭秘Spring依赖注入和SpEL表达式

news2024/11/25 14:48:45

本篇会给大家举出各种Spring属性依赖注入的例子,方便大家理解。

文章目录

  • 1. setter属性注入
    • 1.1 使用XML进行setter方法注入
    • 1.2 使用@Bean注解进行setter方法注入
    • 1.3 setter方法注入完整代码示例
  • 2. 构造器注入
    • 2.1 使用XML进行构造器注入
    • 2.2 使用@Bean注解进行构造器属性注入
    • 2.3 构造器注入的完整代码示例
  • 3. 注解式属性注入
    • 3.1 @Value注解式属性注入的应用
    • 3.2 引入外部配置文件@PropertySource
    • 3.3 在XML中引入外部配置文件
    • 3.4 注解式属性注入完整代码示例
  • 4. SpEL表达式
    • 4.1 使用@Value注解和SpEL表达式实现属性注入
    • 4.2 在XML中使用SpEL表达式实现属性注入:
    • 4.3 SpEL表达式属性注入完整代码示例

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类,并在其中包含usernameage两个属性,以及相应的gettersetter方法和构造器。

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>元素。这个方法的名称就是Beanid,方法的返回值就是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类,并在其中包含usernameage两个属性,以及相应的gettersetter方法和构造器。

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

  1. 创建Bean和配置文件

创建一个新的Blue类,其结构与White类完全相同。然后在项目的resources目录下创建一个新的blue.properties文件,用于存储Blue类的属性配置:

blue.title=blue-value-properties
blue.rank=2
  1. 引入配置文件

使用@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.propertiesapplication.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.propertiesapplication.yml文件。我们可以通过设置spring.config.namespring.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...
}
  1. Blue类的属性注入

对于properties类型的属性,我们这里选择@Value注解和占位符来注入属性:

@Value("${blue.title}")
private String title;

@Value("${blue.rank}")
private Integer rank;

如果你熟悉jspel表达式,会发现这和它非常相似!

  1. 测试启动类

修改启动类,将配置类引入,然后取出并打印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类和配置文件,没有settergetter方法:

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 LanguageSpEL)是从Spring框架 3.0开始支持的强大工具。SpEL不仅是Spring框架的重要组成部分,也可以独立使用。它的功能丰富,包括调用属性值、属性参数、方法调用、数组存储以及逻辑计算等。它与开源项目OGNLObject-Graph Navigation Language)相似,但SpELSpring框架推出的,并默认内嵌在Spring框架中。

4.1 使用@Value注解和SpEL表达式实现属性注入

  SpEL的表达式用#{}表示,花括号中就是我们要编写的表达式。

  我们创建一个Bean,命名为Azure,同样地,我们声明属性namepriority,并提供gettersetter方法以及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属性直接复制Azurename属性,而priority属性则希望比Azurepriority属性大1,我们可以这样编写:

@Component
public class Emerald {
    
    @Value("#{'copy of ' + azure.name}")
    private String name;
    
    @Value("#{azure.priority + 1}")
    private Integer priority;
}

  在SpringSpEL中可以通过bean的名称访问到对应的bean,并通过.操作符访问bean的属性。在这个例子中,azure就是一个bean的名称,它对应的bean就是Azure类的实例。所以,azure.name就是访问Azure类实例的name属性。

如果你在一个不涉及Spring的环境中使用SpEL,这个特性是不会生效的。这是因为这个特性依赖于SpringIoC容器。

我们修改启动类,测试运行,可以看到Azure的属性已经成功被复制:

use spel bean property : Emerald{name='copy of spel-for-azure', priority=11}

  SpEL表达式不仅可以引用对象的属性,还可以直接引用类的常量,以及调用对象的方法。下面我们通过示例进行演示。

  我们新建一个Bean,命名为Ivory。我们按照上述方法初始化属性、toString()方法、注解。

  假设我们有一个需求,让nameazure属性的前3个字符,priorityInteger的最大值。那么我们可以使用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表达式属性注入的BeanAzure.javaEmerald.javaIvory.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, EmeraldIvory,并通过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需要使用#{},而不是${}

  然后修改这3Bean,如果是使用XML来配置SpringBean的话,那么在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();
    }

}

这样就可以在SpringXML配置文件中使用SpEL了。

运行结果如下:

在这里插入图片描述



欢迎一键三连~

有问题请留言,大家一起探讨学习

----------------------Talk is cheap, show me the code-----------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/616441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面试经历:我为什么选择的测试的?

目录 前言&#xff1a; 判定缺陷间的重复及依赖关系需要开发能力 使用自动化测试工具需要开发能力 黑盒测试偏爱开发能力 说明 白盒测试需要开发能力 安全测试需要开发能力 开发测试工具 前言&#xff1a; 不知不觉已经从事软件测试六年了&#xff0c;从毕业到进入外包公司外…

Android系统的Ashmem匿名共享内存系统分析(5)- 实现共享的原理

声明 其实对于Android系统的Ashmem匿名共享内存系统早就有分析的想法&#xff0c;记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的&#xff0c;但因为我个人问题没能实施这个计划&#xff0c;留下些许遗憾…文中参考了很多书籍及博客内容&#xff0c;可能涉及的比较…

SSM 框架

ssm框架是spring MVC &#xff0c;spring和mybatis框架的整合&#xff0c;是标准的MVC模式&#xff0c;将整个系统划分为表现层&#xff0c;controller层&#xff0c;service层&#xff0c;DAO层四层。ssm框架是目前比较主流的Java EE企业级框架&#xff0c;适用于搭建各种大型…

张小飞的Java之路——第四十六章——网络编程基础

写在前面&#xff1a; 视频是什么东西&#xff0c;有看文档精彩吗&#xff1f; 视频是什么东西&#xff0c;有看文档速度快吗&#xff1f; 视频是什么东西&#xff0c;有看文档效率高吗&#xff1f; 诸小亮&#xff1a;关于网络你了解多少&#xff1f; 张小飞&#xff1a…

五月份跳槽了,历经阿里测开岗4轮面试,不出意外,还是被刷了....

大多数情况下&#xff0c;测试员的个人技能成长速度&#xff0c;远远大于公司规模或业务的成长速度。所以&#xff0c;跳槽成为了这个行业里最常见的一个词汇。 前几天&#xff0c;我看到有朋友留言说&#xff0c;他在面试阿里的测试开发工程师的时候&#xff0c;灵魂拷问三小…

Java开发工程师是做什么的?高考结束最重要的专业选择!

各位同学大家好&#xff0c;我是小源&#xff0c;明天就是高考了&#xff0c;对于正常的一个考生来说&#xff0c;专本线的同学已经开始陆陆续续准备看专业。今天&#xff0c;好程序员分享一个专业&#xff0c;他的名字叫做Java开发工程师&#xff0c;不知道同学有没有听说过这…

【Linux学习】多线程——信号量 | 基于环形队列的生产者消费者模型 | 自旋锁 | 读写锁

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 一、 信号量1.1 概念1.2 信号量的基本操作1.3 信号量的基本使用接口 二、基于环形队列的生产…

第8章:SpringMVC的文件上传和下载

一、文件上传和下载 1.文件下载 使用ResponseEntity用于控制器方法的返回值类型&#xff0c;该控制器方法的返回值就是响应到浏览器的响应报文。使用ResponseEntity实现下载文件的功能。 ①创建file.html ② 在FileController.java类里面 文件不管是上传还是下载&#xff0…

思科无线AC旁挂并由第三方网关下发业务上网VLAN的案例

在企业网络环境中&#xff0c;思科无线AC&#xff08;Access Controller&#xff09;常用于无线网络的管理和控制。通常情况下&#xff0c;AC会负责分配无线设备的IP地址和VLAN标识&#xff0c;但在某些特定场景下&#xff0c;我们可能需要通过第三方网关来下发业务上网所需的V…

高频Postman接口测试面试题,我面试没通过的原因找到了

目录 一、Postman在工作中使用流程是什么样的&#xff1f; 二、你使用过Postman的哪些功能&#xff1f; 三、Postman如何管理测试环境&#xff1f; 四、Postman如何实现接口关联&#xff1f; 五、Postman参数化有哪几种方式&#xff1f; 六、Postman中全局/环境/集合变量…

Python和PHP相比有什么优势?我来聊聊深入学习的七个关键点

今天要跟大家谈一下一个高中生被Python培训机构坑的真实案例&#xff0c;披着大数据的壳子。这个高中生进去的时候&#xff0c;他们承诺不管什么学历包分配&#xff0c;毕业后直接上岗。这种承诺肯定是不靠谱的&#xff0c;因为基本上只要说出这句话的包分配&#xff0c;都是很…

如何从零开始构建 API ?

假设你请承包商从零开始建造一座房子&#xff0c;你肯定期望他们交付最高质量的房子。他们必须通过检查、遵守安全规范并遵循项目中约定的要求。因为建房子可容不得走捷径。如果承包商经常走捷径&#xff0c;他们的声誉会受到影响&#xff0c;从而失去客户。其实&#xff0c;开…

jmeter-分布式部署之负载机的设置

目录 引言 一、windows下负载机的配置&#xff08;执行机&#xff09; 二、linux下负载机的配置 三、错误总结 写在最后 引言 今天想和大家聊一下关于jmeter分布式部署中负载机的设置问题。作为一个自动化测试工具&#xff0c;jmeter在性能测试方面有着很强的优势&#x…

NLP(3) Text Classification

文章目录 OverviewText classification 的主要任务Topic ClassificationSentiment AnalysisNative Language IdentificationNatural Language Inference 如何构造 Text ClassifierClassification AlgorithmsBias - Variance Balance朴素贝叶斯Logistic RegressionSupport Vecto…

chatgpt赋能python:Python如何倒序输出:一步步教你实现

Python如何倒序输出&#xff1a;一步步教你实现 Python是一种通用编程语言&#xff0c;具有快速开发、易学易用等诸多优点&#xff0c;在大数据、人工智能、科学计算等领域得到广泛应用。其中&#xff0c;倒序输出是Python编程中非常常见的操作。那么&#xff0c;如何在Python…

STM32F407 移植 FreeRTOS

0. 实验准备 本实验是基于正点原子 STM32F407ZG 探索者开发板完成的&#xff0c;所以需要一个STM32F407ZG 探索者开发板 用于移植的基础工程&#xff08;下面会讲&#xff09; FreeRTOS源码&#xff08;下面会讲&#xff09; 1. FreeRTOS移植 1.1 移植前准备 1.1.1 基础工程…

SpringCloud Gateway网关集成与配置

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

桶装水站点APP小程序管理系统 方便快捷送水上门

夏天到了&#xff0c;又到了疯狂饮水的季节了&#xff0c;桶装饮用水是日常生活办公环境中必不可少的产品&#xff0c;这种必需品消耗快隔三差五就要购买一次。一般人都是通过电话预定的方式来购买桶装水&#xff0c;商家必须保证随时随地有人接听电话才能避免遗漏客户&#xf…

系列六、MongoDB文档相关操作

一、插入文档 1.1、单条插入 # 语法 db.集合名称.insert({json数据})# 案例 db.user.insert({"name":"张三","age":23,"birthday":"1997-07-07" }) 1.2、多条插入 # insertMany语法&#xff1a; db.collection.insertMan…

Docker 的数据管理和Dockerfile

-------------------------------------------Docker 的数据管理-------------------------------------------- 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷&#xff08;Data Volumes&#xff09;和数据卷容器&#xff08;DataVolumes Containers&#xff09;。 …