2026/06/29

Spring IoC

需要管理的物件對象稱為 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;

沒有留言:

張貼留言