Spring使用_进阶
概述
本文主要写了几个关于Spring Aware,多线程,计划任务(定时任务),条件注解,组合注解,元注解,Spring测试的小例子以及关于@Enable*注解的工作原理的理解。
Spring Aware
简介:
Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的。即你可以将你的容器替换成别的容器,如Google Guice,这是Bean之间的耦合度很低。
但是在实际项目中,有时会不可避免的要用到Spring容器本身的功能资源,这是你的Bean必须要意识到Spring容器的存在,才能调用Spring所提供的资源,这就是Spring Aware。
其实Spring Aware本来就是Spring设计用来框架内部使用的,若使用了Spring Aware,你的Bean将会和Spring框架耦合。
例子:
package com.wisely.highlight_spring4.ch3.aware; 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; import java.io.IOException; /**
* Spring Aware演示Bean
* 实现BeanNameAware、ResourceLoaderAware接口,获得Bean名称和资源加载的服务。
*/
@Service
public class AwareService implements BeanNameAware, ResourceLoaderAware {// private String beanName;
private ResourceLoader loader;
//实现ResourceLoaderAware需重写setResourceLoader方法
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {//
this.loader = resourceLoader;
}
//实现BeanNameAware需重写setBeanName方法
@Override
public void setBeanName(String name) {//
this.beanName = name;
} public void outputResult() {
System.out.println("Bean的名称为:" + beanName);
Resource resource = loader.getResource("classpath:com/wisely/highlight_spring4/ch3/aware/test.txt");
try {
System.out.println("ResourceLoader加载的文件内容为: " + IOUtils.toString(resource.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Spring Aware演示Bean
package com.wisely.highlight_spring4.ch3.aware; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; /**
* 配置类
*/
@Configuration
@ComponentScan("com.wisely.highlight_spring4.ch3.aware")
public class AwareConfig { }
配置类
package com.wisely.highlight_spring4.ch3.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();
}
}
运行
多线程
简介:
Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。
而实际开发中任务一般是非阻碍的(异步的),所以我们要在配置类中通过@EnableAsync开启对异步任务的支持,并通过在实际执行的Bean的方法中使用@Async注解来声明其是一个异步任务。
例子:
package com.wisely.highlight_spring4.ch3.taskexecutor; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; /**
* 任务执行类
*/
@Service
public class AsyncTaskService {
/**
* 通过@Async注解表明该方法是一个异步方法,如果注解在类级别,则表明该类所有的方法都是异步方法。
* 而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor。
* @param i
*/
@Async
public void executeAsyncTask(Integer i){
System.out.println("执行异步任务: "+i);
} @Async
public void executeAsyncTaskPlus(Integer i){
System.out.println("执行异步任务+1: "+(i+1));
} }
任务执行类
package com.wisely.highlight_spring4.ch3.taskexecutor; 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; import java.util.concurrent.Executor; /**
* 配置类
*/
@Configuration
@ComponentScan("com.wisely.highlight_spring4.ch3.taskexecutor")
@EnableAsync //使用@EnableAsync注解开启异步任务支持
public class TaskExecutorConfig implements AsyncConfigurer {//实现AsyncConfigurer接口 /**
* 重写AsyncConfigurer接口的getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor
* 这样就可以获得一个基于线程池的TaskExecutor
* @return
*/
@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.wisely.highlight_spring4.ch3.taskexecutor; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /**
* 运行
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskExecutorConfig.class);
AsyncTaskService asyncTaskService = context.getBean(AsyncTaskService.class);
for (int i = 0; i < 10; i++) {
asyncTaskService.executeAsyncTask(i);
asyncTaskService.executeAsyncTaskPlus(i);
}
context.close();
}
}
运行
计划任务(定时任务)
简介:
从Spring3.1开始,计划任务在Spring中的实现变得异常的简单。首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。
Spring通过@Scheduled支持多种类型的计划任务,包含cron、fixDelay、fixRate等。
例子:
package com.wisely.highlight_spring4.ch3.taskscheduler; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import java.text.SimpleDateFormat;
import java.util.Date; /**
* 计划任务执行类
*/
@Service
public class ScheduledTaskService { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); /**
* 通过@Scheduled注解声明该方法是计划任务,使用fixedRate属性每隔固定时间执行。
*/
@Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
System.out.println("每隔五秒执行一次 " + dateFormat.format(new Date()));
} /**
* 1:使用cron属性可按照指定时间执行,这里是每天11点28分执行
* 2:cron是UNIX和类UNIX(Linux)系统下的定时任务
*/
@Scheduled(cron = "0 28 11 ? * *")
public void fixTimeExecution() {
System.out.println("在指定时间 " + dateFormat.format(new Date()) + "执行");
}
}
计划任务执行类
package com.wisely.highlight_spring4.ch3.taskscheduler; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling; /**
* 配置类
*/
@Configuration
@ComponentScan("com.wisely.highlight_spring4.ch3.taskscheduler")
@EnableScheduling //通过@EnableScheduling注解开启对计划任务的支持。
public class TaskSchedulerConfig { }
配置类
package com.wisely.highlight_spring4.ch3.taskscheduler; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /**
* 运行
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskSchedulerConfig.class);
}
}
运行
条件注解(@Conditional)
简介:
一句话来说:@Conditional注解可以根据特定条件创建特定的Bean。
例子:
package com.wisely.highlight_spring4.ch3.conditional; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; /**
* 判断条件定义
* 判断Linux的条件
*/
public class LinuxCondition implements Condition {//实现Condition接口 /**
* 重写Condition接口的matches方法来构造判断条件
* @param context
* @param metadata
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux");
}
}
判断条件定义_Linux
package com.wisely.highlight_spring4.ch3.conditional; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; /**
* 判断条件定义
* 判断Windows的条件
*/
public class WindowsCondition implements Condition {//实现Condition接口 /**
* 重写Condition接口的matches方法来构造判断条件
* @param context
* @param metadata
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
判断条件定义_Windows
package com.wisely.highlight_spring4.ch3.conditional; /**
* 不同系统下Bean的类
* 接口
*/
public interface ListService {
public String showListCmd();
}
不同系统下Bean的类_接口
package com.wisely.highlight_spring4.ch3.conditional; /**
* 不同系统下Bean的类
* Linux下所要创建的Bean的类
*/
public class LinuxListService implements ListService{ @Override
public String showListCmd() {
return "ls";
}
}
不同系统下Bean的类_Linux
package com.wisely.highlight_spring4.ch3.conditional; /**
* 不同系统下Bean的类
* Windows下所要创建的Bean的类
*/
public class WindowsListService implements ListService { @Override
public String showListCmd() {
return "dir";
}
}
不同系统下Bean的类_Windows
package com.wisely.highlight_spring4.ch3.conditional; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; /**
* 配置类
*/
@Configuration
public class ConditionConfig { /**
* 通过@Conditional注解,符合Windows条件则实例化windowsListService
* @return
*/
@Bean
@Conditional(WindowsCondition.class)
public ListService windowsListService() {
return new WindowsListService();
} /**
* 通过@Conditional注解,符合Linux条件则实例化linuxListService
* @return
*/
@Bean
@Conditional(LinuxCondition.class)
public ListService linuxListService() {
return new LinuxListService();
}
}
配置类
package com.wisely.highlight_spring4.ch3.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(context.getEnvironment().getProperty("os.name") + "系统下的列表命令为: " + listService.showListCmd());
context.close();
}
}
运行
组合注解与元注解
简介:
从Spring2开始,为了响应JDK1.5推出的注解功能,Spring开始大量加入注解来替代xml配置。Spring的注解主要用来配置和注入Bean,以及AOP相关配置。
随着注解的大量使用,尤其相同的多个注解用到各个类或者方法中,会相当繁琐。这就形成了所谓的样板代码(boilerplate code),是Spring涉及原则中要消除的代码。
所谓的元注解其实就是可以注解到别的注解上的注解,被注解的注解称之为组合注解,组合主借具备注解在上面的元注解的功能。Spring的很多注解都可以作为元注解,而且Spring本身已经有很多组合注解。如@Configuration就是一个组合@Component注解,表明这个类其实也是一个Bean。
例子:
package com.wisely.highlight_spring4.ch3.annotation; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import java.lang.annotation.*; /**
* 组合注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //组合@Configuration元注解
@ComponentScan //组合@ComponentScan注解
public @interface WiselyConfiguration { String[] value() default {}; //覆盖value参数 }
组合注解
package com.wisely.highlight_spring4.ch3.annotation; import org.springframework.stereotype.Service; /**
* 服务Bean
*/
@Service
public class DemoService { public void outputResult(){
System.out.println("从组合注解配置照样获得的bean");
}
}
服务Bean
package com.wisely.highlight_spring4.ch3.annotation; /**
* 配置类
* 使用@WiselyConfiguration注解替代@Configuration和@ComponentScan注解。
*/
@WiselyConfiguration("com.wisely.highlight_spring4.ch3.annotation")
public class DemoConfig { }
配置类
package com.wisely.highlight_spring4.ch3.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.outputResult();
context.close();
}
}
运行
测试
简介:
测试是开发工作中不可缺少的部分。单元测试只针对当前开发的类和方法进行测试,可以简单通过模拟依赖来实现,对运行环境没有依赖;但是仅仅进行单元测试是不够的,它只能验证当前类或方法能否正常工作,而我们想要知道系统的各个部分组合在一起是否能正常工作,这就是集成测试存在的意义。
集成测试一般需要来自不同层的不同对象的交互,如数据库、网络连接、IoC容器等。其实我们也经常通过运行程序,然后通过自己操作来完成类似于集成测试的流程。集成测试为我们提供了一种无须部署或运行程序来完成验证系统各部分是否能正常协同工作的能力。
Spring通过Spring TestContex Framework对集成测试提供顶级支持。它不依赖于特定的测试框架,既可以用Junit,也可以用TestNG。
例子:
package com.wisely.highlight_spring4.ch3.fortest; /**
* 测试Bean
*/
public class TestBean { private String content; public TestBean(String content) {
super();
this.content = content;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
}
}
测试Bean
package com.wisely.highlight_spring4.ch3.fortest; 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("from development profile");
} @Bean
@Profile("prod")
public TestBean prodTestBean() {
return new TestBean("from production profile");
}
}
测试配置类
package com.wisely.highlight_spring4.ch3.fortest; import org.junit.Assert;
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; /**
* 测试类
*/
@RunWith(SpringJUnit4ClassRunner.class) //SpringJUnit4ClassRunner在JUnit环境下提供Spring TestContext Framework的功能。
@ContextConfiguration(classes = {TestConfig.class}) //@ContextConfiguration用来加载配置ApplicationContext,其中classes属性用来加载配置类。
@ActiveProfiles("prod") //@ActiveProfiles用来声明活动的profile。
public class DemoBeanIntegrationTests { @Autowired //可以使用普通的@Autowired注解注入Bean。
private TestBean testBean; @Test //测试代码,通过JUnit的Assert来校验结果是否和预期一致。
public void prodBeanShouldInject() {
String expected = "from production profile";
String actual = testBean.getContent();
Assert.assertEquals(expected, actual);
}
}
测试类
@Enlable*注解
简介:
在Spring中,大量的使用到了@Enlable*注解来开启各种功能,避免了我们自己配置大量的代码,大大降低了使用难度。下面我们来看一下它的实现原理。
通过观察这些@Enlable*注解的源码,我们发现所有的注解都有一个@Import注解,@Import是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean。
这些导入的配置方式主要分为以下三种类型。
第一类:
直接导入配置类。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.scheduling.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.Import; @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}
直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的Bean,源码如下。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.scheduling.annotation; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role; @Configuration
public class SchedulingConfiguration {
public SchedulingConfiguration() {
} @Bean(
name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
)
@Role(2)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
第二类:
依据条件选择配置类。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.scheduling.annotation; import java.lang.annotation.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.AdviceMode;
import org.springframework.context.annotation.Import; @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class; boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default 2147483647;
}
AsyncConfigurationSelector通过条件来选择需要导入的配置类,AsyncConfigurationSelector的根接口为ImportSelector,这个接口需重写selectImports方法,在此方法中进行事先条件判断。
下面的源码中,若adviceMode为PORXY,则返回ProxyAsyncConfiguration这个配置类,若activeMode为ASPECTJ,则返回AspectJAsyncConfiguration配置类。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.scheduling.annotation; import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector; public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; public AsyncConfigurationSelector() {
} public String[] selectImports(AdviceMode adviceMode) {
switch(adviceMode) {
case PROXY:
return new String[]{ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
default:
return null;
}
}
}
第三类:
动态注册Bean。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.context.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; @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
}
AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类,通过重写方法:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 。
其中,AnnotationMetadata参数用来获得当前配置类上的注解,BeanDefinitionRegistry参数用来注册Bean。源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.context.annotation; import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata; class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
AspectJAutoProxyRegistrar() {
} public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
} }
}
最新文章
- 基于Z-WAVE 协议的LED智能照明系统的研究笔记
- oracle DBlink 【转】
- js断点调试心得
- mvc ajax dropdownlist onchang事件响应
- 抽象数据类型ADT
- 趣谈PHP 多态
- 数据分页 THINKPHP3.2 分页 三种分页方法
- unity3d结合轮廓显示,实现完整的框选目标(附Demo代码)
- Microsoft SQL Server 管理 (常用管理及维护命令)
- 检测字节流是否是UTF8编码
- BackgroundWorker组件使用总结
- PHP学习1 — PHP文件处理
- 201521123099 《Java程序设计》第2周学习总结
- iOS中 MPMoviePlayer 实现视频音频播放 作者:韩俊强
- Vagrant将下载好的镜像装载到本地中
- 消息队列介绍和SpringBoot2.x整合RockketMQ、ActiveMQ 9节课
- 2017-2018-2 20155303 『网络对抗技术』Exp3:免杀原理与实践
- nginx url问题
- [SDOI2010]地精部落[计数dp]
- 如何控制iOS的导航栏和状态栏的样式