Introduksjon til avskjæring av filtermønster i Java

1. Oversikt

I denne opplæringen skal vi introdusere Avskjærer filtermønster presentasjonsnivå Core J2EE-mønster.

Dette er den andre opplæringen i vår Mønster Series og en oppfølging av Fremre kontrollermønster guide som kan bli funnet her.

Avskjære filtre er filtre som utløser handlinger før eller etter at en innkommende forespørsel behandles av en behandler.

Avskjæringsfiltre representerer sentraliserte komponenter i en webapplikasjon, felles for alle forespørsler og utvidbare uten å påvirke eksisterende håndterere.

2. Bruk tilfeller

La oss utvide eksemplet fra forrige guide og implementere en autentiseringsmekanisme, forespørsellogging og en besøksteller. I tillegg ønsker vi muligheten til å levere sidene våre i forskjellige forskjellige koding.

Alt dette er brukstilfeller for å fange opp filtre fordi de er felles for alle forespørsler og bør være uavhengige av håndtererne.

3. Filtrer strategier

La oss introdusere forskjellige filterstrategier og eksempler på brukstilfeller. For å kjøre koden med Jetty Servlet container, bare kjør:

$> mvn installer brygge: kjør

3.1. Tilpasset filterstrategi

Den tilpassede filterstrategien brukes i alle brukstilfeller som krever en bestilt behandling av forespørsler, i betydningen Ett filter er basert på resultatene fra et tidligere filter i en kjøringskjede.

Disse kjedene vil bli opprettet ved å implementere Filterkjede grensesnitt og registrering av forskjellige Filter klasser med det.

Når du bruker flere filterkjeder med forskjellige bekymringer, kan du slå dem sammen i en filterbehandling:

I vårt eksempel jobber besøkstelleren ved å telle unike brukernavn fra innloggede brukere, noe som betyr at den er basert på resultatet av autentiseringsfilteret, og derfor må begge filtrene lenkes.

La oss implementere denne filterkjeden.

Først oppretter vi et autentiseringsfilter som sjekker om økten eksisterer for et angitt 'brukernavn' -attributt og utsteder en påloggingsprosedyre hvis ikke:

offentlig klasse AuthenticationFilter implementerer filter {... @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) {HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) svar; HttpSession session = httpServletRequest.getSession (false); if (session == null || session.getAttribute ("brukernavn") == null) {FrontCommand-kommando = ny LoginCommand (); command.init (httpServletRequest, httpServletResponse); command.process (); } annet {chain.doFilter (forespørsel, svar); }} ...}

La oss nå lage besøkstelleren. Dette filteret vedlikeholder en HashSet av unike brukernavn og legger til en 'counter' attributt til forespørselen:

offentlig klasse VisitorCounterFilter implementerer Filter {private static Set users = new HashSet (); ... @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) {HttpSession session = (((HttpServletRequest) request) .getSession (false); Optional.ofNullable (session.getAttribute ("brukernavn")) .map (Object :: toString) .ifPresent (brukere :: add); request.setAttribute ("teller", brukere.størrelse ()); chain.doFilter (forespørsel, svar); } ...}

Deretter implementerer vi en Filterkjede at itererer registrerte filtre og utfører doFilter metode:

offentlig klasse FilterChainImpl implementerer FilterChain {private Iterator-filtre; public FilterChainImpl (Filter ... filters) {this.filters = Arrays.asList (filters) .iterator (); } @ Override public void doFilter (ServletRequest request, ServletResponse response) {if (filters.hasNext ()) {Filter filter = filters.next (); filter.doFilter (forespørsel, svar, dette); }}}

For å koble komponentene våre sammen, la oss lage en enkel statisk manager som er ansvarlig for å starte filterkjeder, registrere filtrene og starte det:

public class FilterManager {public static void process (HttpServletRequest request, HttpServletResponse response, OnIntercept callback) {FilterChain filterChain = new FilterChainImpl (new AuthenticationFilter (callback), new VisitorCounterFilter ()); filterChain.doFilter (forespørsel, svar); }}

Som det siste trinnet må vi ringe vårt FilterManager som vanlig del av forespørselsbehandlingssekvensen fra vår FrontCommand:

offentlig abstrakt klasse FrontCommand {... offentlig ugyldig prosess () {FilterManager.process (forespørsel, svar); } ...}

3.2. Basisfilterstrategi

I denne delen presenterer vi Basisfilterstrategi, som en vanlig superklasse brukes til alle implementerte filtre.

Denne strategien spiller fint sammen med den tilpassede strategien fra forrige seksjon eller med Standard filterstrategi som vi introduserer i neste avsnitt.

Den abstrakte basisklassen kan brukes til å bruke tilpasset atferd som tilhører en filterkjede. Vi bruker det i vårt eksempel for å redusere kjeleplatekoden relatert til filterkonfigurasjon og feilsøkingslogging:

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 ()); }}

La oss utvide denne baseklassen for å lage et forespørselsloggingsfilter, som vil bli integrert i neste avsnitt:

public class LoggingFilter utvider BaseFilter {private static final Logger log = LoggerFactory.getLogger (LoggingFilter.class); @ Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) {chain.doFilter (request, response); HttpServletRequest httpServletRequest = (HttpServletRequest) forespørsel; Strengbrukernavn = Valgfritt .ofNullable (httpServletRequest.getAttribute ("brukernavn")) .map (Object :: toString) .orElse ("gjest"); log.info ("Forespørsel fra '{} @ {}': {}? {}", brukernavn, request.getRemoteAddr (), httpServletRequest.getRequestURI (), request.getParameterMap ()); }}

3.3. Standard filterstrategi

En mer fleksibel måte å bruke filtre på er å implementere Standard filterstrategi. Dette kan gjøres ved å erklære filtre i en distribusjonsbeskrivelse eller, siden Servlet-spesifikasjon 3.0, ved å kommentere.

Standard filterstrategitillater å plugge inn nye filtre i en standardkjede uten å ha en eksplisitt definert filterbehandling:

Merk at rekkefølgen som filtrene blir brukt i, ikke kan spesifiseres via kommentar. Hvis du trenger en bestilt utførelse, må du følge en distribusjonsbeskrivelse eller implementere en tilpasset filterstrategi.

La oss implementere et merkedrevet kodingsfilter som også bruker basefilterstrategien:

@WebFilter (servletNames = {"intercepting-filter"}, initParams = {@WebInitParam (name = "encoding", value = "UTF-8")}) offentlig klasse EncodingFilter utvider BaseFilter {privat strengkoding; @ Override public void init (FilterConfig filterConfig) kaster ServletException {super.init (filterConfig); this.encoding = filterConfig.getInitParameter ("koding"); } @ Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) {String encoding = Optional .ofNullable (request.getParameter ("encoding")) .orElse (this.encoding); respons.setCharacterEncoding (koding); chain.doFilter (forespørsel, svar); }}

I et Servlet-scenario med en distribusjonsbeskrivelse, vår web.xml inneholder disse ekstra erklæringene:

 kodingsfilter com.baeldung.patterns.intercepting.filter.filters.EncodingFilter kodingsfilter intercepting-filter 

La oss hente loggfilteret vårt og kommentere det for å bli brukt av Servlet:

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

3.4. Malfilterstrategi

De Malfilterstrategi er stort sett den samme som basisfilterstrategien, bortsett fra at den bruker malmetoder som er erklært i basisklassen som må overstyres i implementeringer:

La oss lage en basisfilterklasse med to abstrakte filtermetoder som blir kalt før og etter videre behandling.

Siden denne strategien er mindre vanlig, og vi ikke bruker den i eksemplet vårt, er en konkret implementerings- og brukssak opp til din fantasi:

offentlig abstrakt klasse TemplateFilter utvider BaseFilter {beskyttet abstrakt ugyldig preFilter (HttpServletRequest forespørsel, HttpServletResponse svar); beskyttet abstrakt ugyldig postFilter (HttpServletRequest forespørsel, HttpServletResponse svar); @ Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) {HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) respons; preFilter (httpServletRequest, httpServletResponse); chain.doFilter (forespørsel, svar); postFilter (httpServletRequest, httpServletResponse); }}

4. Konklusjon

Intercepting Filter Pattern fanger tverrgående bekymringer som kan utvikle seg uavhengig av forretningslogikken. Fra perspektivet til forretningsdrift, blir filtre utført som en kjede av handlinger før eller etter.

Som vi har sett så langt, har Avskjærer filtermønster kan implementeres ved hjelp av forskjellige strategier. I en 'virkelig verden' applikasjoner kan disse forskjellige tilnærmingene kombineres.

Som vanlig finner du kildene på GitHub.