Spring 框架

1.全局异常处理

@RestControllerAdvice
publicclass GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
        if (e instanceof ArithmeticException) {
            return"params error";
        }
        if (e instanceof Exception) {
            return"Internal server exception";
        }
        returnnull;
    }
}

 

2. 自定义拦截器

与 Spring 拦截器相比,Spring MVC 拦截器可以在内部获取 HttpServletRequest 和 HttpServletResponse 等 Web 对象实例。

Spring MVC 拦截器的顶级接口是:HandlerInterceptor,它包含三个方法:

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

为了方便起见,在一般情况下,我们通常使用 HandlerInterceptor 接口的实现类 HandlerInterceptorAdapter

第一步,通过继承 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)) {
            returntrue;
        }
        returnfalse;
    }

    private boolean checkAuth(String requestUrl) {
        System.out.println("===Authority Verification===");
        returntrue;
    }
}

第二步,在 Spring 容器中注册此拦截器。

@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {
    @Bean
    public AuthInterceptor getAuthInterceptor() {
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor());
    }
}


3. 获取 Spring 容器对象

3.1 BeanFactoryAware 接口

@Service
public class StudentService implements BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public void add() {
        Student student = (Student) beanFactory.getBean("student");
    }
}

实现 BeanFactoryAware 接口,然后重写 setBeanFactory 方法。从这个方法中,可以获取 Spring 容器对象。

3.2 ApplicationContextAware 接口

@Service
public class StudentService2 implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void add() {
        Student student = (Student) applicationContext.getBean("student");
    }
}

4. 导入配置


有时我们需要在某个配置类中导入其他一些类,并且导入的类也会被添加到 Spring 容器中。此时,可以使用@Import 注解来完成此功能。


4.1 导入普通类


这种导入方式最简单。导入的类将被实例化为一个 bean 对象。


public class A {
}

@Import(A.class)
@Configuration
public class TestConfiguration {
}

通过@Import 注解导入类 A,Spring 可以自动实例化对象 A。然后,可以在需要的地方通过@Autowired 注解进行注入:


@Autowired
private A a;


导入带有@Configuration 注解的配置类

这种导入方式最复杂,因为@Configuration 注解还支持多种组合注解,例如:

  • @Import
  • @ImportResource
  • @PropertySource 等
public class A {
}

publicclass B {
}

@Import(B.class)
@Configuration
public class AConfiguration {
    @Bean
    public A a() {
        returnnew A();
    }
}

@Import(AConfiguration.class)
@Configuration
public class TestConfiguration {
}

通过@Import 注解导入一个带有@Configuration 注解的配置类,与该配置类相关的@Import@ImportResource 和@PropertySource 等注解导入的所有类将一次性全部导入。

 

4.3 ImportSelector


这种导入方式需要实现 ImportSelector 接口:


public class AImportSelector implements ImportSelector {
    private static final String CLASS_NAME = "com.demo.cache.service.A";

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{CLASS_NAME};
    }
}

@Import(AImportSelector.class)
@Configuration
public class TestConfiguration {
}

这种方法的优点是 selectImports 方法返回一个数组,这意味着可以非常方便的导入多个类。

 

4.4 ImportBeanDefinitionRegistrar

这种导入方式需要实现 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 {
}



5. 项目启动时的附加功能
在项目启动时自定义一些附加逻辑,例如加载一些系统参数、资源初始化、预热本地缓存等。Spring Boot 提供了两个接口来帮助我们实现上述要求:
  • CommandLineRunner
  • ApplicationRunner

ApplicationRunner 接口:

@Component
publicclass MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 在这里编写项目启动时需要执行的代码
        System.out.println("项目启动时执行附加功能,加载系统参数...");
        // 假设这里从配置文件中加载系统参数并进行处理
        Properties properties = new Properties();
        try (InputStream inputStream = new FileInputStream("application.properties")) {
            properties.load(inputStream);
            String systemParam = properties.getProperty("system.param");
            System.out.println("加载的系统参数值为:" + systemParam);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,我们实现了 ApplicationRunner 接口,并重写了 run 方法。在 run 方法中,我们可以编写在项目启动时需要执行的附加功能代码,例如加载系统参数、初始化资源、预热缓存等。

6. 修改 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", "Dylan Smith");
        defaultListableBeanFactory.registerBeanDefinition("user", beanDefinitionBuilder.getBeanDefinition());
    }
}

在 postProcessBeanFactory 方法中,可以获取 BeanDefinition 的相关对象并修改该对象的属性。

7. 初始化方法

目前,Spring 中比较常用的初始化 bean 的方法有:

  • 使用@PostConstruct 注解。
  • 实现 InitializingBean 接口。

7.1 使用@PostConstruct 注解

@Service
public class AService {
    @PostConstruct
    public void init() {
        System.out.println("===Initializing===");
    }
}

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

7.2 实现 InitializingBean 接口

@Service
public class BService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("===Initializing===");
    }
}

8. 在初始化 Bean 前后添加逻辑

有时,你希望在初始化 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("Dylan Smith");
        }
        return bean;
    }
}

如果 Spring 中有一个 User 对象,将其 userName 设置为:Dylan Smith

实际上,我们经常使用的注解,如@Autowired@Value@Resource@PostConstruct 等,都是通过 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 实现的。

9. 在关闭容器之前添加操作

有时,我们需要在关闭 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 接口,并覆盖初始化方法和销毁方法。

 

10. 自定义作用域

我们都知道,Spring 只支持两种默认的 Scope:

  • singleton:在单例作用域中,从 Spring 容器中获取的每个 bean 都是同一个对象。
  • prototype:在原型作用域中,从 Spring 容器中获取的每个 bean 都是不同的对象。

Spring Web 扩展了 Scope 并添加了:

  • RequestScope:在同一个请求中,从 Spring 容器中获取的 bean 都是同一个对象。
  • SessionScope:在同一个会话中,从 Spring 容器中获取的 bean 都是同一个对象。

即便如此,有些场景仍然无法满足我们的要求。

例如,如果我们希望在同一个线程中从 Spring 容器中获取的所有 bean 都是同一个对象,该怎么办呢?

这就需要自定义 Scope。

第一步,实现 Scope 接口:
public class ThreadLocalScope implements Scope {
    privatestaticfinal 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();
        returnnull;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
    }

    @Override
    public Object resolveContextualObject(String key) {
        returnnull;
    }

    @Override
    public String getConversationId() {
        returnnull;
    }
}
第二步,将新定义的“Scope”注入到 Spring 容器中:
@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("threadLocalScope", new ThreadLocalScope());
    }
}

第三步,使用新定义的“Scope”:

@Scope("threadLocalScope")
@Service
public class CService {
    public void add() {
    }
}
 
posted @ 2025-06-11 22:11  KLAPT  阅读(7)  评论(0)    收藏  举报