Spring的常用拓展点

自定义拦截器

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

初始化方法

  1. 使用**@PostConstruct** 注解
  2. 实现 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 方法,做一些额外的工作。

通常情况下,我们会同时实现 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 ();

    @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() {
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值