对象的初始化和销毁
Profile
Profile为再不同环境下使用不同的配置提供了支持(开发啊环境下的配置和生产环境下的配置肯定是不一样的)
-
通过设定Enviroment的ActiveProfiles来设定当前的context需要使用的配置环境。再开发中使用@Profile注解类或者方法,达到不同的情况下选择实例化不同的Bean。
-
通过设定JVM的spring.profiles.active参数来设置环境变量.
-
Web项目设置再Servlet的context parameter中。 servlet2.5以下
<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>
Servlet3.0以上
例子: DemoBean.java
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(配置)
@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();
}
执行结果:
事件(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");
}
路径匹配参数配置
访问时如果加了后缀就会在访问的时候后缀会被忽略
配置前:
/**
* 访问时如果加了后缀就会在访问的时候后缀会被忽略
*/
@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>
执行结果: