当前位置:首页 > 科技  > 软件

两万字 + 十张图剖析Spring依赖注入和SpEL表达式

来源: 责编: 时间:2023-10-10 18:31:09 177观看
导读一、setter属性注入1、使用XML进行setter方法注入我们在前面的文章中已经使用过XML进行setter方法的属性注入了,下面让我们再来回顾一下:<bean id="userSetter" class="com.example.demo.bean.User"> <property name

fYt28资讯网——每日最新资讯28at.com

一、setter属性注入

1、使用XML进行setter方法注入

我们在前面的文章中已经使用过XML进行setter方法的属性注入了,下面让我们再来回顾一下:fYt28资讯网——每日最新资讯28at.com

<bean id="userSetter" class="com.example.demo.bean.User">    <property name="username" value="example-username-setter"/>    <property name="age" value="25"/></bean>

2、使用@Bean注解进行setter方法注入

我们在前面的文章中也学习过如何在bean创建时通过编程方式设置属性:fYt28资讯网——每日最新资讯28at.com

@Beanpublic User user() {    User user = new User();    user.setUsername("example-username-anno-setter");    user.setAge(25);    return user;}

3、setter方法注入完整代码示例

使用XML进行setter方法注入。fYt28资讯网——每日最新资讯28at.com

首先,我们需要创建一个User类,并在其中包含username和age两个属性,以及相应的getter、setter方法和构造器。fYt28资讯网——每日最新资讯28at.com

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。fYt28资讯网——每日最新资讯28at.com

<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:fYt28资讯网——每日最新资讯28at.com

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);    }}

运行结果如下:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

使用@Bean注解进行setter方法注入。fYt28资讯网——每日最新资讯28at.com

我们需要创建一个配置类,例如叫AppConfig.java:fYt28资讯网——每日最新资讯28at.com

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic 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的id,方法的返回值就是Bean的类型。fYt28资讯网——每日最新资讯28at.com

然后修改主程序,这里使用AnnotationConfigApplicationContext来创建Spring的应用上下文,并加载配置类。Spring会自动从配置类中获取所有的Bean定义,并创建相应的Bean实例。fYt28资讯网——每日最新资讯28at.com

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);    }}

运行结果如下:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

注意:XML配置方式已经相对陈旧,而且在Spring Boot项目中,主流的做法是使用注解和Java配置方式。对于setter注入,有时会引发循环依赖的问题。在Spring中,可以使用构造器注入来避免这种情况,这里了解即可。fYt28资讯网——每日最新资讯28at.com

二、构造器注入

setter注入是一种在对象被实例化之后(通过调用无参构造器创建实例)再通过setter方法注入依赖的方式。构造器注入则是在创建对象实例的时候就通过构造器参数来注入依赖。fYt28资讯网——每日最新资讯28at.com

为了演示构造器注入,我们需要给User添加一个全参数构造器:fYt28资讯网——每日最新资讯28at.com

public User(String username, Integer age) {    this.username = username;    this.age = age;}

添加这个构造器后,Java不再提供默认的无参构造器,这会导致我们之前的标签创建时失败,因为它找不到默认的构造器。fYt28资讯网——每日最新资讯28at.com

1、使用XML进行构造器注入

我们可以在标签内部声明一个子标签:constructor-arg。它用于指定构造器的参数,来进行属性注入。constructor-arg标签的编写规则如下:fYt28资讯网——每日最新资讯28at.com

<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。fYt28资讯网——每日最新资讯28at.com

使用index属性来指定构造函数参数的位置在大多数情况下是可以的,但是如果构造函数的参数数量或者顺序发生了改变,就可能会出错。另外一种更为可靠的方式是使用name属性来指定参数的名称,如:fYt28资讯网——每日最新资讯28at.com

<bean id="userConstructor" class="com.example.demo.bean.User">    <constructor-arg name="username" value="example-username-constructor"/>    <constructor-arg name="age" value="25"/></bean>

这样无论参数的顺序如何,只要参数名称不变,就不会出错。fYt28资讯网——每日最新资讯28at.com

2、使用@Bean注解进行构造器属性注入

在注解驱动的bean注册中,我们也可以直接使用编程方式赋值:fYt28资讯网——每日最新资讯28at.com

@Beanpublic User user() {    return new User("example-username-anno-constructor", 25);}

3、构造器注入的完整代码示例

使用XML进行构造器注入。fYt28资讯网——每日最新资讯28at.com

首先,我们需要创建一个User类,并在其中包含username和age两个属性,以及相应的getter、setter方法和构造器。fYt28资讯网——每日最新资讯28at.com

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注入方便大家对比。fYt28资讯网——每日最新资讯28at.com

<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:fYt28资讯网——每日最新资讯28at.com

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);    }}

然后,我们需要创建一个DemoApplication类,使用ApplicationContext来加载配置文件并获取Bean:fYt28资讯网——每日最新资讯28at.com

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);    }}

运行结果如下:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

使用@Bean注解进行构造器属性注入。fYt28资讯网——每日最新资讯28at.com

我们需要创建一个配置类,例如叫AppConfig.java:fYt28资讯网——每日最新资讯28at.com

import com.example.demo.bean.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic 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:fYt28资讯网——每日最新资讯28at.com

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);    }}

运行结果:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

注意:如果在类中同时使用构造器注入和setter注入,需要注意它们注入的顺序:先进行构造器注入,然后是setter注入。fYt28资讯网——每日最新资讯28at.com

三、注解式属性注入

上面我们已经说过注解式的setter和构造器注入。我们又是如何处理那些通过@Component扫描而注册的bean的属性的呢?我们来仔细说说这个问题,同时展示如何在xml中进行相同的操作。fYt28资讯网——每日最新资讯28at.com

1、@Value注解式属性注入的应用

首先,让我们从最简单的属性注入方法:@Value开始。创建一个新的White类,并声明一些字段,但是这次我们不会设置setter方法:fYt28资讯网——每日最新资讯28at.com

@Componentpublic 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注解:fYt28资讯网——每日最新资讯28at.com

@Value("white-value-annotation")private String title;@Value("1")private Integer rank;

要注意的是,如果使用 @Value 注解来注入一个不存在的属性,那么应用程序会在启动时抛出异常。fYt28资讯网——每日最新资讯28at.com

然后,我们将通过组件扫描方式将这个White类扫描到IOC容器中,并将其取出并打印:fYt28资讯网——每日最新资讯28at.com

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的字段已经成功注入:fYt28资讯网——每日最新资讯28at.com

Injected value : White{title='white-value-annotation', rank=1}

2、引入外部配置文件@PropertySource

如果我们需要在Spring中使用properties文件,我们应该怎么办呢?Spring考虑到了这一点,并扩展了一个用于导入外部配置文件的注解:@PropertySource。fYt28资讯网——每日最新资讯28at.com

创建Bean和配置文件

创建一个新的Blue类,其结构与White类完全相同。然后在项目的resources目录下创建一个新的blue.properties文件,用于存储Blue类的属性配置:fYt28资讯网——每日最新资讯28at.com

blue.title=blue-value-propertiesblue.rank=2

引入配置文件

使用@PropertySource注解将properties文件导入到配置类:fYt28资讯网——每日最新资讯28at.com

@Configuration@ComponentScan("com.example")@PropertySource("classpath:blue.properties")public class InjectValueConfiguration {}

这个blue.properties文件是一个键值对的列表,Spring 将这些键值对加载到 Environment 中,我们可以通过 @Value 注解或者 Environment 类的方法来获取这些属性值。fYt28资讯网——每日最新资讯28at.com

@Value 注解和 Environment 类都可以用于读取 Spring 上下文中的属性值。这些属性值可能来自于多个不同的源,包括但不限于:fYt28资讯网——每日最新资讯28at.com

  • Spring Boot 的默认配置文件(application.properties 或 application.yml)。
  • 通过 @PropertySource 注解加载的属性文件。
  • 系统环境变量。
  • Java 系统属性(可以通过 -D 命令行参数设置)。

如果你想通过 @Value 注解来获取属性值,如下:fYt28资讯网——每日最新资讯28at.com

@Componentpublic class BlueConfig {    @Value("${blue.title}")    private String title;    @Value("${blue.rank}")    private int rank;    // getters and setters...}

在 Spring 应用中使用 @PropertySource 注解来加载一个 .properties 文件时,这个文件中的所有配置项都会被读取,并存储在一个内部的 Map 结构中。这个 Map 的键是配置项的名称,值是配置项的值。Spring 中的一些内置配置项也会被添加到这个 Map 中。fYt28资讯网——每日最新资讯28at.com

当我们使用 {blue.title} 占位符来引用这个配置项的值。fYt28资讯网——每日最新资讯28at.com

如果想通过 Environment 类的方法来获取属性值,可以像下面这样做:fYt28资讯网——每日最新资讯28at.com

@Componentpublic 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),则需要将获取的属性值转换为适当的类型。fYt28资讯网——每日最新资讯28at.com

注意:@PropertySource 无法加载 YAML 格式的文件,只能加载 properties 格式的文件。如果需要加载 YAML 格式的文件,而且使用的是 Spring Boot框架,那么可以使用@ConfigurationProperties或@Value注解。例如以下的YAML文件:fYt28资讯网——每日最新资讯28at.com

application.yml:fYt28资讯网——每日最新资讯28at.com

appTest:  name: MyApp  version: 1.0.0

可以使用@ConfigurationProperties来加载这些属性:fYt28资讯网——每日最新资讯28at.com

@Configuration@ConfigurationProperties(prefix = "appTest")public class AppConfig {    private String name;    private String version;    // getters and setters...}

@ConfigurationProperties注解主要用于指定配置属性的前缀,@ConfigurationProperties注解本身并不直接指定配置文件的位置, 而是由Spring Boot的自动配置机制处理的。fYt28资讯网——每日最新资讯28at.com

这样,name字段就会被自动绑定到appTest.name配置属性,version字段就会被自动绑定到appTest.version配置属性。fYt28资讯网——每日最新资讯28at.com

默认情况下,Spring Boot会在启动时自动加载src/main/resources目录下的application.properties或application.yml文件。我们可以通过设置spring.config.name和spring.config.location属性来改变默认的配置文件名或位置。fYt28资讯网——每日最新资讯28at.com

注意:@ConfigurationProperties注解需要配合@EnableConfigurationProperties注解或@Configuration注解使用,以确保Spring能够发现并处理这些注解。fYt28资讯网——每日最新资讯28at.com

或者,你也可以使用@Value注解来加载这些属性:fYt28资讯网——每日最新资讯28at.com

@Componentpublic class AppConfig {    @Value("${appTest.name}")    private String name;    @Value("${appTest.version}")    private String version;    // getters and setters...}

Blue类的属性注入

对于properties类型的属性,我们这里选择@Value注解和占位符来注入属性:fYt28资讯网——每日最新资讯28at.com

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

如果你熟悉jsp的el表达式,会发现这和它非常相似!fYt28资讯网——每日最新资讯28at.com

测试启动类

修改启动类,将配置类引入,然后取出并打印Blue:fYt28资讯网——每日最新资讯28at.com

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方法会看到控制台已经成功打印出了配置文件的属性:fYt28资讯网——每日最新资讯28at.com

Properties value : Blue{title='blue-value-properties', rank=2}

3、在XML中引入外部配置文件

在xml中,我们可以和@Value相同的方式使用占位符:fYt28资讯网——每日最新资讯28at.com

<?xml versinotallow="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>

4、注解式属性注入完整代码示例

@Value注解式属性注入的应用。fYt28资讯网——每日最新资讯28at.com

创建White类:fYt28资讯网——每日最新资讯28at.com

package com.example.demo.bean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic 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:fYt28资讯网——每日最新资讯28at.com

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);    }}

运行结果如下:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

引入外部配置文件@PropertySource。fYt28资讯网——每日最新资讯28at.com

创建Blue类和配置文件,没有setter和getter方法:fYt28资讯网——每日最新资讯28at.com

package com.example.demo.bean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic 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文件:fYt28资讯网——每日最新资讯28at.com

blue.title=blue-value-propertiesblue.rank=2

创建配置类InjectValueConfiguration:fYt28资讯网——每日最新资讯28at.com

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 {}

修改启动类,引入配置类:fYt28资讯网——每日最新资讯28at.com

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);    }}

运行结果如下:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

在xml中引入外部配置文件。fYt28资讯网——每日最新资讯28at.com

在使用XML配置的情况下,我们需要创建一个XML文件来替代InjectValueConfiguration类,我们可以先注释掉InjectValueConfiguration类的所有内容。fYt28资讯网——每日最新资讯28at.com

下面是相应的XML文件内容:fYt28资讯网——每日最新资讯28at.com

<?xml versinotallow="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中使用外部配置文件。fYt28资讯网——每日最新资讯28at.com

这里还需要修改下Blue类,因为通过XML方法注入属性需要提供相应的setter方法,修改后的Blue类如下:fYt28资讯网——每日最新资讯28at.com

package com.example.demo.bean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic 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:fYt28资讯网——每日最新资讯28at.com

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);    }}

运行结果如下:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

四、SpEL表达式

当我们谈到属性注入的时候,我们可能会遇到一些复杂的需求,例如我们需要引用另一个Bean的属性,或者我们需要动态处理某个属性值。这种需求无法通过使用${}的占位符方式实现,我们需要一个更强大的工具:SpEL表达式。fYt28资讯网——每日最新资讯28at.com

Spring Expression Language(SpEL)是从Spring框架 3.0开始支持的强大工具。SpEL不仅是Spring框架的重要组成部分,也可以独立使用。它的功能丰富,包括调用属性值、属性参数、方法调用、数组存储以及逻辑计算等。它与开源项目OGNL(Object-Graph Navigation Language)相似,但SpEL是Spring框架推出的,并默认内嵌在Spring框架中。fYt28资讯网——每日最新资讯28at.com

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

SpEL的表达式用#{}表示,花括号中就是我们要编写的表达式。fYt28资讯网——每日最新资讯28at.com

我们创建一个Bean,命名为Azure,同样地,我们声明属性name和priority,并提供getter和setter方法以及toString()方法。然后我们使用@Component注解标注它。fYt28资讯网——每日最新资讯28at.com

使用@Value配合SpEL完成属性注入,如下:fYt28资讯网——每日最新资讯28at.com

@Componentpublic class Azure {        @Value("#{'spel-for-azure'}")    private String name;        @Value("#{10}")    private Integer priority;}

我们修改启动类,从IOC容器中获取Azure并打印,可以看到属性被成功注入:fYt28资讯网——每日最新资讯28at.com

Azure{name='spel-for-azure', priority=10}

SpEL的功能远不止这些,它还可以获取IOC容器中其他Bean的属性,让我们来展示一下。fYt28资讯网——每日最新资讯28at.com

我们已经注册了Azure Bean,现在我们再创建一个Bean,命名为Emerald。我们按照上述方法对字段和方法进行声明,然后使用@Component注解标注。fYt28资讯网——每日最新资讯28at.com

我们希望name属性直接复制Azure的name属性,而priority属性则希望比Azure的priority属性大1,我们可以这样编写:fYt28资讯网——每日最新资讯28at.com

@Componentpublic 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属性。fYt28资讯网——每日最新资讯28at.com

如果你在一个不涉及Spring的环境中使用SpEL,这个特性是不会生效的。这是因为这个特性依赖于Spring的IoC容器。fYt28资讯网——每日最新资讯28at.com

我们修改启动类,测试运行,可以看到Azure的属性已经成功被复制:fYt28资讯网——每日最新资讯28at.com

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

SpEL表达式不仅可以引用对象的属性,还可以直接引用类的常量,以及调用对象的方法。下面我们通过示例进行演示。fYt28资讯网——每日最新资讯28at.com

我们新建一个Bean,命名为Ivory。我们按照上述方法初始化属性、toString()方法、注解。fYt28资讯网——每日最新资讯28at.com

假设我们有一个需求,让name取azure属性的前3个字符,priority取Integer的最大值。那么我们可以使用SpEL这样写:fYt28资讯网——每日最新资讯28at.com

@Componentpublic class Ivory {        @Value("#{azure.name.substring(0, 3)}")    private String name;        @Value("#{T(java.lang.Integer).MAX_VALUE}")    private Integer priority;}

注意,直接引用类的属性,需要在类的全限定名外面使用T()包围。fYt28资讯网——每日最新资讯28at.com

我们修改启动类,测试运行,可以看到Ivory的属性已经是处理之后的值:fYt28资讯网——每日最新资讯28at.com

use spel methods : Ivory{name='spe', priority=2147483647}

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表达式不需要花费大量的精力,掌握基础的使用方法即可。fYt28资讯网——每日最新资讯28at.com

3、SpEL表达式属性注入完整代码示例

使用@Value注解和SpEL表达式实现属性注入。fYt28资讯网——每日最新资讯28at.com

创建三个SpEL表达式属性注入的Bean:Azure.java、Emerald.java和Ivory.java。fYt28资讯网——每日最新资讯28at.com

Azure.java:fYt28资讯网——每日最新资讯28at.com

package com.example.demo.bean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic 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:fYt28资讯网——每日最新资讯28at.com

package com.example.demo.bean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic 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:fYt28资讯网——每日最新资讯28at.com

package com.example.demo.bean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic 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:fYt28资讯网——每日最新资讯28at.com

@Componentpublic 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类的实例中。fYt28资讯网——每日最新资讯28at.com

主程序DemoApplication:fYt28资讯网——每日最新资讯28at.com

@SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {        ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);        MyBean myBean = applicationContext.getBean(MyBean.class);        myBean.init();    }}

运行结果:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

在XML中使用SpEL表达式实现属性注入。fYt28资讯网——每日最新资讯28at.com

对于XML配置,Spring还支持在bean定义中使用SpEL。fYt28资讯网——每日最新资讯28at.com

首先,需要创建一个Spring XML配置文件,我们将其命名为app-config.xml:fYt28资讯网——每日最新资讯28at.com

<?xml versinotallow="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需要使用#{},而不是${}。fYt28资讯网——每日最新资讯28at.com

然后修改这3个Bean,如果是使用XML来配置Spring的Bean的话,那么在Java类中就不需要使用@Component注解了。因为XML配置文件已经明确地告诉Spring这些类是Spring Bean。fYt28资讯网——每日最新资讯28at.com

同样的,如果在XML文件中定义了Bean的属性值,那么在Java类中就不需要使用@Value注解来注入这些值了。因为XML配置文件已经明确地为这些属性赋了值。fYt28资讯网——每日最新资讯28at.com

Azure.java:fYt28资讯网——每日最新资讯28at.com

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:fYt28资讯网——每日最新资讯28at.com

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:fYt28资讯网——每日最新资讯28at.com

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注解实现:fYt28资讯网——每日最新资讯28at.com

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了。fYt28资讯网——每日最新资讯28at.com

运行结果如下:fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

fYt28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-12709-0.html两万字 + 十张图剖析Spring依赖注入和SpEL表达式

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Java String类为什么用final修饰

下一篇: 我们一起聊聊C#堆排序算法

标签:
  • 热门焦点
Top