spring
Creando y usando frijoles
Buscar..
Autowiring todos los frijoles de un tipo específico
Si tiene varias implementaciones de la misma interfaz, Spring puede convertirlas en un objeto de colección. Voy a usar un ejemplo usando un patrón Validator 1
Foo Class:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Interfaz:
public interface FooValidator {
public Foo validate(Foo foo);
}
Nombre Validador Clase:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
Correo electrónico Validador de clase:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
Ahora puede habilitar automáticamente estos validadores individualmente o juntos en una clase.
Interfaz:
public interface FooService {
public void handleFoo(Foo foo);
}
Clase:
@Service
public class FooServiceImpl implements FooService {
/** Autowire all classes implementing FooValidator interface**/
@Autowired
private List<FooValidator> allValidators;
@Override
public void handleFoo(Foo foo) {
/**You can use all instances from the list**/
for(FooValidator validator : allValidators) {
foo = validator.validate(foo);
}
}
}
Vale la pena señalar que si tiene más de una implementación de una interfaz en el contenedor Spring IoC y no especifica cuál desea usar con la anotación @Qualifier
, Spring lanzará una excepción cuando intente iniciar, porque ganó " No sé qué instancia utilizar.
1: Esta no es la forma correcta de hacer validaciones tan simples. Este es un ejemplo simple sobre autowiring. Si desea tener una idea de un método de validación mucho más sencillo, vea cómo Spring realiza la validación con anotaciones.
Declarando frijol
Para declarar un bean, simplemente anote un método con la anotación @Bean
o anote una clase con la anotación @Component
(las anotaciones @Service
, @Repository
, @Controller
podrían usarse también).
Cuando JavaConfig encuentra tal método, ejecutará ese método y registrará el valor de retorno como un bean dentro de un BeanFactory. Por defecto, el nombre del bean será el del nombre del método.
Podemos crear frijol usando una de tres maneras:
Usando la configuración basada en Java : En el archivo de configuración necesitamos declarar el bean usando la anotación @bean
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
Uso de la configuración basada en XML : para la configuración basada en XML necesitamos crear el bean declarar en la configuración de la aplicación XML, es decir
<beans> <bean name="transferService" class="com.acme.TransferServiceImpl"/> </beans>
Componente controlado por anotación: para los componentes controlados por anotación, debemos agregar la anotación @Component a la clase que queremos declarar como bean.
@Component("transferService") public class TransferServiceImpl implements TransferService { ... }
Ahora los tres beans con nombre transferService
están disponibles en BeanFactory
o ApplicationContext
.
Anotación básica autowiring
Interfaz:
public interface FooService {
public int doSomething();
}
Clase:
@Service
public class FooServiceImpl implements FooService {
@Override
public int doSomething() {
//Do some stuff here
return 0;
}
}
Se debe tener en cuenta que una clase debe implementar una interfaz para que Spring pueda conectar automáticamente esta clase. Existe un método para permitir que Spring autowire clases autónomas utilizando el tiempo de carga de tejido, pero está fuera del alcance de este ejemplo.
Puede obtener acceso a este bean en cualquier clase que haya sido instanciada por el contenedor Spring IoC utilizando la anotación @Autowired
.
Uso:
@Autowired([required=true])
La anotación @Autowired
intentará primero autowire por tipo y luego volverá al nombre del bean en caso de ambigüedad.
Esta anotación se puede aplicar de varias maneras diferentes.
Inyección de constructor:
public class BarClass() {
private FooService fooService
@Autowired
public BarClass(FooService fooService) {
this.fooService = fooService;
}
}
Inyección de campo:
public class BarClass() {
@Autowired
private FooService fooService;
}
Inyección de Setter:
public class BarClass() {
private FooService fooService;
@Autowired
public void setFooService(FooService fooService) {
this.fooService = fooService;
}
}
Usando FactoryBean para la instanciación dinámica de frijol
Para decidir dinámicamente qué frijoles inyectar, podemos usar FactoryBean
s. Estas son clases que implementan el patrón de método de fábrica, proporcionando instancias de beans para el contenedor. Son reconocidos por Spring y se pueden usar de manera transparente, sin necesidad de saber que el frijol proviene de una fábrica. Por ejemplo:
public class ExampleFactoryBean extends AbstractFactoryBean<String> {
// This method determines the type of the bean for autowiring purposes
@Override
public Class<?> getObjectType() {
return String.class;
}
// this factory method produces the actual bean
@Override
protected String createInstance() throws Exception {
// The thing you return can be defined dynamically,
// that is read from a file, database, network or just
// simply randomly generated if you wish.
return "Something from factory";
}
}
Configuración:
@Configuration
public class ExampleConfig {
@Bean
public FactoryBean<String> fromFactory() {
return new ExampleFactoryBean();
}
}
Obteniendo el frijol
AbstractApplicationContext context = new AnnotationConfigApplicationContext(ExampleConfig.class);
String exampleString = (String) context.getBean("fromFactory");
Para obtener el FactoryBean real, use el prefijo ampersand antes del nombre del bean:
FactoryBean<String> bean = (FactoryBean<String>) context.getBean("&fromFactory");
Tenga en cuenta que solo puede usar prototype
o ámbitos singleton
: para cambiar el alcance al método de anulación de prototype
anulación isSingleton
:
public class ExampleFactoryBean extends AbstractFactoryBean<String> {
@Override
public boolean isSingleton() {
return false;
}
// other methods omitted for readability reasons
}
Tenga en cuenta que el alcance se refiere a las instancias reales que se crean, no al propio bean de fábrica.
Inyectar frijoles prototipo en singletons
El contenedor crea un bean singleton e inyecta colaboradores en él solo una vez. Este no es el comportamiento deseado cuando un bean singleton tiene un colaborador de ámbito prototipo, ya que el bean de ámbito prototipo debe inyectarse cada vez que se accede a él mediante el acceso.
Hay varias soluciones a este problema:
- Utilice el método de búsqueda de inyección
- Recupere un bean de ámbito de prototipo a través de
javax.inject.Provider
- Recupere un bean de ámbito prototipo a través de
org.springframework.beans.factory.ObjectFactory
(un equivalente de # 2, pero con la clase que es específica de Spring) - Haga que un contenedor de bean singleton sea consciente mediante la implementación de la interfaz
ApplicationContextAware
Los enfoques # 3 y # 4 generalmente no se recomiendan, ya que vinculan fuertemente una aplicación al marco de Spring. Por lo tanto, no están cubiertos en este ejemplo.
Inyección del método de búsqueda a través de la configuración XML y un método abstracto.
Clases de Java
public class Window {
}
public abstract class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
protected abstract Window createNewWindow(); // lookup method
}
XML
<bean id="window" class="somepackage.Window" scope="prototype" lazy-init="true"/>
<bean id="windowGenerator" class="somepackage.WindowGenerator">
<lookup-method name="createNewWindow" bean="window"/>
</bean>
Método de búsqueda de inyección a través de la configuración de Java y @Component.
Clases de Java
public class Window {
}
@Component
public class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
@Lookup
protected Window createNewWindow() {
throw new UnsupportedOperationException();
}
}
Configuración de Java
@Configuration
@ComponentScan("somepackage") // package where WindowGenerator is located
public class MyConfiguration {
@Bean
@Lazy
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Window window() {
return new Window();
}
}
Inyección del método de búsqueda manual a través de la configuración de Java.
Clases de Java
public class Window {
}
public abstract class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
protected abstract Window createNewWindow(); // lookup method
}
Configuración de Java
@Configuration
public class MyConfiguration {
@Bean
@Lazy
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Window window() {
return new Window();
}
@Bean
public WindowGenerator windowGenerator(){
return new WindowGenerator() {
@Override
protected Window createNewWindow(){
return window();
}
};
}
}
Inyección de un bean de ámbito protoype en singleton a través de javax.inject.Provider
Clases de java
public class Window {
}
public class WindowGenerator {
private final Provider<Window> windowProvider;
public WindowGenerator(final Provider<Window> windowProvider) {
this.windowProvider = windowProvider;
}
public Window generateWindow() {
Window window = windowProvider.get(); // new instance for each call
...
}
}
XML
<bean id="window" class="somepackage.Window" scope="prototype" lazy-init="true"/>
<bean id="windowGenerator" class="somepackage.WindowGenerator">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.ProviderCreatingFactoryBean">
<property name="targetBeanName" value="window"/>
</bean>
</constructor-arg>
</bean>
Los mismos enfoques también se pueden utilizar para otros ámbitos (por ejemplo, para inyectar un bean de ámbito de solicitud en singleton).
Autowiring instancias de bean específicas con @Qualifier
Si tiene varias implementaciones de la misma interfaz, Spring necesita saber cuál de ellas debería incluir en una clase. Voy a usar un patrón Validator en este ejemplo. 1
Foo Class:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Interfaz:
public interface FooValidator {
public Foo validate(Foo foo);
}
Nombre Validador Clase:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
Correo electrónico Validador de clase:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
Ahora puede autowire estos validadores individualmente en una clase.
Interfaz:
public interface FooService {
public void handleFoo(Foo foo);
}
Clase:
@Service
public class FooServiceImpl implements FooService {
/** Autowire validators individually **/
@Autowired
/*
* Notice how the String value here matches the value
* on the @Component annotation? That's how Spring knows which
* instance to autowire.
*/
@Qualifier("FooNameValidator")
private FooValidator nameValidator;
@Autowired
@Qualifier("FooEmailValidator")
private FooValidator emailValidator;
@Override
public void handleFoo(Foo foo) {
/**You can use just one instance if you need**/
foo = nameValidator.validate(foo);
}
}
Vale la pena señalar que si tiene más de una implementación de una interfaz en el contenedor Spring IoC y no especifica cuál desea usar con la anotación @Qualifier
, Spring lanzará una excepción cuando intente iniciar, porque ganó " No sé qué instancia utilizar.
1: Esta no es la forma correcta de hacer validaciones tan simples. Este es un ejemplo simple sobre autowiring. Si desea tener una idea de un método de validación mucho más sencillo, vea cómo Spring realiza la validación con anotaciones.
Autowiring instancias específicas de clases usando parámetros de tipo genérico
Si tiene una interfaz con un parámetro de tipo genérico, Spring puede usarlo solo para implementaciones de autowire que implementan un parámetro de tipo que usted especifique.
Interfaz:
public interface GenericValidator<T> {
public T validate(T object);
}
Clase Validador Foo:
@Component
public class FooValidator implements GenericValidator<Foo> {
@Override
public Foo validate(Foo foo) {
//Logic here to validate foo objects.
}
}
Clase Validador de Barras:
@Component
public class BarValidator implements GenericValidator<Bar> {
@Override
public Bar validate(Bar bar) {
//Bar validation logic here
}
}
Ahora puede activar automáticamente estos validadores utilizando parámetros de tipo para decidir qué instancia autowire.
Interfaz:
public interface FooService {
public void handleFoo(Foo foo);
}
Clase:
@Service
public class FooServiceImpl implements FooService {
/** Autowire Foo Validator **/
@Autowired
private GenericValidator<Foo> fooValidator;
@Override
public void handleFoo(Foo foo) {
foo = fooValidator.validate(foo);
}
}