Inleiding tot het onderscheppen van filterpatronen in Java

1. Overzicht

In deze tutorial introduceren we de Filterpatroon onderscheppen presentatie-tier Core J2EE-patroon.

Dit is de tweede tutorial in ons Patroon serie en een vervolg op de Front Controller patroon gids die kan worden gevonden hier.

Filters onderscheppen zijn filters die acties activeren voordat of nadat een inkomend verzoek is verwerkt door een handler.

Onderscheppende filters vertegenwoordigen gecentraliseerde componenten in een webtoepassing, gemeenschappelijk voor alle verzoeken en uitbreidbaar zonder bestaande handlers te beïnvloeden.

2. Gebruik gevallen

Laten we het voorbeeld uit de vorige gids uitbreiden en implementeren een authenticatiemechanisme, logboekregistratie van verzoeken en een bezoekersteller. Bovendien willen we de mogelijkheid hebben om onze pagina's te bezorgen in verschillende verschillende codering.

Dit zijn allemaal use-cases voor het onderscheppen van filters, omdat ze gemeenschappelijk zijn voor alle verzoeken en onafhankelijk zouden moeten zijn van de handlers.

3. Filterstrategieën

Laten we verschillende filterstrategieën en voorbeeldige use-cases introduceren. Om de code uit te voeren met de Jetty Servlet-container, voert u eenvoudig het volgende uit:

$> mvn install jetty: run

3.1. Aangepaste filterstrategie

De aangepaste filterstrategie wordt gebruikt in elke use case die een geordende verwerking van verzoeken vereist, in de zin van één filter is gebaseerd op de resultaten van een vorig filter in een uitvoeringsketen.

Deze ketens worden gecreëerd door de implementatie van het FilterChain interface en het registreren van verschillende Filter lessen mee.

Als u meerdere filterketens met verschillende zorgen gebruikt, kunt u ze samenvoegen in een filtermanager:

In ons voorbeeld werkt de bezoekersteller door unieke gebruikersnamen van ingelogde gebruikers te tellen, wat betekent dat deze is gebaseerd op het resultaat van het authenticatiefilter, daarom moeten beide filters aan elkaar worden gekoppeld.

Laten we deze filterketen implementeren.

Eerst maken we een authenticatiefilter dat controleert of de sessie bestaat voor een ingesteld ‘gebruikersnaam'-attribuut en een inlogprocedure uitvoeren als dit niet het geval is:

public class AuthenticationFilter implementeert Filter {... @Override public void doFilter (ServletRequest-verzoek, ServletResponse-antwoord, FilterChain-keten) {HttpServletRequest httpServletRequest = (HttpServletRequest) -verzoek; HttpServletResponse httpServletResponse = (HttpServletResponse) antwoord; HttpSession-sessie = httpServletRequest.getSession (false); if (session == null || session.getAttribute ("gebruikersnaam") == null) {FrontCommand commando = nieuwe LoginCommand (); command.init (httpServletRequest, httpServletResponse); command.process (); } else {chain.doFilter (verzoek, antwoord); }} ...}

Laten we nu de bezoekersteller maken. Dit filter behoudt een HashSet van unieke gebruikersnamen en voegt een ‘counter'-attribuut toe aan het verzoek:

openbare klasse VisitorCounterFilter implementeert Filter {privé statisch Set gebruikers = nieuwe HashSet (); ... @Override public void doFilter (ServletRequest-verzoek, ServletResponse-antwoord, FilterChain-keten) {HttpSession-sessie = ((HttpServletRequest) -verzoek) .getSession (false); Optioneel.ofNullable (session.getAttribute ("gebruikersnaam")) .map (Object :: toString) .ifPresent (gebruikers :: add); request.setAttribute ("counter", users.size ()); chain.doFilter (verzoek, antwoord); } ...}

Vervolgens implementeren we een FilterChain die geregistreerde filters herhaalt en uitvoert doFilter methode:

openbare klasse FilterChainImpl implementeert FilterChain {privé Iterator-filters; openbare FilterChainImpl (Filter ... filters) {this.filters = Arrays.asList (filters) .iterator (); } @Override public void doFilter (ServletRequest-verzoek, ServletResponse-antwoord) {if (filters.hasNext ()) {Filterfilter = filters.next (); filter.doFilter (verzoek, antwoord, dit); }}}

Om onze componenten met elkaar te verbinden, maken we een eenvoudige statische manager die verantwoordelijk is voor het instantiëren van filterketens, het registreren van de filters en het starten ervan:

openbare klasse FilterManager {openbaar statisch ongeldig proces (verzoek HttpServletRequest, antwoord HttpServletResponse, terugroeping OnIntercept) {FilterChain filterChain = nieuw FilterChainImpl (nieuw AuthenticationFilter (terugbellen), nieuw VisitorCounterFilter ()); filterChain.doFilter (verzoek, antwoord); }}

Als laatste stap moeten we onze bellen Filtermanager als gemeenschappelijk onderdeel van de volgorde van de verwerking van verzoeken vanuit onze FrontCommand:

openbare abstracte klasse FrontCommand {... openbaar ongeldig proces () {FilterManager.process (verzoek, antwoord); } ...}

3.2. Basisfilterstrategie

In deze sectie presenteren we de Basisfilterstrategie, waarmee een gemeenschappelijke superklasse wordt gebruikt voor alle geïmplementeerde filters.

Deze strategie speelt mooi samen met de aangepaste strategie uit de vorige sectie of met de Standaard filterstrategie die we in de volgende sectie zullen introduceren.

De abstracte basisklasse kan worden gebruikt om aangepast gedrag toe te passen dat bij een filterketen hoort. We zullen het in ons voorbeeld gebruiken om standaardcode met betrekking tot filterconfiguratie en foutopsporingsregistratie te verminderen:

openbare abstracte klasse BaseFilter implementeert Filter {privé Logger log = LoggerFactory.getLogger (BaseFilter.class); beschermde FilterConfig filterConfig; @Override public void init (FilterConfig filterConfig) gooit ServletException {log.info ("Initialize filter: {}", getClass (). GetSimpleName ()); this.filterConfig = filterConfig; } @Override public void destroy () {log.info ("Vernietig filter: {}", getClass (). GetSimpleName ()); }}

Laten we deze basisklasse uitbreiden om een ​​logboekfilter voor verzoeken te maken, dat in de volgende sectie zal worden geïntegreerd:

openbare klasse LoggingFilter breidt BaseFilter uit {privé statisch laatste Logger-logboek = LoggerFactory.getLogger (LoggingFilter.class); @Override public void doFilter (ServletRequest-verzoek, ServletResponse-antwoord, FilterChain-keten) {chain.doFilter (verzoek, antwoord); HttpServletRequest httpServletRequest = (HttpServletRequest) verzoek; String gebruikersnaam = Optioneel .ofNullable (httpServletRequest.getAttribute ("gebruikersnaam")) .map (Object :: toString) .orElse ("guest"); log.info ("Verzoek van '{} @ {}': {}? {}", gebruikersnaam, request.getRemoteAddr (), httpServletRequest.getRequestURI (), request.getParameterMap ()); }}

3.3. Standaard filterstrategie

Een meer flexibele manier om filters toe te passen, is door het Standaard filterstrategie. Dit kan worden gedaan door filters te declareren in een deployment descriptor of, sinds Servlet-specificatie 3.0, door annotatie.

De standaard filterstrategiemaakt het mogelijk om nieuwe filters in een standaardketen in te pluggen zonder een expliciet gedefinieerde filtermanager te hebben:

Merk op dat de volgorde waarin de filters worden toegepast, niet kan worden gespecificeerd via annotatie. Als u een geordende uitvoering nodig heeft, moet u zich houden aan een implementatiedescriptor of een aangepaste filterstrategie implementeren.

Laten we een annotatiegestuurd coderingsfilter implementeren dat ook de basisfilterstrategie gebruikt:

@WebFilter (servletNames = {"intercepting-filter"}, initParams = {@WebInitParam (name = "encoding", value = "UTF-8")}) openbare klasse EncodingFilter breidt BaseFilter {private String-codering uit; @Override public void init (FilterConfig filterConfig) gooit ServletException {super.init (filterConfig); this.encoding = filterConfig.getInitParameter ("codering"); } @Override public void doFilter (ServletRequest-verzoek, ServletResponse-antwoord, FilterChain-keten) {String-codering = Optioneel .ofNullable (request.getParameter ("encoding")) .orElse (this.encoding); response.setCharacterEncoding (codering); chain.doFilter (verzoek, antwoord); }}

In een Servlet-scenario met een implementatiedescriptor, onze web.xml zou deze extra verklaringen bevatten:

 coderingsfilter com.baeldung.patterns.intercepting.filter.filters.EncodingFilter coderingsfilter onderscheppingsfilter 

Laten we ons logboekfilter oppakken en ook annoteren om aan de Servlet te wennen:

@WebFilter (servletNames = "intercepting-filter") public class LoggingFilter breidt BaseFilter uit {...}

3.4. Sjabloonfilterstrategie

De Sjabloonfilterstrategie is vrijwel hetzelfde als de basisfilterstrategie, behalve dat het sjabloonmethoden gebruikt die zijn gedeclareerd in de basisklasse en die moeten worden overschreven in implementaties:

Laten we een basisfilterklasse maken met twee abstracte filtermethoden die voor en na verdere verwerking worden aangeroepen.

Aangezien deze strategie minder vaak voorkomt en we deze niet gebruiken in ons voorbeeld, ligt een concrete implementatie en use-case aan uw verbeelding:

openbare abstracte klasse TemplateFilter breidt BaseFilter uit {beschermde abstracte ongeldige preFilter (verzoek HttpServletRequest, antwoord HttpServletResponse); beschermde abstracte ongeldige postFilter (verzoek HttpServletRequest, antwoord HttpServletResponse); @Override public void doFilter (ServletRequest-verzoek, ServletResponse-antwoord, FilterChain-keten) {HttpServletRequest httpServletRequest = (HttpServletRequest) -verzoek; HttpServletResponse httpServletResponse = (HttpServletResponse) antwoord; preFilter (httpServletRequest, httpServletResponse); chain.doFilter (verzoek, antwoord); postFilter (httpServletRequest, httpServletResponse); }}

4. Conclusie

Het onderscheppende filterpatroon legt transversale problemen vast die onafhankelijk van de bedrijfslogica kunnen evolueren. Vanuit het perspectief van de bedrijfsvoering worden filters uitgevoerd als een aaneenschakeling van pre- of post-acties.

Zoals we tot nu toe hebben gezien, is de Filterpatroon onderscheppen kan worden geïmplementeerd met behulp van verschillende strategieën. In een ‘echte wereld’ kunnen deze verschillende benaderingen worden gecombineerd.

Zoals gewoonlijk vind je de bronnen op GitHub.