Custom Bean Scope в Spring

Опубликовано: 22 Февраля, 2023

Предварительное требование : области Singleton и Prototype Bean Scope в Java Spring.

В этой статье мы собираемся обсудить пользовательскую область действия bean-компонента в Spring. Я предполагаю, что вы, ребята, знаете о компоненте Spring и его возможностях, предоставляемых средой Spring. Компонент — это объект, который создается, собирается и иным образом управляется контейнером Spring IoC. Spring предоставляет две стандартные области видимости bean-компонентов, т. е. синглтон и прототип, которые мы можем использовать в любом приложении Spring. Помимо этих двух областей, Spring предоставляет три дополнительных области действия bean-компонента, т. е. request, session, globalSession, которые можно использовать только в веб-приложениях.

Мы не можем переопределить/изменить стандартные области действия bean-компонентов Spring, т.е. синглтон и прототип. Обычно считается плохой практикой переопределять веб-области. Но иногда приложение требует чего-то «из коробки» или дополнительных возможностей из тех, что есть в предоставленных рамках. Начиная с Spring 2.0, мы можем определять пользовательские области действия Spring Bean, а также изменять существующие области действия Spring Bean (кроме областей Singleton и прототипов).

Давайте попробуем понять требования пользовательской области действия компонента на примере. Предположим, если вы разрабатываете многопользовательскую систему, вы можете захотеть предоставить отдельный экземпляр определенного компонента или набора компонентов для каждого арендатора. Spring предоставляет механизм для создания настраиваемых областей действия для таких сценариев, как этот. Итак, давайте посмотрим, как создать пользовательскую область действия Spring bean на примере.

Чтобы интегрировать вашу пользовательскую область (области) в контейнер Spring, вам необходимо реализовать интерфейс org.springframework.beans.factory.config.Scope . Этот интерфейс Scope содержит четыре метода:

  • Получить объект (имя строки, ObjectFactory objectFactory)
  • Удаление объекта (имя строки)
  • void registerDestructionCallback (имя строки, Runnable destroyCallback)
  • Строка getConversationId()

Чтобы реализовать пользовательскую область действия компонента, мы выполним несколько шагов один за другим, как показано ниже:

Пошаговая реализация

Шаг 1. Создайте собственный класс области действия компонента

Java




package com.gfg.scripter;
 
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
 
public class CustomThreadScope implements Scope {
    CustomThreadLocal customThreadLocal = new CustomThreadLocal();
   
    @Override
    public Object get(String str, ObjectFactory objectFactory) {
        System.out.println("Fetched object from scope");
       
        @SuppressWarnings("unchecked")
        Map<String, Object> scope = (Map<String, Object>) customThreadLocal.get();
        Object object = scope.get(str);
        if (object == null) {
            object = objectFactory.getObject();
            scope.put(str, object);
        }
       
        return object;
    }
   
    @Override
    public String getConversationId() {
        return null;
    }
   
    @Override
    public void registerDestructionCallback(String arg0, Runnable arg1) {
    }
   
    @Override
    public Object remove(String str) {
        Map<String, Object> scope = (Map<String, Object>) customThreadLocal.get();
        return scope.remove(str);
    }
   
    @Override
    public Object resolveContextualObject(String arg0) {
        return null;
    }
   
    class CustomThreadLocal extends ThreadLocal {
        protected Map<String, Object> initialValue() {
            System.out.println("Initializing ThreadLocal");
            return new HashMap<String, Object>();
        }
    }
}

Шаг 2. Зарегистрируйте созданную пользовательскую область действия компонента

На предыдущем шаге мы создали класс области видимости компонента Thread. Теперь нам нужно зарегистрировать этот компонент в контексте приложения Spring. Тогда мы получим преимущество только что созданного класса области видимости в приложении. Здесь есть два способа, с помощью которых мы можем зарегистрировать пользовательскую область, т. е. программную регистрацию, или мы можем сделать это с помощью конфигурации на основе XML. В этом уроке мы будем использовать программную регистрацию:

Java




package com.gfg.scripter;
 
import org.springframework.beans.factory.config.Scope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        Scope scope = new CustomThreadScope();
        applicationContext.getBeanFactory().registerScope("threadScope", scope);
    }
}

Шаг 3. Использование пользовательской области

Java




package com.gfg.scripter;
 
public class Volunteer {
    private String name;
    private String qualification;
    private String address;
    private String dob;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getQualification() {
        return qualification;
    }
 
    public void setQualification(String qualification) {
        this.qualification = qualification;
    }
 
    public String getAddress() {
        return address;
    }
 
    public void setAddress(String address) {
        this.address = address;
    }
 
    public String getDob() {
        return dob;
    }
 
    public void setDob(String dob) {
        this.dob = dob;
    }
}

Java




package com.gfg.scripter;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
 
@Configuration
@ComponentScan(basePackages = { "com.gfg.scripter" })
public class AppConfig {
 
    @Bean
    @Scope("threadScope")
    public Volunteer getVolunteer() {
        Volunteer volunteer = new Volunteer();
        volunteer.setName("Bikash Dubey");
        volunteer.setQualification("B.Tech - CSE");
        volunteer.setDob("07-03-1997");
        volunteer.setAddress("Dibrugarh, Assam");
 
        return volunteer;
    }
}

Шаг 4. Тестирование пользовательской области

Java




package com.gfg.scripter;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
    static ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
 
    public static void main(String[] args) {
         
        // This code will be executed by child thread
        Runnable childThread = () -> {
            Volunteer v1 = context.getBean(Volunteer.class);
            Volunteer v2 = context.getBean(Volunteer.class);
            System.out.println("Hashcode of two object created by child thread");
            System.out.println(v1.hashCode() + " & " + v2.hashCode());
            System.out.println("Is both objects created by child thread same ? :" + (v1.hashCode() == v2.hashCode()));
        };
        new Thread(childThread).start();
 
        // This code will be executed by main thread
        Volunteer v1 = context.getBean(Volunteer.class);
        Volunteer v2 = context.getBean(Volunteer.class);
        System.out.println("Hashcode of two object created by main thread");
        System.out.println(v1.hashCode() + " & " + v2.hashCode());
        System.out.println("Is both objects created by main thread same ? :" + (v1.hashCode() == v2.hashCode()));
     
    }
}

Выход:

Hashcode of two object created by child thread
1522311648 & 1522311648
Is both objects created by child thread same ? : true
Hashcode of two object created by main thread
400197113 & 400197113
Is both objects created by main thread same ? : true

Как видите, здесь областью действия bean-компонента является поток, т.е. для каждого потока будет создан один bean-компонент в соответствии с нашим пользовательским bean-компонентом области потока. Знаете ли вы что-нибудь, Spring 3.0 поставляется с именем класса области видимости потока SimpleThreadScope , о котором мы узнаем в следующем руководстве.