Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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.

Info

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 bør 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. 

...

Code Block
languagejava
titleConfiguration
@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.

Info

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. Her

Info

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.

Code Block
languagejava
titleImport
@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 vedligehold af den specifikke servlet.

Info

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 førnævnte konfigurationsklasse. Derudover kan @Value annotationen bruges til properties mm. 

Code Block
languagejava
titleAutowired & Value
@WebServlet("/*")
public class NotificationBrokerServlet extends HttpServlet {

...
	@Value("${dgws.headers.required}")
	private boolean dgwsHeadersRequired;

...

    @Autowired
	private NotificationBrokerService service;

...

...

Derudover skal @Value annotationen bruges til properties (kan ligeledes ses i eksemplet ovenfor). Dette gælder både for servletten og i java-konfigurationsklasserne.

Info

DI.1.6 § I java-konfigurationsklasserne og servlet-klasserne skal @Value annotation bruges til at konfigurere værdier fra properties.

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.

Info

DI.1.7 § Der skal anvendes een af AnnotationConfigApplicationContext / AnnotationConfigWebApplicationContext som ApplicationContext-implementationsklasse.


Denne skal konfigureres med de konfigurationsklasser der er relevante
Gem ApplicationContext'en gemmes i ServletContext for at gøre det den tilgængeligt for resten af applikationen.Lifecycle Management:


Brug ServletContextListener-interfaces til at initialisere og rydde op i ApplicationContext i komponenten. Sørg for, at Ligeledes skal ApplicationContext lukkes korrekt, når webapplikationen stoppes.

Info

DI.1.8 § ApplicationContext'en skal oprettes og nedlægges ved hhv.

...

contextInitialized og contextDestroyed på ServletContext'en.

Her demonstreret ved NotificationBrokerens implementation:

Code Block
languagejava
titleApplicationContext
@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 AnnotationConfigApplicationContext’en ApplicationContext’en hentes (via ServletContexten) og herigennem skal den aktuelle Servlet have sine afhængigheder injected. Dette skal foregå ved kald til autowireBean og give Servlet-instansen med, 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.

Info

DI.1.9 § Servletten skal autowires programatisk via ApplicationContext'en

Dette er demonstreret ved NotificationBroker-servletten her:

Code Block
languagejava
titleInitialisering af autowiring
@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);
		}
	}

Bruger man derimod en JAX-WS genereret servlet kan denne autowiring ske ved brug af @PostConstruct  annotationen, demonstreret ved eksemplet her fra FGVHR:

Code Block
languagejava
titleInitialisering af autowiring
public abstract class AbstractFgvhrWS {

	@Resource
	protected WebServiceContext context;

	@PostConstruct
	public void init() {
		ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);
		Object o = servletContext.getAttribute(ANNOTATION_CONFIG_CONTEXT);
		((AnnotationConfigApplicationContext)o).getAutowireCapableBeanFactory().autowireBean(this);
	}

I eksemplet her extender klasserne der implementerer de genererede JAX-WS klasser den abstrakte klasse.

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.