Introduktion
Denne side beskriver de regler og bedste praksis, der anvendes i forbindelse med brug af dependency injection i NSP's Java-projekter. Det fokuserer specifikt på brugen af klasser som AnnotationConfigApplicationContext, AnnotationConfigWebApplicationContext, og relaterede konfigurationsklasser i NSP's komponenter.
DI.1.1 § Til Dependency Injection i NSP's komponenter skal spring frameworket benyttes.
Konfigurationsklasser
Der oprettes en eller flere konfigurationsklasser, der er mærket med @Configuration. Dette er stederne, hvor Spring-beans (med @Bean) definerers og konfiguration af komponentene foretages. Der skal dermed på NSP anvendes konfigurationsklasser og ikke xml-filer.
Konfigurationsklasser skal være opdelt baseret på funktionalitet eller moduler. For eksempel kan der være en konfigurationsklasse for databasen, en for webservices, en for sikkerhed osv.
DI.1.2 § Der skal benyttes java-konfigurationsklasser annoteret med @Configuration. Disse skal være logisk opdelt og navngivet
Herunder eksmpel på en konfigurationsklasse fra NAS projektet.
@Configuration
@PropertySource("classpath:notificationbroker.properties")
public class NotificationBrokerSetup {
public static final String SERVICE_NAME = "notificationbroker";
private static final Properties properties = Config.load(SERVICE_NAME);
@Bean
public Properties properties() {
return properties;
}
@Bean
public NotificationBrokerService notificationBrokerService(TopicMappingDAO topicMappingDao, Publisher publisher, AuditBuilderFactory auditBuilderFactory) {
return new NotificationBrokerServiceImpl(topicMappingDao, publisher, auditBuilderFactory);
}
...
Brug @Bean-metoder i konfigurationsklasserne til at definere Spring-beans.
Navngiv metoder og beans fornuftigt, så det er nemt at forstå, hvilken rolle de spiller i applikationen (DAO, WsdlResponseGenerator, StatusResponseGenerator mm.) - i eksmplet fra NAS ovenfor Properties og notificationBrokerService.
DI.1.3 § Der skal benyttes @Bean annotations til at definere spring-beans. Disse skal ligeledes være logisk opdelt og navngivet
Ved flere klasser samles de ved brug af @Import i en enkelt klasse.
DI.1.4 § Skal flere klasser samles logisk bruges @Import i en samlende java-klasse
Eksempelvis samler NotificationBrokerServerSetup således konfigurationsklassen ovenfor og bla. en Database-konfigurationsklasse.
@Configuration
@Import({ NotificationBrokerSetup.class, PublisherSetup.class, DatabaseSetup.class })
public class NotificationBrokerServerSetup {
@Bean
public DataSource primaryDataSource(@Value("${datasource.jndi}") String jndiName) {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource(jndiName);
return dataSource;
}
...
Der skal dermed ikke anvendes @Autowired i konfigurationsklasserne.
Afhængigheder og injection
Brug @Autowired for at injecte afhængigheder i HttpServletten. Dette valg er bevidst truffet for kun at implementere injektionen i denne servlet-klasse og ikke introducere yderligere kompleksitet eller struktur i andre dele af applikationen. Denne tilgang fokuserer på at holde koden inden for HttpServlet-konteksten mere kortfattet og læsbar, hvilket letter udvikling og vedligeholde den specifikke servlet.
DI.1.5 § De definerede beans injectes med @Autowired annotationen i HttpServletten. Det er kun i HttpServletten dette må foretages.
Herunder ses inklusionen af NotificationBrokerService-bean'en fra først nævnte konfigurationsklasse. Derudover kan @Value annotationen bruges til properties mm.
@WebServlet("/*")
public class NotificationBrokerServlet extends HttpServlet {
...
@Value("${dgws.headers.required}")
private boolean dgwsHeadersRequired;
...
@Autowired
private NotificationBrokerService service;
...
Lifecycle Management
Opret og konfigurer en ApplicationContext ved hjælp af AnnotationConfigApplicationContext / AnnotationConfigWebApplicationContext. Det skal være en af disse to for at muliggøre brugen af konfigurationsklasserne.
DI.1.6 § Der skal anvendes een af AnnotationConfigApplicationContext / AnnotationConfigWebApplicationContext som ApplicationContext-implementationsklasse.
Denne skal konfigureres med de konfigurationsklasser der er relevante
ApplicationContext'en gemmes i ServletContext for at gøre den tilgængeligt for resten af applikationen.
Brug ServletContextListener-interfaces til at initialisere og rydde op i ApplicationContext i komponenten. Ligeledes skal ApplicationContext lukkes korrekt, når webapplikationen stoppes.
DI.1.7 § ApplicationContext'en skal oprettes og nedlægges ved hhv. contextInitialized og contextDestroyed på ServletContext'en.
Her demonstrert ved NotificationBrokerens implementation:
@WebListener
public class NotificationBrokerServletContextListener implements ServletContextListener {
protected static final String ANNOTATION_CONFIG_CONTEXT = "ANNOTATION_CONFIG_APPLICATION_CONTEXT";
...
@Override
public void contextInitialized(ServletContextEvent sce) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(NotificationBrokerServerSetup.class);
applicationContext.refresh();
sce.getServletContext().setAttribute(ANNOTATION_CONFIG_CONTEXT, applicationContext);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
Object ctx = sce.getServletContext().getAttribute(ANNOTATION_CONFIG_CONTEXT);
if(ctx instanceof AnnotationConfigApplicationContext) {
((AnnotationConfigApplicationContext)ctx).close();
}
}
På servlettens init-metode skal ApplicationContext’en hentes (via ServletContexten) og herigennem skal den aktuelle Servlet have sine afhængigheder injected. Dette skal foregå ved kald til autowireBean, hvor Servlet-instansen gives med. Hermed foretages den sidste sammenkædning af ApplicationContext'en, der nu er konfigureret med java-konfigurationsklasserne og Servletten. Med andre ord sørger det for, at afhængigheder injiceres i servletten ved at benytte Spring's autowiring-mekanisme baseret på konfigurationen i ApplicationContext'en.
DI.1.8 § Servletten skal autowires programatisk via ApplicationContext'en
Dette er demonstreret ved NotificationBroker-servletten her:
@WebServlet("/*")
public class NotificationBrokerServlet extends HttpServlet {
...
@Override
public void init() throws ServletException {
Object o = getServletContext().getAttribute(NotificationBrokerServletContextListener.ANNOTATION_CONFIG_CONTEXT);
if(o != null && o instanceof AnnotationConfigApplicationContext) {
((AnnotationConfigApplicationContext)o).getAutowireCapableBeanFactory().autowireBean(this);
}
}
Afsluttende
Samlet set udgør dokumentet her med eksempler en vejledning til hvordan DI i NSP's java-komponenter skal håndteres med brug af konfigurationsklasser, beans, autowiring og AnnotationConfigApplicationContext / AnnotationConfigWebApplicationContext.