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

Spring Boot实战(3) Spring高级话题的更多相关文章

  1. Spring Boot实战(1) Spring基础

    1. Spring基础配置 Spring框架本身有四大原则: 1) 使用POJO进行轻量级和最小侵入式开发 2) 通过依赖注入和基于接口编程实现松耦合 3) 通过AOP和默认习惯进行声明式编程 4) ...

  2. Spring Boot实战(2) Spring常用配置

    1. Bean的Scope scope描述Spring容器如何新建Bean的实例.通过注解@Scope实现,取值有: a. Singleton:一个Spring容器中只有一个Bean的实例.此为Spr ...

  3. Spring Boot实战

    Spring在java EE开发中是实际意义上的标准,但我们在开发Spring的时候可能会遇到以下令人头疼的问题: 1.大量配置文件的定义.2.与第三方软件整合的技术问题. Spring每个版本的退出 ...

  4. 《spring boot 实战》读书笔记

    前言:虽然已经用spring boot开发过一套系统,但是之前都是拿来主义,没有系统的,全面的了解过这套框架.现在通过学习<spring boot实战>这本书,希望温故知新.顺便实现自己的 ...

  5. 《Spring Boot实战》笔记(目录)

    目录 目 录第一部分 点睛Spring 4.x第1 章 Spring 基础 .............................................................. ...

  6. Spring Boot 实战 —— MyBatis(注解版)使用方法

    原文链接: Spring Boot 实战 -- MyBatis(注解版)使用方法 简介 MyBatis 官网 是这么介绍它自己的: MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过 ...

  7. spring boot实战(第十三篇)自动配置原理分析

    前言 spring Boot中引入了自动配置,让开发者利用起来更加的简便.快捷,本篇讲利用RabbitMQ的自动配置为例讲分析下Spring Boot中的自动配置原理. 在上一篇末尾讲述了Spring ...

  8. spring boot实战(第十二篇)整合RabbitMQ

    前言 最近几篇文章将围绕消息中间件RabbitMQ展开,对于RabbitMQ基本概念这里不阐述,主要讲解RabbitMQ的基本用法.Java客户端API介绍.spring Boot与RabbitMQ整 ...

  9. Spring Boot实战系列-----------邮件发送

    快速导航 添加Maven依赖 配置文件增加邮箱相关配置 Service.Test项目代码构建 五种邮件发送类型讲解 文本邮件 html邮件 附件邮件 html内嵌图片邮件 模板邮件 问题汇总 添加ma ...

随机推荐

  1. gRPC官方文档(概念)

    文章来自gRPC 官方文档中文版 gRPC 概念 本文档通过对于 gRPC 的架构和 RPC 生命周期的概览来介绍 gRPC 的主要概念.本文是在假设你已经读过文档部分的前提下展开的.针对具体语言细节 ...

  2. CentOS6.5上Zabbix3.0的RPM安装【二】-汉化

    六.汉化 zabbix实际是有中文语言的,我们可以通过修改web端源文件来开启中文语言.首先点击zabbix监控页面右上角管理员头像进入“用户基本资料设置页面“. 选择中文语言. 点击“Update” ...

  3. poj2417(Baby-Step Giant-Step)

    题目链接:http://poj.org/problem?id=2417 题意:求满足给出 P, N, B, 求满足条件 BL == N (mod P) 的最小 L, 若不存在则输出 no soluti ...

  4. window下 mysql密码忘记

    1.打开MySQL配置文件 my.ini中,添加上skip-grant-tables,可以添加到文件的末尾或者是这添加到[mysqld]的下面. 2.重启mysql 3.这时登录MySQL不再需要验证 ...

  5. mysql远程访问被禁止

    远程连接Mysql服务器的数据库,错误代码是1130,ERROR 1130: Host xxx.xxx.xxx.xxx  is not allowed to connect to this MySQL ...

  6. 使用remove_constants工具查看Oracle是否使用绑定变量

    https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:1163635055580 http://blog.csdn.n ...

  7. javascript拖拽事件

    <!DOCTYPE html> <html> <head> <title></title> <style type="tex ...

  8. 使用原生js来操作对象dom的class属性

    之前一直都使用jquery来操作dom,今天想自己用原生写一些插件,却发现给dom增删class的时候,使用slice来截取className特别的麻烦,后来发现,原来原生JS本来就有提供api来对d ...

  9. 数据结构31:树(Tree)详解

    复制广义表数据结构中的树 树是数据结构中比较重要也是比较难理解的一类存储结构.本章主要主要围绕二叉树,对树的存储以及遍历做详细的介绍,同时还会涉及到有关树的实际应用,例如构建哈弗曼编码等. 由于树存储 ...

  10. 数据结构20:KMP算法(快速模式匹配算法)详解

    通过上一节的介绍,学习了串的普通模式匹配算法,大体思路是:模式串从主串的第一个字符开始匹配,每匹配失败,主串中记录匹配进度的指针 i 都要进行 i-j+1 的回退操作(这个过程称为“指针回溯”),同时 ...