1. Spring Aware

Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的。即你可以将你的容器替换成别的容器。

实际项目中,不可避免地会用到Spring容器本身的功能资源,这时的Bean必须意识到Spring容器的存在,才能调用Spring所提供的资源,这就是所谓的Spring Aware。

Spring提供的Aware接口如下:

BeanNameAware 获取到容器中Bean的名称
BeanFactoryAware 获得当前bean factory,这样可以调用容器的服务
ApplicationContextAware 当前的Applicaion context, 这样可以调用容器的服务
MessageSourceAware 获得message source,这样可以获得文本信息
ApplicationEventPublisher 应用事件发布器,可以发布事件
ResourceLoaderAware 获得资源加载器,可以获得外部资源文件

Spring Aware的目的是为了让Bean获得Spring容器的服务。

示例:

1) 创建一个test.txt,内容随意

2) Spring Aware演示Bean

 package com.ws.study.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; // 实现BeanNameAware、ResourceLoaderAware接口,获得Bean名称和资源加载的服务
@Service
public class AwareService implements BeanNameAware, ResourceLoaderAware{ private String beanName;
private ResourceLoader loader; // 实现ResourceLoaderAware需要重写setResourceLoader
public void setResourceLoader(ResourceLoader resourceLoader) {
this.loader = resourceLoader;
} // 实现BeanNameAware需重写setBeanName方法
public void setBeanName(String name) {
this.beanName = name;
} public void outputResult(){
System.out.println("Bean的名称为:"+beanName);
Resource resource = loader.getResource("classpath:com/ws/study/aware/test.txt");
try {
System.out.println("ResourceLoader加载的文件内容为:"
+IOUtils.toString(resource.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
}

3) 配置类

 package com.ws.study.aware;

 import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component; @Component
@ComponentScan("com.ws.study.aware")
public class AwareConfig {
}

4) 运行类

 package com.ws.study.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();
}
}

5) 运行结果

 六月 03, 2018 10:56:12 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@dbe50f: startup date [Sun Jun 03 22:56:12 CST 2018]; root of context hierarchy
Bean的名称为:awareService
ResourceLoader加载的文件内容为:Hello Spring!
六月 03, 2018 10:57:24 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@dbe50f: startup date [Sun Jun 03 22:56:12 CST 2018]; root of context hierarchy

2. 多线程

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

示例:

1) 配置类

 package com.ws.study.taskexecutor;

 import java.util.concurrent.Executor;

 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component; @Component
@ComponentScan("com.ws.study.taskexecutor")
// 利用@EnableAysnc注解开启异步任务支持
@EnableAsync
public class TaskExecutorConfig implements AsyncConfigurer{ // 配置类实现AsyncConfigure接口并重写getAsyncExecutor方法,
// 并返回一个ThreadPoolTaskExecutor
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor;
} public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
} }

2) 任务执行类

 package com.ws.study.taskexecutor;

 import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; @Service
public class AsyncTaskService { // 通过@Async注解表明该方法是个异步方法,如果注解在类级别,则表明该类所有的方法都是异步方法
// 而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
@Async
public void executeAsyncTask(Integer number){
System.out.println("执行异步任务: "+number);
} @Async
public void executeAsyncTaskPlus(Integer number){
System.out.println("异步执行任务+1: "+(number+1));
}
}

3) 运行类

 package com.ws.study.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();
}
}

4) 运行结果:结果是并发执行而不是顺序执行

 六月 03, 2018 11:17:41 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3df479: startup date [Sun Jun 03 23:17:41 CST 2018]; root of context hierarchy
六月 03, 2018 11:17:41 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'taskExecutorConfig' of type [class com.ws.study.taskexecutor.TaskExecutorConfig] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
六月 03, 2018 11:17:41 下午 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor initialize
信息: Initializing ExecutorService
六月 03, 2018 11:17:41 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'org.springframework.scheduling.annotation.ProxyAsyncConfiguration' of type [class org.springframework.scheduling.annotation.ProxyAsyncConfiguration$$EnhancerBySpringCGLIB$$c683b4d7] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
六月 03, 2018 11:17:41 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3df479: startup date [Sun Jun 03 23:17:41 CST 2018]; root of context hierarchy
执行异步任务: 0
异步执行任务+1: 3
执行异步任务: 3
异步执行任务+1: 4
执行异步任务: 4
异步执行任务+1: 5
执行异步任务: 5
异步执行任务+1: 6
执行异步任务: 6
异步执行任务+1: 7
执行异步任务: 7
异步执行任务+1: 8
执行异步任务: 8
异步执行任务+1: 9
执行异步任务: 9
异步执行任务+1: 10
异步执行任务+1: 1
异步执行任务+1: 2
执行异步任务: 1
执行异步任务: 2

3. 计划任务

计划任务首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。通过@Scheduled支持多种类型的计划任务,包含cron, fixDelay, fixRate等

示例:

1) 计划任务执行类

 package com.ws.study.taskscheduler;

 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 format = new SimpleDateFormat("HH:mm:ss"); // 通过@Scheduled声明该方法是计划任务,使用fixRate属性每隔固定时间执行
@Scheduled(fixedRate = 5000)
public void reportCurrentTime(){
System.out.println("每隔五秒执行一次 "+format.format(new Date()));
} // 使用cron属性可按照指定时间执行,本例指定每天22点25分执行, cron是Linux系统下的定时任务
@Scheduled(cron = "0 24 22 ? * *")
public void fixTimeExecution(){
System.out.println("在指定时间 "+format.format(new Date()) + "执行");
}
}

2)  配置类

 package com.ws.study.taskscheduler;

 import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling; @Configuration
@ComponentScan("com.ws.study.taskscheduler")
// 通过@EnableScheduling注解开启对计划任务的支持
@EnableScheduling
public class TaskSchedulerConfig {
}

3) 运行类

 package com.ws.study.taskscheduler;

 import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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

4) 执行结果

 六月 07, 2018 10:23:37 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Thu Jun 07 22:23:37 CST 2018]; root of context hierarchy
六月 07, 2018 10:23:38 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'org.springframework.scheduling.annotation.SchedulingConfiguration' of type [class org.springframework.scheduling.annotation.SchedulingConfiguration$$EnhancerBySpringCGLIB$$83a8b643] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
每隔五秒执行一次 22:23:38
每隔五秒执行一次 22:23:43
每隔五秒执行一次 22:23:48
每隔五秒执行一次 22:23:53
每隔五秒执行一次 22:23:58
在指定时间 22:24:00执行
每隔五秒执行一次 22:24:03
每隔五秒执行一次 22:24:08

4. 条件注解@Conditional

@Conditional根据满足某一个特定条件创建一个特定的Bean。即根据特定条件来控制Bean的创建行为,这样可以利用这个特性进行一些自动的配置。

示例:

以不同的OS为例,通过实现Condition接口,并重写matcher方法来构造判断条件,若在windows系统下,则输出dir,若在linux下,则输出ls

1) 判断条件定义之判定Windows的条件

 package com.ws.study.conditional;

 import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; public class WindowsCondition implements Condition{ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
} }

2) 判定Linux条件之判定Linux的条件

 package com.ws.study.conditional;

 import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; public class LinuxCondition implements Condition{ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux");
}
}

3) 不同OS下Bean类之接口

 package com.ws.study.conditional;

 public interface ListService {
String showListCmd();
}

4) Windows下创建的Bean类

 package com.ws.study.conditional;

 public class WindowsListService implements ListService{
public String showListCmd() {
return "dir";
}
}

5) Linux下创建的Bean类

 package com.ws.study.conditional;

 public class LinuxListService implements ListService{

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

6) 配置类

 package com.ws.study.conditional;

 import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; @Configuration
@ComponentScan("com.ws.study.conditional")
public class ConditionConfig { @Bean
// 通过@Conditional注解,符合Windows条件则实例化windowsListService
@Conditional(WindowsCondition.class)
public ListService windowsListService(){
return new WindowsListService();
} @Bean
// 通过@Conditional注解,符合Linux条件则实例化linuxListService
@Conditional(LinuxCondition.class)
public ListService linuxListService(){
return new LinuxListService();
}
}

7) 运行类

 package com.ws.study.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();
}
}

8) 运行结果

 六月 07, 2018 10:48:27 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Thu Jun 07 22:48:27 CST 2018]; root of context hierarchy
六月 07, 2018 10:48:28 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Thu Jun 07 22:48:27 CST 2018]; root of context hierarchy
Windows 7系统下的命令为: dir

5. 组合注解与元注解

所谓元注解就是可以注解到别的注解上的注解,被注解的注解称之为组合注解,组合注解具备元注解的功能。Spring本身已经有很多组合注解,如@Configuration就是一个组合@Component注解,表明这个类其实也是一个Bean。

示例:

1) 组合注解示例

 package com.ws.study.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元注解
@Configuration
// 组合@ComponentScan元注解
@ComponentScan
public @interface WiselyConfiguration {
// 覆盖value参数
String[] value() default {};
}

2) 演示服务Bean

 package com.ws.study.annotation;

 import org.springframework.stereotype.Service;

 @Service
public class DemoService {
public void output(){
System.out.println("从组合注解配置中仍然可以获得Bean");
}
}

3) 组合注解配置类

 package com.ws.study.annotation;

 // 使用@WiselyConfiguration组合注解替代@Configuration和@ComponentScan
@WiselyConfiguration("com.ws.study.annotation")
public class DemoConfig {
}

4) 运行类

 package com.ws.study.annotation;

 import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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

5) 运行结果

 六月 12, 2018 11:11:37 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Tue Jun 12 23:11:37 CST 2018]; root of context hierarchy
六月 12, 2018 11:11:39 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Tue Jun 12 23:11:37 CST 2018]; root of context hierarchy
从组合注解配置中仍然可以获得Bean

6. @Enable*注解的工作原理

@EnalbeAspectAutoProxy开启对AspectJ自动代理的支持;@EnableAsync开启异步方法的支持等@Enable*注解可以开启一项功能,从而避免自己配置大量的代码。

所有的@Enable*注解,都包含了一个@Import注解,用于导入配置类,意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入方式主要分为三种类型:

1) 直接导入配置类

 @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling { }

@EnableScheduling直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了ScheduledAnnotationBeanPostProcessor的Bean

 @Configuration
public class SchedulingConfiguration { @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
} }

2) 依据条件选择配置类

 @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 Ordered.LOWEST_PRECEDENCE;
}

AsyncConfigurationSelector通过条件来选择需要导入的配置类,AsyncConfigurationSelector的根接口为ImportSelector,且重写了selectImports方法,在此方法内进行事先条件判断。此例中,若adviceMode为PORXY,则返回ProxyAsyncConfiguration配置类;若adviceMode为ASPECTJ,则返回AspectAsyncConfiguration配置类。

 public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

 	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; @Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { ProxyAsyncConfiguration.class.getName() };
case ASPECTJ:
return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
return null;
}
} }

3) 动态注册Bean

 @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; }

AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,该接口的作用是在运行时自动添加Bean到已有的配置类,通过重写方法:

 class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

 	@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
} }

其中,AnnotationMetadata参数用于获得当前配置类上的注解,BeanDefinitionRegistry参数用于注册Bean

7. 测试

集成测试提供了一种无须部署或运行程序来完成验证系统各部分是否正常协同工作的能力。Spring提供了一个SpringJunit4ClassRunner类。通过@ContextConfiguration来配置Application Context,通过@ActiveProfiles确定活动的profile。

示例:

1) 增加Spring测试的依赖包

 <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

2) 业务代码

 package com.ws.study.fortest;

 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;
} }

3) 配置类

 package com.ws.study.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");
}
}

4) 测试类,注意测试类写在src/test/java中

 package com.ws.study1;

 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; import com.ws.study.fortest.TestBean;
import com.ws.study.fortest.TestConfig; // SpringJUnit4ClassRunner在JUnit环境下提供Spring Test Context Framework的功能
@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration用来加载配置ApplicationContext,其中classes用来加载配置类
@ContextConfiguration(classes = {TestConfig.class})
// @ActiveProfiles用于声明活动的profile
@ActiveProfiles("prod")
public class DemoBeanIntegrationTests { // 可使用普通的@Autowired注入Bean
@Autowired
private TestBean testBean; @Test
public void prodBeanShouldInject(){
String expected = "from production profile";
String actual = testBean.getContent();
Assert.assertEquals(expected, actual);
}
}

最新文章

  1. PHP SPL(PHP 标准库)
  2. PHP防止跨域提交表单
  3. Derived Column 用法
  4. mysql查看表使用的数据库引擎
  5. 说说GET和POST方法的区别
  6. 第一个Json.Net Demo
  7. jquery 实践总结
  8. 生产库MySQL配置文件my.cnf详解
  9. Determining IP information for eth0… failed; no link present. Check cable
  10. 1031. Hello World for U (20)
  11. Ubuntu下的Samba服务器配置
  12. Spring事务管理----事物回滚
  13. C#高级编程(第九版) 知识点梳理
  14. Django 数据表更改
  15. topcoder srm 515 div1
  16. linux系统坏了,进不了登陆界面怎么办?
  17. github 仓库共享上传权限
  18. nginx配置跨域、gzip加速、代理详细讲解
  19. reCaptcha for ASP.NET
  20. ROS知识(7)----ROS命令中的单引号`和‘的混淆问题

热门文章

  1. webbrowser内容滚动(javascript内容无缝滚动)
  2. CHSpecialEffect(特效)
  3. Centos7 调整磁盘空间
  4. codevs1068(dp)
  5. Linux之sshkey密钥认证实战
  6. web安全-传输安全
  7. php excel开发01
  8. P1900 自我数
  9. cmake相关问题
  10. 18.Jewels and Stones(珠宝和石头)