《Spring Boot实战》学习(二):Spring的常用配置

文章目录

    • spring的基本配置
      • Scope
      • Spring EL和资源调用
      • Bean的初始化和销毁
      • Profile
      • Application Event
    • Spring 的高级话题
      • Spring Aware
      • 多线程
      • 计划任务
      • 条件注解
      • 组合注解与元注解
      • 测试

spring的基本配置

Scope

Scope描述的是Spring容器如何创建Bean的示例的。Spring的Scope有以下几种,通过@Scope注解来实现。

  1. Singleton,一个Spring容器中只有一个Bean实例,这个是默认配置。即单例。
  2. Prototype,每次调用创建一个Bean的实例
  3. Request,Web项目中,给每一个http request新建一个Bean实例。
  4. Session,Web项目中,给每一个http session新建一个Bean实例。
  5. GlobalSession,portal应用中,给每一个global http session新建一个Bean实例。

使用示例

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope("prototype")
public class DemoPrototypeService {

}

Spring EL和资源调用

spring开发当中涉及调用各种资源的情况,包含普通文件,网址,配置文件,系统环境变量等,可以使用Spring的表达式语言实现资源的注入。

示例代码如下:

pom.xml添加

<dependency>
    <groupId>commons-iogroupId>
    <artifactId>commons-ioartifactId>
    <version>2.3version>
dependency>	
@Service
public class DemoService {
	
	@Value("其他类的属性")
	private String another;

	public String getAnother() {
		return another;
	}

	public void setAnother(String another) {
		this.another = another;
	}	
}

@Configuration
@ComponentScan("com.xhf.sample4")
//注入配置文件需要指定文件地址
@PropertySource("classpath:com/xhf/sample4/test.properties")
public class ElConfig {

	//注入字符串
	@Value("I love you")  
	private String normal;
	
	//注入操作系统属性
	@Value("#{systemProperties['os.name']}")
	private String osName;
	
	//注入表达式运算结果
	@Value("#{T(java.lang.Math).random()*100.0}")
	private double randomNumber;
	
	//注入其他bean的属性。demoService应该是bean的默认名称
	@Value("#{demoService.another}")
	private String fromAnother;
	
	//注入文件资源
	@Value("classpath:com/xhf/sample4/test.txt")
	private Resource testFile;
	
	//注入网址资源
	@Value("http://www.baidu.com")
	private Resource testUrl;
	
	//注入配置文件
	@Value("${book.name}")
	private String bookName;
	
	
	//Environment可以获取配置文件属性
	@Autowired
	private Environment environment;
	
	//注入配置文件需要配置这样一个Bean
	@Bean
	public static PropertySourcesPlaceholderConfigurer propertyConfigure() {
		return new PropertySourcesPlaceholderConfigurer();
	}
	
	
	public void outputResource() {
		System.out.println(normal);
		System.out.println(osName);
		System.out.println(randomNumber);
		System.out.println(fromAnother);
		try {
			System.out.println(IOUtils.toString(testFile.getInputStream()));
			System.out.println(IOUtils.toString(testUrl.getInputStream()));
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(bookName);
		
		System.out.println(environment.getProperty("book.name"));
	}
	
}

public class Main {	
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context
		= new AnnotationConfigApplicationContext(ElConfig.class);
		
		ElConfig resourceService = context.getBean(ElConfig.class);
		
		resourceService.outputResource();
		
		context.close();
		
	}
	
}

输出:
I love you
Linux
37.23290117950025
其他类的属性
hello world
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8>......此处省略

spring boot
spring boot

Bean的初始化和销毁

Spring 可以在Bean 的初始化和销毁的时候做一些操作。提供如下两种方式:

  1. Java配置的方式:使用@Bean的 initMethod 和 destroyMethod (相当于 xml 配置的 init-method 和 destroy-method)
  2. 注解的方式:利用JSR-250的 @PostConstruct 和 @PreDestroy

示例:

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");
	}
	
	
}
@Component
public class JSR250WayService {
	
	@PostConstruct
	public void init() {
		System.out.println("JSR250-init-method");
	}
	
	public JSR250WayService() {
		super();
		System.out.println("初始化构造函数-JSR250WayService");
	}
	
	@PreDestroy
	public void destroy() {
		System.out.println("JSR250-destroy-method");
	}
	
}
@Configuration
@ComponentScan("com.xhf.sample5")
public class PrePostConfig {

	@Bean(initMethod="init", destroyMethod="destroy")  //分别在构造函数执行完之后和销毁之前执行
	BeanWayService beanWayService() {
		return new BeanWayService();
	}
	
	
}
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 来设定当前 context 需要使用的配置环境。在开发中使用 @Profile 注解类或者方法,达到在不同情况下选择实例化不同的 Bean。
  2. 通过设定 jvm 的 spring.profiles.active 参数来设置配置环境。
  3. Web 项目设置在 Servlet 的 context parameter 中。

Application Event

Spring的事件需要遵循一下流程

  1. 自定义事件,继承ApplicationEvent
  2. 自定义事件监听器,实现ApplicationListener
  3. 使用容器发布事件。

代码示例

自定义事件:

public class DemoEvent extends ApplicationEvent {
	
	private static final long serialVersionUID = 1L;
	
	private String msg;

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

	public String getMsg() {
		return msg;
	}

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

事件监听

@Component
public class DemoListener implements ApplicationListener<DemoEvent>{

	@Override
	public void onApplicationEvent(DemoEvent event) {
		String msg = event.getMsg();
		System.out.println("我接收到了bean-demopublisher发布的消息:"+msg);
	}

}

事件发布

@Component
public class DemoPublisher {

	@Autowired
	ApplicationContext context;
	
	public void publish(String msg) {
		context.publishEvent(new DemoEvent(this, msg));
	}
	
	
}

配置文件

@Configuration
@ComponentScan("com.xhf.sample6")
public class EventConfig {
}

执行程

public class Main {
	
	public static void main(String[] args) {
		
		AnnotationConfigApplicationContext context
		= new AnnotationConfigApplicationContext(EventConfig.class);
		
		DemoPublisher publisher = context.getBean(DemoPublisher.class);
		
		publisher.publish("hello world");
		
		context.close();
	
	}
}

Spring 的高级话题

Spring Aware

aware是感知的意思,Spring 提供 Aware 相关的接口出来使用目的就是为了让Bean 可以调用到一些容器所拥有的资源和功能。书中大概是这个意思。

我理解的就是把容器本身的一些功能接口暴露出来,方便我们基于容器做一些功能拓展。相应的这会增强Bean和容器的耦合性。

示例代码如下,实现 BeanNameAware 可以获得 bean name 属性, 实现 ResourceLoaderAware 可以获得容器的资源加载器。

package com.xhf.aware;

import java.io.IOException;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

@Service
public class AwareService implements BeanNameAware, ResourceLoaderAware {

	private ResourceLoader resourceLoader;
	
	private String name;
	
	
	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	@Override
	public void setBeanName(String name) {
		this.name = name;
	}
	
	public void outputResult() {
		System.out.println("Bean 的名称为:" + name);
		
		Resource resource = resourceLoader.getResource("classpath:com/xhf/aware/test.txt");
		try {
			String content = IOUtils.toString(resource.getInputStream());
			System.out.println(content);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

配置类

package com.xhf.aware;

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

@Configuration
@ComponentScan("com.xhf.aware")
public class AwareConfig {	
}

执行代码

package com.xhf.aware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class);
		AwareService awareService = context.getBean(AwareService.class);
		awareService.outputResult();
		context.close();
	}	
}

结果如下:

Bean 的名称为:awareService
hello world!!!!!!!!!!!!

多线程

Spring 通过任务执行器 TaskExecutor 来实现多线程和并发编程。使用 ThreadPoolTaskExecutor 可实现一个基于线程池的 TaskExecutor 。而实际开发中任务一般是非阻碍的,即异步的。所以要在配置类中通过 @EnableAsync 开启异步任务的支持,并且通过在实际执行的 Bean 的方法中使用 @Async 注解来声明这是一个异步任务。

配置类

package com.xhf.taskexecutor;

import java.util.concurrent.Executor;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
@ComponentScan("com.xhf.taskexecutor")
public class TaskExecutorConfig implements AsyncConfigurer {

	@Override
	public Executor getAsyncExecutor() {
		ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
		taskExecutor.setCorePoolSize(5);
		taskExecutor.setMaxPoolSize(10);
		taskExecutor.setQueueCapacity(25);
		taskExecutor.initialize();
		return taskExecutor;
	}

	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return null;
	}

}

异步任务定义:

package com.xhf.taskexecutor;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncTaskService {

	@Async
	public void executeAsyncTask(Integer i) {
		System.out.println("执行异步任务:" + i);
	}
	
	@Async
	public void executeAsyncTaskPlus(Integer i) {
		System.out.println("执行异步任务+1:" + (i+1));
	}
}

启动类,执行异步任务:

package com.xhf.taskexecutor;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskExecutorConfig.class);
		AsyncTaskService taskService = context.getBean(AsyncTaskService.class);
		for (int i = 0; i < 10; i++) {
			taskService.executeAsyncTask(i);
			taskService.executeAsyncTaskPlus(i);
		}
		context.close();
	}
	
	
}

计划任务

通过在配置类注解 @EnableScheduling 来开启对计划任务的支持,然后在要执行任务的方法上注解@Scheduled,声明这是一个计划任务。

配置类:

package com.xhf.schedule;

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

@Configuration
@ComponentScan("com.xhf.schedule")
@EnableScheduling
public class TaskSchedulerConfig {

}

定义计划任务:

package com.xhf.schedule;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class ScheduledTaskService {

	private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
	
	@Scheduled(fixedRate=5000)
	public void reportCurrentTime() {
		System.out.println("每隔5秒执行一次,currentTime: "+dateFormat.format(new Date()));
	}
	
	
	@Scheduled(cron="0 28 11 ? * *")
	public void fixTimeExecution() {
		System.out.println("指定时间执行一次,currentTime: "+dateFormat.format(new Date()));
	}
}

启动类,执行计划任务:

package com.xhf.schedule;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskSchedulerConfig.class);
	}
}

条件注解

spring 可以通过 @Conditional 根据满足某一特定条件创建一个特定的 Bean。

以下示例代码,通过实现 Condition 接口,实现 matches 方法来构造判断条件。

package com.xhf.conditional;

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

public class WindowsCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		return context.getEnvironment().getProperty("os.name").contains("Windows");
	}

}
package com.xhf.conditional;

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

public class LinuxCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		return context.getEnvironment().getProperty("os.name").contains("Linux");
	}

}

定义一个接口,后续根据不同的条件来实例化不同的对象。

package com.xhf.conditional;

public interface ListService {

	public String showListCmd();
}
package com.xhf.conditional;

public class WindowsListService implements ListService {

	@Override
	public String showListCmd() {
		return "dir";
	}

}
package com.xhf.conditional;

public class LinuxListService implements ListService{

	@Override
	public String showListCmd() {
		return "ls";
	}

}

配置文件使用 @Conditional 条件来实例化不同的Bean

package com.xhf.conditional;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConditionConfig {
	
	@Bean
	@Conditional(WindowsCondition.class)
	public ListService windowsListService() {
		return new WindowsListService();
	}
	
	@Bean
	@Conditional(LinuxCondition.class)
	public ListService linuxListService() {
		return new LinuxListService();
	}
	
}

启动类

package com.xhf.conditional;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);
		ListService listService = context.getBean(ListService.class);
		System.out.println(listService.showListCmd());
		context.close();
	}
	
}

组合注解与元注解

元注解就是可以注解到别的注解上的注解,被注解的注解称之为组合注解。

组合注解具有其构成的元注解的功能。

示例,定义一个注解

package com.xhf.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@ComponentScan
public @interface MyConfiguration {

	String[] value() default{};
	
}

定义一个Bean

package com.xhf.annotation;

import org.springframework.stereotype.Service;

@Service
public class DemoService {

	public void outPut() {
		System.out.println("从组合注解获得Bean	");
	}
	
}

配置类

package com.xhf.annotation;

@MyConfiguration("com.xhf.annotation")
public class DemoConfig {

}

启动类

package com.xhf.annotation;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
		DemoService demoService = context.getBean(DemoService.class);
		demoService.outPut();
		context.close();
	}
	
}

测试

添加测试用的依赖

		<dependency>
			<groupId>org.springframeworkgroupId>
			<artifactId>spring-testartifactId>
			<version>4.1.6.RELEASEversion>
		dependency>
	
		<dependency>
			<groupId>junitgroupId>
			<artifactId>junitartifactId>
			<version>4.11version>
		dependency>

定义一个 Bean

package com.xhf.test;

public class TestBean {
	
	
	private String context;

	public TestBean(String context) {
		super();
		this.context = context;
	}
	
	public String getContext() {
		return context;
	}

	public void setContext(String context) {
		this.context = context;
	}
	
	
	
}

配置类,不同profile实例化不同的Bean

package com.xhf.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class TestConfig {

	@Bean
	@Profile("dev")
	public TestBean devTestBean() {
		return new TestBean(" form dev profile ");
	}
	
	@Bean
	@Profile("prod")
	public TestBean prodTestBean() {
		return new TestBean(" form prod profile ");
	}
	
	
}

测试用例,

@RunWith(SpringJUnit4ClassRunner.class) 它提供了 Spring Testcontext Framework的功能。
@ContextConfiguration(classes={TestConfig.class}) 配置配置文件。
@ActiveProfiles(“prod”) 去定活动的profile

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.xhf.test.TestBean;
import com.xhf.test.TestConfig;

import junit.framework.Assert;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={TestConfig.class})
@ActiveProfiles("prod")

public class DemoBeanIntegrationTests {

	@Autowired
	private TestBean testBean;
	
	
	@Test
	public void prodBean() {
		String expect = " form prod profile ";
		String actual = testBean.getContext();
		Assert.assertEquals(expect, actual);
	}
}

你可能感兴趣的:(Spring,学习)