成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

掌握 Spring 框架這十個擴展點,讓你的能力更上一層樓

開發 架構
Spring 具有很強的擴展性。許多第三方應用程序,如 rocketmq、mybatis、redis 等,都可以輕松集成到 Spring 系統中。讓我們一起來看看 Spring 中最常用的十個擴展點。

當我們提到 Spring 時,或許首先映入腦海的是 IOC(控制反轉)和 AOP(面向切面編程)。它們可以被視為 Spring 的基石。正是憑借其出色的設計,Spring 才能在眾多優秀框架中脫穎而出。

Spring 具有很強的擴展性。許多第三方應用程序,如 rocketmq、mybatis、redis 等,都可以輕松集成到 Spring 系統中。讓我們一起來看看 Spring 中最常用的十個擴展點。

1. 全局異常處理

過去,在開發接口時,如果發生異常,我們通常需要給用戶一個更友好的提示。但如果不進行錯誤處理,例如:

@RequestMapping("/test")
@RestController
public class TestController {
    @GetMapping("/division")
    public String division(@RequestParam("a") int a, @RequestParam("b")int b) {
        return String.valueOf(a / b);
    }
}

這是一個計算 a/b 結果的方法,通過127.0.0.1:8080/test/division?a=10&b=2訪問后會出現以下結果:

圖片

什么?用戶能直接看到如此詳細的錯誤信息嗎?

這種報錯方式給用戶帶來了非常糟糕的體驗。為了解決這個問題,我們通常在接口中捕獲異常。

@GetMapping("/division")
public String division(@RequestParam("a") int a, @RequestParam("b") int b) {
    String result = "";
    try {
        result = String.valueOf(a / b);
    } catch (ArithmeticException e) {
        result = "params error";
    }
    return result;
}

接口改造后,當發生異常時,會提示:“params error”,用戶體驗會更好。

如果只是一個接口,那沒問題。但如果項目中有成百上千個接口,我們是否需要為所有接口添加異常處理代碼呢?

肯定不能這樣做的。這時,全局異常處理就派上用場了:RestControllerAdvice。

@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;
    }
}

只需在 handleException 方法中處理異常情況。業務接口可以放心使用,不再需要捕獲異常(遵循統一的處理邏輯)。

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 Verificatinotallow===");
        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());
    }
}

隨后,當請求接口時,Spring MVC 可以通過此攔截器自動攔截接口并驗證權限。

3. 獲取 Spring 容器對象

在日常開發中,我們經常需要從 Spring 容器中獲取 Beans。但是你知道如何獲取 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 注解來完成此功能。

如果你看過它的源代碼,會發現導入的類支持三種不同的類型。

然而,我認為最好將普通類和帶有@Configuration 注解的配置類分開解釋。因此,列出了四種不同的類型:

4.1 導入普通類

這種導入方式最簡單。導入的類將被實例化為一個 bean 對象。

public class A {
}

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

通過@Import 注解導入類 A,Spring 可以自動實例化對象 A。然后,可以在需要的地方通過@Autowired 注解進行注入:

@Autowired
private A a;

是不是很神奇?不需要添加@Bean 注解就可以實例化對象。

4.2 導入帶有@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 方法中,我們可以編寫在項目啟動時需要執行的附加功能代碼,例如加載系統參數、初始化資源、預熱緩存等。這里只是簡單地模擬了從配置文件中加載系統參數并打印出來,實際應用中可以根據具體需求進行更復雜的操作。

當項目啟動時,Spring Boot 會自動檢測并執行實現了 ApplicationRunner 或 CommandLineRunner 接口的類中的 run 方法,從而實現項目啟動時的附加功能。

這兩個接口的區別在于參數類型不同,ApplicationRunner 的 run 方法參數是 ApplicationArguments,它提供了更多關于應用程序參數的信息,而 CommandLineRunner 的 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 的方法有:

  1. 使用@PostConstruct 注解。
  2. 實現 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() {
    }
}

總結

好了,今天的內容就到這里。對 Spring 框架感興趣的讀者可以關注我,后續會分享更多有關 Spring 的相關知識。

責任編輯:武曉燕 來源: 程序猿技術充電站
相關推薦

2023-12-19 18:08:47

MySQL方法優化查詢

2019-12-24 09:05:08

框架薪資Web

2023-09-24 23:07:24

流量抑制風暴控制

2014-08-18 14:54:54

Git

2018-05-10 14:34:48

薪資Java開發

2012-05-28 14:18:33

Web

2023-11-01 13:34:37

Python

2023-07-04 08:33:46

Python對象編程

2021-09-21 15:17:09

API微服務后端

2023-07-21 08:01:13

CSSInherit?

2011-03-31 09:51:45

Windows XP

2011-03-31 09:57:54

Windows XP

2009-10-23 14:46:43

2021-01-21 11:24:16

智能安全首席信息安全官CISO

2024-06-20 13:22:13

C++11C++模板

2019-08-26 14:53:32

數據中心運維管理宕機

2017-08-02 11:38:15

AndroidCoding技巧

2019-08-26 10:10:57

數據中心運維宕機

2015-03-30 09:48:33

程序員更上一層樓

2021-03-25 15:07:50

編程技術工具
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 女女百合av大片一区二区三区九县 | 香蕉久久av | 成人精品一区二区三区中文字幕 | 亚洲精品国产第一综合99久久 | 久久久婷婷 | 久日精品 | 免费中文字幕日韩欧美 | 有码在线| 91精品久久久久久久久久 | www.国产精 | a久久久久久 | 午夜精品久久久久久久久久久久 | 日本免费视频在线观看 | 久久久久精 | 国产成人av一区二区三区 | 午夜精品久久 | 影音先锋中文字幕在线观看 | 国产二区三区 | 亚欧精品 | 亚洲成人一区 | 久久久久亚洲精品 | 天堂一区在线 | www..com18午夜观看 | 国产电影一区二区三区爱妃记 | 国产免费看| 日韩欧美中文 | 久久精品久久久久久 | 欧美在线一区二区三区 | 成人免费网站www网站高清 | 国产999精品久久久 日本视频一区二区三区 | 国产欧美精品一区二区 | 亚洲欧美一区二区三区在线 | 成人一级黄色毛片 | 国产一级片在线播放 | 成人日批视频 | 久久久久久成人 | 97国产精品视频人人做人人爱 | 亚洲性人人天天夜夜摸 | 国产一区二区久久 | 午夜免费精品视频 | 韩国av一区二区 |