Pengenalan Corak Saringan Memintas di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan memperkenalkan Corak J2EE tingkatan persembahan Intercepting Filter Pattern.

Ini adalah tutorial kedua dalam Corak Siri kami dan ikuti panduan Corak Pengawal Depan yang boleh didapati di sini.

Memintas Filters adalah penapis yang mencetuskan tindakan sebelum atau selepas permintaan masuk diproses oleh pengendali.

Memintas penapis mewakili komponen terpusat dalam aplikasi web, umum untuk semua permintaan dan dapat diperluas tanpa mempengaruhi pengendali yang ada.

2. Kes Penggunaan

Mari kita berikan contoh dari panduan sebelumnya dan laksanakan mekanisme pengesahan, log permintaan, dan kaunter pelawat . Sebagai tambahan, kami mahukan kemampuan untuk menyampaikan halaman kami dalam pelbagai pengekodan yang berbeza .

Semua ini adalah kes penggunaan untuk memintas penyaring kerana ia biasa untuk semua permintaan dan harus bebas dari pengendali.

3. Strategi Tapisan

Marilah kita memperkenalkan strategi penapis yang berbeza dan contoh penggunaan contoh. Untuk menjalankan kod dengan bekas Jetty Servlet, jalankan:

$> mvn install jetty:run

3.1. Strategi Penapis Tersuai

Strategi penapis khusus digunakan dalam setiap kasus penggunaan yang memerlukan pemrosesan permintaan yang teratur, dalam arti satu penapis didasarkan pada hasil penapis sebelumnya dalam rantai pelaksanaan .

Rantai ini akan dibuat dengan menerapkan antara muka FilterChain dan mendaftarkan pelbagai kelas Penapis dengannya.

Apabila menggunakan beberapa rantai penapis dengan kebimbangan yang berbeza, anda boleh menyatukannya dalam pengurus penapis:

Dalam contoh kami, kaunter pengunjung berfungsi dengan menghitung nama pengguna yang unik dari pengguna yang log masuk, yang bermaksud ia berdasarkan hasil penapis pengesahan, oleh itu, kedua-dua penapis harus dirantai.

Mari kita laksanakan rangkaian penapis ini.

Pertama, kami akan membuat penapis pengesahan yang memeriksa sama ada sesi ada untuk atribut 'username' yang ditetapkan dan mengeluarkan prosedur log masuk jika tidak:

public class AuthenticationFilter implements Filter { ... @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; HttpSession session = httpServletRequest.getSession(false); if (session == null || session.getAttribute("username") == null) { FrontCommand command = new LoginCommand(); command.init(httpServletRequest, httpServletResponse); command.process(); } else { chain.doFilter(request, response); } } ... }

Sekarang mari buat kaunter pelawat. Penapis ini mengekalkan HashSet nama pengguna yang unik dan menambahkan atribut 'counter' pada permintaan:

public class VisitorCounterFilter implements 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("username")) .map(Object::toString) .ifPresent(users::add); request.setAttribute("counter", users.size()); chain.doFilter(request, response); } ... }

Seterusnya, kami akan melaksanakan FilterChain yang iterates berdaftar penapis dan melaksanakan doFilter kaedah:

public class FilterChainImpl implements FilterChain { private Iterator filters; 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(request, response, this); } } }

Untuk menyatukan komponen kami, mari buat pengurus statik ringkas yang bertanggungjawab untuk membuat rantai penapis, mendaftarkan penapisnya, dan memulakannya:

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

Sebagai langkah terakhir kita harus memanggil FilterManager kita sebagai bahagian biasa dari urutan pemprosesan permintaan dari dalam FrontCommand kami :

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

3.2. Strategi Penapis Pangkalan

Di bahagian ini, kami akan membentangkan Strategi Penapis Pangkalan, dengan mana superclass biasa digunakan untuk semua penapis yang dilaksanakan.

Strategi ini bermain dengan baik bersama strategi khusus dari bahagian sebelumnya atau dengan Strategi Penapis Piawai yang akan kami perkenalkan di bahagian berikutnya.

Kelas asas abstrak boleh digunakan untuk menerapkan tingkah laku tersuai yang termasuk dalam rantai penapis. Kami akan menggunakannya dalam contoh kami untuk mengurangkan kod plat boiler yang berkaitan dengan konfigurasi penapis dan log debug:

public abstract class BaseFilter implements Filter { private Logger log = LoggerFactory.getLogger(BaseFilter.class); protected FilterConfig filterConfig; @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("Initialize filter: {}", getClass().getSimpleName()); this.filterConfig = filterConfig; } @Override public void destroy() { log.info("Destroy filter: {}", getClass().getSimpleName()); } }

Mari kembangkan kelas asas ini untuk membuat penapis log permintaan, yang akan disatukan ke bahagian seterusnya:

public class LoggingFilter extends 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) request; String username = Optional .ofNullable(httpServletRequest.getAttribute("username")) .map(Object::toString) .orElse("guest"); log.info( "Request from '{}@{}': {}?{}", username, request.getRemoteAddr(), httpServletRequest.getRequestURI(), request.getParameterMap()); } }

3.3. Strategi Penapis Piawai

Kaedah penapisan yang lebih fleksibel adalah dengan menerapkan Strategi Penapis Piawai . Ini dapat dilakukan dengan menyatakan saringan dalam deskriptor penyebaran atau, sejak spesifikasi Servlet 3.0, dengan anotasi.

Strategi penapis standardmembolehkan memasukkan penapis baru ke rantai lalai tanpa mempunyai pengurus penapis yang ditentukan secara jelas:

Perhatikan bahawa urutan, penapis digunakan, tidak dapat ditentukan melalui anotasi. Sekiranya anda memerlukan pelaksanaan yang teratur, anda harus mengikuti penerangan penerapan atau menerapkan strategi penapis khusus.

Mari laksanakan penapis pengekodan berdasarkan anotasi yang juga menggunakan strategi penapis asas:

@WebFilter(servletNames = {"intercepting-filter"}, initParams = {@WebInitParam(name = "encoding", value = "UTF-8")}) public class EncodingFilter extends BaseFilter { private String encoding; @Override public void init(FilterConfig filterConfig) throws ServletException { super.init(filterConfig); this.encoding = filterConfig.getInitParameter("encoding"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { String encoding = Optional .ofNullable(request.getParameter("encoding")) .orElse(this.encoding); response.setCharacterEncoding(encoding); chain.doFilter(request, response); } }

Dalam senario Servlet dengan penerangan penerapan, web.xml kami akan mengandungi pernyataan tambahan ini:

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

Mari ambil penapis log kami dan beri anotasinya juga, agar dapat digunakan oleh Servlet:

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

3.4. Strategi Penapis Templat

The Template Filter Strategy is pretty much the same as the base filter strategy, except that it uses template methods declared in the base class that must be overridden in implementations:

Let's create a base filter class with two abstract filter methods that get called before and after further processing.

Since this strategy is less common and we don't use it in our example, a concrete implementation and use case is up to your imagination:

public abstract class TemplateFilter extends BaseFilter { protected abstract void preFilter(HttpServletRequest request, HttpServletResponse response); protected abstract void postFilter(HttpServletRequest request, HttpServletResponse response); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; preFilter(httpServletRequest, httpServletResponse); chain.doFilter(request, response); postFilter(httpServletRequest, httpServletResponse); } }

4. Conclusion

The Intercepting Filter Pattern captures cross-cutting concerns that can evolve independently of the business logic. From the perspective of business operations, filters are executed as a chain of pre or post actions.

Seperti yang telah kita lihat setakat ini, Pola Penapis Memintas dapat dilaksanakan dengan menggunakan strategi yang berbeza. Dalam aplikasi 'dunia nyata' pendekatan yang berbeza ini dapat digabungkan.

Seperti biasa, anda akan menemui sumber di GitHub .