【Spring Boot實戰】Spring常用配置

Spring常用配置介紹

  • Scope
  • PropertySource
  • Bean的初始化與銷毀
  • Profile
  • Application Event

【Scope】

  1. Singleton:一個容器中只有一個,為預設
  2. Prototype:每次都建立新的
  3. Request:一個 http request 一個
  4. Session:一個 http session 一個
  5. GlobalSession:一個 global http session 一個
  6. @StepScope:Spring Batch中使用

【範例】

  • Singleton Bean
@Service // 預設為Singleton,不寫等同於@Scope("singleton")
public class DemoSingletonService {

}
  • Prototype Bean
@Service
@Scope("prototype") // 設定scope為prototype
public class DemoPrototypeService {

}
  • Configuration
@Configuration
@ComponentScan("com.myPackage")
public class ScopeConfig {

}
  • Context
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(ScopeConfig.class); 
		DemoSingletonService s1 = context.getBean(DemoSingletonService.class);
        DemoSingletonService s2 = context.getBean(DemoSingletonService.class);

        DemoPrototypeService p1 = context.getBean(DemoPrototypeService.class);
        DemoPrototypeService p2 = context.getBean(DemoPrototypeService.class);

        System.out.println(s1.equals(s2)); // true
        System.out.println(p1.equals(p2)); // false
        
        context.close();
	}
}

【Spring EL 及資源調用】

  • @PropertySource:讀取property檔內容
  • @Value:注入值的方式
    • @Value("#{systemProperties['os.name']}"):注入系統屬性
    • @Value("#{ T(java.lang.Math).random() * 100.0 }):注入運算式
    • @Value("${book.name}"):注入讀取的property中的內容

【範例】

  • test.properties
book.author = AAA
book.name = spring boot
  • Service
@Service
public class DemoService {
	@Value("其他類的屬性") // 注入String
    private String another;

	public String getAnother() {
		return another;
	}

	public void setAnother(String another) {
		this.another = another;
	}
	
}
  • Configuration
@Configuration
@ComponentScan("com.myPackage")
@PropertySource("classpath:com/myPackage/test.properties") // 指定property檔的位置
public class ElConfig {
	
	@Value("I Love You!") // 注入String
    private String normal;

	@Value("#{systemProperties['os.name']}") // 注入系統屬性
	private String osName;
	
	@Value("#{ T(java.lang.Math).random() * 100.0 }") // 注入運算式
    private double randomNumber;

	@Value("#{demoService.another}") // 注入其他類之屬性
	private String fromAnother;

	@Value("classpath:com/myPackage/test.txt") // 注入文件資源
	private Resource testFile;

	@Value("http://www.baidu.com") // 注入網址資源
	private Resource testUrl;

	@Value("${book.name}") // 注入property檔內容
	private String bookName;

	@Autowired
	private Environment environment; // 也可透過Environment取得property檔中的內容
	
	@Bean // 使用@Value注入property檔內容時,要有此配置
	public static PropertySourcesPlaceholderConfigurer propertyConfigure() {
		return new PropertySourcesPlaceholderConfigurer();
	}
	
	public void outputResource() {
		try {
			System.out.println(normal); // I Love You!
			System.out.println(osName); // 系統名稱
			System.out.println(randomNumber); // 隨機數字
			System.out.println(fromAnother); // 其他類的屬性
			
			System.out.println(IOUtils.toString(testFile.getInputStream())); // 文件內容
			System.out.println(IOUtils.toString(testUrl.getInputStream())); // 網站內容
			System.out.println(bookName); // Spring Boot
			System.out.println(environment.getProperty("book.author")); // 透過Environment取得內容 AAA
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

【Bean的初始化及銷毀】

  1. Java配置:@Bean的initMethod、destroyMethod
  2. 註解:JSR-250中的@PostConstruct、@PreDestroy

【範例】

  • Java配置的Bean
public class BeanWayService {
	  public void init(){
	        System.out.println("Bean init method");
	    }
	    public BeanWayService() {
	        super();
	        System.out.println("初始化BeanWayService");
	    }
	    public void destroy(){
	        System.out.println("Bean destroy method");
	    }
}
  • JSR-250的Bean
public class JSR250WayService {
    @PostConstruct // 建立Bean之後執行
    public void init(){
        System.out.println("jsr250 init method");
    }
    public JSR250WayService() {
        super();
        System.out.println("初始化JSR250WayService");
    }
    @PreDestroy // Bean銷毀前執行
    public void destroy(){
        System.out.println("jsr250 destory method");
    }

}
  • Configuration
@Configuration
@ComponentScan("com.myPackage")
public class PrePostConfig {
	
        // 指定beanWayService的init及destroy方法,於Bean建立後、銷毀前執行
	@Bean(initMethod="init",destroyMethod="destroy") 
	BeanWayService beanWayService(){
		return new BeanWayService();
	}
	
	@Bean
	JSR250WayService jsr250WayService(){
		return new JSR250WayService();
	}

}
  • Context
public class Main {
	
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(PrePostConfig.class);
		
		BeanWayService beanWayService = context.getBean(BeanWayService.class);
		JSR250WayService jsr250WayService = context.getBean(JSR250WayService.class);
		
		context.close();
	}
}

【Profile】

Profile目的為方便在不同環境下,使用不同配置(如:開發環境與生產環境的資料庫配置),方法有三:

  1. 透過Environment的ActiveProfiles設定當前使用的Profile,實例化不同的Bean
  2. 透過設定jvm的spring.profiles.active參數設置
    <servlet>
    	<servlet-name>dispatcher</servlet-name>
    	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    	<init-param>
    		<param-name>spring.profiles.active</param-name>
    		<param-value>production</param-value>
    	</init-param>
    </servlet>

     

  3. Web中設置在Servlet的context parameter中
    public class WebInit implenments WebApplicationInitializer {
    	@Override
    	public void onStartup(ServletContext container) throws ServletException {
    		container.setInitParameter("spring.profiles.default", "dev");
    	}
    }

     

【範例】

  • Bean
public class DemoBean {

	private String content;

	public DemoBean(String content) {
		super();
		this.content = content;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
	
}
  • Configuration
@Configuration
public class ProfileConfig {
	@Bean
	@Profile("dev") // 用Profile註釋,設定為開發用
	public DemoBean devDemoBean() {
		return new DemoBean("from development profile");
	}

	@Bean
	@Profile("prod") // 生產用
	public DemoBean prodDemoBean() {
		return new DemoBean("from production profile");
	}

}
  • Context
public class Main {

	public static void main(String[] args) {
		  AnnotationConfigApplicationContext context =  
				  new AnnotationConfigApplicationContext();

		  // 透過Environment,選擇要實例化的Bean
		  context.getEnvironment().setActiveProfiles("dev"); 
		  context.register(ProfileConfig.class); // 後置註冊Configuration,不然會報Bean未定義的錯誤
		  context.refresh(); // 刷新容器
		  
	      DemoBean demoBean = context.getBean(DemoBean.class);
	      
	      System.out.println(demoBean.getContent());
	      
	      context.close(); // 印出 from development profile
	}
}

【Application Event】

替Bean和Bean之間的消息通信提供支持,透過讓Bean監聽另一Bean發送的事件,能夠做相應的處理。

【範例】

  • Bean ( Event )
// 事件需繼承 ApplicationEvent
public class DemoEvent extends ApplicationEvent{
    private static final long serialVersionUID = 1L;
    private String msg;

    public DemoEvent(Object source,String msg) {
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}
  • 監聽器
@Component
// 需實現ApplicationListener,並指定事件類型
public class DemoListener implements ApplicationListener<DemoEvent>{

        // 使用onApplicationEvent方法,對事件消息進行接受處理
	public void onApplicationEvent(DemoEvent event) {
		
		String msg = event.getMsg();
		
		System.out.println("接收到publisher發送的訊息:" + msg);
	}
}
  • 事件發布
@Component
public class DemoPublisher {
	@Autowired
	ApplicationContext applicationContext; // 用ApplicationContext發布事件
	
	public void publish(String msg){
        // 透過publishEvent方法發布
		applicationContext.publishEvent(new DemoEvent(this, msg));
	}

}
  • Configuration
@Configuration
@ComponentScan("com.myPackage")
public class EventConfig {

}
  • Context
public class Main {

	public static void main(String[] args) {
		 AnnotationConfigApplicationContext context =
	                new AnnotationConfigApplicationContext(EventConfig.class);
		 
		 DemoPublisher demoPublisher = context.getBean(DemoPublisher.class);
		 
		 demoPublisher.publish("hello"); // 印出 接收到publisher發送的訊息:hello
		 
		 context.close();
	}
}