需要管理的物件對象稱為 Spring Bean,Bean 的管理器稱為 Spring IoC Container。IoC container 有兩個功能
管理 bean 的定義、發布、設定、銷毀
描述 bean 之間的依賴關係
所有 IoC Container 都需要實作 BeanFactory Interface,主要的 method:
getBean
依照名稱/類別,取得 bean
Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;getBeanProvider
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);containsBean
boolean containsBean(String name);isSingleton
IoC 裡面,bean 預設都是 singleton,getBean 預設都會回傳同一個 bean
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;isPrototype
getBean 會透過 IoC 產生新的 bean 回傳
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;isTypeMatch
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;getType
Class<?> getType(String name) throws NoSuchBeanDefinitionException; Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;getAliases
String[] getAliases(String name);
spring 在 BeanFactory 基礎上,加上 ApplicationContext,實際的 application 大部分都是使用 ApplicationContext 的 interface
@Component vs @Bean
ref: 注解中@Component和@Bean的区别 - JAVA 牛牛
@Component(@Controller、@Service、@Repository)是透過 ComponentScan 掃描來做自動偵測,並自動設定到IoC。@Component 只是一個類別的定義,告知 IoC 要產生這個類別的 bean
在 @Compoent 類別的屬性裡面,加上 @Value,可直接設定該欄位的值
@Bean 是使用在 method,在有該 annotation 的 method 會回傳一個 bean 的物件。該 method 裡面是產生 bean 的邏輯。
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component("user")
//@Scope("prototype")
public class User {
private Long id;
private String userName;
private String note;
}
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component("admin")
public class Admin {
@Value("1")
private Long id;
@Value("user_name_1")
private String userName;
@Value("note_1")
private String note;
}
AppConfig.java
@Configuration
@ComponentScan(basePackages = "com.test.ioc.*"
// excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, classes = Service.class)
)
public class AppConfig {
// name 代表 Bean名稱
@Bean(name = "user2")
public User initUser() {
var user = new User();
user.setId(2L);
user.setUserName("user_name_2");
user.setNote("note_2");
return user;
}
}
測試
public class IoCTest {
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
try {
var admimbean1 = ctx.getBean(Admin.class);
var admimbean2 = ctx.getBean(Admin.class);
var admimbean3 = ctx.getBean("admin");
System.out.println("Admin: admimbean1, name=" + admimbean1.getUserName());
System.out.println("Admin 2==3:" + (admimbean1 == admimbean2)+", name="+admimbean2.getUserName());
System.out.println("Admin 1==3:" + (admimbean1 == admimbean3)+", name="+((Admin)admimbean3).getUserName());
var userbaen1 = ctx.getBean("user");
var userbaen2 = ctx.getBean("user2");
// var userbaen3 = ctx.getBean(User.class);
System.out.println("User: userbaen1, name=" + ((User)userbaen1).getUserName());
System.out.println("User: userbaen2, name=" + ((User)userbaen2).getUserName());
// System.out.println("User: userbaen3, name=" + ((User)userbaen3).getUserName());
System.out.println("User 1==2:" + (userbaen1 == userbaen2)+", name="+((User)userbaen1).getUserName());
// System.out.println("User 2==3:" + (userbaen2 == userbaen3)+", name="+((User)userbaen3).getUserName());
} finally {
ctx.close();
}
}
}
結果
Admin: admimbean1, name=user_name_1
Admin 2==3:true, name=user_name_1
Admin 1==3:true, name=user_name_1
User: userbaen1, name=null
User: userbaen2, name=user_name_2
User 1==2:false, name=null
@ComponentScan 可使用的參數
basePackages
basePackageClasses
includeFilters
excludeFilters
lazyInit
includeFilters, excludeFilters 都需要有 @ComponentScan.Filter 定義
type: 選擇 filter type: ANNOTATION/ASSIGNABLE_TYPE/ASPECTJ/REGEX/CUSTOM
classes
pattern
ex:
@ComponentScan(basePackages = "com.test.ioc.*",
excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, classes = Service.class)
)
@ComponentScan(basePackages = {"com.test.ioc.*"})
@ComponentScan("com.test.ioc.*")
@ComponentScan(basePackageClasses = {"User.class"})
library bean
使用第三方函式庫的物件時,可透過 @Bean 產生 bean 放到 IoC container 管理
pom.xml 使用 mysql jdbc driver
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
在剛剛的 AppConfig.java 加上 dataSource 定義
// @Bean 產生物件,name為 名稱
@Bean(name = "dataSource")
public DataSource getDataSource() {
var dataSource = new MysqlDataSource();
try {
dataSource.setUrl("jdbc:mysql://localhost:3306/testweb");
dataSource.setUser("root");
dataSource.setPassword("password");
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
回到 IoCTest.java
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
try {
testDatasource( (DataSource)ctx.getBean("dataSource") );
} finally {
ctx.close();
}
}
public static void testDatasource(DataSource ds) {
System.out.println("testDatasource");
try (Connection con = ds.getConnection();
PreparedStatement ps = createPreparedStatement(con, 1);
ResultSet rs = ps.executeQuery()) {
// process the resultset here, all resources will be cleaned up
while (rs.next()) {
System.out.print("id:" + rs.getInt(1));
System.out.print(",username:" + rs.getString(2));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private static PreparedStatement createPreparedStatement(Connection con, int userId) throws SQLException {
String sql = "SELECT id, username FROM user WHERE id = ?";
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, userId);
return ps;
}
執行結果
testDatasource
id:1,username:Lily
Dependency Injection
先定義 兩個 interface
Animal.java
package com.test.ioc.dependency.pojo;
public interface Animal {
public void use();
}
Person.java
package com.test.ioc.dependency.pojo;
public interface Person {
public void service();
public void setAnimal(Animal animal);
}
AnimalDog.java implements Animal
package com.test.ioc.dependency.pojo.impl;
import com.test.ioc.dependency.pojo.Animal;
import org.springframework.stereotype.Component;
@Component
public class AnimalDog implements Animal {
@Override
public void use() {
System.out.println("Dog " + AnimalDog.class.getSimpleName() + " for help.");
}
}
PersonSales.java
使用 @Autowired 自動綁定一個 animal 物件
package com.test.ioc.dependency.pojo.impl;
import com.test.ioc.dependency.pojo.Animal;
import com.test.ioc.dependency.pojo.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PersonSales implements Person {
@Autowired
private Animal animal = null;
@Override
public void service() {
this.animal.use();
}
@Override
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
AppConfig2.java
package com.test.ioc.dependency;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.test.ioc.dependency.*")
public class AppConfig2 {
}
IoCTest2.java
package com.test.ioc.dependency;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.test.ioc.dependency.pojo.impl.PersonSales;
public class IoCTest2 {
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(AppConfig2.class);
try {
var person = ctx.getBean(PersonSales.class);
person.service();
} finally {
ctx.close();
}
}
}
執行結果
Dog AnimalDog for help.
如果再增加一個 AnimalCat
package com.test.ioc.dependency.pojo.impl;
import com.test.ioc.dependency.pojo.Animal;
import org.springframework.stereotype.Component;
@Component
public class AnimalCat implements Animal {
@Override
public void use() {
System.out.println("Cat " + AnimalCat.class.getSimpleName() + " for rats");
}
}
執行後會發生 Exception
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'personSales':
Unsatisfied dependency expressed through field 'animal':
No qualifying bean of type 'com.test.ioc.dependency.pojo.Animal' available:
expected single matching bean but found 2: animalCat,animalDog
因為在 @AutoWired 時,只使用了 animal 屬性,但 container 不確定要使用 Cat 還是 Dog
如果將 animal 改成 animalDog,就很明確。因為 @Autowired 會先根據類別產生 bean,然後再根據屬性名稱匹配。
@Component
public class PersonSales implements Person {
@Autowired
private Animal animalDog = null;
@Override
public void service() {
this.animalDog.use();
}
@Override
public void setAnimal(Animal animal) {
this.animalDog = animal;
}
}
@Autowired 也可以寫在 method 上面
@Override
@Autowired
public void setAnimal(Animal animal) {
this.animal = animal;
}
@Primary 與 @Qualifier
在 AnimalCat 加上 @Primary 就看可以確定先使用這個類別
@Component
@Primary
public class AnimalCat implements Animal {
@Override
public void use() {
System.out.println("Cat " + AnimalCat.class.getSimpleName() + " for rats");
}
}
但如果又在 AnimalDog 加上 @Primary,還是會發生問題。
所以應該在使用 bean 的地方,用 @Qualifier 限制應該要用哪一個 bean
@Override
@Autowired
@Qualifier("animalDog")
public void setAnimal(Animal animal) {
this.animal = animal;
}
在 class constructor 的參數,也可使用 @Autowired
@Component
public class PersonSales implements Person {
private Animal animal = null;
public PersonSales(@Autowired @Qualifier("animalDog") Animal animal) {
this.animal = animal;
}
@Override
public void service() {
this.animal.use();
}
@Override
// @Autowired
// @Qualifier("animalDog")
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
Bean 的生命週期
bean 的初始化流程
flowchart TB
A["資源定位\n使用 @ComponentScan"] --> B["Bean 的定義\n存到 BeanDefinition"]
B --> C["發布 bean 的定義\nIoC Container 載入定義"]
C --> D["實例化\n產生 bean object"]
D --> E["DI\n使用 @Autowired 注入資源"]
@ComponentScan 裡面有個 lazyInit 參數,預設為 false,預設不會延遲 bean 初始化,而是在 getBean 時,才會初始化
修改 AppConfig2.java 加上 lazyInit = true
@Configuration
@ComponentScan(basePackages = "com.test.ioc.dependency.*", lazyInit = true)
public class AppConfig2 {
}
Spring 在完成 DI 以後,會依照此流程管理 bean 的 life cycle
flowchart TB
A["初始化"] --> B["DI"]
B --> C["setBeanName()\n介面 BeanNameAware"]
C --> D["setBeanFactory()\n介面 BeanFactoryAware"]
D --> E["setAppicationContext()\n介面 ApplicationContextAware\n容器要實作ApplicationContext才會被呼叫"]
E --> F["postProcessBeforeInitialization()\n BeanPostProcessor method"]
F --> G["自訂初始化 method\n@PostConstruct method"]
G --> H["afterPropertiesSet()\n介面 InitializingBean"]
H --> I["postProcessAfterInitialization()\nBeanPostProcessor method"]
I --> J["bean 的使用期間"]
J --> K["自訂銷毀 method\n@PreDestory method"]
K --> L["destory method\n介面 DisposableBean"]
IoC container 基本是需要實作 BeanFactory 介面,不一定要實作 ApplicationContext
修改 PersonBussiness.java
package com.test.ioc.dependency.pojo.impl;
import com.test.ioc.dependency.pojo.Animal;
import com.test.ioc.dependency.pojo.Person;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class PersonBussiness implements Person, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
private Animal animal = null;
@Override
public void service() {
this.animal.use();
}
@Override
@Autowired
@Qualifier("animalDog")
public void setAnimal(Animal animal) {
System.out.println("lazyInit");
this.animal = animal;
}
@Override
public void setBeanName(String beanName) {
System.out.println("【" + this.getClass().getSimpleName() + "】invoke BeanNameAware.setBeanName");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("【" + this.getClass().getSimpleName() + "】invoke BeanFactoryAware.setBeanFactory");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("【" + this.getClass().getSimpleName() + "】invoke ApplicationContextAware.setApplicationContext");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【" + this.getClass().getSimpleName() + "】invoke InitializingBean.afterPropertiesSet");
}
@PostConstruct
public void init() {
System.out.println("【" + this.getClass().getSimpleName() + "】@PostConstruct 定義的自訂初始化方法");
}
@PreDestroy
public void destroy1() {
System.out.println("【" + this.getClass().getSimpleName() + "】@PreDestroy 定義的自訂銷毀方法");
}
@Override
public void destroy() throws Exception {
System.out.println("【" + this.getClass().getSimpleName() + "】 DisposableBean");
}
}
IoCTest2.java
public class IoCTest2 {
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(AppConfig2.class);
try {
var person = ctx.getBean(PersonBussiness.class);
person.service();
} finally {
ctx.close();
}
}
}
執行結果
lazyInit
【PersonBussiness】invoke BeanNameAware.setBeanName
【PersonBussiness】invoke BeanFactoryAware.setBeanFactory
【PersonBussiness】invoke ApplicationContextAware.setApplicationContext
【PersonBussiness】@PostConstruct 定義的自訂初始化方法
【PersonBussiness】invoke InitializingBean.afterPropertiesSet
Dog AnimalDog for help.
【PersonBussiness】@PreDestroy 定義的自訂銷毀方法
【PersonBussiness】 DisposableBean
增加 BeanPostProcessorExample.java
package com.test.ioc.dependency.life;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class BeanPostProcessorExample implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor invoke " + "postProcessBeforeInitialization method 參數【" + bean.getClass().getSimpleName() + "】【" + beanName + "】 ");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor invoke " + "postProcessAfterInitialization method 參數【" + bean.getClass().getSimpleName() + "】【" + beanName + "】 ");
return bean;
}
}
執行結果
BeanPostProcessor invoke postProcessBeforeInitialization method 參數【AppConfig2$$SpringCGLIB$$0】【appConfig2】
BeanPostProcessor invoke postProcessAfterInitialization method 參數【AppConfig2$$SpringCGLIB$$0】【appConfig2】
BeanPostProcessor invoke postProcessBeforeInitialization method 參數【AnimalDog】【animalDog】
BeanPostProcessor invoke postProcessAfterInitialization method 參數【AnimalDog】【animalDog】
lazyInit
【PersonBussiness】invoke BeanNameAware.setBeanName
【PersonBussiness】invoke BeanFactoryAware.setBeanFactory
【PersonBussiness】invoke ApplicationContextAware.setApplicationContext
BeanPostProcessor invoke postProcessBeforeInitialization method 參數【PersonBussiness】【personBussiness】
【PersonBussiness】@PostConstruct 定義的自訂初始化方法
【PersonBussiness】invoke InitializingBean.afterPropertiesSet
BeanPostProcessor invoke postProcessAfterInitialization method 參數【PersonBussiness】【personBussiness】
Dog AnimalDog for help.
【PersonBussiness】@PreDestroy 定義的自訂銷毀方法
【PersonBussiness】 DisposableBean
Properties file
spring boot 通常使用一個 application.properties 檔案處理設定
先在 pom.xml 加上 spring-boot-configuration-processor,就可以直接使用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
用 SpEL 語法,可參考到 database.properties 設定
DataBaseProperties.java
package com.test.ioc.prop.props;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Data
public class DataBaseProperties {
@Value("${database.driverName}")
private String driverName = null;
@Value("${database.url}")
private String url = null;
@Value("${database.username}")
private String username = null;
@Value("${database.password}")
private String password = null;
}
也可以直接用 @ConfigurationProperties("database")
package com.test.ioc.prop.props;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("database")
@Data
public class DataBaseProperties2 {
private String driverName = null;
private String url = null;
private String username = null;
private String password = null;
}
IoCTest3.java
package com.test.ioc.prop;
import com.test.ioc.prop.props.DataBaseProperties;
import com.test.ioc.prop.props.DataBaseProperties2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class IoCTest3 {
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(AppConfig3.class);
try {
var prop1 = ctx.getBean(DataBaseProperties.class);
System.out.println("prop1 url="+prop1.getUrl());
var prop2 = ctx.getBean(DataBaseProperties2.class);
System.out.println("prop2 url="+prop2.getUrl());
} finally {
ctx.close();
}
}
}
執行結果
prop1 url=jdbc:mysql://localhost:3306/testweb
prop2 url=jdbc:mysql://localhost:3306/testweb
@Conditional
有時候會遇到缺少一些參數,讓 bean 無法初始化,可透過 @Conditional 限制 bean 的條件
DatabaseConditional.java
package com.test.ioc.prop.conditional;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class DatabaseConditional implements Condition {
/**
* data source condition
* @param context
* @param metadata
* @return true -> init bean
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 取得 env
var env = context.getEnvironment();
// 判断 .properties 內容,是否有相關參數
return env.containsProperty("database.url")
&& env.containsProperty("database.username")
&& env.containsProperty("database.password");
}
}
PropApplication.java
@SpringBootApplication(scanBasePackages = "com.test.ioc.prop")
@PropertySource(value = "classpath:jdbc.properties", ignoreResourceNotFound = true)
@EnableConfigurationProperties
public class PropApplication {
public static void main(String[] args) {
SpringApplication.run(PropApplication.class, args);
}
@Bean(name = "dataSource2")
// 透過 DatabaseConditional 限制 bean 的條件
@Conditional(DatabaseConditional.class)
public DataSource getDataSource(
@Value("${database.url}") String url,
@Value("${database.username}") String username,
@Value("${database.password}") String password
) {
System.out.println("init datasource");
var dataSource = new MysqlDataSource();
dataSource.setUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
如果 database.properties 缺少了一些參數,也不會讓 PropApplication 無法啟動
Bean Scope
一般來說,bean 有 singleton, prototype 兩種,在 Jakarta EE 的 web container 裡面,還增加了 page, reqeust, session, application 四種 scope
page scope 作用範圍是 JSP,Spring 沒有支援
| scope | 使用範圍 | desc |
|---|---|---|
| singleton | spring application | 預設值 |
| prototype | spring application | 從 IoC container 取得一個 bean 都是產生新的 |
| session | spring web application | HTTP session |
| application | spring web application | web application life cycle |
| request | spring web application | web application http request |
| globalSession | spring web application | global http session,一個 bean 對應一個 object,不常使用 |
ScopBean.java
package com.test.ioc.scope;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScopeBean {
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 是定義為 prototype
如果是 spring web application 還可以使用
WebApplicationContext.SCOPE_REQUEST
WebApplicationContext.SCOPE_SESSION
WebApplicationContext.SCOPE_APPLICATION
@Profile
專案開發時,可能會有開發、測試、正式測試、正式環境這些不同的運作環境,不同的環境有不同的設定。
假設有 dev, test 兩種資料庫,可使用 @Profile 切換
ScopeApplication.java
package com.test.ioc.scope;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.test.ioc.prop.conditional.DatabaseConditional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@SpringBootApplication(scanBasePackages = "com.test.ioc.scope")
@PropertySource(value = "classpath:jdbc.properties", ignoreResourceNotFound = true)
@EnableConfigurationProperties
public class ScopeApplication {
public static void main(String[] args) {
SpringApplication.run(ScopeApplication.class, args);
}
@Bean(name = "dataSource")
@Profile("dev")
public DataSource getDevDataSource() {
System.out.println("getDevDataSource");
var dataSource = new MysqlDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/dev_testweb");
dataSource.setUser("root");
dataSource.setPassword("password");
return dataSource;
}
@Bean(name = "dataSource")
@Profile("test")
public DataSource getTestDataSource() {
System.out.println("getTestDataSource");
var dataSource = new MysqlDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test_testweb");
dataSource.setUser("root");
dataSource.setPassword("password");
return dataSource;
}
}
在啟動時切換
JAVA_OPTS="-Dspring.profiles.active=dev"
spring 會自動使用 application-{profile}.properties
SpEL
讀取 properties 的值
@Value("${database.driverName}")
String driver;
記錄 bean 的初始化時間
@Value("#{T(System).currentTimeMillis()}")
private Long initTime = null;
直接設定屬性
@Value("#{'teststring'}")
private String str=null;
@Value("#{3.1E2}")
private double d;
@Value("#{3.14}")
private double e;
取得其他 bean 的屬性
?. 是 null 判斷
@Value("#{bean2.str}")
private String otherStr=null;
@Value("#{bean2.str?.toUpperCase()}")
private String otherStrUpperCase=null;
運算
@Value("#{1+2}")
private int run;
// 浮點數比較運算
@Value("#{beanName.pi == 3.14f}")
private boolean piFlag;
// 字串比較運算
@Value("#{beanName.str eq 'Spring Boot'}")
private boolean strFlag;
// 字串連接
@Value("#{beanName.str + ' 連接字串'}")
private String strApp = null;
// 三元運算
@Value("#{beanName.d > 1000 ? '大於' : '小於'}")
private String resultDesc = null;