Spring的常用拓展点
创始人
2024-02-08 17:25:18

文章目录

  • 自定义拦截器
  • 获取 Spring 容器对象
  • 修改 BeanDefinition
    • 添加BeanDefinition
    • 测试
  • 初始化 Bean 前后
  • 初始化方法
    • 使用@PostConstruct 注解
    • 实现 InitializingBean 接口
  • BeanFactoryPostProcessor 接口
  • 关闭容器前
  • 自定义作用域

自定义拦截器

spring mvc 拦截器的顶层接口是:HandlerInterceptor,包含三个方法:

  • preHandle 目标方法执行前执行
  • postHandle 目标方法执行后执行
  • afterCompletion 请求完成时执行

一 般 情 况 会 用 HandlerInterceptor 接 口 的 实 现 类HandlerInterceptorAdapter 类。
假如有权限认证、日志、统计的场景,可以使用该拦截器

第一步,继承 HandlerInterceptorAdapter 类定义拦截器:


import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Slf4j
public class AuthInterceptor extends HandlerInterceptorAdapter {@Overridepublic 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) {System.out.println ("===权限校验===");return true;}
}

第二步,将该拦截器注册到 spring 容器:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import javax.annotation.Resource;@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {@Resourceprivate AuthInterceptor authInterceptor;@Beanpublic AuthInterceptor getAuthInterceptor() {return new AuthInterceptor ();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor (authInterceptor);}
}

第三步,在请求接口时 spring mvc 通过该拦截器,能够自动拦截该接口,并且校验权限

获取 Spring 容器对象

在我们日常开发中,经常需要从 Spring 容器中获取 Bean,但你知道如何获取 Spring 容器对象吗?

ApplicationListener 接口


@Service
public class PersonService2 implements ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext)throws BeansException {this.applicationContext = applicationContext;}public void add() {Person person = (Person) applicationContext.getBean ("person");}
}

实现 ApplicationContextAware 接口,然后重写 setApplicationContext 方法,也能从该方法中获取到 spring 容器对象。

修改 BeanDefinition

Spring IOC 在实例化 Bean 对象之前,需要 先读取 Bean 的相关属性
保存到 BeanDefinition 对象中,然后通过 BeanDefinition 对象,实例化 Bean 对象。
如果想修改 BeanDefinition 对象中的属性,该怎么办呢?

答:我们可以实现 BeanFactoryPostProcessor 接口

添加BeanDefinition

import lombok.Data;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)throws BeansException {DefaultListableBeanFactory defaultListableBeanFactory =(DefaultListableBeanFactory) configurableListableBeanFactory;BeanDefinitionBuilder beanDefinitionBuilder =BeanDefinitionBuilder.genericBeanDefinition (User.class);beanDefinitionBuilder.addPropertyValue ("id", 123);beanDefinitionBuilder.addPropertyValue ("name", "xiaoding");defaultListableBeanFactory.registerBeanDefinition ("user",beanDefinitionBuilder.getBeanDefinition ());}
}@Data
class User {private int id;private String name;
}

测试

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;@SpringBootTest
@RunWith(SpringRunner.class)
public class TestBean {@Resourceprivate User user;@Testpublic void test() {System.out.println (user.getId ());System.out.println (user.getName ());}
}

在这里插入图片描述
postProcessBeanFactory 方法中,可以获取 BeanDefinition 的相关对象,并且修改该对象的属性。

初始化 Bean 前后

这时可以实现:BeanPostProcessor 接口。

该接口目前有两个方法:

  • postProcessBeforeInitialization 该在初始化方法之前调用
  • postProcessAfterInitialization 该方法在初始化方法之后调用
import lombok.Data;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean,String beanName) throws BeansException {if (bean instanceof User) {((User) bean).setUserName ("mr ding");}return bean;}
}@Data
class User {private int id;private String UserName;
}

如果 spring 中存在 User 对象,则将它的 userName 设置成:mr ding

初始化方法

  1. 使用**@PostConstruct** 注解
  2. 实现 InitializingBean 接口

使用@PostConstruct 注解

@Service
public class AService {@PostConstructpublic void init() {System.out.println ("===初始化===");}
}

在需要初始化的方法上增加@PostConstruct 注解,这样就有初始化的能力。

实现 InitializingBean 接口

@Service
public class BService implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println ("===初始化===");}
}

实现 InitializingBean 接口,重写 afterPropertiesSet 方法,该方法中可以完成初始化功能

BeanFactoryPostProcessor 接口

beanFactory后置处理器,可以获取BeanDefinition 进行修改

@Component
class UserServiceImpl implements BeanFactoryPostProcessor {@Override//实现BeanFactoryPostProcessor ,可以获取beanDefinition ,修改bean的作用范围和classNamepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition userService = beanFactory.getBeanDefinition("userService");userService.setScope("singleton");userService.setBeanClassName("");}
}

关闭容器前

有时候,我们需要在关闭 spring 容器前,做一些额外的工作,比如:关闭资源文件等。
这时可以实现 DisposableBean 接口,并且重写它的 destroy 方法

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;@Service
public class DService implements InitializingBean, DisposableBean {@Overridepublic void destroy() throws Exception {System.out.println ("DisposableBean destroy");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println ("InitializingBean afterPropertiesSet");}
}

这样 spring 容器销毁前,会调用该 destroy 方法,做一些额外的工作。

通常情况下,我们会同时实现 InitializingBeanDisposableBean接口,重写初始化方法和销毁方法

自定义作用域

我们都知道 spring 默认支持的 Scope 只有两种:

  • singleton 单例,每次从 spring 容器中获取到的 bean 都是同一个对象。
  • prototype 多例,每次从 spring 容器中获取到的 bean 都是不同的对象。

spring web 又对 Scope 进行了扩展,增加了:

  • RequestScope 同一次请求从 spring 容器中获取到的 bean 都是同一个对象。
  • SessionScope 同一个会话从 spring 容器中获取到的 bean 都是同一个对象。
    即便如此,有些场景还是无法满足我们的要求。

比如,我们想在 同一个线程中 从 spring 容器获取到的 bean 都是同一个对象,该怎么办?

这就需要自定义 Scope 了。
第一步实现 Scope 接口

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;public class ThreadLocalScope implements Scope {private static final ThreadLocal THREAD_LOCAL_SCOPE = new ThreadLocal ();@Overridepublic 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;}@Overridepublic Object remove(String name) {THREAD_LOCAL_SCOPE.remove ();return null;}@Overridepublic void registerDestructionCallback(String name, Runnable callback) {}@Overridepublic Object resolveContextualObject(String key) {return null;}@Overridepublic String getConversationId() {return null;}
}

第二步将新定义的 Scope 注入到 spring 容器中:


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {beanFactory.registerScope ("threadLocalScope", new ThreadLocalScope ());}
}

第三步使用新定义的 Scope:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;@Scope("threadLocalScope")
@Service
public class CService {public void add() {}
}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...