Spring Boot全注解下的IOC

目录

1.IOC容器简介

2.装配Bean

2.1 通过扫描装配Bean

2.2 自定义第三方Bean

3.依赖注入

4.生命周期

5.使用属性文件

6.条件装配Bean

7.Bean的作用域

8.@Profile

9.引入XML配置的Bean

10.Spring EL表达式


IOC(控制反转)是Spring的核心,可以说Spring是一种基于IOC容器编程的框架。因为Spring Boot是基于注解的开发Spring IOC,所以我们使用全注解的方式讲述Spring IOC技术。

1.IOC容器简介

SpringIOC容器是一个管理Bean的容器,在Spring的定义中,它要求所有的IOC容器都需要实现接口BeanFactory,它是一个顶级的容器接口。BeanFactory的实现代码如下;

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;

public interface BeanFactory {
    //前缀
    String FACTORY_BEAN_PREFIX = "&";
    //多个getBean方法
    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
    //是否包含Bean
    boolean containsBean(String var1);
    //是否是单例
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
    //是否原型
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
    //是否类型匹配
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
    //获取Bean类型
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
    //获取Bean别名
    String[] getAliases(String var1);
}

Spring IOC容器中,默认的情况下,Bean都是以单例存在的,也就是说getBean方法返回的都是同一个对象。isSingleton方法则判断Bean是否在Spring IOC中为单例。与isSingleton相反的是isPrototype方法,如果它返回的是true,那么我们使用getBean方法获取Bean的时候,Spring IOC容器就会创建一个新的Bean返回给调用者。

由于BeanFactory的功能还不够强大,因此Spring在BeanFactory的基础上,设计了更为高级的接口ApplicationContext。它是BeanFactory的子接口之一,在Spring的体系中BeanFactory和ApplicationContext是最重要的两个接口。在Spring中,我们使用的大部分的Spring IOC容器都是ApplicationContext接口的实现类,它们的关系如下所示:

ApplicationContext接口通过继承上级接口,进而继承BeanFactory接口,但是在BeanFactory的基础上,扩展了消息国际化接口、环境可配置接口、应用事件发布接口和资源模式解析接口,所以它的功能会更为强大。

为了贴近Spring Boot的需要,这里主要介绍一个基于注解的IOC容器:AnnotationConfigApplicationContext,它是一个基于注解的IOC容器。Spring Boot装配和获取Bean的方法和该容器如出一辙。首先看如下简单的实例:

定义一个pojo:

package com.martin.config.chapter3.pojo;

import lombok.Data;

/**
 * @author: martin
 * @date: 2019/10/27 17:33
 * @description:
 */
@Data
public class User {
    private Long id;
    private String userName;
    private String note;
}

定义Java配置文件:

package com.martin.config.chapter3.config;

import com.martin.config.chapter3.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: martin
 * @date: 2019/10/27 17:39
 * @description:
 */
@Configuration
public class AppConfig {
    @Bean("user")
    public User initUser() {
        User user = new User();
        user.setId(1L);
        user.setUserName("martin");
        user.setNote("note_1");
        return user;
    }
}

这里@Configuration代表这是一个Java配置文件,Spring的容器会根据它来生成IOC容器去装配Bean;@Bean代表将initUser方法返回的POJO装配到IOC容器中,而其属性name定义了这个Bean的名称,如果没有配置它,则将方法名称“initUser”作为Bean的名称保存到Spring IOC容器中。最后,我们使用AnnotationConfigApplicationContext来构建自己的IOC容器,代码清单如下:

package com.martin.config.chapter3;

import com.martin.config.chapter3.config.AppConfig;
import com.martin.config.chapter3.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author: martin
 * @date: 2019/10/27 18:57
 * @description:
 */
public class IocTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = ctx.getBean(User.class);
        System.out.println(user.getId());
    }
}

2.装配Bean

2.1 通过扫描装配Bean

除了使用注解@Bean注入Spring IOC容器,我们还可以使用扫描装配Bean到IOC容器中,对于扫描装配而言使用的注解是@Component和@ComponentScan。@Component是标明哪个类被扫描进入Spring IOC容器,而@ComponentScan则是标明采用何种策略去扫描装配Bean。User使用@Component的实现代码如下:

package com.martin.config.chapter3.pojo;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author: martin
 * @date: 2019/10/27 17:33
 * @description:
 */
@Data
@Component("user")
public class User {
    @Value("1")
    private Long id;
    @Value("martin")
    private String userName;
    @Value("note")
    private String note;
}

这里的注解@Component表明这个类将被Spring IOC容器扫描装配,其中配置的“user”则是作为Bean的名称。如果不配置这个字符串,IOC容器会把类名的第一个字母作为小写,其他不变作为Bean名称放入到IOC容器中;注解@Value则指定具体的值,使得Spring IOC给与对应的属性注入对应的值。

为了让IOC容器能装配User这个类,需要改造AppConfig,代码如下:

package com.martin.config.chapter3.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author: martin
 * @date: 2019/10/27 17:39
 * @description:
 */
@Configuration
@ComponentScan
public class AppConfig {
}

这里加入了@ComponentScan,意味着它会进行自动扫描,但是它只会扫描类AppConfig所在的当前包和其子包,User类不属于AppConfig的包或子包中的类,因此无法扫描到。如果想被扫描到,可以自定义扫描的包,可以通过basePackages定义扫描的包名,还可以通过basePackageClasses定义扫描的类。代码如下:

@ComponentScan("com.martin.config.chapter3.pojo.*")
@ComponentScan(basePackages = "com.martin.config.chapter3.pojo.*")
@ComponentScan(basePackageClasses = User.class)

按照以上的配置策略,User类会被扫描到Spring IOC容器中。

2.2 自定义第三方Bean

现实的Java的应用往往需要引入许多来自第三方的包,并且希望把第三方包的类对象也放入到Spring IOC容器中,这时@Bean注解就可以发挥作用了。例如我们定义DHCP数据源,POM文件如下:

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

这样DHCP和数据库驱动就被加入到了项目中,接着将使用它提供的机制来生成数据源。代码如下:

package com.martin.config.chapter3.config;

import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * @author: martin
 * @date: 2019/10/27 17:39
 * @description:
 */
@Configuration
public class AppConfig {
    @Bean("dataSource")
    public DataSource getDataSource() {
        Properties properties = new Properties();
        properties.setProperty("driver", "com.mysql.jdbc.Driver");
        properties.setProperty("url", "jdbc:mysql://localhost:3306/chapter3");
        properties.setProperty("username", "root");
        properties.setProperty("password", "123456");
        try {
            return BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

这里通过@Bean定义了其配置项name为dataSource,IOC容器就会把它作为对象名保存起来。

3.依赖注入

不同Bean之间的依赖被称为依赖注入,依赖注入主要使用@Autowired注解。例如我们注入一个user属性:

  @Autowired
  private User user;

@Autowired注解也可以用到方法上:

@Autowired
public void setUser(User user) {
    this.user = user;
}

@Autowired是一个默认必须找到对应Bean的注解,如果不能确定其标注属性一定会存在并且允许这个被标注的属性为null,那么可以配置@Autowired属性required为false。

 @Autowired(required = false)
 private User user;

如果User的实现类有多个,比如管理员admin,普通用户general ,此时单纯使用@Autowired注解,IOC容器时无法区分采用哪个Bean实例注入。我们可以使用@Quelifier注解,与@Autowired组合在一起,通过类型和名称一起找到Bean。

@Autowired
@Qualifier("admin")
private User user;

Spring IOC将会以类型和名称一起去寻找对应的Bean进行注入。

4.生命周期

Bean的声明周期过程,大致分为Bean定义、Bean的初始化、Bean的生存期和Bean的销毁4个部分,其中Bean定义过程大致如下:

  • Spring通过配置,如@ComponentScan定义的扫描路径去找到带有@Component的类,这个过程就是一个资源定位的过程
  • 一旦找到资源,那么就开始解析,并且将定义的信息保存起来。注意,此时还没有初始化Bean,也没有Bean的实例,仅仅有Bean的定义
  • 把Bean定义发布到Spring IOC容器中。

完成了这3步只是一个资源定位并将Bean的定义发布到IOC容器的过程,还没有Bean实例的生成,更没有完成依赖注入。在默认情况下,接下来的步骤是完成Bean的实例化和依赖注入,这样就能从IOC容器中获取到一个Bean实例。如果我们设置lazyInit(懒加载)的值为true,那么Spring并不会在发布Bean定义后马上为我们完成实例化和依赖注入,而是要等到真正使用到的时候才开始实例化和依赖注入。

Spring在完成依赖注入之后,还提供了一系列的接口和配置来完成Bean的初始化过程,整个流程如下:

Spring IOC容器最低的要求是实现BeanFactory接口,而不是实现ApplicationContext接口。对于那些没有实现ApplicationContext接口的容器,在生命周期对应的ApplicationContextAware定义的方法也不会调用,只有实现了ApplicationContext接口的容器,才会在生命周期调用ApplicationContextAware所定义的setApplicationContext方法。为了测试生命周期我们定义一个BussinessPerson类,代码如下:

package com.martin.config.chapter3.service;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @author: martin
 * @date: 2019/10/28 22:09
 * @description:
 */
@Component
public class BussinessPerson implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("调用BeanFactoryAware的setBeanFactory");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("调用BeanNameAware的setBeanName方法:" + name);
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("调用DisposableBean的destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用InitializingBean的afterPropertiesSet");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("调用ApplicationContextAware的setApplicationContext");
    }

    @PostConstruct
    public void init() {
        System.out.println("调用@PostConstruct标识的方法");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("调用@PreDestroy标识的方法");
    }
}

这样,BussinessPerson 这个Bean就实现了生命周期中单个Bean可以实现的所有接口。为了测试Bean的后置处理器,我们创建一个类BeanPostProcessorExample,该后置处理器将对所有的Bean都有效,代码如下:

package com.martin.config.chapter3.service;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * @author: martin
 * @date: 2019/10/28 22:31
 * @description:
 */
@Component
public class BeanPostProcessorTest implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor调用before" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor调用After" + beanName);
        return bean;
    }
}

运行Spring Boot应用程序,输出结果如下:

调用BeanNameAware的setBeanName方法:bussinessPerson
调用BeanFactoryAware的setBeanFactory
调用ApplicationContextAware的setApplicationContext
BeanPostProcessor调用beforebussinessPerson
调用@PostConstruct标识的方法
调用InitializingBean的afterPropertiesSet
BeanPostProcessor调用AfterbussinessPerson
BeanPostProcessor调用beforedataSource
BeanPostProcessor调用AfterdataSource

对于Bean后置处理器(BeanPostProcessor)而言,它对所有的Bean都起作用,而其他的接口则是对于单个Bean起作用。有时候Bean的定义可能使用的是第三方的类,此时可以使用注解@Bean来配置自定义初始化和销毁方法,代码如下:

@Bean(initMethod='init',destroyMethod='destroy')

5.使用属性文件

在Spring Boot中使用属性文件,可以采用默认的application.properties,也可以使用自定义的配置文件。在Spring Boot中,我们在Maven中添加依赖:

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-configuration-processor</artifactId>
     <optional>true</optional>
 </dependency>

有了该依赖,就可以直接使用application.properties文件为我们工作了。配置文件如下:

database.driverName=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/test
database.username=root
database.password=123456

我们使用Spring表达式的方式获取配置的属性值:

package com.martin.config.chapter3.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author: martin
 * @date: 2019/11/2 17:23
 * @description:
 */
@Component
public class DataBaseProperties {
    @Value("${database.driverName}")
    private String driverName;
    @Value("${database.url}")
    private String url;
    private String password;
    private String userName;

    public String getDriverName() {
        return driverName;
    }

    public void setDriverName(String driverName) {
        this.driverName = driverName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getPassword() {
        return password;
    }

    @Value("${database.password}")
    public void setPassword(String password) {
        this.password = password;
    }

    public String getUserName() {
        return userName;
    }

    @Value("${database.username}")
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

通过Spring表达式@Value注解,读取配置在属性文件的内容。@Value注解既可以加载属性,也可以加在方法上。

我们也可以使用注解@ConfigurationProperties来配置属性,代码如下:

package com.martin.config.chapter3.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author: martin
 * @date: 2019/11/2 17:23
 * @description:
 */
@Component
@ConfigurationProperties("database")
public class DataBaseProperties {
    private String driverName;
    private String url;
    private String password;
    private String userName;

    public String getDriverName() {
        return driverName;
    }

    public void setDriverName(String driverName) {
        this.driverName = driverName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

这里在@ConfigurationProperties中配置的字符串database,将与POJO的属性名称组成属性属性的全限定名去配置文件里查找,这样就能将对应的属性读取到POJO当中。

有时候我们会觉得如果把所有的属性配置到放置到application.properties中,这个文件将会有很多属性内容。为了更好的配置,我们将数据库的属性配置到jdbc.properties中,然后使用@PropertySource去定义对应的属性文件,把它加载到Spring的上下文中。代码如下所示:

package com.martin.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;

/**
 * 配置启动类
 *
 * @author martin
 * @create 2019-01-03 下午 11:20
 **/
@SpringBootApplication
@PropertySource(value = {"classpath:jdbc.properties"}, ignoreResourceNotFound = true)
public class SpringBootConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootConfigApplication.class, args);
    }
}

value可以配置多个配置文件,使用classpath前缀,意味着去类文件路径下找到属性文件;ignoreResourceNotFound则是是否忽略配置文件找不到的问题,默认值是false,也就是找不到属性文件就会报错。

6.条件装配Bean

有时候我们希望IOC容器在某些条件满足下才去装配Bean,例如我们要求在数据库的配置中,驱动名、url、账号和密码都存在的情况下才去连接数据库。此时我们需要使用@Conditional注解和实现Condition接口的类。代码如下:

package com.martin.config.chapter3.config;

import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * @author: martin
 * @date: 2019/10/27 17:39
 * @description:
 */
@Configuration
public class AppConfig {
    @Bean("dataSource")
    @Conditional(DatabaseConditional.class)
    public DataSource getDataSource(@Value("${database.driverName") String driver,
                                    @Value("${database.url") String url,
                                    @Value("${database.username") String username,
                                    @Value("${database.password") String password) {
        Properties properties = new Properties();
        properties.setProperty("driver", driver);
        properties.setProperty("url", url);
        properties.setProperty("username", username);
        properties.setProperty("password", password);
        try {
            return BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

加入了@Conditionnal注解,并且配置了类DatabaseConditional,那么这个类就必须实现Condition接口。代码如下:

package com.martin.config.chapter3.config;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @author: martin
 * @date: 2019/11/2 20:08
 * @description:
 */
public class DatabaseConditional implements Condition {
    /**
     * 数据库装配条件
     *
     * @param context  条件上下文
     * @param metadata 注释类型的元数据
     * @return true装配Bean,否则不装配
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        //判断属性文件是否存在对应的数据库配置
        return environment.containsProperty("database.driverName")
                && environment.containsProperty("database.url")
                && environment.containsProperty("database.username")
                && environment.containsProperty("database.password");
    }
}

maches方法读取上下文环境,判断是否已经配置了对应的数据库信息。当都配置好了以后返回true,Spring会装配数据库连接池的Bean,否则不装配。

7.Bean的作用域

IOC容器最顶级接口BeanFactory中,可以看到isSingleton和isPrototype两个方法。其中,如果isSingleton方法如果返回true,则Bean在IOC容器中以单例存在,这也是Spring IOC容器的默认值。如果isPrototype方法返回true,则当我们每次获取Bean的时候,IOC容器都会创建一个新的Bean。除了这些,Bean还存在其他类型的作用域:

对于application作用域,完全可以使用单例来代替。

作用域的定义使用@Scope注解,实例代码如下:

package com.martin.config.chapter3.service;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

/**
 * @author: martin
 * @date: 2019/11/2 22:25
 * @description:
 */
@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class ScopeBean {
}

8.@Profile

在企业开发的过程中,项目往往需要面临开发环境,测试环境,愈发环境和生产环境的切换,而每一套环境的上下文是不一样的。例如,它们会有各自的数据库资源,这样就需要我们再不同的数据库之前进行切换。为了方便,Spring提供了Profile机制,使得我们可以方便地实现各个环境之间的切换。

在Spring中存在两个配置参数可以提供给我们修改启动Profile机制,一个是spring.profiles.active,另外一个是spring.profiles.default。在两个属性参数都没有配置的情况下,Spring将不会启动Profile机制。Spring先判断是否存在spring.profiles.active配置之后,再去查找spring.profiles.default配置,所以spring.profiles.active优先级大于spring.profiles.default。

在Java项目启动时,我们配置如下就能够启动Profile机制:

JAVA_OPTS=“-Dspring.profiles.active=dev”

在Spring Boot的规则中,假设把选项-Dspring.profiles.active配置的值记为{profile},它就会用application-{profile}.properties文件去代替原来默认的application.properties文件,然后启动Spring Boot的程序。

9.引入XML配置的Bean

尽管Spring Boot建议使用注解和扫描配置Bean,但是它并不拒绝使用XML配置Bean。如果我们想在Spring Boot中使用XML对Bean进行配置,可以使用注解@ImportResource,通过它可以引入对应的XML文件,用以加载Bean。实例代码如下:

Configuration
@ImportResource(value = {"classpath:spring-job.xml"})
public class AppConfig {

}

这样就可以引入对应的XML,从而将XML定义的Bean装配到IOC容器中。

10.Spring EL表达式

在前面的代码中,我们是在没有任何运算规则的情况下装配Bean的。为了更加灵活,Spring EL表达式为我们提供了强大的运算规则来更好的装配Bean。

EL表达式最常用的是读取配置属性文件中的值,例如:

@Value("${database.driverName}")

除此之外,它还能够调用方法,例如,记录Bean的初始化时间:

    @Value("#{T(System).currentTimeMillis()}")
    private Long time;

这里采用#{.......}代表启用Spring表达式,它将具有运算的功能;T(......)代表的是引入类;System是java.lang.*包的类,这是java 默认加载的包,可以不用写全限定名。如果是其他的包,需要写出全限定名才能引用类。此外EL表达式支持多种赋值方式,代码如下:

package com.martin.config.chapter3.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author: martin
 * @date: 2019/11/2 23:06
 * @description:
 */
@Component
public class SpringEl {
    @Value("${database.driverName}")
    private String driver;
    @Value("#{T(System).currentTimeMillis()}")
    private Long time;
    /**
     * 使用字符串直接赋值
     */
    @Value("#{'使用Spring EL赋值字符串'}")
    private String str;
    /**
     * 科学计数法赋值
     */
    @Value("#{9.3E3}")
    private Double d;
    /**
     * 赋值浮点数
     */
    @Value("#{3.14}")
    private float pi;
    /**
     * 使用其他Bean的属性来赋值
     */
    @Value("#{beanName.str}")
    private String otherBeanProp;

    /**
     * 使用其他Bean的属性来赋值,转换为大写
     */
    @Value("#{beanName.str?.toUpperCase()}")
    private String otherBeanPropUpperCase;
    /**
     * 数学运算
     */
    @Value("#{1+2}")
    private int run;
    /**
     * 浮点数比较运算
     */
    @Value("#{beanName.pi == 3.14f}")
    private boolean piFlag;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值