");
// 遍历结果集
while (rs.next()) {
out.write("");
// 逐列输出查询到的数据
for (int i = 1; i <= columnCount; i++) {
out.write("");
out.write(rs.getString(i));
out.write(" ");
}
out.write(" ");
}
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
throw new JspException("自定义标签错误" + cnfe.getMessage());
} catch (SQLException ex) {
ex.printStackTrace();
throw new JspException("自定义标签错误" + ex.getMessage());
} finally {
// 关闭结果集
try {
if (rs != null)
rs.close();
if (stmt != null)
stmt.close();
if (conn != null)
conn.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
}
}
该标签类包含了5个属性,而为标签处理类定义成员变量即可代表标签的属性,而且程序需要为这5个属性提供setter()和getter()方法。该标签输出的内容依然由doTag()方法决定,该方法会根据SQL语句查询数据库,并将查询结果显示在当前页面中。
对于有属性的标签,需要在tld中为 元素增加 子元素,每个 定义一个标签属性。 子元素通常还需要指定如下几个子元素:
name:设置属性名,子元素的值是字符串内容
required:设置该属性是否为必须属性,该子元素的值时true或false
fragment:设置该属性是否支持JSP脚本、表达式等动态内容,子元素的值true或false
为了配置上面的QueryTag标签,需要在tld文件中增加如下配置片段:
query
lee.QueryTag
empty
driver
true
true
url
true
true
user
true
true
pass
true
true
sql
true
true
配置完毕后,就可在页面中使用标签了,先导入标签库,然后使用标签,代码如下:
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
......
2.5 带标签体的标签(重点)
带标签体的标签,可以在标签内嵌入其他内容(包括静态的HTML内容和动态的JSP内容),通常用于完成一些逻辑运算。示例如下:
标签处理类:
public class IteratorTag extends SimpleTagSupport {
// 标签属性,用于指定需要被迭代的集合
private String collection;
// 标签属性,指定迭代集合元素,为集合元素指定的名称
private String item;
// 省略get与set...
// 省略get与set...
// 标签的处理方法,标签处理类只需要重写doTag()方法
public void doTag() throws JspException, IOException {
// 从page scope中获取名为collection的集合
Collection> itemList = (Collection>) getJspContext().getAttribute(collection);
// 遍历集合
for (Object s : itemList) {
// 将集合的元素设置到page范围内
getJspContext().setAttribute(item, s);
// 输出标签体
getJspBody().invoke(null);
}
}
// 标签的处理方法,标签处理类只需要重写doTag()方法
public void doTag() throws JspException, IOException {
// 从page scope中获取名为collection的集合
Collection> itemList = (Collection>) getJspContext().getAttribute(collection);
// 遍历集合
for (Object s : itemList) {
// 将集合的元素设置到page范围内
getJspContext().setAttribute(item, s);
// 输出标签体
getJspBody().invoke(null);
}
}
}
该标签处理类的doTag()方法首先从page范围内获取了指定名称的Collection对象,然后遍历Collection对象的元素,每次遍历都调用getJspBody()方法,该方法返回该标签所包含的标签体:JSPFragment对象,执行该对象的invoke()方法,即可输出标签体内容。该标签的作用是:遍历指定集合,每遍历一个集合元素,技术处标签体一次。
因为该标签的标签体不为空,配置该标签时指定body-content为scriptless,该标签的配置代码如下:
iterator
lee.IteratorTag
scriptless
collection
true
true
item
true
true
上面的配置指定该标签的标签体可以是静态HTML内容,也可以是表达式语言,但不允许出现JSP脚本。下面是测试代码:(导入代码省略)
带标签体的标签-迭代器标签
<%
//创建一个List对象
List a = new ArrayList();
a.add("疯狂Java");
a.add("www.crazyit.org");
a.add("www.fkit.org");
//将List对象放入page范围内
pageContext.setAttribute("a", a);
%>
测试结果如下:
从输出结果看,使用iterator标签遍历集合元素比使用JSP脚本遍历集合元素要优雅得多,这就是自定义标签的魅力。实际上JSTL标签库提供了一套功能强大的标签,如果有现成的轮子就没必要再造轮子了。
可能你有所疑惑,这个JSP页面自己先把多个字符串添加到ArrayList,然后再使用这个iterator标签进行迭代输出,好像没有意义。实际上这个标签的用处非常大,在严格的MVC规范下,JSP页面只负责显示数据——而数据通常由控制器(Servlet)放入request范围内,而JSP页面就通过iterator标签迭代出输出request范围内的数据。
2.6 以页面片段作为属性的标签(暂不理解)
JSP2规范的自定义标签还允许直接将一段“页面片段”作为属性,这种方式给自定义标签提供了更大的灵活性。以“页面片段”为属性的标签与与普通标签区别并不大,只有两个简单的改变:
标签处理类中定义类型为JSPFragment的属性,该属性代表了“页面片段”
使用标签库,通过 动作指令为标签的属性指定值
定义一个标签处理类,示例:
public class FragmentTag extends SimpleTagSupport {
private JspFragment fragment;
//省略fragment的setter和getter方法
@Override
public void doTag() throws JspException, IOException {
JspWriter out = getJspContext().getOut();
out.println("");
out.println("
下面是动态传入的JSP片段 ");
// 调用、输出“页面片段”
fragment.invoke(null);
out.println("
上面的程序中定义了fragment成员变量,该成员变量代表了使用该标签时的“页面片段”,配置该标签与配置普通标签并无任何区别,增加如下配置片段即可。
fragment
lee.FragmentTag
empty
fragment
true
true
这个自定义标签并没有任何特别之处,就是一个普通的带属性标签,该标签的标签体为空。由于该标签需要一个fragment属性,该属性的类型为JspFragment,因此使用该标签时需要使用 动作指令来设置属性值,如下所示:
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
.....
<%-- 使用jsp:attribute标签传入fragment参数(该注释不能放在fragment内) -->
<%-- 下面是动态的JSP页面片段 --%>
<%-- 下面是动态的JSP页面片段 --%>
${pageContext.request.remoteAddr}
注意:由于程序指定了fragment标签的标签体为empty,因此程序中fragment开始标签和fragment结束标签之间只能使用 子元素,不允许出现其他内容,甚至连注释都不允许。
2.7 动态属性的标签(终点)
前面的标签的属性个数是确定的,绝大部分情况下这种带属性的标签能处理得很好,但在某些特殊情况下,需要传入自定义标签的属性个数是不确定的,属性名也不确定,这就需要借助于动态属性的标签了。
动态属性标签比普通标签多了两个额外需求:
标签处理类还需要实现DynamicAttributes接口
配置标签时通过 子元素指定该标签支持动态属性
动态属性标签的处理类,示例:
public class DynaAttributesTag extends SimpleTagSupport implements DynamicAttributes {
// 保存每个属性名的集合
private ArrayList keys = new ArrayList();
// 保存每个属性值的集合
private ArrayList values = new ArrayList();
@Override
public void doTag() throws JspException, IOException {
JspWriter out = getJspContext().getOut();
// 此处只是简单地输出每个属性
out.println("");
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
Object value = values.get(i);
out.println("" + key + " = " + value + " ");
}
out.println(" ");
}
@Override
public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
// 添加属性名
keys.add(localName);
// 添加属性值
values.add(value);
}
}
上面的标签处理类实现了DynamicAttributes接口,就是动态属性标签处理类必须实现的接口,实现该接口必须实现setDynamicAttribute()方法,该方法用于为该标签处理类动态地添加属性名和属性值。标签处理类使用ArrayList类型的Keys属性来保存标签的所有属性名,使用ArrayList类型的values属性来保存标签的所有属性值。 配置该标签时需要额外地指定 子元素,表明该标签时带动态属性的标签,如下:
dynaAttr
lee.DynaAttributesTag
empty
true
一旦定义了动态属性的标签,接下来在页面中使用该标签时将十分灵活,完全可以为该标签设置任意的属性,如下:
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
...
下面显示的是自定义标签中的内容
指定两个属性
指定四个属性
三、Filter
Filter主要用于对用户请求进行预处理,也可对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter有如下几个用处:
在HttpServletRequest到达Servlet之前,拦截客户的HttpServletResponse
根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据
在HttpServletResponse到达客户端之前,拦截HttpServletResponse
根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据
Filter有如下几个种类:
用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求
日志Filter:详细记录某些特殊的用户请求
负责解码的Filter:包括对非标准编码的请求解码
能改变XML内容的XSLT Filter等
Filter可负责拦截多个请求或相应:一个请求或相应也可被多个Filter拦截
创建一个Filter只需要两个步骤:
创建Filter处理类
web.xml文件中配置Filter
3.1 创建Filter类
创建Filter必须实现javax.Servlet.Filter接口,在该接口中定义了如下三个方法:
void init(FilterConfig config):用于完成Filter的初始化
void destroy():用于Filter销毁前,完成某些资源的回收
void doFilter(ServletRequest request , ServletResponse response , FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理
终点在doFilter方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理——它们的分界线为是否调用了chain.doFilter(request, response);方法,执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。
示例:(使用注解配置,详细介绍在下面)
@WebFilter(filterName = "log", urlPatterns = { "/*" })
public class LogFilter implements Filter {
// FilterConfig可用于访问Filter的配置信息
private FilterConfig config;
// 实现初始化方法
public void init(FilterConfig config) {
this.config = config;
}
// 实现销毁方法
public void destroy() {
this.config = null;
}
// 执行过滤的核心方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// ---------下面代码用于对用户请求执行预处理---------
// 获取ServletContext对象,用于记录日志
ServletContext context = this.config.getServletContext();
long before = System.currentTimeMillis();
System.out.println("开始过滤...");
// 将请求转换成HttpServletRequest请求
HttpServletRequest hrequest = (HttpServletRequest) request;
// 输出提示信息
System.out.println("Filter已经截获到用户的请求的地址: " + hrequest.getServletPath());
// Filter只是链式处理,请求依然放行到目的地址
chain.doFilter(request, response);//放行用户请求
// ---------下面代码用于对服务器响应执行后处理---------
long after = System.currentTimeMillis();
// 输出提示信息
System.out.println("过滤结束");
// 输出提示信息
System.out.println("请求被定位到" + hrequest.getRequestURI() + " 所花的时间为: " + (after - before));
}
}
这个Filter的作用是拦截所有的请求,然后输出请求地址,然后放行这个请求,然后,输出响应地址,并且输出整个响应过程所需要的时间。这只是个简单的拦截器,我们完全也可以在Filter中根据用户请求的HttpSession,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用chain.doFilter(request, response);方法。
3.2 配置Filter
Filter可以认为是Servlet的“增强版”,因此配置Filter与配置Servlet非常相似,都需要配置如下两个部分:
配置Filter名
配置Filter拦截URL模式
区别在于:Servlet通常指配置一个URL,而Filter可以同时拦截多个请求的URL。因此,在配置Filter的URL模式时通常会使用模式字符串,使得Filter可以拦截多个请求。与配置Servlet相似的是,配置Filter同样有两种方式:
在Filter类中通过注解进行配置
在web.xml文件中通过配置文件进行配置
上面Filter类代码使用@WebFilter配置该Filter的名字为log,它会拦截向/*发送的所有请求。而用web.xml文件配置其作用相同,如下:
log
lee.LogFilter
log
/*
3.2.1 @WebFilter注解常用属性(都是非必须)
filterName:指定该Filter的名称
urlPatterns / value:这两个属性的作用完全相同。都指定该Filter所拦截的URL
initParams:用于为该Filter配置参数
servletNames:该属性值指定多个Servlet的名称,用于指定该Filter仅对这几个Servlet执行过滤
displayName:指定该Filter的显示名
asyncSupported:指定该Filter是否支持异步操作模式。
dispatcherTypes:指定该Filter仅对那种dispatcher模式的请求进行过滤。该属性支持ASYNC、ERROR、FORWARD、INCLUDE、REQUEST这5个值的任何组合。默认值为同时过滤5种模式的请求。
3.2.2 使用示例
假设系统有包含多个Servlet,这些Servlet都需要进行一些的通用处理:比如权限控制、记录日志等,这将导致在这些Servlet的service方法中有部分代码是相同的——为了解决这种代码重复的问题,可以考虑把这些通用处理提取到Filter中完成,这样各Servlet中剩下的只是特定请求相关的处理代码,而通过处理则交给Filter完成。
由于Filter和Servlet如此相似,所以Filter和Servlet具有完全相同的生命周期行为,且Filter也可以通过 元素或@WebFilter的initParams属性来配置初始化参数,获取Filter的初始化参数则使用FilterConfig的getInitParameter()方法。
下面我们定一个较为实用的Filter,该Filter对用户请求进行过滤,Filter将通过doFilter方法来设置request编码的字符集,从而避免每个JSP、Servlet都需要设置;而且还会验证用户是否登录,如下:
Filter源码:
@WebFilter(filterName = "authority", urlPatterns = { "/*" }, initParams = {
@WebInitParam(name = "encoding", value = "GBK"), @WebInitParam(name = "loginPage", value = "/login.jsp") })
public class AuthorityFilter implements Filter {
// FilterConfig可用于访问Filter的配置信息
private FilterConfig config;
// 实现初始化方法
public void init(FilterConfig config) {
this.config = config;
}
// 实现销毁方法
public void destroy() {
this.config = null;
}
// 执行过滤的核心方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 获取该Filter的配置参数
String encoding = config.getInitParameter("encoding");
String loginPage = config.getInitParameter("loginPage");
// 设置request编码用的字符集
request.setCharacterEncoding(encoding);
HttpServletRequest requ = (HttpServletRequest) request;
HttpSession session = requ.getSession(true);
// 获取客户请求的页面
String requestPath = requ.getServletPath();
// 如果session范围的user为null,即表明没有登录
// 且用户请求的既不是登录页面,也不是处理登录的页面
if (session.getAttribute("user") == null && !requestPath.endsWith(loginPage)) {
// forward到登录页面
request.setAttribute("tip", "您还没有登录");
request.getRequestDispatcher(loginPage).forward(request, response);
}
// "放行"请求
else {
chain.doFilter(request, response);
}
}
}
当然也可以使用web.xml文件中配置该Filter,如下:
authority
lee.AuthorityFilter
encoding
GBK
loginPage
/login.jsp
authority
/*
login.jsp(可以写几个其它页面,以作测试)
登录页面
<%
if (request.getAttribute("tip") != null) {
out.println("" + request.getAttribute("tip") + " ");
}
%>
实现的效果是,如果没有的登录,则只能访问/login.jsp,反之则可以自由访问其他页面。
四、使用URL Rewrite实现网站伪静态
大部分搜索引擎都会优先考虑收录静态的HTML页面,而不是这种动态的*.jsp、*.php页面。但实际上绝大多数网站都是动态的,不可能全部是静态的HTML页面,因此互联网上的大部分网站都会考虑使用伪静态——就是*.jsp、*.php这种动态URL伪装成静态的HTML页面。
对于Java Web应用来说,要实现伪静态非常简单:可以通过Filter拦截所有发向*.html请求,然后按某种规则将请求forward到实际的*.jsp页面即可。现有的URL Rewrite开源项目为这种思路提供了实现,使用URL Rewrite实现网站伪静态也很简单。
4.1 利用URL Rewrite实现网站伪静态
<1> 下载URL Rewrite jar包
<2> 添加urlrewritefilter-4.0.3.jar 到 WEB-INF/lib目录下,或者添加Maven依赖如下:
org.tuckey
urlrewritefilter
4.0.3
<3> 在web.xml文件中配置启用URL Rewrite Filter,在web.xml文件中添加如下配置:
UrlRewriteFilter
org.tuckey.web.filters.urlrewrite.UrlRewriteFilter
UrlRewriteFilter
/*
REQUEST
FORWARD
注意:需要在所有的servlet mappings的上面。<4> 在应用的WEB-INF路径下增加urlrewrite.xml文件,该文件定义了伪静态映射规则,这份伪静态规则是基于正则表达式。
添加响应的urlrewrite.xml伪静态规则文件如下:
/userinf-(\w*).html
/userinf.jsp?username=$1
上面的规则文件中只定义了一个简单的规则:所有发向/userinf-(\w*).html的请求都将被forward到userinf.jsp,并将(\w*)正则表达式所匹配的内容作为username参数值。根据这个伪静态规则,需要为该应用提供了一个userinf.jsp页面,该页面只是一个模拟了一个显示用户信息的页面,页面代码如下:
<%@ page contentType="text/html; charset=GBK" language="java"
errorPage=""%>
<%
// 获取请求参数
String user = request.getParameter("username");
%>
<%=user%>的个人信息
<%
// 此处应该通过数据库读取该用户对应的信息
// 此处只是模拟,因此简单输出:
out.println("现在时间是:" + new java.util.Date() + " ");
out.println("用户名:" + user);
%>
五、Listener介绍
Servlet API提供了大量监听器来监听Web项目的内部事件,从而允许当Web内部事件发生时回调事件监听器内的方法。使用Listener只需要两个步骤:
定义Listener实现类
通过注解或web.xml文件中配置Listener
5.1 实现Listener类
常用的Web事件监听器接口有如下几个:
ServletContextListener:用于监听Web应用的启动和关闭
ServletContextAttributeListener:用于监听ServletContext范围(application)内属性的改变
ServletRequestListener:用于监听用户请求
ServletRequestAttributeListener:用于监听ServletRequest范围(request)内属性的改变
HttpSessionListener:用于监听用户session的开始和结束
HttpSessionAttributeListener:用于监听HttpSession范围(session)内属性的改变
5.2 配置Listener
配置Listener只要向Web应用注册Listener实现类即可,无须配置参数之类的东西,因此十分简单。为Web应用配置Listener也有两种方式:
使用@WebListener修饰Listener实现类即可
在web.xml文档中使用 元素进行配置
使用@WebListener时通常无须指定任何属性,只要使用该注解修饰Listener实现类即可向Web应用注册该监听器。在web.xml中使用 元素进行配置时只要配置如下子元素即可。
listener-class:指定Listener实现类
5.3 使用ServletContextListener
ServletContextListener 用于监听Web应用的启动和关闭,该接口包含如下两个方法:
contextInitialized(ServletContextEvent sce):启动Web应用时,系统调用Listener的该方法
contextDestroyed(ServletContextEvent sce):关闭Web应用时,系统调用Listener的该方法
示例:
@WebListener
public class GetConnListener implements ServletContextListener {
// 应该启动时,该方法被调用。
public void contextInitialized(ServletContextEvent sce) {
try {
// 取得该应用的ServletContext实例
ServletContext application = sce.getServletContext();
// 从配置参数中获取驱动
String driver = application.getInitParameter("driver");
// 从配置参数中获取数据库url
String url = application.getInitParameter("url");
// 从配置参数中获取用户名
String user = application.getInitParameter("user");
// 从配置参数中获取密码
String pass = application.getInitParameter("pass");
// 注册驱动
Class.forName(driver);
// 获取数据库连接
Connection conn = DriverManager.getConnection(url, user, pass);
// 将数据库连接设置成application范围内的属性
application.setAttribute("conn", conn);
} catch (Exception ex) {
System.out.println("Listener中获取数据库连接出现异常" + ex.getMessage());
}
}
// 应该关闭时,该方法被调用。
public void contextDestroyed(ServletContextEvent sce) {
// 取得该应用的ServletContext实例
ServletContext application = sce.getServletContext();
Connection conn = (Connection) application.getAttribute("conn");
// 关闭数据库连接
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
}
需要在web.xml文件中配置连接数据库的几个参数:
driver
com.mysql.jdbc.Driver
url
jdbc:mysql://localhost:3306/ceshi
user
root
pass
root
lee.GetConnListener
5.4 使用ServletContextAttributeListener
ServletContextAttributeListener用于监听ServletContext(application)范围内属性的变化,实现该接口的监听器需要实现如下三个方法:
attributeAdded(ServletContextAttributeEvent event):当程序把一个属性存入application范围时触发该方法
attributeRemoved(ServletContextAttributeEvent event):当程序把一个属性从application范围删除时触发该方法
attributeReplaced(ServletContextAttributeEvent event):当程序替换application范围内的属性时将触发该方法
示例:
@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
// 当程序向application范围添加属性时触发该方法
public void attributeAdded(ServletContextAttributeEvent event) {
ServletContext application = event.getServletContext();
// 获取添加的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(application + "范围内添加了名为" + name + ",值为" + value + "的属性!");
}
// 当程序从application范围删除属性时触发该方法
public void attributeRemoved(ServletContextAttributeEvent event) {
ServletContext application = event.getServletContext();
// 获取被删除的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(application + "范围内名为" + name + ",值为" + value + "的属性被删除了!");
}
// 当application范围的属性被替换时触发该方法
public void attributeReplaced(ServletContextAttributeEvent event) {
ServletContext application = event.getServletContext();
// 获取被替换的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(application + "范围内名为" + name + ",值为" + value + "的属性被替换了!");
}
}
5.5 使用ServletRequestListener和ServletRequestAttributeListener
ServletRequestListener用于监听用户请求的到达,实现该接口的监听器需要实现如下两个方法:
requestInitialized(ServletRequestEvent sre):用户请求到达、被初始化时触发该方法
requestDestroyed(ServletRequestEvent sre):用户请求结束、被销毁时触发该方法
ServletRequestAttributeListener则用于监听ServletRequest(request)范围内属性的变化,实现该接口的监听器需要实现attributeAdded()、attributeRemoved()、attributeReplaced()三个方法。ServletRequestAttributeListener与ServletContextAttributeListener的作用相似,都用于监听属性的改变,只是ServletRequestAttributeListener监听request范围内属性的改变。
@WebListener
public class RequestListener implements ServletRequestListener, ServletRequestAttributeListener {
// 当用户请求到达、被初始化时触发该方法
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
System.out.println("----发向" + request.getRequestURI() + "请求被初始化----");
}
// 当用户请求结束、被销毁时触发该方法
public void requestDestroyed(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
System.out.println("----发向" + request.getRequestURI() + "请求被销毁----");
}
// 当程序向request范围添加属性时触发该方法
public void attributeAdded(ServletRequestAttributeEvent event) {
ServletRequest request = event.getServletRequest();
// 获取添加的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(request + "范围内添加了名为" + name + ",值为" + value + "的属性!");
}
// 当程序从request范围删除属性时触发该方法
public void attributeRemoved(ServletRequestAttributeEvent event) {
ServletRequest request = event.getServletRequest();
// 获取被删除的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(request + "范围内名为" + name + ",值为" + value + "的属性被删除了!");
}
// 当request范围的属性被替换时触发该方法
public void attributeReplaced(ServletRequestAttributeEvent event) {
ServletRequest request = event.getServletRequest();
// 获取被替换的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(request + "范围内名为" + name + ",值为" + value + "的属性被替换了!");
}
}
5.6 使用HttpSessionListener 和HttpSessionAttributeListener
HttpSessionListener 用于监听用户session的创建和销毁,实现该接口的监听器需要实现如下两个方法:
sessionCreated(HttpSessionEvent se)
sessionDestroyed(HttpSessionEvent se)
示例:
@WebListener
public class OnlineListener implements HttpSessionListener {
// 当用户与服务器之间开始session时触发该方法
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
ServletContext application = session.getServletContext();
// 获取session ID
String sessionId = session.getId();
// 如果是一次新的会话
if (session.isNew()) {
String user = (String) session.getAttribute("user");
// 未登录用户当游客处理
user = (user == null) ? "游客" : user;
Map online = (Map) application.getAttribute("online");
if (online == null) {
online = new Hashtable();
}
// 将用户在线信息放入Map中
online.put(sessionId, user);
application.setAttribute("online", online);
}
}
// 当用户与服务器之间session断开时触发该方法
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
ServletContext application = session.getServletContext();
String sessionId = session.getId();
Map online = (Map) application.getAttribute("online");
if (online != null) {
// 删除该用户的在线信息
online.remove(sessionId);
}
application.setAttribute("online", online);
}
}
六、JSP2特性
JSP2主要增加了如下新特性:
直接配置JSP属性
表达式语言
简化的自定义标签API
Tag文件语法
如果需要使用JSP2语法,其web.xml文件必须使用Servlet2.4以上版本的配置文件。Servlet2.4配置文件的根元素写法如下:
我在写博客的时候常用的是Servlet3.1,其根元素写法可参考:web.xml部署描述符的例子
6.1 配置JSP属性
JSP属性定义使用 元素配置,主要包括如下4个方面:
是否允许使用表达式语言:使用 元素确定,默认值为false,即允许使用表达式语言
是否允许使用JSP脚本:使用 元素确定,默认值为false,即允许使用JSP脚本
声明JSP页面的编码:使用 元素确定,配置该元素后,可以代替每个页面里page指令contentType属性的charset部分
使用隐士包含:使用 和 元素确定,可以代替在每个页面里使用include编译指令来包含其他页面
注:此处隐式包含的作用与JSP提供的静态包含的作用相似。
6.2 表达式语言&Tag File
表达式语言:可参考《EL表达式—学习笔记》
Tag File支持:可参考《标准标签库JSTL——学习笔记》
6.3 Servlet3.0新特性
6.3.1 Servlet3.0的注解
Servlet3.0规范在javax.servlet.annotation包下提供了如下注解:
@WebServlet:用于修饰一个Servlet雷,用于部署Servlet类
@WebInitParam:用于与@WebServlet或@WebFilter一起使用,为Servlet、Filter配置参数
@WebListener:用于修饰Listener类,用于部署Listener类
@WebFilter:用于修饰Filter类,用于部署Filter类
@MultipartConfig:用于修饰Servlet,指定该Servlet将会负责处理multipart/form-data类型的请求(主要用于文件上传)
@ServletSecurity:这是一个与JAAS有关的注解,修饰Servlet指定该Servlet的安全与授权控制
@HttpConstraint:用于与@ServletSecurity一起使用,用于指定该Servlet的安全与授权控制
@HttpMethodConstraint:用于与@ServletSecurity一起使用,用于指定该Servlet的安全与授权控制
6.3.2 Servlet 3.0 的Web模块支持
Servlet 3.0为模块化开发提供了良好的支持,Servlet 3.0规范不再要求所有Web组件(如Servlet、Listener、Filter)都部署在web.xml文件中,而是允许采用“Web模块”来部署、管理它们。
一个Web模块通常对应于一个JAR包,这个JAR包有如下文件结构:
从上面的文件结构可以看出,Web模块与普通JAR的最大区别在于需要在META-INF目录下添加一个Web-fragment.xml文件,这个文件也被称为Web模块部署描述符。
web-fragment.xml文件与web.xml文件的作用、文件结构都基本相似,因为它们都用于部署、管理各种Web组件。只是web-fragment.xml用于部署、管理Web模块而已,但web-fragment.xml可以指定多个下面的两个元素:
:用于指定该模块的名称
:用于指定加载该Web模块的相对顺序
Web应用除了可按web-fragment.xml文件中指定的加载顺序来加载Web模块之外,还可以通过web.xml文件指定个Web模块加载的绝对顺序。在web.xml文件中指定的加载顺序将会覆盖Web模块中web-fragment.xml文件所指定的加载顺序。
假如在Web应用的web.xml文件中增加如下配置片段:
moudle-name
moudle-name
Servlet 3.0的Web模块支持为模块化开发、框架使用提供了巨大的方便,例如需要在Web应用中使用Web框架,这就只要将该框架的JAR包复制到Web应用中即可。因为这个JAR包的META-INF目录下可以通过web-fragment.xml文件来配置该框架所需要的Servlet、Listener、Filter等,从而避免修改Web应用的web.xml文件。Web模块支持对于模块化开发也有很大的帮助,开发者可以将不同模块的Web组件部署在不同的web-fragment.xml文件中,从而避免所有模块的配置、部署信息都写在web.xml文件中,这对以后的升级、维护将更加方便。
6.3.3 Servlet3.0提供的异步处理
在之前的Servlet规范中,如果Servlet作为控制器调用一个耗时的业务方法,nameServlet必须等到业务方法完全返回之后才会生成响应,这将使得Servlet对业务方法的调用变成一种阻塞式的调用,因此效率比较低。
Servlet 3.0规范引入了异步处理来解决这个问题,异步处理允许Servlet重新发起一条新线程去调用耗时的业务方法,这样就可避免等待。
Servlet 3.0的异步处理是通过AsyncContext类来处理的,Servlet可通过ServletRequest的如下连个方法开启异步调用、创建AsyncContext对象:
AsyncContext startAsync()
AsyncContext startAsync(ServletRequest,ServletResponse)
重复调用上面的方法将得到同一个AsyncContext对象,AsyncContext对象代表异步处理的上下文,它提供了一些工具方法,可完成设置异步调用的超时时长,dispatch用于请求、启动后台线程、获取request、response对象等功能。
示例:
创建异步处理的Servlet类:
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html;charset=GBK");
PrintWriter out = response.getWriter();
out.println("异步调用示例 ");
out.println("进入Servlet的时间:" + new java.util.Date() + ". ");
// 创建AsyncContext,开始异步调用
AsyncContext actx = request.startAsync();
// 设置异步调用的超时时长
actx.setTimeout(60 * 1000);
// 启动异步调用的线程,该线程以异步方式执行
actx.start(new GetBooksTarget(actx));
out.println("结束Servlet的时间:" + new java.util.Date() + ". ");
out.flush();
}
}
下面创建线程执行体代码:
public class GetBooksTarget implements Runnable {
private AsyncContext actx = null;
public GetBooksTarget(AsyncContext actx) {
this.actx = actx;
}
public void run() {
try {
// 等待5秒钟,以模拟业务方法的执行
Thread.sleep(5 * 1000);
ServletRequest request = actx.getRequest();
List books = new ArrayList();
books.add("疯狂Java讲义");
books.add("轻量级Java EE企业应用实战");
books.add("疯狂Ajax讲义");
request.setAttribute("books", books);
actx.dispatch("/async.jsp");
} catch (Exception e) {
e.printStackTrace();
}
}
}
该线程体让线程暂停5秒来模拟调用耗时的业务方法,最后调用AsyncContext的dispatch方法把请求dispatch到指定JSP页面。
被异步请求dispatch的目标页面需要指定session=“false”,表明该页面不会重新创建session。下面是async.jsp页面代码:
<%@ page contentType="text/html; charset=GBK" language="java"
session="false" isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
out.println("业务调用结束的时间:" + new java.util.Date());
if (request.isAsyncStarted()) {
// 完成异步调用
request.getAsyncContext().complete();
}
%>
上面使用注解的方式已经配置好,如果要使用web.xml配置,则需要如下配置:
async
lee.AsyncServlet
true
async
/async
对于支持异步调用的Servlet来说,当Servlet以异步方式启用新线程之后,该Servlet的执行不会被阻塞,该Servlet将可以向客户端浏览器生成响应——当新线程执行完成后,新线程生成的响应再次被送往客户端浏览器。
输出如下:
当Servlet启用异步调用的线程之后,该线程的执行过程对开发者是透明的。但在有些情况下,开发者需要了解该异步线程的执行细节,并针对特定的执行结果进行针对性处理,这可借助于Servlet3.0提供的异步监听器来实现。 异步监听器需要实现AsyncListener接口,实现该接口的监听器类需要实现如下4个方法:
onStartAsync(AsyncEvent event):当异步调用开始时触发该方法
onComplete(AsyncEvent event):当异步调用完成时触发该方法
onError(AsyncEvent event):当异步调用错误时触发该方法
onTimeout(AsyncEvent event):当异步调用超时时触发该方法
设计好异步监听器之后,需要通过AsyncContext来注册监听器,调用该对象的addListener()方法即可注册监听器。
Filter与Servlet具有很大的相似性,因此Servlet3.0规范支持在Filter中使用异步调用。在Filter中进行异步调用与在Servlet中进行异步调用的效果完全相似,因此不进行叙述。
6.3.4 改进的Servlet API
重大的改进包括:
HttpServletRequest增加了对文件上传的支持
ServletContext允许通过编程的方式动态注册Servlet、Filter
HttpServletRequest提供了如下两个方法来处理文件上传:
Part getPart(String name):根据名称来获取文件上传域
Collection getParts():获取所有的文件上传域
上面两个方法的返回值都涉及一个API:Part,每个Part对象对应于一个文件上传域,该对象提供了大量方法来访问上传文件的文件类型、大小、输入流等,并提供了一个write(String file)方法将上传文件写入服务器磁盘。
为了向服务器上传文件,需要在表单里使用 文件域,这个文件域会在HTML页面上产生一个单行文本框和一个“浏览”按钮,浏览者可通过该按钮选择需要上传的文件。除此之外,上传文件一定要为表单域设置enctype属性。
表单的enctype属性指定的是表单数据的编码方式,该属性有如下三个值:
application/x-www-form-urlencoded:这是默认的编码,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式
multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容封装到请求参数里
text/plain:这种编码方式当表单的action属性为mailto:URL的形式时比较方便,这种方式主要适用于直接通过表单发送邮件的形式
如果将enctype设置为application/x-www-form-urlencoded,或不设置enctype属性,提交表单时只会发送文件域的文本框里的字符串,也就是浏览者所选择文件的绝对路径,对服务器获取该文件在客户端上的绝对路径没有任何作用,因为服务器不可能访问客户机的文件系统。
示例如下:
创建上传页面upload.jsp
上面的页面中的表单需要设置enctype=“multipart/form-data”,这表明该表单可用于上传文件。上面表单中定义了两个表单域:一个普通的文本框,它将生成普通请求参数;一个文件上传域,它用于上传文件。对于传统的文件上传需要借助于common-fileupload等工具,处理起来极为复杂,借助于Servlet 3.0的API,处理文件上传将变得十分简单。如下面的Servlet代码:
@WebServlet(name = "upload", urlPatterns = { "/upload" })
@MultipartConfig
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html;charset=GBK");
PrintWriter out = response.getWriter();
request.setCharacterEncoding("GBK");
// 获取普通请求参数
String name = request.getParameter("name");
out.println("普通的name参数为:" + name + " ");
// 获取文件上传域
Part part = request.getPart("file");
// 获取上传文件的文件类型
out.println("上传文件的的类型为:" + part.getContentType() + " ");
// 获取上传文件的大小。
out.println("上传文件的的大小为:" + part.getSize() + " ");
// 获取该文件上传域的Header Name
Collection headerNames = part.getHeaderNames();
// 遍历文件上传域的Header Name、Value
for (String headerName : headerNames) {
out.println(headerName + "--->" + part.getHeader(headerName) + " ");
}
// 获取包含原始文件名的字符串
String fileNameInfo = part.getHeader("content-disposition");
// 提取上传文件的原始文件名
String fileName = fileNameInfo.substring(fileNameInfo.indexOf("filename=\"") + 10, fileNameInfo.length() - 1);
// 将上传的文件写入服务器
part.write(getServletContext().getRealPath("/uploadFiles") + "/" + fileName); // ①
}
}
上面Servlet使用了@MultipartConfig修饰,处理文件上传的Servlet应该使用该注解修饰。接下来该Servlet中HttpServletRequest就可通过getPart(String name)方法来获取文件上传域——就像获取普通请求参数一样。
注:上面的Servlet中将会把上传的文件保存到Web应用的根路径下的uploadFiles目录下,因此我们还需要在该Web应用的根目录下创建uploadFiles目录。 上面Servlet上传时保存的文件名直接使用了上传文件的原始文件名,在实际项目中一般不会这么做,因为可能多个用户可能上传同名的文件,这样将导致后面用户上传的文件覆盖前面用户上传的文件。在实际项目中可借助于java.util.UUID工具类生成文件名。
ServletContext则提供了如下方法来动态地注册Servlet、Filter,并允许动态设置Web应用的初始化参数:
多个重载的addServlet()方法:动态地注册Servlet
多个重载的addFilter()方法:动态地注册Filter
多个重载的addListener():动态地注册Listener
setInitParameter(String name,String value)方法:为Web应用设置初始化参数
6.4 Servlet 3.1 新增的非阻塞式IO
Servlet 3.1提供的非阻塞IO进行输入、输出,可以更好地提升性能:
ServletInputStream:Servlet用于读取数据的输入流
ServletOutputStream:Servlet用于输出数据的输出流
传统读取方式采用阻塞式IO——当Servlet读取浏览器提交的数据时,如果数据暂时不可用,或数据没有读取完成,Servlet当前所在线程将会被阻塞,无法继续向下执行。 从Servlet 3.1 开始,ServletInputStream新增了一个setReadListener(ReadListener readListener)方法,该方法允许以非阻塞IO读取数据,实现ReadListener监听器需要实现如下三个方法:
onAllDataRead():当所有数据读取完成时激发该方法
onDataAvailable():当有数据可用时激发该方法
onError(Throwable t):读取数据出现错误时激发该方法
类似地ServletOutputStream也提供了SetWriterListener()方法。
在Servlet中使用费阻塞IO非常简单,主要按如下步骤进行即可:
调用ServletRequest的startAsync()方法开启异步模式
通过ServletRequest获取ServletInputStream,并为ServletInputStream设置监听器(ReadListener实现类)
实现ReadListener接口来实现监听器,在该监听器的方法中以非阻塞方式读取数据。
示例如下:
创建Servlet类
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html;charset=GBK");
PrintWriter out = response.getWriter();
out.println("非阻塞IO示例 ");
out.println("进入Servlet的时间:" + new java.util.Date() + ". ");
// 创建AsyncContext,开始异步调用
AsyncContext context = request.startAsync();
// 设置异步调用的超时时长
context.setTimeout(60 * 1000);
ServletInputStream input = request.getInputStream();
// 为输入流注册监听器
input.setReadListener(new MyReadListener(input, context));
out.println("结束Servlet的时间:" + new java.util.Date() + ". ");
out.flush();
}
}
创建实现ReadListener接口的类
public class MyReadListener implements ReadListener {
private ServletInputStream input;
private AsyncContext context;
public MyReadListener(ServletInputStream input, AsyncContext context) {
this.input = input;
this.context = context;
}
@Override
public void onDataAvailable() {
System.out.println("数据可用!!");
try {
// 暂停5秒,模拟读取数据是一个耗时操作。
Thread.sleep(5000);
StringBuilder sb = new StringBuilder();
int len = -1;
byte[] buff = new byte[1024];
// 采用原始IO方式读取浏览器向Servlet提交的数据
while (input.isReady() && (len = input.read(buff)) > 0) {
String data = new String(buff, 0, len);
sb.append(data);
}
System.out.println(sb);
// 将数据设置为request范围的属性
context.getRequest().setAttribute("info", sb.toString());
// 转发到视图页面
context.dispatch("/async.jsp");
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
public void onAllDataRead() {
System.out.println("数据读取完成");
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
}
下面创建页面:
form.html
async.jsp
<%@ page contentType="text/html; charset=GBK" language="java"
session="false"%>
浏览器提交数据为:${info}
<%=new java.util.Date()%>
6.5 Tomcat 8的WebSock支持(暂未完成)
你可能感兴趣的:(Web开发)
java web开发实战经典 当当_java web项目实战开发
飞翔的酋长大人
java web开发实战经典 当当
本书以问题为导向,涵盖JavaWeb开发技术中所有的核心知识点;实战开发案例丰富,所有的知识案例都来源于企业真实项目,实战性和可操作性强;代码注释详细准确,结构简洁清晰;语言简明易懂,由浅入深地带你学会JavaWeb技术及应用。本书面向广大Java开发工程师和普通高校学生,可作为高等学校计算机及其相关专业Web编程技术课程教材,也可供从事JavaWeb应用开发的技术人员学习参考。¥80.00定价:
MutationObserver接口性能分析与优化:DOM监控利器背后的性能陷阱与内存危机
coding随想
JavaScript javascript 前端 开发语言
一、DOM的“哨兵”:MutationObserver的崛起在Web开发的江湖中,MutationObserver是一个低调却强大的角色。它像一位忠诚的哨兵,时刻监控着DOM树的风吹草动——属性变化、子节点增删、文本内容更新……开发者们用它来实现动态内容监听、表单验证、甚至自动化测试。然而,这位“哨兵”的背后,却隐藏着不容忽视的性能陷阱和内存危机。1.1MutationObserver的诞生背景在
开启 PHP 初阶之旅:解锁高效入门之道
API_Zevin
php 开发语言 人工智能 大数据 前端 python 后端
在当今动态网页开发领域,PHP作为一门久经沙场的服务器端脚本语言,以其强大功能与广泛适用性,持续吸引着无数初学者踏入编程殿堂。若你决心攻克PHP,为Web开发世界添砖加瓦,一系列精准策略与实用方法将成为你加速入门的得力伙伴,引领你穿越初期懵懂,迈向熟练驾驭的新征程。一、筑牢基础:语法地基稳扎稳打PHP语法虽具灵活性,初学者仍需系统研习,构建扎实根基。从变量声明起步,领会PHP变量“$”符号前缀特色
rabbitmq java 乱码,透彻分析和解决一切javaWeb项目乱码问题
前言乱码是我们在程序开发中经常碰到且让人头疼的一件事,尤其是我们在做javaweb开发,如果我们没有清楚乱码产生的原理,碰到乱码问题了就容易摸不着头脑,无从下手。乱码主要出现在两部分,如下:第一,浏览器通过表单提交到后台,如果表单内容有中文,那么后台收到的数据可能会出现乱码。第二,后端服务器需要返回给浏览器数据,如果数据中带有中文,那么浏览器上可能会显示乱码。接下来我们逐一分析乱码产生的原因,以及
JSP编程从入门到精通:现代Web开发与AI集成实战指南
AI编程员
001AI传统&编程语言 002AI编程工具汇总 003AI编程作品汇总 人工智能 AI编程 笔记 开发语言 深度学习
JSP编程从入门到精通:现代Web开发与AI集成实战指南一、JSP基础与环境搭建1.1JSP核心概念与工作原理JSP(JavaServerPages)是一种动态网页开发技术,通过在HTML中嵌入Java代码实现服务器端逻辑。其本质是Servlet的扩展,当客户端首次请求JSP页面时,服务器会将其编译为Servlet类(如index_jsp.java),再执行该类的_jspService()方法生成
Java过滤器与拦截器深度解析
目录概述过滤器Filter拦截器Interceptor执行流程图解核心区别对比代码实现示例使用场景最佳实践概述在JavaWeb开发中,过滤器(Filter)和拦截器(Interceptor)是两种重要的请求处理机制。它们都能够对HTTP请求进行预处理和后处理,但在实现方式、执行时机和应用场景上有着显著的区别。关键特征对比表特征过滤器(Filter)拦截器(Interceptor)️基于Servle
Java Web开发核心技术解析与实践指南
2301_81709812
java 前端 开发语言
目录一、JavaWeb开发基础1.1核心概念解析1.2开发环境搭建二、Servlet与JSP核心技术2.1Servlet编程深度解析生命周期管理(关键代码示例):核心对象关系:2.2JSP进阶开发技巧动态页面模板示例:三、Spring框架整合开发3.1SpringMVC核心原理3.2现代开发方案对比四、项目实战:图书管理系统4.1系统架构设计4.2性能优化策略五、学习路线建议一、JavaWeb开发
Python 的 GIL 时代即将终结,迈向真正的多线程时代
技术狂潮AI
Python开发实战 AI编程实战 AI应用实战 开发语言 GIL Python
Python功能强大、灵活且对程序员友好,广泛应用于从Web开发到机器学习的各个领域。根据引用次数最多的两项指标,Python甚至超越了Java和C等语言,成为最流行的编程语言。经过多年的流行,Python似乎势不可挡。但Python作为一种编程语言的未来发展至少面临一个重大障碍。它被称为GIL,即全局解释器锁,几十年来,Python开发人员一直试图将其从Python的默认实现中删除。虽然GIL在
Django 科普介绍:从入门到了解其核心魅力
在当今的Web开发领域,有许多优秀的框架助力开发者高效构建应用,而Django便是其中极具影响力的一员。作为一个高级PythonWeb框架,它以“电池已包含”的理念著称,为开发者提供了丰富的工具和功能,让Web开发变得更加简单、高效且安全。接下来,我们就详细了解一下Django。什么是Django?Django是一个基于Python的开源Web应用框架,于2005年首次发布。它遵循MVC(Mode
在vue项目中嵌入Python项目
钱亚锋
vue.js python 前端 javascript ecmascript
在Vue项目中嵌入Python项目在现代Web开发中,前后端分离的架构已成为一种流行趋势。前端使用现代化框架(如Vue.js)来构建用户界面,而后端则使用其他语言(如Python)来处理复杂的业务逻辑和数据库交互。将Python项目嵌入到Vue项目中,可以有效利用两种语言的优势,提升开发效率。本文将介绍如何在Vue项目中集成Python项目,并附带代码示例和可视化工具。流程概述在将Python项目
探索Comlink:解锁WebWorker的潜能
嵇殉嵘Eliza
探索Comlink:解锁WebWorker的潜能在现代Web开发中,响应速度和用户体验至关重要。Comlink,一个轻量级(仅1.1kB压缩后)的库,正是为了解决这一挑战而生。它通过简化WebWorker的使用方式,让并发处理变得前所未有的简单直接,从而让我们向更加流畅、高效的前端应用迈进一步。项目技术剖析Comlink作为一款基于postMessage的RPC实现工具,利用了ES6Proxies
kh dksl.php,php中取得URL的根域名的代码
克勒kk
kh dksl.php
php中取得URL的根域名的代码发布于2014-12-1310:14:08|121次阅读|评论:0|来源:网友投递PHP开源脚本语言PHP(外文名:HypertextPreprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,入门门槛较低,易于学习,使用广泛,主要适用于Web开发领域。PHP的文件后缀名为php。本文为大家讲解的是php
web开发学习日志1
奋斗的岑爸
之前在一个学习编程的网站花费一万多元,没多久就坚持不下去了,感觉自己虎头蛇尾(确切的说是虎头无尾)的性格一时间也无法改变。从此,对学习新东西彻底失去信心。上个月在微博上看吴主任转了一条微博,是关于某编程网站的推广视频,两位google开发者在2分钟左右时间里生动而清晰的介绍了编程里面“对象”这一概念,完全颠覆了我对视频教学的认知,原来编程课可以这样讲。二话不说,立马报了前端开发入门课程(webde
golang学习线路图
gopher.guo
golang go golang go语言
学习Go(Golang)开发,应该从基础语法开始,逐步深入到并发编程、网络编程、Web开发、微服务架构、项目部署与调试等方向。以下是一个系统性的学习路线和内容详解,适合从零到进阶的开发者参考:一、Go语言基础Go语言的安装与配置Go的安装包下载与环境变量配置(GOROOT,GOPATH,gomod)使用GoModules管理依赖基础语法变量与常量的定义(var,const,类型推断:=)数据类型:
Golang Gorilla框架入门指南:从零开始构建Web应用
GolangGorilla框架入门指南:从零开始构建Web应用关键词:Golang、Gorilla框架、Web开发、路由、中间件、RESTfulAPI、WebSocket摘要:本文将从零开始介绍如何使用Golang的Gorilla框架构建Web应用。我们将首先了解Gorilla框架的核心组件,然后通过实际案例演示如何构建一个完整的Web应用,包括路由设置、中间件使用、RESTfulAPI开发和We
Gorilla 在 Golang 中的实战应用:从入门到精通
Golang编程笔记
golang 开发语言 后端 ai
Gorilla在Golang中的实战应用:从入门到精通关键词:Gorilla,Golang,Web开发,路由,WebSocket,中间件,RESTfulAPI摘要:本文将深入探讨Gorilla工具包在GolangWeb开发中的实战应用。从基础概念到高级用法,我们将全面解析Gorilla的核心组件,包括mux路由、WebSocket、中间件等。通过详细的代码示例和实际项目案例,帮助开发者掌握Gori
Golang Gorilla 框架性能优化:10 个必知技巧
GolangGorilla框架性能优化:10个必知技巧关键词:Gorilla框架、性能优化、Go语言、路由匹配、中间件、内存管理、并发处理、HTTP服务、Web开发、实战技巧摘要:Gorilla是Go语言生态中最受欢迎的Web开发框架之一,广泛用于构建高并发API和实时应用(如WebSocket聊天)。但随着业务规模扩大,如何让Gorilla应用保持“丝滑”性能?本文将从路由优化、内存管理、并发设
如何入门搭建Laravel框架(教程)
随着互联网的发展,web开发的需求日益增长。在web开发领域,php语言一直扮演着重要的角色,而laravel就是php语言下一个备受欢迎的web开发框架。在本文中,我们将介绍如何入门搭建laravel框架。一、Laravel介绍Laravel是一个开源的PHPWeb框架,由TaylorOtwell在2011年创建。Laravel框架是基于MVC模式构建的,它提供了一系列灵活的工具和组件,能够帮助
PHP框架之Laravel框架
Laravel框架详解Laravel,作为一款广受欢迎的PHPWeb开发框架,以其优雅、简洁的语法和强大的功能特性,赢得了全球众多开发者的青睐。下面,我们将从Laravel的特点、应用案例以及具体的框架使用等方面进行详细解析。一、Laravel框架的特点优雅的语法Laravel框架采用简洁、优雅的语法,使得开发者能够编写出易于阅读和维护的代码。例如,通过EloquentORM,开发者可以方便地进行
请求拦截器,响应拦截器,都是些啥呀,我好懵逼呀!
请求拦截器,响应拦截器,都是些啥呀,我好懵逼呀!别慌!“请求拦截器”和“响应拦截器”其实是Web开发中用于干预请求和响应流程的组件,目的是在请求到达目标接口(比如Controller)之前、或响应返回给用户之前,做一些通用处理(比如日志记录、权限校验、数据修改等)。它们的核心区别就在于拦截的时机不同,下面用生活化的例子和技术细节帮你理清:一、先搞懂:什么是“请求”和“响应”?请求(Request)
《Effective Python》总结
不学无术の码农
Effective Python 精读笔记 python 开发语言
引言Python以其简洁的语法、强大的标准库和广泛的应用场景,成为编程领域的支柱语言之一。从Web开发、数据科学到自动化脚本和分布式系统,Python的灵活性使其在各类项目中大放异彩。然而,编写高效、可读性强且易于维护的Python代码并非易事,需要深入理解语言的设计哲学、最佳实践和现代特性。《EffectivePython:125SpecificWaystoWriteBetterPython,3
WebSocket:构建实时交互的 Web 应用
IsPrisoner
websocket 交互 前端 golang 网络
在现代Web开发中,实现实时交互功能是提升用户体验的重要一环。无论是实时聊天、在线游戏、实时协作编辑,还是实时数据监控等场景,都需要一种能够支持客户端与服务器之间双向通信的技术。而WebSocket正是这样一种强大的技术,它突破了传统HTTP协议单向通信的限制,为Web应用的实时交互提供了可能。在众多编程语言中,Go语言以其简洁高效、并发友好等特性,在构建高性能的WebSocket服务端方面具有独
Gin 框架中如何实现 JWT 鉴权中间件
IsPrisoner
gin 中间件 golang JWT
在现代Web开发中,安全地验证用户身份是构建可靠应用程序的关键环节。JWT(JSONWebToken)作为一种流行的认证方式,因其简洁、高效和易于扩展等特性,被广泛应用于许多应用中。而Gin框架作为Go语言中一个高性能的HTTPWeb框架,在构建微服务和RESTfulAPI应用中表现卓越。将JWT鉴权集成到Gin框架中,可以为我们的应用提供更安全的用户认证与授权机制。本篇博客将深入探讨如何在Gin
推荐项目:Hugo Theme Stack 快速启动模板 —— 构建静态站点的高效起点
汤中岱Wonderful
推荐项目:HugoThemeStack快速启动模板——构建静态站点的高效起点在快速迭代的Web开发领域中,找到一个既高效又灵活的博客或网站搭建方案至关重要。今天,我们向您隆重推荐【HugoThemeStackStarterTemplate】——一款基于[Hugo框架]和[ThemeStack]设计的快速启动模板。它不仅简化了网站构建过程,还通过集成现代工具链,让您的个人站点或小企业网站能够在瞬间起
PHP语言基础
一.PHP介绍1.什么是PHPPHP是超文本预处理器。是运行在服务端的开源语言,它可以让Web开发人员快速的书写生成动态的页面。--->PHP是服务端语言(后端)语言HTML+JS为前端语言:主要用来对页面的布局,动态美化PHP,JAVA,Python为后端语言:用来实现业务逻辑功能,一般不做美化2.PHP作用(1)PHP可以生成动态页面(2)PHP可以用来接收于发送Cookies值(3)PHP可
python怎么学?
靓仔668
python 开发语言 青少年编程
一、python是什么?Python是一种高级、解释型、面向对象的编程语言。它具有简洁的语法和易于阅读的代码,被广泛用于计算机编程和科学计算。Python支持多种编程范式,包括面向过程、面向对象和函数式编程。它拥有广泛的标准库和第三方库,可以用于各种任务,例如Web开发、数据分析、人工智能等。Python的设计思想强调代码的可读性和简洁性,使得它成为许多新手和专业开发者的首选编程语言。二、使用步骤
Flask框架全面详解
PythonicCC
flask python 后端
Flask是一个轻量级的PythonWeb框架,因其简洁、灵活和可扩展性而广受开发者喜爱。本文将全面介绍Flask框架的各个方面,从基础概念到高级应用,帮助初学者快速掌握Flask开发。一、Flask基础概念1.1什么是Flask?Flask是一个基于Python的微型Web开发框架,诞生于2010年,由ArminRonacher开发。它的核心思想是保持简单但易于扩展。Flask的特点:轻量级:核
三种主流数据库特点和作用(看看你用的哪种?)
恩比贤AmbitioN
算法 java spring maven spring boot
以下是对MySQL、MongoDB和Redis三种主流数据库的详细介绍,涵盖其特点、优势以及适用场景,内容尽量丰富以满足需求。1.MySQL特点MySQL是一种开源的关系型数据库管理系统(RDBMS),基于表格存储数据,使用结构化查询语言(SQL)进行操作。它最初由瑞典公司MySQLAB开发,现由Oracle维护。MySQL以其高性能、可靠性和易用性闻名,广泛应用于Web开发、企业应用和数据分析场
排名前十的编程语言及其详细对比
NurDroid
开发语言
根据2025年4月的最新TIOBE排行榜以及其他综合榜单,当前排名前十的编程语言及其详细对比如下:1.Python•排名:第1位•核心特点:简洁语法、动态类型、丰富的生态库(如NumPy、TensorFlow)。•应用领域:AI/机器学习、数据分析、自动化脚本、Web开发(Django/Flask框架)。•性能:解释型语言,执行速度较慢,但开发效率极高,适合快速原型设计。•趋势:持续领跑AI领域,
Django Ninja 路由系统详解:模块化API开发指南
DjangoNinja路由系统详解:模块化API开发指南引言在现代Web开发中,良好的代码组织架构至关重要。DjangoNinja作为高性能的API框架,提供了强大的路由系统,帮助开发者实现API的模块化管理。本文将深入解析DjangoNinja的路由机制,展示如何构建清晰、可维护的API结构。基础路由配置项目结构规划典型的Django项目通常包含多个应用,每个应用处理特定的业务逻辑。Django
java解析APK
3213213333332132
java apk linux 解析APK
解析apk有两种方法
1、结合安卓提供apktool工具,用java执行cmd解析命令获取apk信息
2、利用相关jar包里的集成方法解析apk
这里只给出第二种方法,因为第一种方法在linux服务器下会出现不在控制范围之内的结果。
public class ApkUtil
{
/**
* 日志对象
*/
private static Logger
nginx自定义ip访问N种方法
ronin47
nginx 禁止ip访问
因业务需要,禁止一部分内网访问接口, 由于前端架了F5,直接用deny或allow是不行的,这是因为直接获取的前端F5的地址。
所以开始思考有哪些主案可以实现这样的需求,目前可实施的是三种:
一:把ip段放在redis里,写一段lua
二:利用geo传递变量,写一段
mysql timestamp类型字段的CURRENT_TIMESTAMP与ON UPDATE CURRENT_TIMESTAMP属性
dcj3sjt126com
mysql
timestamp有两个属性,分别是CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP两种,使用情况分别如下:
1.
CURRENT_TIMESTAMP
当要向数据库执行insert操作时,如果有个timestamp字段属性设为
CURRENT_TIMESTAMP,则无论这
struts2+spring+hibernate分页显示
171815164
Hibernate
分页显示一直是web开发中一大烦琐的难题,传统的网页设计只在一个JSP或者ASP页面中书写所有关于数据库操作的代码,那样做分页可能简单一点,但当把网站分层开发后,分页就比较困难了,下面是我做Spring+Hibernate+Struts2项目时设计的分页代码,与大家分享交流。
1、DAO层接口的设计,在MemberDao接口中定义了如下两个方法:
public in
构建自己的Wrapper应用
g21121
rap
我们已经了解Wrapper的目录结构,下面可是正式利用Wrapper来包装我们自己的应用,这里假设Wrapper的安装目录为:/usr/local/wrapper。
首先,创建项目应用
&nb
[简单]工作记录_多线程相关
53873039oycg
多线程
最近遇到多线程的问题,原来使用异步请求多个接口(n*3次请求) 方案一 使用多线程一次返回数据,最开始是使用5个线程,一个线程顺序请求3个接口,超时终止返回 缺点 测试发现必须3个接
调试jdk中的源码,查看jdk局部变量
程序员是怎么炼成的
jdk 源码
转自:http://www.douban.com/note/211369821/
学习jdk源码时使用--
学习java最好的办法就是看jdk源代码,面对浩瀚的jdk(光源码就有40M多,比一个大型网站的源码都多)从何入手呢,要是能单步调试跟进到jdk源码里并且能查看其中的局部变量最好了。
可惜的是sun提供的jdk并不能查看运行中的局部变量
Oracle RAC Failover 详解
aijuans
oracle
Oracle RAC 同时具备HA(High Availiablity) 和LB(LoadBalance). 而其高可用性的基础就是Failover(故障转移). 它指集群中任何一个节点的故障都不会影响用户的使用,连接到故障节点的用户会被自动转移到健康节点,从用户感受而言, 是感觉不到这种切换。
Oracle 10g RAC 的Failover 可以分为3种:
1. Client-Si
form表单提交数据编码方式及tomcat的接受编码方式
antonyup_2006
JavaScript tomcat 浏览器 互联网 servlet
原帖地址:http://www.iteye.com/topic/266705
form有2中方法把数据提交给服务器,get和post,分别说下吧。
(一)get提交
1.首先说下客户端(浏览器)的form表单用get方法是如何将数据编码后提交给服务器端的吧。
对于get方法来说,都是把数据串联在请求的url后面作为参数,如:http://localhost:
JS初学者必知的基础
百合不是茶
js函数 js入门基础
JavaScript是网页的交互语言,实现网页的各种效果,
JavaScript 是世界上最流行的脚本语言。
JavaScript 是属于 web 的语言,它适用于 PC、笔记本电脑、平板电脑和移动电话。
JavaScript 被设计为向 HTML 页面增加交互性。
许多 HTML 开发者都不是程序员,但是 JavaScript 却拥有非常简单的语法。几乎每个人都有能力将小的
iBatis的分页分析与详解
bijian1013
java ibatis
分页是操作数据库型系统常遇到的问题。分页实现方法很多,但效率的差异就很大了。iBatis是通过什么方式来实现这个分页的了。查看它的实现部分,发现返回的PaginatedList实际上是个接口,实现这个接口的是PaginatedDataList类的对象,查看PaginatedDataList类发现,每次翻页的时候最
精通Oracle10编程SQL(15)使用对象类型
bijian1013
oracle 数据库 plsql
/*
*使用对象类型
*/
--建立和使用简单对象类型
--对象类型包括对象类型规范和对象类型体两部分。
--建立和使用不包含任何方法的对象类型
CREATE OR REPLACE TYPE person_typ1 as OBJECT(
name varchar2(10),gender varchar2(4),birthdate date
);
drop type p
【Linux命令二】文本处理命令awk
bit1129
linux命令
awk是Linux用来进行文本处理的命令,在日常工作中,广泛应用于日志分析。awk是一门解释型编程语言,包含变量,数组,循环控制结构,条件控制结构等。它的语法采用类C语言的语法。
awk命令用来做什么?
1.awk适用于具有一定结构的文本行,对其中的列进行提取信息
2.awk可以把当前正在处理的文本行提交给Linux的其它命令处理,然后把直接结构返回给awk
3.awk实际工
JAVA(ssh2框架)+Flex实现权限控制方案分析
白糖_
java
目前项目使用的是Struts2+Hibernate+Spring的架构模式,目前已经有一套针对SSH2的权限系统,运行良好。但是项目有了新需求:在目前系统的基础上使用Flex逐步取代JSP,在取代JSP过程中可能存在Flex与JSP并存的情况,所以权限系统需要进行修改。
【SSH2权限系统的实现机制】
权限控制分为页面和后台两块:不同类型用户的帐号分配的访问权限是不同的,用户使
angular.forEach
boyitech
AngularJS AngularJS API angular.forEach
angular.forEach 描述: 循环对obj对象的每个元素调用iterator, obj对象可以是一个Object或一个Array. Iterator函数调用方法: iterator(value, key, obj), 其中obj是被迭代对象,key是obj的property key或者是数组的index,value就是相应的值啦. (此函数不能够迭代继承的属性.)
java-谷歌面试题-给定一个排序数组,如何构造一个二叉排序树
bylijinnan
二叉排序树
import java.util.LinkedList;
public class CreateBSTfromSortedArray {
/**
* 题目:给定一个排序数组,如何构造一个二叉排序树
* 递归
*/
public static void main(String[] args) {
int[] data = { 1, 2, 3, 4,
action执行2次
Chen.H
JavaScript jsp XHTML css Webwork
xwork 写道 <action name="userTypeAction"
class="com.ekangcount.website.system.view.action.UserTypeAction">
<result name="ssss" type="dispatcher">
[时空与能量]逆转时空需要消耗大量能源
comsci
能源
无论如何,人类始终都想摆脱时间和空间的限制....但是受到质量与能量关系的限制,我们人类在目前和今后很长一段时间内,都无法获得大量廉价的能源来进行时空跨越.....
在进行时空穿梭的实验中,消耗超大规模的能源是必然
oracle的正则表达式(regular expression)详细介绍
daizj
oracle 正则表达式
正则表达式是很多编程语言中都有的。可惜oracle8i、oracle9i中一直迟迟不肯加入,好在oracle10g中终于增加了期盼已久的正则表达式功能。你可以在oracle10g中使用正则表达式肆意地匹配你想匹配的任何字符串了。
正则表达式中常用到的元数据(metacharacter)如下:
^ 匹配字符串的开头位置。
$ 匹配支付传的结尾位置。
*
报表工具与报表性能的关系
datamachine
报表工具 birt 报表性能 润乾报表
在选择报表工具时,性能一直是用户关心的指标,但是,报表工具的性能和整个报表系统的性能有多大关系呢?
要回答这个问题,首先要分析一下报表的处理过程包含哪些环节,哪些环节容易出现性能瓶颈,如何优化这些环节。
一、报表处理的一般过程分析
1、用户选择报表输入参数后,报表引擎会根据报表模板和输入参数来解析报表,并将数据计算和读取请求以SQL的方式发送给数据库。
2、
初一上学期难记忆单词背诵第一课
dcj3sjt126com
word english
what 什么
your 你
name 名字
my 我的
am 是
one 一
two 二
three 三
four 四
five 五
class 班级,课
six 六
seven 七
eight 八
nince 九
ten 十
zero 零
how 怎样
old 老的
eleven 十一
twelve 十二
thirteen
我学过和准备学的各种技术
dcj3sjt126com
技术
语言VB https://msdn.microsoft.com/zh-cn/library/2x7h1hfk.aspxJava http://docs.oracle.com/javase/8/C# https://msdn.microsoft.com/library/vstudioPHP http://php.net/manual/en/Html
struts2中token防止重复提交表单
蕃薯耀
重复提交表单 struts2中token
struts2中token防止重复提交表单
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
蕃薯耀 2015年7月12日 11:52:32 星期日
ht
线性查找二维数组
hao3100590
二维数组
1.算法描述
有序(行有序,列有序,且每行从左至右递增,列从上至下递增)二维数组查找,要求复杂度O(n)
2.使用到的相关知识:
结构体定义和使用,二维数组传递(http://blog.csdn.net/yzhhmhm/article/details/2045816)
3.使用数组名传递
这个的不便之处很明显,一旦确定就是不能设置列值
//使
spring security 3中推荐使用BCrypt算法加密密码
jackyrong
Spring Security
spring security 3中推荐使用BCrypt算法加密密码了,以前使用的是md5,
Md5PasswordEncoder 和 ShaPasswordEncoder,现在不推荐了,推荐用bcrpt
Bcrpt中的salt可以是随机的,比如:
int i = 0;
while (i < 10) {
String password = "1234
学习编程并不难,做到以下几点即可!
lampcy
java html 编程语言
不论你是想自己设计游戏,还是开发iPhone或安卓手机上的应用,还是仅仅为了娱乐,学习编程语言都是一条必经之路。编程语言种类繁多,用途各 异,然而一旦掌握其中之一,其他的也就迎刃而解。作为初学者,你可能要先从Java或HTML开始学,一旦掌握了一门编程语言,你就发挥无穷的想象,开发 各种神奇的软件啦。
1、确定目标
学习编程语言既充满乐趣,又充满挑战。有些花费多年时间学习一门编程语言的大学生到
架构师之mysql----------------用group+inner join,left join ,right join 查重复数据(替代in)
nannan408
right join
1.前言。
如题。
2.代码
(1)单表查重复数据,根据a分组
SELECT m.a,m.b, INNER JOIN (select a,b,COUNT(*) AS rank FROM test.`A` A GROUP BY a HAVING rank>1 )k ON m.a=k.a
(2)多表查询 ,
使用改为le
jQuery选择器小结 VS 节点查找(附css的一些东西)
Everyday都不同
jquery css name选择器 追加元素 查找节点
最近做前端页面,频繁用到一些jQuery的选择器,所以特意来总结一下:
测试页面:
<html>
<head>
<script src="jquery-1.7.2.min.js"></script>
<script>
/*$(function() {
$(documen
关于EXT
tntxia
ext
ExtJS是一个很不错的Ajax框架,可以用来开发带有华丽外观的富客户端应用,使得我们的b/s应用更加具有活力及生命力。ExtJS是一个用 javascript编写,与后台技术无关的前端ajax框架。因此,可以把ExtJS用在.Net、Java、Php等各种开发语言开发的应用中。
ExtJs最开始基于YUI技术,由开发人员Jack
一个MIT计算机博士对数学的思考
xjnine
Math
在过去的一年中,我一直在数学的海洋中游荡,research进展不多,对于数学世界的阅历算是有了一些长进。为什么要深入数学的世界?作为计算机的学生,我没有任何企图要成为一个数学家。我学习数学的目的,是要想爬上巨人的肩膀,希望站在更高的高度,能把我自己研究的东西看得更深广一些。说起来,我在刚来这个学校的时候,并没有预料到我将会有一个深入数学的旅程。我的导师最初希望我去做的题目,是对appe