偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

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

1. 全局異常處理

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

@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 結(jié)果的方法,通過127.0.0.1:8080/test/division?a=10&b=2訪問后會出現(xiàn)以下結(jié)果:

圖片

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

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

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

接口改造后,當(dāng)發(fā)生異常時,會提示:“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 方法中處理異常情況。業(yè)務(wù)接口可以放心使用,不再需要捕獲異常(遵循統(tǒng)一的處理邏輯)。

2. 自定義攔截器

與 Spring 攔截器相比,Spring MVC 攔截器可以在內(nèi)部獲取 HttpServletRequest 和 HttpServletResponse 等 Web 對象實例。

Spring MVC 攔截器的頂級接口是:HandlerInterceptor,它包含三個方法:

  • preHandle:在目標方法執(zhí)行前執(zhí)行。
  • postHandle:在目標方法執(zhí)行后執(zhí)行。
  • afterCompletion:在請求完成時執(zhí)行。

為了方便起見,在一般情況下,我們通常使用 HandlerInterceptor 接口的實現(xiàn)類 HandlerInterceptorAdapter。

如果存在權(quán)限認證、日志記錄和統(tǒng)計等場景,可以使用此攔截器。

第一步,通過繼承 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());
    }
}

隨后,當(dāng)請求接口時,Spring MVC 可以通過此攔截器自動攔截接口并驗證權(quán)限。

3. 獲取 Spring 容器對象

在日常開發(fā)中,我們經(jīng)常需要從 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");
    }
}

實現(xiàn) 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. 導(dǎo)入配置

有時我們需要在某個配置類中導(dǎo)入其他一些類,并且導(dǎo)入的類也會被添加到 Spring 容器中。此時,可以使用@Import 注解來完成此功能。

如果你看過它的源代碼,會發(fā)現(xiàn)導(dǎo)入的類支持三種不同的類型。

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

4.1 導(dǎo)入普通類

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

public class A {
}

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

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

@Autowired
private A a;

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

4.2 導(dǎo)入帶有@Configuration 注解的配置類

這種導(dǎo)入方式最復(fù)雜,因為@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 注解導(dǎo)入一個帶有@Configuration 注解的配置類,與該配置類相關(guān)的@Import、@ImportResource 和@PropertySource 等注解導(dǎo)入的所有類將一次性全部導(dǎo)入。

4.3 ImportSelector

這種導(dǎo)入方式需要實現(xiàn) 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 {
}

這種方法的優(yōu)點是 selectImports 方法返回一個數(shù)組,這意味著可以非常方便的導(dǎo)入多個類。

4.4 ImportBeanDefinitionRegistrar

這種導(dǎo)入方式需要實現(xiàn) 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. 項目啟動時的附加功能

有時我們需要在項目啟動時自定義一些附加邏輯,例如加載一些系統(tǒng)參數(shù)、資源初始化、預(yù)熱本地緩存等。我們該怎么做呢?Spring Boot 提供了兩個接口來幫助我們實現(xiàn)上述要求:

  • CommandLineRunner
  • ApplicationRunner

它們的用法非常簡單。以 ApplicationRunner 接口為例:

@Component
publicclass MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 在這里編寫項目啟動時需要執(zhí)行的代碼
        System.out.println("項目啟動時執(zhí)行附加功能,加載系統(tǒng)參數(shù)...");
        // 假設(shè)這里從配置文件中加載系統(tǒng)參數(shù)并進行處理
        Properties properties = new Properties();
        try (InputStream inputStream = new FileInputStream("application.properties")) {
            properties.load(inputStream);
            String systemParam = properties.getProperty("system.param");
            System.out.println("加載的系統(tǒng)參數(shù)值為:" + systemParam);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上述代碼中,我們實現(xiàn)了 ApplicationRunner 接口,并重寫了 run 方法。在 run 方法中,我們可以編寫在項目啟動時需要執(zhí)行的附加功能代碼,例如加載系統(tǒng)參數(shù)、初始化資源、預(yù)熱緩存等。這里只是簡單地模擬了從配置文件中加載系統(tǒng)參數(shù)并打印出來,實際應(yīng)用中可以根據(jù)具體需求進行更復(fù)雜的操作。

當(dāng)項目啟動時,Spring Boot 會自動檢測并執(zhí)行實現(xiàn)了 ApplicationRunner 或 CommandLineRunner 接口的類中的 run 方法,從而實現(xiàn)項目啟動時的附加功能。

這兩個接口的區(qū)別在于參數(shù)類型不同,ApplicationRunner 的 run 方法參數(shù)是 ApplicationArguments,它提供了更多關(guān)于應(yīng)用程序參數(shù)的信息,而 CommandLineRunner 的 run 方法參數(shù)是原始的字符串?dāng)?shù)組,直接包含了命令行參數(shù)。根據(jù)具體需求可以選擇使用其中一個接口來實現(xiàn)項目啟動時的附加功能。

6. 修改 BeanDefinition

在實例化 Bean 對象之前,Spring IOC 需要先讀取 Bean 的相關(guān)屬性,將它們保存在 BeanDefinition 對象中,然后通過 BeanDefinition 對象實例化 Bean 對象。

如果你想修改 BeanDefinition 對象中的屬性,該怎么做呢?我們可以實現(xiàn) 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 的相關(guān)對象并修改該對象的屬性。

7. 初始化方法

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

  1. 使用@PostConstruct 注解。
  2. 實現(xiàn) InitializingBean 接口。

7.1 使用@PostConstruct 注解

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

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

7.2 實現(xiàn) InitializingBean 接口

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

8. 在初始化 Bean 前后添加邏輯

有時,你希望在初始化 bean 之前和之后實現(xiàn)一些自己的邏輯。

這時,可以實現(xiàn) BeanPostProcessor 接口。

這個接口目前有兩個方法:

  • postProcessBeforeInitialization:在初始化方法之前調(diào)用。
  • postProcessAfterInitialization:在初始化方法之后調(diào)用。

例如:

@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 設(shè)置為:Dylan Smith。

實際上,我們經(jīng)常使用的注解,如@Autowired、@Value、@Resource、@PostConstruct 等,都是通過 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 實現(xiàn)的。

9. 在關(guān)閉容器之前添加操作

有時,我們需要在關(guān)閉 Spring 容器之前做一些額外的工作,例如關(guān)閉資源文件。

這時,我們可以實現(xiàn) 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 容器銷毀之前會調(diào)用 destroy 方法。通常,我們會同時實現(xiàn) InitializingBean 和 DisposableBean 接口,并覆蓋初始化方法和銷毀方法。

10. 自定義作用域

我們都知道,Spring 只支持兩種默認的 Scope:

  • singleton:在單例作用域中,從 Spring 容器中獲取的每個 bean 都是同一個對象。
  • prototype:在原型作用域中,從 Spring 容器中獲取的每個 bean 都是不同的對象。

Spring Web 擴展了 Scope 并添加了:

  • RequestScope:在同一個請求中,從 Spring 容器中獲取的 bean 都是同一個對象。
  • SessionScope:在同一個會話中,從 Spring 容器中獲取的 bean 都是同一個對象。

即便如此,有些場景仍然無法滿足我們的要求。

例如,如果我們希望在同一個線程中從 Spring 容器中獲取的所有 bean 都是同一個對象,該怎么辦呢?

這就需要自定義 Scope。

第一步,實現(xiàn) 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() {
    }
}

總結(jié)

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

責(zé)任編輯:武曉燕 來源: 程序猿技術(shù)充電站
相關(guān)推薦

2023-12-19 18:08:47

MySQL方法優(yōu)化查詢

2019-12-24 09:05:08

框架薪資Web

2014-08-18 14:54:54

Git

2023-09-24 23:07:24

流量抑制風(fēng)暴控制

2018-05-10 14:34:48

薪資Java開發(fā)

2012-05-28 14:18:33

Web

2023-11-01 13:34:37

Python

2023-07-21 08:01:13

CSSInherit?

2023-07-04 08:33:46

Python對象編程

2021-09-21 15:17:09

API微服務(wù)后端

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

數(shù)據(jù)中心運維管理宕機

2017-08-02 11:38:15

AndroidCoding技巧

2019-08-26 10:10:57

數(shù)據(jù)中心運維宕機

2015-03-30 09:48:33

程序員更上一層樓

2021-03-25 15:07:50

編程技術(shù)工具
點贊
收藏

51CTO技術(shù)棧公眾號