spring
Skapa och använda bönor
Sök…
Autowiring alla bönor av en viss typ
Om du har flera implementeringar av samma gränssnitt, kan Spring autowire dem alla till ett samlingsobjekt. Jag kommer att använda ett exempel med ett valideringsmönster 1
Foo Class:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Gränssnitt:
public interface FooValidator {
public Foo validate(Foo foo);
}
Namn Validator Class:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
E-postvalideringsklass:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
Du kan nu autowire dessa validerare individuellt eller tillsammans i en klass.
Gränssnitt:
public interface FooService {
public void handleFoo(Foo foo);
}
Klass:
@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);
}
}
}
Det är värt att notera att om du har mer än en implementering av ett gränssnitt i Spring IoC-behållaren och inte anger vilken du vill använda med @Qualifier
kommentaren, kommer Spring att göra ett undantag när du försöker starta, eftersom det vann ' t vet vilken instans jag ska använda.
1: Detta är inte det rätta sättet att göra så enkla valideringar. Detta är ett enkelt exempel på autowiring. Om du vill ha en idé om en mycket enklare valideringsmetod, leta upp hur våren validerar med kommentarer.
Förklara Bean
För att förklara en böna, antecknar du bara en metod med @Bean
anteckningen eller kommenterar en klass med @Component
annotationen (kommentarer @Service
, @Repository
, @Controller
kan också användas).
När JavaConfig möter en sådan metod kommer den att utföra den metoden och registrera returvärdet som en böna i en BeanFactory. Som standard är bönornamnet namnet på metodnamnet.
Vi kan skapa bönor på ett av tre sätt:
Använda Java-baserad konfiguration : I konfigurationsfilen måste vi deklarera bönor med @bean-kommentering
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
Använda XML-baserad konfiguration : För XML-baserad konfiguration måste vi skapa deklarera bönor i applikationskonfiguration XML, dvs.
<beans> <bean name="transferService" class="com.acme.TransferServiceImpl"/> </beans>
Annotation-Driven Component : För kommenteringsdrivna komponenter måste vi lägga till @Component-kommentaren till den klass vi vill förklara som bönor.
@Component("transferService") public class TransferServiceImpl implements TransferService { ... }
Nu finns alla tre bönor med name transferService
tillgängliga i BeanFactory
eller ApplicationContext
.
Grundläggande annotering autowiring
Gränssnitt:
public interface FooService {
public int doSomething();
}
Klass:
@Service
public class FooServiceImpl implements FooService {
@Override
public int doSomething() {
//Do some stuff here
return 0;
}
}
Det bör noteras att en klass måste implementera ett gränssnitt för våren för att kunna autowire denna klass. Det finns en metod för att låta Spring att autowire fristående klasser med vävning av lasttid, men det är utanför ramen för detta exempel.
Du kan få tillgång till den här bönan i vilken klass som helst som anordnades av Spring IoC-behållaren med @Autowired
kommentaren.
Användande:
@Autowired([required=true])
@Autowired
anteckningen kommer först att försöka autowire efter typ och sedan falla tillbaka på bönnamn i händelse av tvetydighet.
Denna kommentar kan tillämpas på flera olika sätt.
Konstruktörsinjektion:
public class BarClass() {
private FooService fooService
@Autowired
public BarClass(FooService fooService) {
this.fooService = fooService;
}
}
Fältinjektion:
public class BarClass() {
@Autowired
private FooService fooService;
}
Seterinjektion:
public class BarClass() {
private FooService fooService;
@Autowired
public void setFooService(FooService fooService) {
this.fooService = fooService;
}
}
Använda FactoryBean för dynamisk böneangivning
För att dynamiskt bestämma vilka bönor som ska injiceras kan vi använda FactoryBean
s. Dessa är klasser som implementerar fabriksmetodsmönstret och ger instanser av bönor för behållaren. De känns igen av våren och kan användas transparent, utan att behöva veta att bönan kommer från en fabrik. Till exempel:
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";
}
}
Konfiguration:
@Configuration
public class ExampleConfig {
@Bean
public FactoryBean<String> fromFactory() {
return new ExampleFactoryBean();
}
}
Skaffa bönan:
AbstractApplicationContext context = new AnnotationConfigApplicationContext(ExampleConfig.class);
String exampleString = (String) context.getBean("fromFactory");
För att få den faktiska FactoryBean, använd ampersand-prefixet före bönans namn:
FactoryBean<String> bean = (FactoryBean<String>) context.getBean("&fromFactory");
Observera att du bara kan använda prototype
eller singleton
omfång - för att ändra räckvidden till prototype
isSingleton
metoden:
public class ExampleFactoryBean extends AbstractFactoryBean<String> {
@Override
public boolean isSingleton() {
return false;
}
// other methods omitted for readability reasons
}
Observera att scoping avser faktiska instanser som skapas, inte själva fabriksbönan.
Injicera prototyp-scoped bönor i singletons
Behållaren skapar en singletonböna och injicerar samarbetspartners bara en gång. Detta är inte det önskade beteendet när en singleton böna har en prototyp-scoped samarbetspartner, eftersom den prototyp-scoped bönan bör injiceras varje gång den åtkomst via accessor.
Det finns flera lösningar på detta problem:
- Använd uppslagsmetodinjektion
- Hämta en prototyp-scoped böna via
javax.inject.Provider
- Hämta en prototyp-scoped böna via
org.springframework.beans.factory.ObjectFactory
(motsvarande nr 2, men med klassen som är specifik för våren) - Gör en singleton bönebehållare medveten via implementeringsgränssnittet
ApplicationContextAware
Tillvägagångssätt nr 3 och # 4 är generellt avskräckta, eftersom de starkt binder en app till vårens ramverk. Således omfattas de inte av detta exempel.
Injektionsmetodinjektion via XML-konfiguration och en abstrakt metod
Java-klasser
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>
Injektion av uppslagsmetod via Java-konfiguration och @Component
Java-klasser
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();
}
}
Java-konfiguration
@Configuration
@ComponentScan("somepackage") // package where WindowGenerator is located
public class MyConfiguration {
@Bean
@Lazy
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Window window() {
return new Window();
}
}
Manuell injektionsmetodinjektion via Java-konfiguration
Java-klasser
public class Window {
}
public abstract class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
protected abstract Window createNewWindow(); // lookup method
}
Java-konfiguration
@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();
}
};
}
}
Injektion av en prototyp-scoped bönor i singleton via javax.inject.Provider
Java-klasser
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>
Samma tillvägagångssätt kan också användas för andra räckvidd (t.ex. för injektion av en förfrågningsskopa i singleton).
Autowiring specifika böna instanser med @Qualifier
Om du har flera implementationer av samma gränssnitt, måste Spring veta vilken den ska autowire till en klass. Jag kommer att använda ett Validator-mönster i det här exemplet. 1
Foo Class:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Gränssnitt:
public interface FooValidator {
public Foo validate(Foo foo);
}
Namn Validator Class:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
E-postvalideringsklass:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
Du kan nu autowire dessa validerare individuellt i en klass.
Gränssnitt:
public interface FooService {
public void handleFoo(Foo foo);
}
Klass:
@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);
}
}
Det är värt att notera att om du har mer än en implementering av ett gränssnitt i Spring IoC-behållaren och inte anger vilken du vill använda med @Qualifier
kommentaren, kommer Spring att göra ett undantag när du försöker starta, eftersom det vann ' t vet vilken instans jag ska använda.
1: Detta är inte det rätta sättet att göra så enkla valideringar. Detta är ett enkelt exempel på autowiring. Om du vill ha en idé om en mycket enklare valideringsmetod, leta upp hur våren validerar med kommentarer.
Autowiring specifika instanser av klasser med hjälp av generiska typparametrar
Om du har ett gränssnitt med en generisk typparameter, kan Spring använda det för att bara använda autowire-implementationer som implementerar en typparameter som du anger.
Gränssnitt:
public interface GenericValidator<T> {
public T validate(T object);
}
Foo Validator Class:
@Component
public class FooValidator implements GenericValidator<Foo> {
@Override
public Foo validate(Foo foo) {
//Logic here to validate foo objects.
}
}
Bar Validator Class:
@Component
public class BarValidator implements GenericValidator<Bar> {
@Override
public Bar validate(Bar bar) {
//Bar validation logic here
}
}
Du kan nu autowire dessa validatorer med hjälp av typparametrar för att bestämma vilken instans som ska autowire.
Gränssnitt:
public interface FooService {
public void handleFoo(Foo foo);
}
Klass:
@Service
public class FooServiceImpl implements FooService {
/** Autowire Foo Validator **/
@Autowired
private GenericValidator<Foo> fooValidator;
@Override
public void handleFoo(Foo foo) {
foo = fooValidator.validate(foo);
}
}