1. 过滤器
概念: servlet过滤器作用于 service() 前后,与服务器同生共死:
- 开发过滤器类并实现
javax.servlet.Filter接口。 - 重写过滤器类的
init()/destroy()/doFilter():初始化/销毁/过滤器任务方法:doFilter()中获取HttpServletRequest和HttpServletResponse对象。doFilter()中为请求和响应对象统一转码。doFilter()中使用chain.doFilter()以放行请求到service()中。
- 注解方式配置:为过滤器类添加
@WebFilter并使用value属性指定过滤规则。 - XML方式配置:
web.xml中:- 使用
<filter>/<filter-name>/<filter-class>配置过滤器类。 - 使用
<filter-mapping>/<filter-name>/<url-pattern>配置过滤规则。
- 使用
- 开发
FilterTestServlet类:接收中文参数,并写回客户端。 - psm:
/api/encoding?name=赵四
过滤规则中不支持前模糊如
/*/api或/*User,但支持后模糊如/user*或/api/*。
过滤器布局:
/**
* @author yap
**/
@WebFilter("/api/*")
public class EncodingAnnotationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
System.out.println("EncodingAnnotationFilter init()...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
before(req, resp);
chain.doFilter(req, resp);
after();
}
private void before(HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException {
System.out.println("EncodingAnnotationFilter: before service()...");
req.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8");
}
private void after() {
System.out.println("EncodingAnnotationFilter: after service()...");
}
@Override
public void destroy() {
System.out.println("EncodingAnnotationFilter destroy()...");
}
}
servlet测试: EncodingServlet.java
/**
* @author yap
*/
@WebServlet("/api/encoding")
public class EncodingServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("FilterTestServlet: doGet()...");
resp.getWriter().print("{\"name\":" + req.getParameter("name") + "}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
1.1 非法请求案例
需求:
- 已登录状态下,所有
/api/*路径,都属于正常请求。 - 未登录状态下,除了
/api/login路径之外的所有/api/*路径,都属于非法请求。 - psm:
/api/encoding?name=赵四,非法请求。 - psm:
/api/login?name=admin,登陆成功。 - psm:
/api/encoding?name=赵四,允许访问。
过滤器布局: IllegalRequestFilter.java
/**
* @author yap
*/
@WebFilter("/api/*")
public class IllegalRequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
System.out.println("IllegalLoginFilter init()...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
req.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8");
final String effectivePath = "/api/login";
if (req.getRequestURI().contains(effectivePath) || isLoggedIn(req, resp)) {
chain.doFilter(req, resp);
} else {
resp.getWriter().println("{\"message\":\"非法登陆!\"}");
}
}
private boolean isLoggedIn(HttpServletRequest req, HttpServletResponse resp) {
HttpSession session;
synchronized (session = req.getSession()) {
return session.getAttribute("name") != null;
}
}
@Override
public void destroy() {
System.out.println("IllegalLoginFilter destroy()...");
}
}
servlet测试: IllegalRequestServlet.java
/**
* @author yap
*/
@WebServlet("/api/illegal-request")
public class IllegalRequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// suppose login success
HttpSession session;
synchronized (session = req.getSession()) {
session.setAttribute("name", req.getParameter("name"));
}
resp.getWriter().print("{\"message\":\"登陆成功!\"}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2. 监听器
概念: servlet监听器与服务器同生共死,每种监听器都有不同的使命:
- 开发监听器类并实现
javax.servlet.ServletContextListener接口,监视服务器的生死。 - 重写监听器类的监听方法:
contextInitialized():服务器启动时触发。contextDestroyed():服务器关闭时触发。
- 注解方式配置:为监听器类添加
@WebListener。 - XML方式配置:
web.xml中使用<listener>/<listener-class>配置监听器类。
监听器布局: AnnotationListener.java
/**
* @author yap
*/
@WebListener
public class AnnotationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("AnnotationListener: contextInitialized()...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("AnnotationListener: contextDestroyed()...");
}
}
2.1 访客计数案例
需求: 某客户端登录成功时,回写:"当前第x个人登录了您的网站!"
- psm:
/api/visitor-count?meta=login
监听器布局: VisitorCountListener.java
/**
* @author yap
*/
@WebListener
public class VisitorCountListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
//存值,从0开始
event.getServletContext().setAttribute("visitorCount", 0);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
event.getServletContext().removeAttribute("visitorCount");
}
}
servlet测试: VisitorCountServlet.java
/**
* @author yap
*/
@WebServlet("/api/visitor-count")
public class VisitorCountServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String meta = req.getParameter("meta");
if (Meta.LOGIN.equals(meta)) {
login(req, resp);
}
}
interface Meta {
String LOGIN = "login";
}
public void login(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// suppose login success
ServletContext application;
int visitorCount;
//取值 强转之后重新赋值
synchronized (application = req.getServletContext()) {
visitorCount = (int) application.getAttribute("visitorCount");
application.setAttribute("visitorCount", ++visitorCount);
}
resp.getWriter().print("{\"message\":\"当前第" + visitorCount + "个人登录了您的网站!\"}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2.2 重复登录案例
需求: 某客户端重复登陆同一账号时,回写:"不允许重复登陆!"
- psm:
/api/repeat-login?meta=login - psm:
/api/exit-login?meta=login
监听器布局: RepeatLoginListener.java
/**
* @author yap
*/
@WebListener
public class RepeatLoginListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
//服务器启动的时候先创建一个空数组
event.getServletContext().setAttribute("onlineUsers", new ArrayList<>());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
event.getServletContext().removeAttribute("onlineUsers");
}
}
过滤器布局: RepeatLoginFilter.java
/**
* @author yap
*/
@WebFilter("/api/repeat-login")
public class RepeatLoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
System.out.println("CrossDomainLoginFilter: init()...");
}
@SuppressWarnings("all")
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String name = req.getParameter("name");
ServletContext application;
synchronized (application = req.getServletContext()) {
// 获取空数组
List<String> onlineUsers = (List<String>) application.getAttribute("onlineUsers");
//数组是空的或者 没值
if (onlineUsers.isEmpty() || !onlineUsers.contains(name)) {
//放行
chain.doFilter(req, resp);
} else {
resp.getWriter().print(name + " 不允许重复登录!");
}
}
}
@Override
public void destroy() {
System.out.println("CrossDomainLoginFilter: destroy()...");
}
}
servlet测试: RepeatLoginServlet.java
/**
* @author yap
*/
@WebServlet("/api/repeat-login")
public class RepeatLoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String meta = req.getParameter("meta");
if (Meta.LOGIN.equals(meta)) {
login(req, resp);
}
}
interface Meta {
String LOGIN = "login";
}
@SuppressWarnings("all")
public void login(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// suppose login success
// 前端传来的name
String name = req.getParameter("name");
ServletContext application;
synchronized (application = req.getServletContext()) {
List<String> onlineUsers = (List<String>) application.getAttribute("onlineUsers");
//把name添加到空数组
onlineUsers.add(name);
}
//过滤器是空的显示登录 不是空的显示以登录
resp.getWriter().print(name + "登录...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
servlet测试: ExitLoginServlet.java
/**
* @author yap
*/
@WebServlet("/api/exit-login")
public class ExitLoginServlet extends HttpServlet {
@SuppressWarnings("all")
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取前端传来的name
String name = req.getParameter("name");
ServletContext application;
synchronized (application = req.getServletContext()){
// 获取数组里的值
List<String> onlineUsers = (List<String>) application.getAttribute("onlineUsers");
//删除 前端传来的name
onlineUsers.remove(name);
// 把删除后的数组重新存值,如果不重新存值 数组删除失败
application.setAttribute("onlineUsers", onlineUsers);
resp.getWriter().print("{\"message\":\"" + name + "退出登陆!\"}");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2.3 其他监听器
概念: servlet中除了 ServletContextListener 外,还提供了7种类型的监听:
ServletRequestListener:监听请求对象的生死。ServletRequestAttributeListener:监听请求域中属性添加,修改和移除。HttpSessionListener:监听会话对象的生死。HttpSessionAttributeListener:监听会话域中属性添加,修改和移除。HttpSessionBindingListener:监听其实现类绑定/解绑会话。HttpSessionActivationListener:监听会话的迁移(vm-a钝化,vm-b激活),笔记本目前测试不了ServletContextAttributeListener:监听应用域中属性添加,修改和移除。
ServletRequestListener:监听请求对象的生死。
监听器布局: MyRequestListener.java
/**
* @author yap
*/
@WebListener
public class MyRequestListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent event) {
HttpServletRequest req = (HttpServletRequest) event.getServletRequest();
System.out.println("request init: " + req.getRequestURI());
}
@Override
public void requestDestroyed(ServletRequestEvent event) {
HttpServletRequest req = (HttpServletRequest) event.getServletRequest();
System.out.println("request destroy: " + req.getRequestURI());
}
}
servlet测试: MyRequestServlet.java
/**
* @author yap
*/
@WebServlet("/api/my-request")
public class MyRequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("request success!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
ServletRequestAttributeListener:监听请求域中属性添加,修改和移除。
监听器布局: MyApplicationAttributeListener.java
/**
* @author yap
*/
@WebListener
public class MyApplicationAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
System.out.println("application add: " + event.getName() + "/" + event.getValue());
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
// old value
System.out.println("application replace: " + event.getName() + "/" + event.getValue());
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
System.out.println("application remove: " + event.getName() + "/" + event.getValue());
}
}
servlet测试: MyApplicationAttributeServlet.java
/**
* @author yap
*/
@WebServlet("/api/my-application-attribute")
public class MyApplicationAttributeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String meta = req.getParameter("meta");
if (Meta.ADD.equals(meta)) {
req.getServletContext().setAttribute("name", "admin");
resp.getWriter().print("add success!");
} else if (Meta.REPLACE.equals(meta)) {
req.getServletContext().setAttribute("name", "admin");
req.getServletContext().setAttribute("name", "joe");
resp.getWriter().print("replace success!");
} else if (Meta.REMOVE.equals(meta)) {
req.getServletContext().setAttribute("name", "admin");
req.getServletContext().removeAttribute("name");
resp.getWriter().print("remove success!");
}
}
interface Meta {
String ADD = "add";
String REPLACE = "replace";
String REMOVE = "remove";
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
this.doGet(req, resp);
}
}
HttpSessionListener:监听会话对象的生死。
监听器布局: MySessionListener.java
/**
* @author yap
*/
@WebListener
public class MySessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("session init: " + se.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("session destroy: " + se.getSession().getId());
}
}
servlet测试: MySessionServlet.java
/**
* @author yap
*/
@WebServlet("/api/my-session")
public class MySessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String meta = req.getParameter("meta");
HttpSession session;
if (Meta.CREATE.equals(meta)) {
session = req.getSession();
resp.getWriter().print("session create success: " + session.getId());
} else if (Meta.DESTROY.equals(meta)) {
session = req.getSession();
session.invalidate();
resp.getWriter().print("session destroy success: " + session.getId());
}
}
interface Meta {
String CREATE = "create";
String DESTROY = "destroy";
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
HttpSessionAttributeListener:监听会话域中属性添加,修改和移除。
监听器布局: MySessionAttributeListener.java
/**
* @author yap
*/
@WebListener
public class MySessionAttributeListener implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
System.out.println("session add: " + event.getName() + "/" + event.getValue());
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
System.out.println("session remove: " + event.getName() + "/" + event.getValue());
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
// value is old...
System.out.println("session replace: " + event.getName() + "/" + event.getValue());
}
}
servlet测试: MySessionAttributeServlet.java
/**
* @author yap
*/
@WebServlet("/api/my-session-attribute")
public class MySessionAttributeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String meta = req.getParameter("meta");
if (Meta.ADD.equals(meta)) {
req.getSession().setAttribute("name", "admin");
resp.getWriter().print("add success!");
} else if (Meta.REPLACE.equals(meta)) {
req.getSession().setAttribute("name", "admin");
req.getSession().setAttribute("name", "joe");
resp.getWriter().print("replace success!");
} else if (Meta.REMOVE.equals(meta)) {
req.getSession().setAttribute("name", "admin");
req.getSession().removeAttribute("name");
resp.getWriter().print("remove success!");
}
}
interface Meta {
String ADD = "add";
String REPLACE = "replace";
String REMOVE = "remove";
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
this.doGet(req, resp);
}
}
HttpSessionBindingListener:监听其实现类绑定/解绑会话。
监听器布局: MyUser.java
/**
* @author yap
*/
public class MyUser implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("MyUser bound to session: " + event.getSession().getId());
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("MyUser unbound from session: " + event.getSession().getId());
}
}
servlet测试: MyUserBindingServlet.java
/**
* @author yap
*/
@WebServlet("/api/my-session-binding")
public class MyUserBindingServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String meta = req.getParameter("meta");
MyUser myUser = new MyUser();
if (Meta.BOUND.equals(meta)) {
req.getSession().setAttribute("myUser", myUser);
resp.getWriter().print("myUser bound success!");
} else if (Meta.UNBOUND.equals(meta)) {
req.getSession().removeAttribute("myUser");
resp.getWriter().print("myUser unbound success!");
}
}
interface Meta {
String BOUND = "bound";
String UNBOUND = "unbound";
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
ServletContextAttributeListener:监听应用域中属性添加,修改和移除。
监听器布局: MyApplicationAttributeListener.java
/**
* @author yap
*/
@WebListener
public class MyApplicationAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
System.out.println("application add: " + event.getName() + "/" + event.getValue());
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
// old value
System.out.println("application replace: " + event.getName() + "/" + event.getValue());
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
System.out.println("application remove: " + event.getName() + "/" + event.getValue());
}
}
servlet测试: MyApplicationAttributeServlet.java
/**
* @author yap
*/
@WebServlet("/api/my-application-attribute")
public class MyApplicationAttributeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String meta = req.getParameter("meta");
if (Meta.ADD.equals(meta)) {
req.getServletContext().setAttribute("name", "admin");
resp.getWriter().print("add success!");
} else if (Meta.REPLACE.equals(meta)) {
req.getServletContext().setAttribute("name", "admin");
req.getServletContext().setAttribute("name", "joe");
resp.getWriter().print("replace success!");
} else if (Meta.REMOVE.equals(meta)) {
req.getServletContext().setAttribute("name", "admin");
req.getServletContext().removeAttribute("name");
resp.getWriter().print("remove success!");
}
}
interface Meta {
String ADD = "add";
String REPLACE = "replace";
String REMOVE = "remove";
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
this.doGet(req, resp);
}
}
3. 报表打印
概念: 报表打印就是将数据生成到一个Excel文件中:
- 引入依赖:
jxls-core。 - 准备一个Excel模板,模板中支持插值符号
${}和<jx:>标签。 - 开发
UserService.listAndPrintExcel():- 准备一个Map类型的用户数据。
- 获取转换器对象
XLSTransformer。 - 调用
transformXLS()指定模板位置,数据和输出路径(带文件名)。
- 开发
ExcelServlet:调用业务层listAndPrintExcel()。 - 如果想下载下来,可以直接重定向这个文件的位置。
依赖
<!--jxls-core-->
<dependency>
<groupId>net.sf.jxls</groupId>
<artifactId>jxls-core</artifactId>
<version>1.0-RC-2</version>
</dependency>
user-template.xls
布局: ExcelService.java
/**
* @author yap
*/
public interface ExcelService {
/**
* 获取所有的用户信息并打印报表
*
* @param templatePath 模板真实物理路径
* @param outputDirectory Excel文件输出位置
*/
void listAndPrintExcel(String templatePath, String outputDirectory);
}
布局: ExcelServiceImpl.java
/**
* @author yap
*/
public class ExcelServiceImpl implements ExcelService {
@Override
public void listAndPrintExcel(String templatePath, String outputDirectory) {
File directory = new File(outputDirectory);
if (!directory.exists()) {
System.out.println(directory.mkdirs());
}
XLSTransformer transformer = new XLSTransformer();
try {
transformer.transformXLS(templatePath, getExcelData(), outputDirectory + "/user.xls");
} catch (Exception e) {
e.printStackTrace();
}
}
private Map<String, Object> getExcelData() {
Map<String, Object> map = new HashMap<>(2);
List<Map<String, Object>> users = new ArrayList<>();
Map<String, Object> user;
int amountOfUsers = 100;
for (int i = 1; i <= amountOfUsers; i++) {
user = new HashMap<>(3);
user.put("id", i);
user.put("username", "admin-" + i);
user.put("password", "123" + i);
users.add(user);
}
map.put("title", "用户信息表");
map.put("users", users);
return map;
}
}
布局: ExcelServlet.java
/**
* @author yap
*/
@WebServlet("/api/excel")
public class ExcelServlet extends HttpServlet {
@SneakyThrows
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String templatePath = req.getServletContext().getRealPath("/template/user-template.xls");
String outputDirectory = req.getServletContext().getRealPath("/excel");
ExcelService userService = new ExcelServiceImpl();
userService.listAndPrintExcel(templatePath, outputDirectory);
// 直接重定向访问要打印的文件可以下载文件,但这种方式无法识别中文,如果是中文,使用IO流下载
resp.sendRedirect(req.getContextPath() + "/excel/user.xls");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.doGet(request, response);
}
}