学习笔记:JavaEE开发的颠覆者SpringBoot实战(三)spring 高级话题

资源信息都来自网络,本人只是记录作为个人笔记

一. Spring Aware

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

spring提供的Aware接口
BeanNameAware获取容器中的bean
BeanFactoryAware获取当前bean factory,这样可以调用容器的服务
ApplicationContextAware获取当前的application context,这样可以调用容器的服务
MessageSourceAware获得message source,这样可以获得文本信息
ApplicationEventPublisherAware应用时间发布器,可以发布事件
ResourceLooaderAware获取资源加载器,可以获得外部资源文件
//实现BeanNameAware,ResourceLoaderAware接口,获取bean名称和资源加载服务
public class AwareService implements BeanNameAware,ResourceLoaderAware{

    private String beanName;
    pirvate RresourceLoader loader;

    //实现ResourceLoaderAware需要重写setResourceLoader方法
    @Verride
    public void setResourceLoader(ResourceLoader resourceLoader){
        this.loader = resourceLoader;
    }

    //实现BeanNameAware需要重写setBeanName方法
    @Verride
    public void setBeanName(String name){
        this.beanName = name;
    }

    public void outputResult(){

        sout(beanName);
        Resource resource = loader.getResource("classpath:test.txt");
        sout(IOUtils.toString(resource.getInputStream()));
    }

}

二.多线程

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

1.配置类:

@Configuration
@ComponentScan("com.spring.demo.ch03.taskexecutor")
@EnableAsync  //该注解开启异步任务支持
public class TaskExecutorConfig implements AsyncConfigurer{
    
    //配置类实现AsyncConfigurer接口并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,这样我们就获得了一个基于线程池TaskExecutor
    @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;    
    }
    
}

2.任务执行类

@Service
public class AsyncTaskService{
    
    @Async //该注解表明该方法是一个异步方法,如果注解在类级别,则表明该类所欲的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
    public void executeAsyncTask(Integer i){
        sout("执行异步任务:"+i);
    }
    
    @Async
    public void executeAsyncTaskPlus(Integer i){
        sout("执行异步任务+1:"+(i+1));
    }
    
}

三.计划任务

从spring3.1开始,计划任务在spring中的实现变得异常的简单。首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是个计划任务。

Spring通过@Scheduled支持多种类型的计划任务,包含cron,fixDelay,fixRate等

1.计划任务执行类

@Service
public class ScheduledTaskService{
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Scheduled(fixedRate=5000) //通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行。
    public void reportCurrentTime(){
        System.out.println("每隔五秒执行一次:"+dateFormat.format(new Date()));
    }

    @Scheduled(cron="0 15 16 ? * *") //使用cron属性可按照指定时间执行。本例是每天16点15分执行
    public void fixTimeExecution(){
        System.out.println("在指定实际:"+dateFormat.format(new Date())+"执行");
    }
    
    
}

2.配置类

@Configuration
@ComponentScan("com.spring.demo.ch03.taskscheduler")
@EnableScheduling  //该注解开启对计划任务的支持
public class TaskSchedulerConfig{
}

​

四.条件注解@Conditonal

根据满足某一个特定条件创建一个特定的Bean。

比如,以不同的操作系统为条件,我们通过实现Condition接口,并重写其mathces方法来构造条件判断。若在Windows系统下运行程序,则输出列表名利为dir;若在linux操作系统下运行程序,则输出列表命令为ls

1.判断条件定义

//判定Windows的条件
public class WindowsCondition implements Condition{
    @Override
    public boolean mathces(ConditionContext context,AnnotatedTypeMetadata metadata){
        return context.getEnvironment.getProperty("os.name").contains("Windows");
    }
}

//判定Linux的条件
public class LinuxCondition implementsCondition{
    @Override
    public boolean mathces(ConditionContext context,AnnotatedTypeMetadata metadata){
        return context.getEnvironment.getProperty("os.name").contains("Linux");
    }
}

2.不同系统下Bean的类

//接口
public interface ListService{
    public String showListCmd();
}

//windows下所要创建的bean类
public class WindowsListService implements ListService{
    @Override
    public String showListCmd(){
        renturn "dir";
    }
}

//linux下所要创建的bean类
public class LinuxListService implements ListService{
    @Override
    public String showListCmd(){
        renturn "ls";
    }
}

3.配置类

@Configuration
public class ConditionConfig{
    @Bean
    @Conditional(SeindowsCondition.class) //符合windows条件则实例化WindowsListService
    public ListService windowsListService(){
        return new WindowsListService();
    }

    @Bean
    @Conditional(SeindowsCondition.class) //服务linux条件则实例化LinuxListService
    public ListService linuxListService(){
        return new LinuxListService();
    }


}

五.组合注解与元注解

所谓的元注解其实就是可以注解到别的注解上的注解,被注解的注解称为组合注解,组合注解具备元注解的功能。

spring有很多注解都可以作为元注解,而且spring本身已经有很多组合租界,如@Configuration就是一个组合@Component注解。

实例:将两个元注解@Configuration和@ComponentScan组成一个组合注解,这样我们就只需要一个注解就可以表示两个注解。

1.实例组合注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //元注解
@ComponentScan //元注解
public @interface WiselyConfiguration{
    String[] value() default{}; //覆盖value参数
}

2.新的配置类

@WiselyConfiguration("com.spring.demo.ch03.annotation")
public class DemoConfig{
}

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

通过简单的@Enable*来开启一项功能的支持,从而避免自己配置大量的代码,大大减低使用难度。

@EnableAspectJautoProxy

开启对AspectJ自动代理的支持

@EnableAsync开启异步方法的支持(多线程)
@EnableScheduling开启计划任务的支持
@EnableWebMvc开启WebMVC的配置支持
@EnableConfigurationProperties开启对@ConfigurationProperties注解配置bean的支持
@EnableJpaRepositories开启对SpringData JPA Repository的支持
@EnableTransactionManagement开启注解式事务支持
@EnableCaching开启注解式的缓存支持

所有的注解都有要给@Import注解,用来导入配置类的,这就意味着这些自动开启的实现,其实是导入了一些自动配置的bena。这些导入的配置方式组要分为三种类型:

1.第一类:直接导入配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling{
//开启任务计划
}

//直接导入配置类SchedulingConfiguration,这个类注解了@Configration,而且注册了一个scheduledAnnotationProcessor的bean

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
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方法,在此方法内进行事先条件判断。
*/
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync>{
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME=
        "org.springframework,scheduling.aspectj.AspectJAsyncConfiguration";
    
    @Verride
    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 EnableAsperctJAutoProxy{ //开启对AsperctJ自动代理的支持
    boolean proxyTargetClass() default false;
}


/**
*AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,
*ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类
*重写方法registerBeanDefinitions,AnnotationMetadata参数用来获取当前配置类上的注解;
*BeanDefinitinRegistry参数用来注册Bean
*/
public class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar{
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitinRegistry registry){
        AopConfigUtils.registerAspecyJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata,EnableAspectJAutoProxy.class);
        if(enableAJAutoProxy .getBoolean("proxyTargetClass")){
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
    }
    

}

七.测试

单元测试:针对当前开发的类和方法进行测试,可以简单通过模拟依赖来实现,对运行环境没有依赖;

集成测试:需要来自不同层的不同对象的交互,如数据库,网络连接,IOC容器等。

spring通过Spring TestContext Framework对集成测试提供顶级支持。它不依赖于特定的测试框架,既可以使用Junit,也可以使用TestNG。

Spring提供了一个SpringJUnit4ClassRunner类,它提供了Spring TestContext Framework的功能。通过@ContextConfiguration来配置Application Context,通过@ActivePofiles确定活动的profile

依赖:

<!--spring test 支持-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.14.RELEASE</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

业务代码:
 

public class DemoBean{

    private String content;

    public TestBean(String content){
        super();
        this.content = content;
    }

    public String getContent(){
        return content;
    }

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

}

配置类

@Configuration
public class DemoConfig{
    @Bean
    @Profile("dev")
    public TestBean devTestBean(){
        return new TestBean("from development profile");
    }

    @Bean
    @Profile("prod")
    public TestBean prodTestBean(){
        return new TestBean("from production profile");
    }

}

测试

@RunWith(SpringJUnit4ClassRunner.class)  //在JUnit环境下提供spring testContext Framework的功能
@ContextConfiguration(classes={DemoConfig.class}) //用来加载配置ApplictionContext,其中classes属性用来加载配置类。
@ActiveProfiles("prod") //用来声明活动的profile
public class DemoBenaTests{
    @Autowired
    private DemoBean demoBean;

    @Test
    public void prodBeanShouldInject(){
        String expected = "from production profile";
        String actual = demoBean.getContent();
        Assert.asserEquals(expected ,actual );
    }    

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值