Úvod do zachytávania filtračných vzorov v Jave

1. Prehľad

V tomto výučbe si predstavíme Zachytávací vzor filtra vzor Core J2EE na úrovni prezentácie.

Toto je druhý návod v našom Séria vzorov a nadväzujúcich opatrení na Predný ovládač sprievodca, ktorý nájdete tu.

Zachytávajúce filtre sú filtre, ktoré spúšťajú akcie pred alebo po spracovaní prichádzajúcej požiadavky obslužným programom.

Zachytávacie filtre predstavujú centralizované komponenty vo webovej aplikácii, spoločné pre všetky požiadavky a rozšíriteľné bez ovplyvnenia existujúcich obslužných programov.

2. Používajte puzdrá

Rozšírime príklad z predchádzajúcej príručky a implementujeme mechanizmus autentifikácie, protokolovanie požiadaviek a počítadlo návštevníkov. Okrem toho chceme schopnosť dodávať naše stránky v rôznych rôznych kódovanie.

Toto všetko sú prípady použitia na zachytenie filtrov, pretože sú spoločné pre všetky požiadavky a mali by byť nezávislé od obslužných programov.

3. Filtrujte stratégie

Poďme si predstaviť rôzne stratégie filtrovania a príkladné prípady použitia. Ak chcete spustiť kód s kontajnerom Jetty Servlet, jednoducho vykonajte:

$> mvn nainštalovať mólo: spustiť

3.1. Stratégia vlastného filtra

Stratégia vlastného filtra sa používa v každom prípade použitia, ktoré si vyžaduje objednané spracovanie požiadaviek, v zmysle jeden filter je založený na výsledkoch predchádzajúceho filtra v reťazci vykonávania.

Tieto reťazce sa vytvoria implementáciou FilterChain rozhranie a registrácia rôznych Filtrovať triedy s tým.

Pri použití viacerých reťazcov filtra s rôznymi záujmami ich môžete spojiť v správcovi filtrov:

V našom príklade funguje počítadlo návštevníkov počítaním jedinečných používateľských mien od prihlásených používateľov, čo znamená, že je založené na výsledku autentifikačného filtra, preto musia byť oba filtre zreťazené.

Implementujme tento filtračný reťazec.

Najskôr vytvoríme autentifikačný filter, ktorý skontroluje, či relácia existuje pre nastavený atribút „používateľské meno“, a vydá postup prihlásenia, ak nie:

verejná trieda AuthenticationFilter implementuje filter {... @Override public void doFilter (požiadavka ServletRequest, odpoveď ServletResponse, reťazec FilterChain) {HttpServletRequest httpServletRequest = (HttpServletRequest) požiadavka; HttpServletResponse httpServletResponse = (HttpServletResponse) odpoveď; HttpSession session = httpServletRequest.getSession (false); if (session == null || session.getAttribute ("username") == null) {príkaz FrontCommand = nový LoginCommand (); command.init (httpServletRequest, httpServletResponse); command.process (); } else {chain.doFilter (požiadavka, odpoveď); }} ...}

Teraz vytvorme počítadlo návštevníkov. Tento filter zachováva a HashSet jedinečných používateľských mien a k žiadosti pridá atribút „counter“:

verejná trieda VisitorCounterFilter implementuje Filter {private static Set users = new HashSet (); ... @Override public void doFilter (požiadavka ServletRequest, odpoveď ServletResponse, reťazec FilterChain) {HttpSession session = ((HttpServletRequest) požiadavka) .getSession (false); Optional.ofNullable (session.getAttribute ("username")) .map (Object :: toString) .ifPresent (users :: add); request.setAttribute ("counter", users.size ()); chain.doFilter (požiadavka, odpoveď); } ...}

Ďalej implementujeme a FilterChain že iteruje registrované filtre a vykonáva doFilter metóda:

verejná trieda FilterChainImpl implementuje FilterChain {súkromné ​​filtre Iterator; public FilterChainImpl (Filter ... filtre) {this.filters = Arrays.asList (filtre) .iterator (); } @Override public void doFilter (požiadavka ServletRequest, odpoveď ServletResponse) {if (filters.hasNext ()) {filter filter = filters.next (); filter.doFilter (požiadavka, odpoveď, toto); }}}

Na prepojenie našich komponentov vytvorme jednoduchého statického manažéra, ktorý je zodpovedný za vytvorenie inštancie filtračných reťazcov, registráciu ich filtrov a ich spustenie:

public class FilterManager {public static void process (HttpServletRequest request, HttpServletResponse response, OnIntercept callback) {FilterChain filterChain = new FilterChainImpl (new AuthenticationFilter (callback), new VisitorCounterFilter ()); filterChain.doFilter (požiadavka, odpoveď); }}

Ako posledný krok budeme musieť zavolať náš FilterManager ako bežná súčasť postupnosti spracovania požiadaviek z nášho FrontCommand:

public abstract class FrontCommand {... public void process () {FilterManager.process (request, response); } ...}

3.2. Stratégia základného filtra

V tejto časti predstavíme Stratégia základného filtra, s ktorou sa pre všetky implementované filtre používa spoločná nadtrieda.

Táto stratégia sa dobre hrá s vlastnou stratégiou z predchádzajúcej časti alebo s Štandardná stratégia filtra ktoré si predstavíme v nasledujúcej časti.

Abstraktnú základnú triedu možno použiť na použitie vlastného správania, ktoré patrí do reťazca filtra. Použijeme ho v našom príklade na zníženie štandardného kódu súvisiaceho s konfiguráciou filtra a protokolom ladenia:

verejná abstraktná trieda BaseFilter implementuje Filter {private Logger log = LoggerFactory.getLogger (BaseFilter.class); chránený FilterConfig filterConfig; @Override public void init (FilterConfig filterConfig) hodí ServletException {log.info ("Inicializovať filter: {}", getClass (). GetSimpleName ()); this.filterConfig = filterConfig; } @Override public void destru () {log.info ("Zničiť filter: {}", getClass (). GetSimpleName ()); }}

Rozšírime túto základnú triedu a vytvoríme filter protokolovania požiadaviek, ktorý bude integrovaný do nasledujúcej časti:

public class LoggingFilter extends BaseFilter {private static final Logger log = LoggerFactory.getLogger (LoggingFilter.class); @Override public void doFilter (požiadavka ServletRequest, odpoveď ServletResponse, reťazec FilterChain) {chain.doFilter (požiadavka, odpoveď); HttpServletRequest httpServletRequest = (HttpServletRequest) požiadavka; Reťazec username = Voliteľné .ofNullable (httpServletRequest.getAttribute ("username")) .map (Object :: toString) .orElse ("guest"); log.info ("Žiadosť od '{} @ {}': {}? {}", používateľské meno, request.getRemoteAddr (), httpServletRequest.getRequestURI (), request.getParameterMap ()); }}

3.3. Štandardná stratégia filtra

Pružnejším spôsobom použitia filtrov je implementácia Štandardná stratégia filtra. To sa dá urobiť vyhlásením filtrov v deskriptore nasadenia alebo, od špecifikácie servletu 3.0, anotáciou.

Štandardná stratégia filtraumožňuje pripojiť nové filtre do predvoleného reťazca bez toho, aby ste mali explicitne definovaného správcu filtrov:

Poradie, v ktorom sa filtre použijú, nie je možné určiť pomocou anotácie. Ak potrebujete nariadené spustenie, musíte sa držať deskriptora nasadenia alebo implementovať vlastnú stratégiu filtra.

Implementujme anotačný kódovací filter, ktorý tiež používa stratégiu základného filtra:

@WebFilter (servletNames = {"intercepting-filter"}, initParams = {@WebInitParam (name = "encoding", value = "UTF-8")}) verejná trieda EncodingFilter rozširuje BaseFilter {súkromné ​​kódovanie reťazcov; @Override public void init (FilterConfig filterConfig) hodí ServletException {super.init (filterConfig); this.encoding = filterConfig.getInitParameter ("kódovanie"); } @Override public void doFilter (požiadavka ServletRequest, odpoveď ServletResponse, reťazec FilterChain) {kódovanie reťazca = voliteľné .ofNullable (request.getParameter ("kódovanie")) .orElse (this.encoding); response.setCharacterEncoding (kódovanie); chain.doFilter (požiadavka, odpoveď); }}

V scenári servletov s deskriptorom nasadenia náš web.xml bude obsahovať tieto ďalšie vyhlásenia:

 kódovací filter com.baeldung.patterns.intercepting.filter.filters.EncodingFilter kódovací filter filter zachytávajúci filter 

Zoberme si náš protokolovací filter a tiež ho anotujme, aby sme si ho Servlet mohli zvyknúť:

@WebFilter (servletNames = "intercepting-filter") verejná trieda LoggingFilter rozširuje BaseFilter {...}

3.4. Stratégia filtra šablón

The Stratégia filtra šablón je skoro to isté ako stratégia základného filtra, až na to, že používa metódy šablón deklarované v základnej triede, ktoré musia byť v implementáciách prepísané:

Vytvorme triedu základného filtra s dvoma metódami abstraktného filtra, ktoré sa vyvolajú pred a po ďalšom spracovaní.

Pretože táto stratégia je menej častá a nepoužívame ju v našom príklade, konkrétna implementácia a prípad použitia je na vašej fantázii:

verejná abstraktná trieda TemplateFilter rozširuje BaseFilter {chránený abstraktný neplatný preFilter (požiadavka HttpServletRequest, odpoveď HttpServletResponse); chránený abstraktný neplatný postFilter (požiadavka HttpServletRequest, odpoveď HttpServletResponse); @Override public void doFilter (požiadavka ServletRequest, odpoveď ServletResponse, reťazec FilterChain) {HttpServletRequest httpServletRequest = (HttpServletRequest) požiadavka; HttpServletResponse httpServletResponse = (HttpServletResponse) odpoveď; preFilter (httpServletRequest, httpServletResponse); chain.doFilter (požiadavka, odpoveď); postFilter (httpServletRequest, httpServletResponse); }}

4. Záver

Vzor zachytávacieho filtra zachytáva prierezové problémy, ktoré sa môžu vyvíjať nezávisle od obchodnej logiky. Z hľadiska obchodných operácií sa filtre vykonávajú ako reťaz akcií pred alebo po.

Ako sme už videli, Zachytávací vzor filtra možno implementovať pomocou rôznych stratégií. V aplikáciách „v reálnom svete“ je možné tieto rôzne prístupy kombinovať.

Ako obvykle nájdete zdroje na GitHub.