Introduktion til at opfange filtermønster i Java

1. Oversigt

I denne vejledning introducerer vi Opfangning af filtermønster præsentation-tier Core J2EE mønster.

Dette er den anden tutorial i vores Mønster serie og en opfølgning på Forreste controller mønster guide, som kan findes her.

Opfangning af filtre er filtre, der udløser handlinger før eller efter en indgående anmodning behandles af en handler.

Opfangningsfiltre repræsenterer centraliserede komponenter i en webapplikation, der er fælles for alle anmodninger og kan udvides uden at påvirke eksisterende håndterere.

2. Brug sager

Lad os udvide eksemplet fra den foregående guide og implementere en godkendelsesmekanisme, anmodningslogning og en besøgstæller. Derudover ønsker vi muligheden for at levere vores sider i forskellige forskellige indkodning.

Alt dette er brugssager til at opfange filtre, fordi de er fælles for alle anmodninger og bør være uafhængige af håndtererne.

3. Filtrer strategier

Lad os introducere forskellige filterstrategier og eksempler på brugssager. For at køre koden med Jetty Servlet container, skal du blot udføre:

$> mvn installer anløbsbro: kør

3.1. Brugerdefineret filterstrategi

Den brugerdefinerede filterstrategi bruges i enhver brugssag, der kræver en ordnet behandling af anmodninger i betydningen af et filter er baseret på resultaterne af et tidligere filter i en udførelseskæde.

Disse kæder oprettes ved at implementere Filterkæde interface og registrering af forskellige Filter klasser med det.

Når du bruger flere filterkæder med forskellige bekymringer, kan du slutte dem sammen i en filterhåndtering:

I vores eksempel arbejder besøgstælleren ved at tælle unikke brugernavne fra indloggede brugere, hvilket betyder, at den er baseret på resultatet af godkendelsesfilteret, og derfor skal begge filtre lænkes.

Lad os implementere denne filterkæde.

Først opretter vi et godkendelsesfilter, der kontrollerer, om sessionen findes for en sæt 'brugernavn'-attribut og udsteder en loginprocedure, hvis ikke:

offentlig klasse AuthenticationFilter implementerer filter {... @Override offentligt ugyldigt doFilter (ServletRequest anmodning, ServletResponse svar, FilterChain kæde) {HttpServletRequest httpServletRequest = (HttpServletRequest) anmodning; HttpServletResponse httpServletResponse = (HttpServletResponse) svar; HttpSession-session = httpServletRequest.getSession (falsk); hvis (session == null || session.getAttribute ("brugernavn") == null) {FrontCommand-kommando = nyt LoginCommand (); command.init (httpServletRequest, httpServletResponse); command.process (); } andet {chain.doFilter (anmodning, svar); }} ...}

Lad os nu oprette besøgstælleren. Dette filter opretholder en HashSet af unikke brugernavne og tilføjer en 'counter' attribut til anmodningen:

offentlig klasse VisitorCounterFilter implementerer filter {private static Set users = new HashSet (); ... @ Overstyr offentlig ugyldig doFilter (ServletRequest anmodning, ServletResponse svar, FilterChain kæde) {HttpSession session = ((HttpServletRequest) anmodning) .getSession (falsk); Valgfri.ofNullable (session.getAttribute ("brugernavn")) .map (Objekt :: toString) .ifPresent (brugere :: tilføj); request.setAttribute ("tæller", users.size ()); chain.doFilter (anmodning, svar); } ...}

Dernæst implementerer vi en Filterkæde at itererer registrerede filtre og udfører doFilter metode:

offentlig klasse FilterChainImpl implementerer FilterChain {private Iterator-filtre; public FilterChainImpl (Filter ... filters) {this.filters = Arrays.asList (filters) .iterator (); } @ Overstyr offentlig ugyldighed doFilter (ServletRequest-anmodning, ServletResponse-svar) {if (filters.hasNext ()) {Filter filter = filters.next (); filter.doFilter (anmodning, svar, dette); }}}

For at koble vores komponenter sammen, lad os oprette en simpel statisk manager, der er ansvarlig for at starte filterkæder, registrere dets filtre og starte den:

offentlig klasse FilterManager {offentlig statisk ugyldig proces (HttpServletRequest anmodning, HttpServletResponse svar, OnIntercept tilbagekald) {FilterChain filterChain = nyt FilterChainImpl (nyt AuthenticationFilter (tilbagekald), nyt VisitorCounterFilter ()); filterChain.doFilter (anmodning, svar); }}

Som det sidste trin bliver vi nødt til at kalde vores FilterManager som almindelig del af anmodningen behandlingssekvens inden for vores FrontCommand:

offentlig abstrakt klasse FrontCommand {... offentlig ugyldig proces () {FilterManager.process (anmodning, svar); } ...}

3.2. Basisfilterstrategi

I dette afsnit præsenterer vi Basisfilterstrategi, med hvilken en fælles superklasse bruges til alle implementerede filtre.

Denne strategi spiller pænt sammen med den brugerdefinerede strategi fra det foregående afsnit eller med Standardfilterstrategi som vi introducerer i næste afsnit.

Den abstrakte basisklasse kan bruges til at anvende brugerdefineret adfærd, der hører til en filterkæde. Vi bruger det i vores eksempel til at reducere kedelpladekoden relateret til filterkonfiguration og fejlretningslogning:

offentlig abstrakt klasse BaseFilter implementerer Filter {private Logger log = LoggerFactory.getLogger (BaseFilter.class); beskyttet FilterConfig filterConfig; @ Override public void init (FilterConfig filterConfig) kaster ServletException {log.info ("Initialiser filter: {}", getClass (). GetSimpleName ()); this.filterConfig = filterConfig; } @ Override public void destroy () {log.info ("Destroy filter: {}", getClass (). GetSimpleName ()); }}

Lad os udvide denne basisklasse for at oprette et filter til anmodningslogning, som integreres i næste afsnit:

public class LoggingFilter udvider BaseFilter {privat statisk endelig Logger log = LoggerFactory.getLogger (LoggingFilter.class); @Override public void doFilter (ServletRequest anmodning, ServletResponse svar, FilterChain kæde) {chain.doFilter (anmodning, svar); HttpServletRequest httpServletRequest = (HttpServletRequest) anmodning; String-brugernavn = Valgfrit .ofNullable (httpServletRequest.getAttribute ("brugernavn")) .map (Objekt :: toString) .orElse ("gæst"); log.info ("Anmodning fra '{} @ {}': {}? {}", brugernavn, anmodning.getRemoteAddr (), httpServletRequest.getRequestURI (), anmodning.getParameterMap ()); }}

3.3. Standardfilterstrategi

En mere fleksibel måde at anvende filtre på er at implementere Standardfilterstrategi. Dette kan gøres ved at erklære filtre i en installationsbeskrivelse eller, siden Servlet-specifikation 3.0, ved annotering.

Standardfilterstrategiengiver mulighed for at plug-in nye filtre i en standardkæde uden at have en eksplicit defineret filterhåndtering:

Bemærk, at rækkefølgen, i hvilken filtre bliver anvendt, ikke kan specificeres via kommentar. Hvis du har brug for en bestilt udførelse, skal du holde fast ved en installationsbeskrivelse eller implementere en brugerdefineret filterstrategi.

Lad os implementere et annoteringsdrevet kodningsfilter, der også bruger basisfilterstrategien:

@WebFilter (servletNames = {"intercepting-filter"}, initParams = {@WebInitParam (name = "encoding", value = "UTF-8")}) public class EncodingFilter udvider BaseFilter {privat strengkodning; @ Override public void init (FilterConfig filterConfig) kaster ServletException {super.init (filterConfig); this.encoding = filterConfig.getInitParameter ("kodning"); } @ Override offentlig ugyldighed doFilter (ServletRequest anmodning, ServletResponse svar, FilterChain kæde) {String kodning = Valgfri .ofNullable (request.getParameter ("kodning")) .ellerElse (denne.kodning) respons.setCharacterEncoding (kodning); chain.doFilter (anmodning, svar); }}

I et Servlet-scenarie med en implementeringsbeskrivelse, vores web.xml indeholder disse ekstra erklæringer:

 kodningsfilter com.baeldung.patterns.intercepting.filter.filters.EncodingFilter encoding-filter intercepting-filter 

Lad os hente vores logfilter og kommentere det også for at blive brugt af Servlet:

@WebFilter (servletNames = "intercepting-filter") offentlig klasse LoggingFilter udvider BaseFilter {...}

3.4. Skabelonfilterstrategi

Det Skabelonfilterstrategi er stort set den samme som basisfilterstrategien, bortset fra at den bruger skabelonmetoder, der er angivet i baseklassen, der skal tilsidesættes i implementeringer:

Lad os oprette en basisfilterklasse med to abstrakte filtermetoder, der bliver kaldt før og efter yderligere behandling.

Da denne strategi er mindre almindelig, og vi ikke bruger den i vores eksempel, er en konkret implementerings- og brugssag op til din fantasi:

offentlig abstrakt klasse TemplateFilter udvider BaseFilter {beskyttet abstrakt ugyldigt preFilter (HttpServletRequest anmodning, HttpServletResponse svar); beskyttet abstrakt ugyldigt postFilter (HttpServletRequest anmodning, HttpServletResponse svar); @Override public void doFilter (ServletRequest anmodning, ServletResponse svar, FilterChain kæde) {HttpServletRequest httpServletRequest = (HttpServletRequest) anmodning; HttpServletResponse httpServletResponse = (HttpServletResponse) svar; preFilter (httpServletRequest, httpServletResponse); chain.doFilter (anmodning, svar); postFilter (httpServletRequest, httpServletResponse); }}

4. Konklusion

Opfangningsfiltermønsteret fanger tværgående bekymringer, der kan udvikle sig uafhængigt af forretningslogikken. Fra perspektivet af forretningsdriften udføres filtre som en kæde af præ- eller post-handlinger.

Som vi hidtil har set, Opfangning af filtermønster kan implementeres ved hjælp af forskellige strategier. I en 'rigtig verden' applikationer kan disse forskellige tilgange kombineres.

Som normalt finder du kilderne på GitHub.