SpringBoot(二)

對象的初始化和銷燬

Profile

Profile爲再不一樣環境下使用不一樣的配置提供了支持(開發啊環境下的配置和生產環境下的配置確定是不同的)javascript

  1. 經過設定Enviroment的ActiveProfiles來設定當前的context須要使用的配置環境。再開發中使用@Profile註解類或者方法,達到不一樣的狀況下選擇實例化不一樣的Bean。html

  2. 經過設定JVM的spring.profiles.active參數來設置環境變量.java

  3. Web項目設置再Servlet的context parameter中。 servlet2.5如下jquery

    <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>linux

Servlet3.0以上web

例子: DemoBean.javaajax

public class DemoBean {

private String content;

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

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
}

ProfileConfig.java(配置)spring

@Configuration
public class ProfileConfig {
	[@Bean](https://my.oschina.net/bean)
	@Profile("dev")
	public DemoBean devDemoBean() {
		return new DemoBean("from development profile");
	}
	[@Bean](https://my.oschina.net/bean)
	@Profile("prod")
	public DemoBean prodDemoBean() {
		return new DemoBean("from production profile");
	}
}

測試代碼:編程

@Test
	public void testMethod4(){
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.getEnvironment().setActiveProfiles("prod");//設置環境
		context.register(ProfileConfig.class);
		context.refresh();//此處要刷新容器,不然就沒法知道設置的是什麼環境

		DemoBean demoBean = context.getBean(DemoBean.class);
		System.out.println(demoBean.getContent());
		context.close();
	}

執行結果:windows

事件(Application Event)

Spring的事件(Application Event)爲Bean與Bean之間的消息痛惜提供了支持。當Bean處理完一個任務以後,但願另一個Bean知道,此時咱們須要讓另一個Bean監聽當前Bean所發送的事件。

Spring的事件須要遵頊以下流程

  • 自定義事件,集成ApplicationEvent

    public class DemoEvent extends ApplicationEvent {
      	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;
      	}
      }
  • 定義事件監聽器,實現ApplicationListener

    public class DemoListener implements ApplicationListener<DemoEvent> {
      	@Override
      	public void onApplicationEvent(DemoEvent event) {
      	String msg = event.getMsg();
      		System.out.println("接收到消息:"+msg);
      	}
      }
  • 使用容器發佈事件

    @Component
      public class DemoPublisher {
      	@Autowired
      	ApplicationContext applicationContext;
    
      	public void publish(String msg) {
      		applicationContext.publishEvent(new DemoEvent(this, msg));
      	}
      }

測試代碼:

@Test
	public void testMethod5(){
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
		DemoPublisher demoPublisher = (DemoPublisher)context.getBean(DemoPublisher.class);
		demoPublisher.publish("test demo");
		context.close();
	}

執行結果:

接收到消息:test demo

Spring Aware

Spring Aware的目的是爲了讓Bean得到Spring容器的服務,由於ApplicationContext接口集成了MessageSource接口,ApplicationEventPublisher接口和ResourceLoader接口,因此Bean集成了ApplicationContextAware能夠得到Spring容器的全部服務,但原則上咱們仍是用到了什麼接口就實現什麼接口。

例子:

AwareService.java 該類實現了BeanAware獲取bean的名字,實現ResourceLoaderAware獲取資源的內容

@Service
public class AwareService implements BeanNameAware,ResourceLoaderAware{
	private String beanName;
	private ResourceLoader loader;

	@Override
	public void setBeanName(String s) {
		this.beanName=s;
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
	this.loader=resourceLoader;
	}

	public void getResult() {
		System.out.println("Bean的名字爲:"+beanName);
		Resource resource = loader.getResource("classpath:com/flexible/aware/demo.text");
		try {
			System.out.println("ResourceLoader加載的文件內容爲:" + IOUtils.toString(resource.getInputStream()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

AwareConfig.java

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

測試代碼:

@Test
	public void testMethod6(){
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class);
		AwareService awareService = context.getBean(AwareService.class);
		awareService.getResult();
	}

執行結果:

Bean的名字爲:awareService

ResourceLoader加載的文件內容爲:test demo

多線程

Spring是經過多線程和併發編程時經過任務執行器(TaskExecutor)來實現的。使用ThreadPoolExecutor可實現一個基於線程池的TaskExecutor。開發中通常時異步的處理任務的,須要使用@EnableAsync在配置類中開啓對異步任務的支持,並經過在實際的執行的Bean的方法中使用@Asyn註解聲明其時一個異步任務.

例子:

配置類:

@Configuration
@EnableAsync//開啓異步任務的支持
@ComponentScan("com.flexible")
public class TaskExecutorConfig implements AsyncConfigurer{
	@Override
	public Executor getAsyncExecutor() {
		//返回一個帶線程池的taskExecutor
		ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
		taskExecutor.setCorePoolSize(5);
		taskExecutor.setMaxPoolSize(10);
		taskExecutor.setQueueCapacity(25);
		taskExecutor.initialize();
		return taskExecutor;
	}

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

任務執行類:

@Service
public class AsyncTaskService {
	//代表該方法時一個異步方法,若是註解時類級別,則編碼該類全部的方法都是異步的方法,而這裏的方法自動
	//被注入ThreadPoolTaskExecutor做爲TaskExecutor
	@Async
	public void executeAsynTask(Integer i) {
		System.out.println("執行異步任務1:" + i);
	}

	@Async
	public void executeAsynTask2(Integer i) {
		System.out.println("執行異步任務2:" + i);
	}
}

測試代碼:

@Test
	public void testMethod7(){
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskExecutorConfig.class);
		AsyncTaskService service = (AsyncTaskService)context.getBean(AsyncTaskService.class);
		for (int i=0;i<10;i++){
			service.executeAsynTask(i);
			service.executeAsynTask2(i);
		}
		context.close();
	}

執行結果:

計劃任務

從Spring3.1以後,籍化任務就變得簡單起來,在配置類使用@EnableScheduling來開啓對籍化任務的支持,而後要執行的計劃任務的方法上註解@Scheduled,聲明這是一個計劃任務.Spring經過@Scheduled支持多種類型的計劃任務,包含cron,fixDelay,fixRate等.

例子:

任務類:

@Service
public class ScheDuledTaskService {

	private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
	//經過@Scheduled聲明該方法時計劃任務,使用fixedRate屬性每隔固定時間執行
	@Scheduled(fixedRate = 5000) //1
	public void reportCurrentTime() {
		System.out.println("每隔五秒執行一次 " + dateFormat.format(new Date()));
	}
//使用cron屬性能夠按照指定的時間執行,
	@Scheduled(cron = "0/10 * * * * ?"  ) //2
	public void fixTimeExecution(){
		System.out.println("在指定時間 " + dateFormat.format(new Date())+"執行");
	}
}

配置類:

@Configuration
@ComponentScan("com.flexible")
@EnableScheduling//開啓計劃任務
public class ScheDuledTaskConfig {
}

測試代碼:

@Test
	public void testMethod8() throws InterruptedException {
		AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(ScheDuledTaskConfig.class);
		Thread.sleep(10000000);
	}

執行結果:

條件註解@Conditional

@Conditional根據知足某一個特定條件建立一個特定的Bean。

例子: 判斷條件類:

public class WindowsCondition implements Condition {
	@Override
	public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
		return conditionContext.getEnvironment().getProperty("os.name").contains("Windows");
	}
}

public class LinuxCondition implements Condition {
	@Override
	public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
		return conditionContext.getEnvironment().getProperty("os.name").contains("Linux");
	}
}

功能類:

public class WindowsService implements ListService {
	@Override
	public String showListCmd() {
		return "dir";
	}
}

public class LinuxListService implements ListService {
	@Override
	public String showListCmd() {
		return "ls";
	}
}

判斷類(根據條件判斷生成具體的對象)

@Configuration
public class ConditionConfig {
@Bean
@Conditional(WindowsCondition.class)
public ListService windowsService(){
	return new WindowsService();
}
@Bean
@Conditional(LinuxCondition.class)
public ListService linuxService(){
	return new LinuxListService();
}
}

測試代碼:

@Test
	public void testMethod9(){
		AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(ConditionConfig.class);
		ListService listService = context.getBean(ListService.class);
		System.out.println(context.getEnvironment().getProperty("os.name")+" 系統下的列表命令:"+listService.showListCmd());
	}

執行結果:

組合註解和元註解

Spring的註解主要用來配置注入的Bean,切面相關配置。使用類相同的註解在多個類中就會顯得囉嗦,這個就是模板代碼,是Spring原則中須要消除的。元註解是能夠註解到其餘註解上的註解,被註解的代碼成爲組合註解。

Spring MVC基本配置

Spring MVC的定製配置須要咱們的配置類集成一個WebMvcConfigurerAdapter類,而且使用@EnableWebMvc註解來開啓對Spring MVC的配置支持,這樣咱們就能夠重寫這個類的方法完成咱們的經常使用配置

靜態資源映射(在WebmvcConfig.java裏面配置)

/**
	 * 靜態資源映射配置
	 * @param registry
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/asserts/**")
			.addResourceLocations("classpath:/asserts/");//文件放置的地方
	}

攔截器

攔截器實現對每個請求處理先後進行相關的業務處理,相似於Servlet的Filter.可讓普通的Bean實現HandlerIntercepter接口或者繼承HandlerInterceptorAdapter類來實現自定義攔截器.經過重寫WebConfigurerAdapter的addInterceptors方法來註冊自定義的攔截器。

例子:

public class DemoInterceptor extends HandlerInterceptorAdapter {
	/**
	 * 請求執行前發生執行
	 * @param request
	 * @param response
	 * @param handler
	 * @return
	 * @throws Exception
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		long startTime = System.currentTimeMillis();
		request.setAttribute("startTime", startTime);
		return true;
	}

	/**
	 * 請求執行後完成執行
	 * @param request
	 * @param response
	 * @param handler
	 * @param modelAndView
	 * @throws Exception
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		long startTime = (Long) request.getAttribute("startTime");
		request.removeAttribute("startTime");
		long endTime = System.currentTimeMillis();
		System.out.println("本次請求處理時間爲:" + new Long(endTime - startTime) + "ms");

	}
}

註冊攔截器:

WebmvcConfig.java

@Configuration
@EnableWebMvc//開啓MVC的支持,沒有這句的話即便繼承了WebMvcConfigurerAdapter重寫了它的方法也是無效的
@ComponentScan("com.flexible")
public class WebmvcConfig extends WebMvcConfigurerAdapter{
	@Bean
	public InternalResourceViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setPrefix("/WEB-INF/classes/views/");
		viewResolver.setSuffix(".jsp");
		viewResolver.setViewClass(JstlView.class);
		return viewResolver;
	}

	/**
	 * 生成一個攔截器的bean
	 * @return
	 */
	@Bean
	public DemoInterceptor demoInterceptor(){
		return new DemoInterceptor();
	}
	/**
	 * 靜態資源映射配置
	 * @param registry
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/asserts/**")
				.addResourceLocations("classpath:/asserts/");//文件放置的地方
	}
	/**
	 * 註冊攔截器
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(demoInterceptor());
	}
}

快捷的ViewController

在開發中若是涉及到沒有任何業務的跳轉能夠獎跳轉統一的在配置類設置

只須要在配置類作以下配置:

@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/index").setViewName("/index");
	}

訪問http://localhost:8080/index

路徑匹配參數配置

訪問時若是加了後綴就會在訪問的時候後綴會被忽略

配置前:

/**
 * 訪問時若是加了後綴就會在訪問的時候後綴會被忽略
 */
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.setUseSuffixPatternMatch(false);//後綴不會被忽略
}

配置後:

文件上傳配置

在Spring的控制器中,經過MultipartFile file來接收文件,經過MultipartFile[] files接收多個文件上傳。

添加依賴:

<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.1</version>
		</dependency>

添加上傳文件的jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
	<html>
	<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>upload page</title>
	</head>
	<body>

	<div class="upload">
		<form action="upload" enctype="multipart/form-data" method="post">
			<input type="file" name="file"/><br/>
			<input type="submit" value="上傳">
		</form>
	</div>

	</body>
	</html>

配置跳轉頁面

/**
	 * 在開發中若是涉及到沒有任何業務的跳轉能夠獎跳轉統一的在配置類設置。
	 * @param registry
	 */
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/index").setViewName("/index");
		//添加了轉向upload頁面的viewController
		registry.addViewController("/toUpload").setViewName("/upload");
	}

添加控制器:

@Controller
public class UploadController {
	/**
	 * @param file MultipartFile file是接受上傳的文件
	 * @return
	 */
	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	public @ResponseBody String upload(MultipartFile file) {
		try {
			//FileUtils.writeByteArrayToFile快速寫文件到磁盤.
			FileUtils.writeByteArrayToFile(new File("e:/upload/" + file.getOriginalFilename()), file.getBytes());
			return "ok";
		} catch (IOException e) {
			e.printStackTrace();
			return "error" + e.getMessage();
		}
	}
}

自定義HttpMessageConverter

HttpMessageConverter是用來處理request和response裏的數據的。Spring爲咱們的內置了大量的HttpMessageConverter,好比MappingJack2HttpMessageConverter,StringHttpMessageConverter等。

MyMessageConverter.java(自定義的HttpMessageConverter)

//1.此處集成AbstractHttpMessageConverter接口實現自定義的HttpMessageConverter
	public class MyMessageConverter extends AbstractHttpMessageConverter<DemoObj> {
		//2.新建一個自定義的媒體類型application/x-demo
	   public MyMessageConverter(){
		   super(new MediaType("application","x-demo", Charset.forName("UTF-8")));
	   }

		//3重寫readInternal,讀取請求的數據,代碼代表咱們處理由"-"間隔開的數據,最後轉換成DemoObj對象
		@Override
		protected DemoObj readInternal(Class<? extends DemoObj> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
		  String temp = StreamUtils.copyToString(httpInputMessage.getBody(),Charset.forName("UTF-8"));
		  String[] tempArr = temp.split("-");
			return new DemoObj(Long.valueOf(tempArr[0]),tempArr[1]);
		}
		//4.代表MyMessageConverter只處理DemoObj這個類
		@Override
		protected boolean supports(Class<?> aClass) {
			return DemoObj.class.isAssignableFrom(aClass);
		}
		//5重寫writeInternal,處理如何輸出數據到response,在這裏加上了test
		@Override
		protected void writeInternal(DemoObj demoObj, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
		String out = "test:"+demoObj.getId()+" * "+demoObj.getName();
		httpOutputMessage.getBody().write(out.getBytes());
		}
	}

配置自定義的HttpMessageConverter的Bean,在SPringle Mvc裏註冊HttpMessageConverter的兩個方法: 1.configureMessageConverters:重載會覆蓋調SpringMVC默認註冊的多個HttpMessageConverter.

2.extendMessageConverters:僅添加一個自定義的HttpMessageConverter,不覆蓋默認註冊HttpMessageConverter.

/**
	 * 註冊自定義的HttpMessageConverters
	 * 僅添加一個自定義的HttpMessageConverter,不覆蓋默認註冊HttpMessageConverter
	 * @param converters
	 */
	@Override
	public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
	converters.add(converter());
	}

	/**
	 * 自定義Bean
	 * @return
	 */
	@Bean
	public MyMessageConverter converter(){
		return new MyMessageConverter();
	}

頁面代碼

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>HttpMessageConverter Demo</title>
</head>
<body>
	<div id="resp"></div><input type="button" onclick="req();" value="請求"/>
<script src="assets/js/jquery.js" type="text/javascript"></script>
<script>
	function req(){
		$.ajax({
			url: "convert",
			data: "1-wangyunfei", //1
			type:"POST",
			contentType:"application/x-demo", //2
			success: function(data){
				$("#resp").html(data);
			}
		});
	}

</script>
</body>
</html>

請求結果:

服務端推送技術.

SSE(Server Send Event服務段發送事件)的服務器端推送和基於Servlet3.0+的異步方法特性,其中第一種方式須要新式的瀏覽器的支持,第二種是跨瀏覽器的。

服務端:

@Controller
public class SseController {
	/**
	 * 這裏使用輸出的媒體類型爲text/event-stream,也是服務器端SSE的支持。
	 * @return
	 */
	@RequestMapping(value="/push",produces="text/event-stream") //1
	public @ResponseBody String push(){
		Random r = new Random();
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return "data:Testing 1,2,3" + r.nextInt() +"\n\n";
	}
}

客戶端:

<script type="text/javascript">
 if (!!window.EventSource) { //1
	   var source = new EventSource('push'); 
	   s='';
	   source.addEventListener('message', function(e) {//2
		   s+=e.data+"<br/>";
		   $("#msgFrompPush").html(s);

	   });

	   source.addEventListener('open', function(e) {
			console.log("鏈接打開.");
	   }, false);

	   source.addEventListener('error', function(e) {
			if (e.readyState == EventSource.CLOSED) {
			   console.log("鏈接關閉");
			} else {
				console.log(e.readyState);    
			}
	   }, false);
	} else {
			console.log("你的瀏覽器不支持SSE");
	}
</script>

推送:

使用異步的方式:

配置類加跳轉路徑而且開啓計劃任務,除此以外還需開啓異步調用:

//條轉到異步獲取數據
		registry.addViewController("/async").setViewName("/async");

//開啓計劃任務
	@Configuration
	@EnableWebMvc
	@EnableScheduling//開啓計劃任務
	@ComponentScan("com.flexible")
	public class WebmvcConfig extends WebMvcConfigurerAdapter{

public class WebInitializer implements WebApplicationInitializer{
		@Override
		public void onStartup(ServletContext servletContext) throws ServletException {
			AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
			ctx.register(WebmvcConfig.class);
			ctx.setServletContext(servletContext); //2

			ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx)); //3
			servlet.addMapping("/");
			servlet.setLoadOnStartup(1);
			servlet.setAsyncSupported(true);//1支持異步
	}
	}

控制器:

@Controller
	public class AsyncController {

		@Autowired
		PushService pushService;

		/**
		 * 異步任務的實現經過控制器從另一個線程返回一個DeferredResult,這裏的DeferredResult
		 * 是從pushService中獲取
		 * @return
		 */
		@RequestMapping("/defer")
		@ResponseBody
		public DeferredResult<String> defferredCall() {
			return pushService.getAsyncUpdate();
		}

	}

服務層:

@Service
	public class PushService {

	private DeferredResult<String> deferredResult;

		public DeferredResult<String> getAsyncUpdate() {
			deferredResult = new DeferredResult<>();
			return deferredResult;
		}
		@Scheduled(fixedRate = 5000)
		public void refresh(){
			if (deferredResult!=null){
				deferredResult.setResult(new Long(System.currentTimeMillis()).toString());
			}
		}
	}

頁面代碼以下:

<script type="text/javascript" src="assets/js/jquery.js"></script>
	<script type="text/javascript">

		deferred();//1

		function deferred(){
			$.get('defer',function(data){
				console.log(data); 
				deferred(); 
			});
		}
	</script>

執行結果:

相關文章
相關標籤/搜索