文章目录
- 自定义拦截器
- 获取 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 {
    @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) {
        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 {
    @Resource
    private AuthInterceptor authInterceptor;
    @Bean
    public AuthInterceptor getAuthInterceptor() {
        return new AuthInterceptor ();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor (authInterceptor);
    }
}
第三步,在请求接口时 spring mvc 通过该拦截器,能够自动拦截该接口,并且校验权限
获取 Spring 容器对象
在我们日常开发中,经常需要从 Spring 容器中获取 Bean,但你知道如何获取 Spring 容器对象吗?
ApplicationListener 接口
@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 容器对象。
修改 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 {
    @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", "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 {
    @Resource
    private User user;
    @Test
    public 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 {
    @Override
    public 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
初始化方法
- 使用**@PostConstruct** 注解
- 实现 InitializingBean 接口
使用@PostConstruct 注解
@Service
public class AService {
    @PostConstruct
    public void init() {
        System.out.println ("===初始化===");
    }
}
在需要初始化的方法上增加@PostConstruct 注解,这样就有初始化的能力。
实现 InitializingBean 接口
@Service
public class BService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println ("===初始化===");
    }
}
实现 InitializingBean 接口,重写 afterPropertiesSet 方法,该方法中可以完成初始化功能
BeanFactoryPostProcessor 接口
beanFactory后置处理器,可以
获取BeanDefinition 进行修改
@Component
class UserServiceImpl implements BeanFactoryPostProcessor {
    @Override
    //实现BeanFactoryPostProcessor ,可以获取beanDefinition ,修改bean的作用范围和className
    public 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 {
    @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接口,重写初始化方法和销毁方法
自定义作用域
我们都知道 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 ();
    @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;
    }
}
第二步将新定义的 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 {
    @Override
    public 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() {
    }
}





![[附源码]java毕业设计新冠疫苗线上预约系统](https://img-blog.csdnimg.cn/9426cd7ba95244b8b2730a8fd6b6331d.png)













