Spring 中那些让你爱不释手的代码技巧
Posted Java知音
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 中那些让你爱不释手的代码技巧相关的知识,希望对你有一定的参考价值。
BeanFactory beanFactory;
BeansException
Person person = (Person) beanFactory.getBean(
实现BeanFactoryAware
接口,然后重写setBeanFactory
方法,就能从该方法中获取到spring容器对象。
ApplicationContext applicationContext;
BeansException
Person person = (Person) applicationContext.getBean(
实现ApplicationContextAware
接口,然后重写setApplicationContext
方法,也能从该方法中获取到spring容器对象。
ApplicationContext applicationContext;
applicationContext = event.getApplicationContext();
Person person = (Person) applicationContext.getBean(
实现ApplicationListener
接口,需要注意的是该接口接收的泛型是ContextRefreshedEvent
类,然后重写onApplicationEvent
方法,也能从该方法中获取到spring容器对象。
此外,不得不提一下Aware
接口,它其实是一个空接口,里面不包含任何方法。
它表示已感知的意思,通过这类接口可以获取指定对象,比如:
通过BeanFactoryAware获取BeanFactory 通过ApplicationContextAware获取ApplicationContext 通过BeanNameAware获取BeanName等
System.out.println(
在需要初始化的方法上增加@PostConstruct
注解,这样就有初始化的能力。
Exception
System.out.println(
实现InitializingBean
接口,重写afterPropertiesSet
方法,该方法中可以完成初始化功能。
这里顺便抛出一个有趣的问题:init-method
、PostConstruct
和 InitializingBean
的执行顺序是什么样的?
决定他们调用顺序的关键代码在AbstractAutowireCapableBeanFactory
类的initializeBean
方法中。

这段代码中会先调用BeanPostProcessor
的postProcessBeforeInitialization
方法,而PostConstruct
是通过InitDestroyAnnotationBeanPostProcessor
实现的,它就是一个BeanPostProcessor
,所以PostConstruct
先执行。
而invokeInitMethods
方法中的代码:

决定了先调用InitializingBean
,再调用init-method
。
所以得出结论,他们的调用顺序是:

ThreadLocal THREAD_LOCAL_SCOPE = ThreadLocal();
Object
Object value = THREAD_LOCAL_SCOPE.get();
(value != value;
Object object = objectFactory.getObject();
THREAD_LOCAL_SCOPE.set(object);
object;
Object
THREAD_LOCAL_SCOPE.remove();
Object
String
第二步将新定义的Scope
注入到spring容器中:
BeansException
beanFactory.registerScope(ThreadLocalScope());
第三步使用新定义的Scope
:
Object Exception
String data1 = buildData1();
String data2 = buildData2();
buildData3(data1, data2);
String
String
String
data1 + data2;
Class<?> getObjectType()
获取FactoryBean
实例对象:
BeanFactory beanFactory;
BeansException
Object myFactoryBean = beanFactory.getBean( System.out.println(myFactoryBean);
Object myFactoryBean1 = beanFactory.getBean( System.out.println(myFactoryBean1);
getBean("myFactoryBean");
获取的是MyFactoryBeanService类中getObject方法返回的对象,
getBean("&myFactoryBean");
获取的才是MyFactoryBean对象。
Long id;
String name;
Date registerDate;
第二步,实现Converter
接口:
SimpleDateFormat simpleDateFormat = SimpleDateFormat( Date
(source != && !
simpleDateFormat.parse(source);
(ParseException e)
e.printStackTrace();
第三步,将新定义的类型转换器注入到spring容器中:
registry.addConverter(DateConverter());
第四步,调用接口
String
请求接口时User
对象中registerDate
字段会被自动转换成Date
类型。
等web对象实例。spring mvc拦截器的顶层接口是:HandlerInterceptor
,包含三个方法:
preHandle 目标方法执行前执行 postHandle 目标方法执行后执行 afterCompletion 请求完成时执行
为了方便我们一般情况会用
Exception
String requestUrl = request.getRequestURI();
(checkAuth(requestUrl))
System.out.println(
第二步,将该拦截器注册到spring容器:
AuthInterceptor
AuthInterceptor();
registry.addInterceptor(AuthInterceptor());
第三步,在请求接口时spring mvc通过该拦截器,能够自动拦截该接口,并且校验权限。
该拦截器其实相对来说,比较简单,可以在DispatcherServlet
类的doDispatch
方法中看到调用过程:

顺便说一句,这里只讲了spring mvc的拦截器,并
ServletException
IOException, ServletException
System.out.println( chain.doFilter(request, response);
System.out.println(
第二步,注册LogFilter:
LogFilter
LogFilter();
注意,这里用了@ConditionalOnWebApplication
注解,没有直接使用@Configuration
注解。
第三步,定义开关@EnableLog
注解:
@
第四步,只需在springboot
启动类加上@EnableLog
注解即可开启LogFilter记录请求和响应日志的功能。
ClientHttpResponse IOException
request.getHeaders().set( execution.execute(request, body);
第二步,定义配置类:
RestTemplate
RestTemplate restTemplate = RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(restTemplateInterceptor()));
restTemplate;
RestTemplateInterceptor
RestTemplateInterceptor();
其中MdcUtil其实是利用MDC
工具在ThreadLocal
中存储和获取traceId
String TRACE_ID = String
MDC.get(TRACE_ID);
MDC.put(TRACE_ID, value);
当然,这个例子中没有演示MdcUtil类的add方法具体调的地方,我们可以在filter中执行接口方法之前,生成traceId,调用MdcUtil类的add方法添加到MDC
中,然后在同一个请求的其他地方就能通过MdcUtil类的get方法获取到该traceId。
String
a = /
如果不做任何处理请求add接口结果直接报错:

what?用户能直接看到错误信息?
这种交互方式给用户的体验非常差,为了解决这个问题,我们通常会在接口中捕获异常:
String
String result =
a = / (Exception e)
result =
result;
接口改造后,出现异常时会提示:“数据异常”,对用户来说更友好。
看起来挺不错的,但是有问题。。。
如果只是一个接口还好,但是如果项目中有成百上千个接口,都要加上异常捕获代码吗?
答案是否定的,这时全局异常处理就派上用场了:RestControllerAdvice
。
(e ArithmeticException)
(e Exception)
只需在handleException
方法中处理异常情况,业务接口中可以放心使用,不再需要捕获异常(有人统一处理了)。真是爽歪歪。
System.out.println(
MyThread().start();
实现Runable接口
System.out.println(
Thread(MyWork()).start();
使用线程池
ExecutorService executorService = ThreadPoolExecutor(ArrayBlockingQueue<>(
System.out.println(
executorService.submit(MyThreadPool.Work());
executorService.shutdown();
这三种实现异步的方法不能说不好,但是spring已经帮我们抽取了一些公共的地方,我们无需再继承Thread
类或实现Runable
接口,它都搞定了。
如何spring异步功能呢?
第一步,springboot项目启动类上加@EnableAsync
注解。
SpringApplicationBuilder(Application
第二步,在需要使用异步的方法上加上@Async
注解:
String
System.out.println(
然后在使用的地方调用一下:personService.get();就拥有了异步功能,是不是很神奇。
默认情况下,spring会为我们的异步方法创建一个线程去执行,如果该方法被调用次数非常多的话,需要创建大量的线程,会导致资源浪费。
这时,我们可以定义一个线程池,异步方法将会被自动提交到线程池中执行。
corePoolSize;
maxPoolSize;
queueCapacity;
keepAliveSeconds;
String threadNamePrefix;
Executor
ThreadPoolTaskExecutor executor = ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(threadNamePrefix);
executor.setRejectedExecutionHandler(ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
executor;
spring异步的核心方法:

根据返回值不同,处理情况也不太一样,具体分为如下情况:

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>
CacheManager CaffeineCacheManager cacheManager = CaffeineCacheManager();
Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
.expireAfterWrite( .maximumSize( cacheManager.setCaffeine(caffeine);
cacheManager;
第三步,使用Cacheable
注解获取数据
CategoryModel
getCategoryByType(type);
CategoryModel
System.out.println(+ type + CategoryModel categoryModel = CategoryModel();
categoryModel.setId( categoryModel.setParentId( categoryModel.setName( categoryModel.setLevel( categoryModel;
调用categoryService.getCategory()方法时,先从caffine
缓存中获取数据,如果能够获取到数据则直接返回该数据,不会进入方法体。如果不能获取到数据,则直接方法体中的代码获取到数据,然后放到caffine
缓存中。
唠唠家常
spring中不错的功能其实还有很多,比如:BeanPostProcessor,BeanFactoryPostProcessor,AOP,动态数据源,ImportSelector等等。我原本打算一篇文章写全的,但是有两件事情改变了我的注意:
有个大佬原本打算转载我文章的,却因为篇幅太长一直没有保存成功。 最近经常加班,真的没多少时间写文章,晚上还要带娃,喂奶,换尿布,其实挺累的。
END
推荐好文
ApplicationContextAware
接口,然后重写setApplicationContext
方法,也能从该方法中获取到spring容器对象。applicationContext = event.getApplicationContext();
Person person = (Person) applicationContext.getBean(
实现ApplicationListener
接口,需要注意的是该接口接收的泛型是ContextRefreshedEvent
类,然后重写onApplicationEvent
方法,也能从该方法中获取到spring容器对象。
此外,不得不提一下Aware
接口,它其实是一个空接口,里面不包含任何方法。
它表示已感知的意思,通过这类接口可以获取指定对象,比如:
System.out.println(
在需要初始化的方法上增加@PostConstruct
注解,这样就有初始化的能力。
Exception
System.out.println(
实现InitializingBean
接口,重写afterPropertiesSet
方法,该方法中可以完成初始化功能。
这里顺便抛出一个有趣的问题:init-method
、PostConstruct
和 InitializingBean
的执行顺序是什么样的?
决定他们调用顺序的关键代码在AbstractAutowireCapableBeanFactory
类的initializeBean
方法中。

这段代码中会先调用BeanPostProcessor
的postProcessBeforeInitialization
方法,而PostConstruct
是通过InitDestroyAnnotationBeanPostProcessor
实现的,它就是一个BeanPostProcessor
,所以PostConstruct
先执行。
而invokeInitMethods
方法中的代码:

决定了先调用InitializingBean
,再调用init-method
。
所以得出结论,他们的调用顺序是:

ThreadLocal THREAD_LOCAL_SCOPE = ThreadLocal();
Object
Object value = THREAD_LOCAL_SCOPE.get();
(value != value;
Object object = objectFactory.getObject();
THREAD_LOCAL_SCOPE.set(object);
object;
Object
THREAD_LOCAL_SCOPE.remove();
Object
String
第二步将新定义的Scope
注入到spring容器中:
BeansException
beanFactory.registerScope(ThreadLocalScope());
第三步使用新定义的Scope
:
Object Exception
String data1 = buildData1();
String data2 = buildData2();
buildData3(data1, data2);
String
String
String
data1 + data2;
Class<?> getObjectType()
获取FactoryBean
实例对象:
BeanFactory beanFactory;
BeansException
Object myFactoryBean = beanFactory.getBean( System.out.println(myFactoryBean);
Object myFactoryBean1 = beanFactory.getBean( System.out.println(myFactoryBean1);
getBean("myFactoryBean");
获取的是MyFactoryBeanService类中getObject方法返回的对象,
getBean("&myFactoryBean");
获取的才是MyFactoryBean对象。
Long id;
String name;
Date registerDate;
第二步,实现Converter
接口:
SimpleDateFormat simpleDateFormat = SimpleDateFormat( Date
(source != && !
simpleDateFormat.parse(source);
(ParseException e)
e.printStackTrace();
第三步,将新定义的类型转换器注入到spring容器中:
registry.addConverter(DateConverter());
第四步,调用接口
String
请求接口时User
对象中registerDate
字段会被自动转换成Date
类型。
等web对象实例。spring mvc拦截器的顶层接口是:HandlerInterceptor
,包含三个方法:
preHandle 目标方法执行前执行 postHandle 目标方法执行后执行 afterCompletion 请求完成时执行
为了方便我们一般情况会用
Exception
String requestUrl = request.getRequestURI();
(checkAuth(requestUrl))
System.out.println(
第二步,将该拦截器注册到spring容器:
AuthInterceptor
AuthInterceptor();
registry.addInterceptor(AuthInterceptor());
第三步,在请求接口时spring mvc通过该拦截器,能够自动拦截该接口,并且校验权限。
该拦截器其实相对来说,比较简单,可以在DispatcherServlet
类的doDispatch
方法中看到调用过程:

顺便说一句,这里只讲了spring mvc的拦截器,并
ServletException
IOException, ServletException
System.out.println( chain.doFilter(request, response);
System.out.println(
第二步,注册LogFilter:
LogFilter
LogFilter();
注意,这里用了@ConditionalOnWebApplication
注解,没有直接使用@Configuration
注解。
第三步,定义开关@EnableLog
注解:
@
第四步,只需在springboot
启动类加上@EnableLog
注解即可开启LogFilter记录请求和响应日志的功能。
ClientHttpResponse IOException
request.getHeaders().set( execution.execute(request, body);
第二步,定义配置类:
RestTemplate
RestTemplate restTemplate = RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(restTemplateInterceptor()));
restTemplate;
RestTemplateInterceptor
RestTemplateInterceptor();
其中MdcUtil其实是利用MDC
工具在ThreadLocal
中存储和获取traceId
String TRACE_ID = String
MDC.get(TRACE_ID);
MDC.put(TRACE_ID, value);
当然,这个例子中没有演示MdcUtil类的add方法具体调的地方,我们可以在filter中执行接口方法之前,生成traceId,调用MdcUtil类的add方法添加到MDC
中,然后在同一个请求的其他地方就能通过MdcUtil类的get方法获取到该traceId。
String
a = /
如果不做任何处理请求add接口结果直接报错:

what?用户能直接看到错误信息?
这种交互方式给用户的体验非常差,为了解决这个问题,我们通常会在接口中捕获异常:
String
String result =
a = / (Exception e)
result =
result;
接口改造后,出现异常时会提示:“数据异常”,对用户来说更友好。
看起来挺不错的,但是有问题。。。
如果只是一个接口还好,但是如果项目中有成百上千个接口,都要加上异常捕获代码吗?
答案是否定的,这时全局异常处理就派上用场了:RestControllerAdvice
。
(e ArithmeticException)
(e Exception)
只需在handleException
方法中处理异常情况,业务接口中可以放心使用,不再需要捕获异常(有人统一处理了)。真是爽歪歪。
System.out.println(
MyThread().start();
实现Runable接口
System.out.println(
Thread(MyWork()).start();
使用线程池
ExecutorService executorService = ThreadPoolExecutor(ArrayBlockingQueue<>(
System.out.println(
executorService.submit(MyThreadPool.Work());
executorService.shutdown();
这三种实现异步的方法不能说不好,但是spring已经帮我们抽取了一些公共的地方,我们无需再继承Thread
类或实现Runable
接口,它都搞定了。
如何spring异步功能呢?
第一步,springboot项目启动类上加@EnableAsync
注解。
SpringApplicationBuilder(Application
第二步,在需要使用异步的方法上加上@Async
注解:
String
System.out.println(
然后在使用的地方调用一下:personService.get();就拥有了异步功能,是不是很神奇。
默认情况下,spring会为我们的异步方法创建一个线程去执行,如果该方法被调用次数非常多的话,需要创建大量的线程,会导致资源浪费。
这时,我们可以定义一个线程池,异步方法将会被自动提交到线程池中执行。
corePoolSize;
maxPoolSize;
queueCapacity;
keepAliveSeconds;
String threadNamePrefix;
Executor
ThreadPoolTaskExecutor executor = ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(threadNamePrefix);
executor.setRejectedExecutionHandler(ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
executor;
spring异步的核心方法:

根据返回值不同,处理情况也不太一样,具体分为如下情况:

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>
CacheManager CaffeineCacheManager cacheManager = CaffeineCacheManager();
Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
.expireAfterWrite( .maximumSize( cacheManager.setCaffeine(caffeine);
cacheManager;
第三步,使用Cacheable
注解获取数据
CategoryModel
getCategoryByType(type);
CategoryModel
System.out.println(+ type + CategoryModel categoryModel = CategoryModel();
categoryModel.setId( categoryModel.setParentId( categoryModel.setName( categoryModel.setLevel( categoryModel;
调用categoryService.getCategory()方法时,先从caffine
缓存中获取数据,如果能够获取到数据则直接返回该数据,不会进入方法体。如果不能获取到数据,则直接方法体中的代码获取到数据,然后放到caffine
缓存中。
唠唠家常
spring中不错的功能其实还有很多,比如:BeanPostProcessor,BeanFactoryPostProcessor,AOP,动态数据源,ImportSelector等等。我原本打算一篇文章写全的,但是有两件事情改变了我的注意:
有个大佬原本打算转载我文章的,却因为篇幅太长一直没有保存成功。 最近经常加班,真的没多少时间写文章,晚上还要带娃,喂奶,换尿布,其实挺累的。
ENDInitializingBean
接口,重写afterPropertiesSet
方法,该方法中可以完成初始化功能。init-method
、PostConstruct
和 InitializingBean
的执行顺序是什么样的?AbstractAutowireCapableBeanFactory
类的initializeBean
方法中。
BeanPostProcessor
的postProcessBeforeInitialization
方法,而PostConstruct
是通过InitDestroyAnnotationBeanPostProcessor
实现的,它就是一个BeanPostProcessor
,所以PostConstruct
先执行。invokeInitMethods
方法中的代码:
InitializingBean
,再调用init-method
。
Scope
注入到spring容器中:
BeansException
beanFactory.registerScope(ThreadLocalScope());
Scope
:
FactoryBean
实例对象:
BeanFactory beanFactory;
BeansException
Object myFactoryBean = beanFactory.getBean( System.out.println(myFactoryBean);
Object myFactoryBean1 = beanFactory.getBean( System.out.println(myFactoryBean1);
getBean("myFactoryBean");
获取的是MyFactoryBeanService类中getObject方法返回的对象,
getBean("&myFactoryBean");
获取的才是MyFactoryBean对象。
Converter
接口: SimpleDateFormat simpleDateFormat = SimpleDateFormat( Date
(source != && !
simpleDateFormat.parse(source);
(ParseException e)
e.printStackTrace();
registry.addConverter(DateConverter());
String
User
对象中registerDate
字段会被自动转换成Date
类型。HandlerInterceptor
,包含三个方法:Exception
String requestUrl = request.getRequestURI();
(checkAuth(requestUrl))
System.out.println(
AuthInterceptor
AuthInterceptor();
registry.addInterceptor(AuthInterceptor());
DispatcherServlet
类的doDispatch
方法中看到调用过程:
ServletException
IOException, ServletException
System.out.println( chain.doFilter(request, response);
System.out.println(
LogFilter
LogFilter();
@ConditionalOnWebApplication
注解,没有直接使用@Configuration
注解。@EnableLog
注解:@
springboot
启动类加上@EnableLog
注解即可开启LogFilter记录请求和响应日志的功能。
RestTemplate
RestTemplate restTemplate = RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(restTemplateInterceptor()));
restTemplate;
RestTemplateInterceptor
RestTemplateInterceptor();
MDC
工具在ThreadLocal
中存储和获取traceId
String TRACE_ID = String
MDC.get(TRACE_ID);
MDC.put(TRACE_ID, value);
MDC
中,然后在同一个请求的其他地方就能通过MdcUtil类的get方法获取到该traceId。
String
String result =
a = / (Exception e)
result =
result;
RestControllerAdvice
。
(e ArithmeticException)
(e Exception)
handleException
方法中处理异常情况,业务接口中可以放心使用,不再需要捕获异常(有人统一处理了)。真是爽歪歪。
System.out.println(
Thread(MyWork()).start();
ExecutorService executorService = ThreadPoolExecutor(ArrayBlockingQueue<>(
System.out.println(
executorService.submit(MyThreadPool.Work());
executorService.shutdown();
Thread
类或实现Runable
接口,它都搞定了。@EnableAsync
注解。
SpringApplicationBuilder(Application
@Async
注解:
String
System.out.println(
corePoolSize;
maxPoolSize;
queueCapacity;
keepAliveSeconds;
String threadNamePrefix;
Executor
ThreadPoolTaskExecutor executor = ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(threadNamePrefix);
executor.setRejectedExecutionHandler(ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
executor;


Cacheable
注解获取数据
CategoryModel
getCategoryByType(type);
CategoryModel
System.out.println(+ type + CategoryModel categoryModel = CategoryModel();
categoryModel.setId( categoryModel.setParentId( categoryModel.setName( categoryModel.setLevel( categoryModel;
caffine
缓存中获取数据,如果能够获取到数据则直接返回该数据,不会进入方法体。如果不能获取到数据,则直接方法体中的代码获取到数据,然后放到caffine
缓存中。强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!
分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!
能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!