Spring factory bean java configuration

Создание бинов

Определение бина — это, по сути, набор правил для создания одного или нескольких объектов. В ответ на запрос контейнер просматривает набор правил именованного бина и использует конфигурационные метаданные, заключенные в определении этого бина, для создания (или получения) фактического объекта.

Если вы используете конфигурационные метаданные на основе XML, вы указываете тип (или класс) объекта, экземпляр которого нужно создать, в атрибуте class элемента . Этот атрибут class (который внутренне является свойством Class экземпляра BeanDefinition ) как правило является обязательным. Свойство Class можно использовать одним из двух способов:

  • Как правило, для задания класса бина, который должен быть построен в случае, если контейнер сам непосредственно создает бин путем рефлексивного вызова его конструктора, что в некоторой степени эквивалентно коду Java с оператором new .
  • Для задания фактического класса, содержащего статический фабричный метод, который вызывается для создания объекта, в менее распространенном случае, когда контейнер вызывает статический фабричный метод на классе для создания бина. Тип объекта, возвращаемый в результате вызова статического фабричного метода, может быть тем же классом или совсем другим классом.

Если вы хотите настроить определение бина для вложенного класса, вы можете использовать либо двоичное имя, либо исходное имя вложенного класса.

Например, если у вас есть класс SomeThing в пакете com.example , и этот класс SomeThing имеет статический вложенный класс, который называется OtherThing , они могут быть разделены знаком доллара ($ ) или точкой ( . ). Таким образом, значение атрибута class в определении бина будет com.example.SomeThing$OtherThing или com.example.SomeThing.OtherThing .

Читайте также:  Php максимальное значение int

Если бин создается с помощью конструктора, могут быть использованы все стандартные классы, и они будут совместимы со Spring. То есть разрабатываемый класс не обязательно должен реализовывать какие-либо конкретные интерфейсы или быть написанным определенным образом. Простого задания класса бина должно быть достаточно. Однако в зависимости от того, какой тип IoC используется для данного конкретного бина, может понадобиться конструктор по умолчанию (пустой).

IoC-контейнер Spring может управлять практически любым классом, которым вам необходимо управлять. Он не ограничивается управлением истинными JavaBeans. Большинство пользователей Spring предпочитают фактические JavaBeans с использованием конструктора по умолчанию (без аргументов) и соответствующими сеттерами и геттерами, смоделированными по свойствам контейнера. В вашем контейнере также могут присутствовать более экзотические классы не в стиле бинов. Если, например, нужно использовать устаревший пул соединений, который абсолютно не соответствует спецификации JavaBean, Spring может управлять и им.

С помощью конфигурационных метаданных на основе XML можно задать свой класс бина следующим образом:

Подробнее о механизме предоставления аргументов конструктору (если требуется) и установки свойств экземпляра объекта после того, как объект построен, см. в разделе Внедрение зависимостей.

Создание экземпляра с помощью статического фабричного метода

При определении бина, который вы создаете с помощью статического фабричного метода, используйте атрибут class для задания класса, который содержит статический фабричный метод, и атрибут factory-method для задания имени самого фабричного метода. Вам должна быть доступна возможность вызвать этот метод (с необязательными аргументами, как описано далее) и возвращать работающий объект, который впоследствии будет рассматриваться так, как если бы он был создан с помощью конструктора. Одним из вариантов использования такого определения бина является вызов статических фабрик в унаследованном коде.

Следующее определение бина указывает, что бин будет создан путем вызова метода фабрики. В определении указывается не тип (класс) возвращаемого объекта, а только класс, содержащий фабричный метод. В этом примере метод createInstance() должен быть статическим . В следующем примере показано, как задать фабричный метод:

В следующем примере показан класс, который будет работать с предыдущим определением бина:

public class ClientService < private static ClientService clientService = new ClientService(); private ClientService() <>public static ClientService createInstance() < return clientService; >>
class ClientService private constructor() < companion object < private val clientService = ClientService() @JvmStatic fun createInstance() = clientService >>

Подробнее о механизме предоставления (необязательных) аргументов фабричному методу и установки свойств экземпляра объекта после возврата объекта из фабрики.

Создание экземпляра с помощью экземплярного фабричного метода

Подобно созданию экземпляра через статический фабричный метод, создание экземпляра с помощью экземплярного фабричного метода вызывает нестатический метод существующего бина из контейнера для создания нового бина. Чтобы использовать этот механизм, оставьте атрибут class пустым, а в атрибуте factory-bean укажите имя бина в текущем (или родительском, или предшествующем) контейнере, который содержит экземплярный метод, который будет вызван для создания объекта. Задайте имя самого фабричного метода с помощью атрибута factory-method . В следующем примере показано, как сконфигурировать такой бин:

В следующем примере показан соответствующий класс:

public class DefaultServiceLocator < private static ClientService clientService = new ClientServiceImpl(); public ClientService createClientServiceInstance() < return clientService; >>
class DefaultServiceLocator < companion object < private val clientService = ClientServiceImpl() >fun createClientServiceInstance(): ClientService < return clientService >>

Один фабричный класс может содержать более одного фабричного метода, как показано в следующем примере:

В следующем примере показан соответствующий класс:

public class DefaultServiceLocator < private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); public ClientService createClientServiceInstance() < return clientService; >public AccountService createAccountServiceInstance() < return accountService; >>
class DefaultServiceLocator < companion object < private val clientService = ClientServiceImpl() private val accountService = AccountServiceImpl() >fun createClientServiceInstance(): ClientService < return clientService >fun createAccountServiceInstance(): AccountService < return accountService >>

Данный подход демонстрирует, что самим бином-фабрикой можно управлять и конфигурировать его с помощью внедрения зависимостей (DI). См. раздел «Подробно о зависимостях и конфигурации».

В документации Spring «бин-фабрика (factory bean)» означает бин, который конфигурируется в контейнере Spring и создает объекты с помощью instance или static фабричного метода. И наоборот, FactoryBean (обратите внимание на написание заглавными буквами) относится к специфическому для Spring классу реализации FactoryBean .

Определение типа бина во время выполнения

Определить тип конкретного бина во время выполнения — это нетривиальная задача. Заданный класс в определении метаданных бина — это просто исходная ссылка на класс, потенциально объединенный с объявленным фабричным методом или являющийся классом FactoryBean , что может привести к получению другого типа бина во время выполнения, или вообще не заданный в случае фабричного метода на уровне экземпляра (который разрешается вместо этого через заданное имя factory-bean ). Кроме того, АОП-проксирование может поместить экземпляр бина в прокси на основе интерфейса с ограниченным открытием реального типа целевого бина (только его реализованные интерфейсы).

Рекомендуемый способ узнать фактический тип времени выполнения конкретного бина — это вызов BeanFactory.getType для заданного имени бина. При этом учитываются все вышеперечисленные случаи и возвращается тип объекта, который будет возвращен вызовом BeanFactory.getBean для того же имени бина.

Источник

[Recipe] Factory Beans in Spring with Java Configuration

Implement factory pattern in Spring when using Java configuration.

Factory design patterns help you create objects without using the new keyword directly within your code. The factory intern will create object for you. You can read more about factory pattern @ Factory Design Patterns.

We will newly create a factory class with a method that accept a String and return the object of a class. We will create a bean for factory within the java configuration file, which will be injected to other bean methods and using this bean we will be creating other beans. In the example, I have used a maven pom file with dependencies for spring-context and have specified the compiler level as Java 8. You may even use Spring Boot to do the same example.

Bean Components, Config and Main Class

Package: com.javajee.spring.service

JJWriter.java

package com.javajee.spring.service;

public interface JJWriter

public void write();

>

JJFileWriter.java

package com.javajee.spring.service;

public class JJFileWriter implements JJWriter

public void write() System.out.println(«Writing to File. «);
>

@Override
public String toString() return «Writing to File. «;
>

>

JJDatabaseWriter.java

package com.javajee.spring.service;

public class JJDatabaseWriter implements JJWriter

public void write() System.out.println(«Writing to Database. «);
>

@Override
public String toString() return «Writing to Database. «;
>
>

WriterFactory.java

package com.javajee.spring.service;

public class WriterFactory

public JJWriter getWriter(String str) switch (str) case «file»:
return new JJFileWriter();
case «db»:
return new JJDatabaseWriter();
default:
return null;
>
>

>

Package: com.javajee.spring.controller

WriterController.java

package com.javajee.spring.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import com.javajee.spring.service.JJWriter;

@Controller
public class WriterController

@Autowired
JJWriter writer;

Package: com.javajee.spring.config

DemoConfig.java

package com.javajee.spring.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;

import com.javajee.spring.service.JJWriter;
import com.javajee.spring.service.WriterFactory;

@Configuration
@ComponentScan(basePackages=»com.javajee.spring»)
@PropertySource(«classpath:/application.properties»)
public class DemoConfig

@Bean
public WriterFactory getWriterFactory() return new WriterFactory();
>

@Bean
@Profile(«fileprofile»)
public JJWriter getJJFIleWriter(WriterFactory factory)
return factory.getWriter(«file»);
>

@Bean
@Profile()
public JJWriter getJJDatabaseWriter(WriterFactory factory)
return factory.getWriter(«db»);
>

>

Package: com.javajee.spring

Main class: JJWriterMain.java

package com.javajee.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.javajee.spring.config.DemoConfig;
import com.javajee.spring.controller.WriterController;
import com.javajee.spring.service.JJWriter;

public class JJWriterMain

@Autowired
JJWriter writer;

public static void main(String[] args)

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);

WriterController writerController = context.getBean(«writerController», WriterController.class);

writerController.write();

Note:

In this example, I have used annotations and Java configuration. However, factory implementation makes more sense in xml configuration due to some useful tag support provided by Spring xml configuration.

This example also makes use of many other annotations and concepts we have learned in this section like controller, componentscan, configuration, bean, profile, autowired etc. We will refactor the code into different packages and show the use of annotations like @Controller. Note that, technically you can use @Component or any other layer based annotations here. You can use @ComponentScan for all of these annotations. I have used the same application.properties file with a single line for activating the profile: spring.profiles.active=dbprofile. I have specified the file through @PropertySource annotation over my java configuration file.

Источник

Оцените статью