- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
在使用spring的过程中,我们有没有发现它的扩展能力很强呢? 由于这个优势的存在,使得spring具有很强的包容性,所以很多第三方应用或者框架可以很容易的投入到spring的怀抱中。今天我们主要来学习Spring中很常用的11个扩展点,你用过几个呢?
如果接口中接收参数的实体对象中,有一个字段类型为Date,但实际传递的参数是字符串类型:2022-12-15 10:20:15,该如何处理?
Spring提供了一个扩展点,类型转换器 Type Converter ,具体分为3类:
Converter<S,T>
: 将类型 S 的对象转换为类型 T 的对象 ConverterFactory<S, R>
: 将 S 类型对象转换为 R 类型或其子类对象 GenericConverter
:它支持多种源和目标类型的转换,还提供了源和目标类型的上下文。 此上下文允许您根据注释或属性信息执行类型转换。 还是不明白的话,我们举个例子吧.
@Data
public class User {
private Long id;
private String name;
private Date registerDate;
}
Converter
接口
public class DateConverter implements Converter<String, Date> {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public Date convert(String source) {
if (source != null && !"".equals(source)) {
try {
simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DateConverter());
}
}
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/save")
public String save(@RequestBody User user) {
return "success";
}
}
请求接口时,前端传入的日期字符串,会自动转换成Date类型.
在我们日常开发中,经常需要从Spring容器中获取bean,但是你知道如何获取Spring容器对象吗?
@Service
public class PersonService implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void add() {
Person person = (Person) beanFactory.getBean("person");
}
}
实现BeanFactoryAware接口,然后重写setBeanFactory方法,可以从方法中获取spring容器对象.
@Service
public class PersonService2 implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void add() {
Person person = (Person) applicationContext.getBean("person");
}
}
实现 ApplicationContextAware 接口,然后重写 setApplicationContext 方法,也可以通过该方法获取spring容器对象.
@Service
public class PersonService3 implements ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
applicationContext = event.getApplicationContext();
}
public void add() {
Person person = (Person) applicationContext.getBean("person");
}
}
以往我们在开发界面的时候,如果出现异常,要给用户更友好的提示,例如:
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/add")
public String add() {
int a = 10 / 0;
return "su";
}
}
如果不对请求添加接口结果做任何处理,会直接报错:
用户可以直接看到错误信息吗?
这种交互给用户带来的体验非常差。 为了解决这个问题,我们通常在接口中捕获异常:
@GetMapping("/add")
public String add() {
String result = "success";
try {
int a = 10 / 0;
} catch (Exception e) {
result = "error";
}
return result;
}
界面修改后,出现异常时会提示:“数据异常”,更加人性化.
看起来不错,但是有一个问题.
如果只是一个接口还好,但是如果项目中有成百上千个接口,还得加异常捕获代码吗?
答案是否定的,这就是全局异常处理派上用场的地方: RestControllerAdvice .
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
if (e instanceof ArithmeticException) {
return "data error";
}
if (e instanceof Exception) {
return "service error";
}
retur null;
}
}
方法中处理异常只需要 handleException ,在业务接口中就可以安心使用,不再需要捕获异常(统一有人处理).
Spring MVC拦截器,它可以获得 HttpServletRequest 和 HttpServletResponse 等web对象实例.
Spring MVC拦截器的顶层接口是 HandlerInterceptor ,它包含三个方法:
preHandle
在目标方法执行之前执行 postHandle
afterCompletion
在请求完成时执行 为了方便,我们一般继承 HandlerInterceptorAdapter ,它实现了 HandlerInterceptor .
如果有授权鉴权、日志、统计等场景,可以使用该拦截器,我们来演示下吧.
HandlerInterceptorAdapter
:
public class AuthInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestUrl = request.getRequestURI();
if (checkAuth(requestUrl)) {
return true;
}
return false;
}
private boolean checkAuth(String requestUrl) {
return true;
}
}
@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {
@Bean
public AuthInterceptor getAuthInterceptor() {
return new AuthInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor());
}
}
有时我们需要在某个配置类中引入其他的类,引入的类也加入到Spring容器中。 这时候可以使用注解 @Import 来完成这个功能.
如果你查看它的源代码,你会发现导入的类支持三种不同的类型.
但是我觉得最好把普通类的配置类和 @Configuration 注解分开解释,所以列出了四种不同的类型:
这种引入方式是最简单的,引入的类会被实例化为一个bean对象.
public class A {
}
@Import(A.class)
@Configuration
public class TestConfiguration {
}
通过 @Import 注解引入类A,spring可以自动实例化A对象,然后在需要使用的地方通过注解 @Autowired 注入:
@Autowired
private A a;
这种引入方式是最复杂的,因为@Configuration支持还支持多种组合注解,比如:
@Import
@ImportResource
@PropertySource
public class A {
}
public class B {
}
@Import(B.class)
@Configuration
public class AConfiguration {
@Bean
public A a() {
return new A();
}
}
@Import(AConfiguration.class)
@Configuration
public class TestConfiguration {
}
@Configuration 注解的配置类通过 @Import 注解导入,配置类 @Import 、 @ImportResource 相关注解引入的类会一次性全部递归引入 @PropertySource 所在的属性.
该导入方法需要实现 ImportSelector 接口 。
public class AImportSelector implements ImportSelector {
private static final String CLASS_NAME = "com.sue.cache.service.test13.A";
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{CLASS_NAME};
}
}
@Import(AImportSelector.class)
@Configuration
public class TestConfiguration {
}
这种方法的好处是 selectImports 方法返回的是一个数组,也就是说可以同时引入多个类,非常方便.
该导入方法需要实现 ImportBeanDefinitionRegistrar 接口:
public class AImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(A.class);
registry.registerBeanDefinition("a", rootBeanDefinition);
}
}
@Import(AImportBeanDefinitionRegistrar.class)
@Configuration
public class TestConfiguration {
}
这种方法是最灵活的。 容器注册对象可以在 registerBeanDefinitions 方法中获取,可以手动创建 BeanDefinition 注册到 BeanDefinitionRegistry 种.
有时候我们需要在项目启动的时候自定义一些额外的功能,比如加载一些系统参数,完成初始化,预热本地缓存等。 我们应该做什么?
好消息是 SpringBoot 提供了:
CommandLineRunner
ApplicationRunner
这两个接口帮助我们实现了上面的需求.
它们的用法很简单,以 ApplicationRunner 接口为例:
@Component
public class TestRunner implements ApplicationRunner {
@Autowired
private LoadDataService loadDataService;
public void run(ApplicationArguments args) throws Exception {
loadDataService.load();
}
}
实现 ApplicationRunner 接口,重写 run 方法,在该方法中实现您的自定义需求.
如果项目中有多个类实现了 ApplicationRunner 接口,如何指定它们的执行顺序?
答案是使用@Order(n)注解,n的值越小越早执行。 当然,顺序也可以通过 @Priority 注解来指定.
BeanDefinition
在实例化Bean对象之前, Spring IOC 需要读取Bean的相关属性,保存在 BeanDefinition 对象中,然后通过 BeanDefinition 对象实例化 Bean 对象.
如果要修改BeanDefinition对象中的属性怎么办?
答案 :我们可以实现 BeanFactoryPostProcessor 接口.
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("id", 123);
beanDefinitionBuilder.addPropertyValue("name", "Tom");
defaultListableBeanFactory.registerBeanDefinition("user", beanDefinitionBuilder.getBeanDefinition());
}
}
在 postProcessBeanFactory 方法中,可以获取 BeanDefinition 的相关对象,修改对象的属性.
有时,您想在 bean 初始化前后实现一些您自己的逻辑.
这时候就可以实现: BeanPostProcessor 接口.
该接口目前有两个方法:
postProcessBeforeInitialization
:应该在初始化方法之前调用。 postProcessAfterInitialization
:此方法在初始化方法之后调用。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof User) {
((User) bean).setUserName("Tom");
}
return bean;
}
}
我们经常使用的 @Autowired 、 @Value 、 @Resource 、 @PostConstruct 等注解都是通过 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 来实现的.
目前在Spring中初始化bean的方式有很多种:
@PostConstruct
注解 InitializingBean
接口 @PostConstruct
@Service
public class AService {
@PostConstruct
public void init() {
System.out.println("===init===");
}
}
为需要初始化的方法添加注解 @PostConstruct ,使其在Bean初始化时执行.
InitializingBean
@Service
public class BService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("===init===");
}
}
实现 InitializingBean 接口,重写 afterPropertiesSet 方法,在该方法中可以完成初始化功能.
有时候,我们需要在关闭spring容器之前做一些额外的工作,比如关闭资源文件.
此时你可以实现 DisposableBean 接口并重写它的 destroy 方法.
@Service
public class DService implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet");
}
}
这样,在spring容器销毁之前,会调用 destroy 方法做一些额外的工作.
通常我们会同时实现 InitializingBean 和 DisposableBean 接口,重写初始化方法和销毁方法.
Bean
的 scope
我们都知道spring core默认只支持两种 Scope :
Singleton
单例,从spring容器中获取的每一个bean都是同一个对象。 prototype
多实例,每次从spring容器中获取的bean都是不同的对象。 Spring Web 再次扩展了 Scope,添加 。
RequestScope
:同一个请求中从spring容器中获取的bean都是同一个对象。 SessionScope
:同一个session从spring容器中获取的bean都是同一个对象。 尽管如此,有些场景还是不符合我们的要求.
比如我们在同一个线程中要从 spring 容器中获取的 bean 都是同一个对象,怎么办?
答案 :这需要一个自定义范围.
Scope
接口
public class ThreadLocalScope implements Scope {
private static final ThreadLocal THREAD_LOCAL_SCOPE = new ThreadLocal();
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Object value = THREAD_LOCAL_SCOPE.get();
if (value != null) {
return value;
}
Object object = objectFactory.getObject();
THREAD_LOCAL_SCOPE.set(object);
return object;
}
@Override
public Object remove(String name) {
THREAD_LOCAL_SCOPE.remove();
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
}
@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope("threadLocalScope", new ThreadLocalScope());
}
}
@Scope("threadLocalScope")
@Service
public class CService {
public void add() {
}
}
本文总结了Spring中很常用的11个扩展点,可以在Bean创建、初始化到销毁各个阶段注入自己想要的逻辑,也有Spring MVC相关的拦截器等扩展点,希望对大家有帮助.
欢迎关注个人公众号——JAVA旭阳 更多学习资料请移步: 程序员成神之路 。
最后此篇关于Spring中11个最常用的扩展点,你知道几个?的文章就讲到这里了,如果你想了解更多关于Spring中11个最常用的扩展点,你知道几个?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我尝试阅读有关 Spring BOM、Spring Boot 和 Spring IO 的文档。 但是没有说明,我们应该如何一起使用它们? 在我的项目中,我们已经有了自己的 Parent POM ,所以
我正在开发的很酷的企业应用程序正在转向 Spring。这对所有团队来说都是非常酷和令人兴奋的练习,但也是一个巨大的压力源。我们所做的是逐渐将遗留组件移至 Spring 上下文。现在我们有一个 huuu
我正在尝试使用 @Scheduled 运行 Spring 批处理作业注释如下: @Scheduled(cron = "* * * * * ?") public void launchMessageDi
我对这两个概念有点困惑。阅读 Spring 文档,我发现,例如。 bean 工厂是 Spring 容器。我还读到“ApplicationContext 是 BeanFactory 的完整超集”。但两者
我们有一个使用 Spring BlazeDS 集成的应用程序。到目前为止,我们一直在使用 Spring 和 Flex,它运行良好。我们现在还需要添加一些 Spring MVC Controller 。
假设我有一个类(class) Person带属性name和 age ,它可以像这样用 Spring 配置: 我想要一个自定义的 Spring 模式元素,这很容易做到,允许我在我的 Sp
如何在 Java 中以编程方式使用 Spring Data 创建 MongoDB 复合索引? 使用 MongoTemplate 我可以创建一个这样的索引:mongoTemplate.indexOps(
我想使用 spring-complex-task 执行我的应用程序,并且我已经构建了复杂的 spring-batch Flow Jobs,它执行得非常好。 你能解释一下spring批处理流作业与spr
我实现了 spring-boot 应用程序,现在我想将它用作非 spring 应用程序的库。 如何初始化 lib 类,以便 Autowiring 的依赖项按预期工作?显然,如果我使用“new”创建类实
我刚开始学习 spring cloud security,我有一个基本问题。它与 Spring Security 有何不同?我们是否需要在 spring boot 上构建我们的应用程序才能使用 spr
有很多人建议我使用 Spring Boot 而不是 Spring 来开发 REST Web 服务。我想知道这两者到底有什么区别? 最佳答案 总之 Spring Boot 减少了编写大量配置和样板代码的
您能向我解释一下如何使用 Spring 正确构建 Web 应用程序吗?我知道 Spring 框架的最新版本是 4.0.0.RELEASE,但是 Spring Security 的最新版本是 3.2.0
我如何才能知道作为 Spring Boot 应用程序的一部分加载的所有 bean 的名称?我想在 main 方法中有一些代码来打印服务器启动后加载的 bean 的详细信息。 最佳答案 如spring-
我有一个使用 Spring 3.1 构建的 RESTful API,也使用 Spring Security。我有一个 Web 应用程序,也是一个 Spring 3.1 MVC 应用程序。我计划让移动客
升级到 Spring 5 后,我在 Spring Rabbit 和 Spring AMQP 中遇到错误。 两者现在都设置为 1.5.6.RELEASE 有谁知道哪些版本应该与 Spring 5 兼容?
我现在已经使用 Spring Framework 3.0.5 和 Spring Security 3.0.5 多次了。我知道Spring框架使用DI和AOP。我还知道 Spring Security
我收到错误 Unable to Location NamespaceHandler when using context:annotation-config running (java -jar) 由
在 Spring 应用程序中嵌入唯一版本号的策略是什么? 我有一个使用 Spring Boot 和 Spring Web 的应用程序。 它已经足够成熟,我想对其进行版本控制并在运行时看到它显示在屏幕上
我正在使用 spring data jpa 进行持久化。如果存在多个具有相同名称的实体,是否有一种方法可以将一个实体标记为默认值。类似@Primary注解的东西用来解决多个bean的依赖问题 @Ent
我阅读了 Spring 框架的 DAOSupport 类。但是我无法理解这些 DAOSuport 类的优点。在 DAOSupport 类中,我们调用 getXXXTemplate() 方法来获取特定的
我是一名优秀的程序员,十分优秀!