spring mvc 拦截器的顶层接口是:HandlerInterceptor,包含三个方法:
执行前执行执行后执行一 般 情 况 会 用 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 容器中获取 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 容器对象。
Spring IOC 在实例化 Bean 对象之前,需要 先读取 Bean 的相关属性,
保存到 BeanDefinition 对象中,然后通过 BeanDefinition 对象,实例化 Bean 对象。
如果想修改 BeanDefinition 对象中的属性,该怎么办呢?
答:我们可以
实现 BeanFactoryPostProcessor 接口
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 的相关对象,并且修改该对象的属性。
这时可以实现:BeanPostProcessor 接口。
该接口目前有两个方法:
之前调用。之后调用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
@Service
public class AService {@PostConstructpublic void init() {System.out.println ("===初始化===");}
}
在需要初始化的方法上增加@PostConstruct 注解,这样就有初始化的能力。
@Service
public class BService implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println ("===初始化===");}
}
实现 InitializingBean 接口,重写 afterPropertiesSet 方法,该方法中可以完成初始化功能
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 方法,做一些额外的工作。
通常情况下,我们会同时实现 InitializingBean 和 DisposableBean接口,重写初始化方法和销毁方法
我们都知道 spring 默认支持的 Scope 只有两种:
spring web 又对 Scope 进行了扩展,增加了:
比如,我们想在
同一个线程中从 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() {}
}