JSP&&Servlet

阅读更多
JSP
对forward的行为的汇总:
1.控制的转移完全在服务器上进行。不涉及任何网络数据流。
2.用户不会看到目的JSP页面的地址,而且还可以将页面放在WEB-INF中,防止用户不经过建立数据的servlet直接访问页面。如果JSP页面只在由servlet生成的数据上下文中才有意义,则更应该这样做。

对sendRedirect的汇总:
1.控制的转移通过向客户发送302状态码和Location响应报头来完成。转移需要另外的网络往返。
2.用户能够看到目的页面的地址,并可以记下来,独立地访问。如果将JSP设计为数据缺失时使用默认值,这种方式比较适用。

基于request的数据共享
ValueObject value = new ValueObject();
request.setAttribute("key", value);
RequestDispatcher dispatcher = request.getRequestDispatcher(somepath);
dispatcher.forward(request, response);




基于session的数据共享
ValueObject value = new ValueObject();
HttpSession session = request.getSession();
session.setAttribute("key", value);
RequestDispatcher dispatcher = request.getRequestDispatcher(somepath);
dispatcher.forward(request, response);




基于application的数据共享
synchronized(this){
    ValueObject value = new ValueObject();
    getServletContext().setAttribute("key", value);
    RequestDispatcher dispatcher = request.getRequestDispatcher(somepath);
    dispatcher.forward(request, response);
}




Servlet
在web.xml中

ParamTest
TestInitParams

adminEmail
[email protected]



在servlet文件中
out.println(getServletConfig().getInitParameter("adminEmail"));

每个servlet 都继承一个getServletConfig()方法
上面会输出[email protected]
一旦生成ServletConfig引用,你能使用getInitParameter()方法。记住不能在构造体内调用。
容器从DD文件中读取Servlet init parameters,然后传给ServletConfig对象,再把ServletConfig传递到Servlet中的init()方法去。

ServletConfig
1、String getInitParameter(String)
2、Enumeration getInitParameterNames()
3、ServletContext getServletContext()
4、String getServletName()

在web.xml中

ParamTest
TestInitParams


adminEmail
[email protected]


在servlet文件中
out.println(getServletContext().getInitParameter("adminEmail"));
ServletContext 是每个web-app一个
ServletConfig 是每个servlet一个
在整个web app中有且只有一个ServletContext,web app的所有部份都共享它。
但一个app 中的每个servlet都有自己的ServletConfig。
当web app部署时容器生成一个ServletContext,同时使这个ServletContext对这个web app里的每个Servlet和JSP都可用。
可以通过两种方法获取ServletContext
getServletConfig().getServletContext().getInitParameter();

this.getServletContext().getInitParameter();

<>javax.servlet.ServletContextListener
  contextInitialized(ServletContextEvent);
  contextDestroyed(ServletContextEvent);

当ServletContext初始化(即app被部署)时引起注意:
1、从ServletContext中获取context init paramete。
2、使用init parameter 去查找名字创建数据库连接。
3、把数据库连接当作属性来存储,以致整个web app都可以访问到。

当ServletContext被破坏(即app被部署)时引起注意:
1、关闭数据库连接

import javax.servlet.*;
public class MyServletContextListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent event){
//code to initialize the database connection
//and store it as a context attribute
}
public void contextDestroyed(ServletContextEvent event){
//code to close the database connection
}
}

这个例子中,把字符串类型的init parameter 变为一个对象--Dog对象。
listener的工作是为Dog的breed()方法获取context init parameter。
⑴、listener向ServletContextEvent对象请求一个引用到app ServletContext。
ServletContext sc = event.getServletContext();
⑵、listener使用这个引用来获取context init parameter "breed",它是表示dog breed的字符串。
String dogBreed = sc.getInitParameter("breed");
⑶、listener使用此字符串来构造一个Dog对象。
Dog dog = new Dog(dogBreed);
⑷、listener用之前的引用将Dog对象保存在ServletContext属性中。
sc.setAttribute("dog", dog);
⑸、测试的Servlet从ServletContext中获取Dog对象,并调用Dog类的getBreed()方法。

构建和使用context listener
1、创建listener类,实现ServletContextListener接口 (实现接口中的两个方法)。
2、将这个类放置于WEB-INF/classes目录中 (这并不是唯一的地方)。
3、在部署描述器web.xml中增加一个元素。(置于元素下)
import javax.servlet.*;
public class MyServletContextListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent event){
ServletContext sc = event.getServletContext();
String dogBreed = sc.getInitParameter();
Dog dog = new Dog(dogBreed);
sc.setAttribute("dog", dog);
}
public void contextDestroyed(ServletContextEvent event){
//do nothing
}
}

public class Dog{
private String breed;
public Dog(String breed){
this.breed = breed;
}
public String getBreed(){
return breed;
}
}

public class ListenerTester extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("test servlet listener");
out.println("
");
Dog dog = (Dog)getServletContext().getAttribute("dog");
out.println("The dog's breed is "+dog.getBreed());
}
}

HttpSessionAttributeListener 知道session中任何一种attribute 何时被增加,删除或代替。
HttpSessionBuildingListener 使得session中attribute本身能够知道自己何时被增加或删除。

Context scope 不是线程安全的。
一个app中的每个人都能够访问context attribute,就是说多servlets。多servlets意味着多线程。因为请求是并发处理的,每个发生在不同的线程,不管请求来自相同还是不同的servlet。
保护context attribute的典型方法是对context 对象进行同步(synchronize)。如果之前的人锁住了context对象,则能保证一个时刻只有一个线程能够对context attribute进行getter、setter操作。
synchronized(getServletContext()){
}
Session attribute也不是线程安全的。
synchronized(request.getSession()){
}

只有request attribute和local variables是线程安全的。
You can get a RequestDispatcher in two ways: from the request or from the context.

RequestDispatcher view = request.getRequestDispatcher("result.jsp");
and
RequestDispatcher view = getServletContext().getRequestDispatcher("/result.jsp");
不能是相对于当前资源的路径

So, don't be fooled if you see questions that forward a request after a response is sent. The Container will throw an illegalStateException.

The idea is simple: on the client's first request, the Container generates a unique session ID and gives it back to the client with the response. The Container sees the ID, finds the matching session, and associates the session with the request.

request.getSession(false) false 表示返回一个已经存在的session,或如果没有session则是null。
A client with cookies disabled will ignore "Set-Cookie" response headers.
如果用户禁用cookie,可以用URL回写技术。
out.println("Click me");
重定向时用回写:response.encodeRedirectURL("test.do");

Filter 能做的事情
Request:
perform security checks
reformat request headers or bodies
audit or log requests

Response:
compress the response stream
append or alter the response stream
create a different response altogether

url-pattern:必须以/开头
如:/login.do对应的路径:/工程名/login.do
在html的form表单中,则是
,浏览器会自动把"/工程名/"加上去的。
JDBC
next/pervious
将ResultSet中的游标分别移动到下一行(任何JDBC版本)或前一行(JDBC版本2.0及之后的版本)

relative/absolute
relative 方法将游标相对地移动特定数目的行,或正或负(向前或向后)。absolute方法将游标移动到给定的行号。如果absolute是负数,那么游标将相对于ResultSet的结尾进行定位(JDBC2.0)。

findColumn
返回ResultSet中与指定列名对应的索引。

getRow
返回当前的行号。第一行从1开始(JDBC2.0)。

getMetaData
返回描述ResultSet的ResultSetMetaData 对象。ResultSetMetaData给出列的数目和名称。

getMetaData方法尤为有用。仅仅有ResultSet的情况下,我们必须知道列的名称、数目和类型才能正确地对表进行处理。对于大部分固定格式的查询,这也是合理的预期情况。但是,对于某些特定的查询,如果能够动态地得出与结果相关的高级信息会更有效率。这就是ResultSetMetaData 类的作用:通过它,我们能够确定ResultSet中列的数目、名称和类型。

ResultSetMetaData对象:
getColumnCount
返回ResultSet中列的数目。

getColumnName
返回列在数据库中的名称(索引从1开始)。

getColumnType
返回列的SQL类型,对应java.sql.Types中的项。

isReadOnly
表示数据项是否为只读值。

isSearchable
表明该列是否可以用在where 子句中。

isNullable
表明该列是否可以在存储NULL。

ResultSet和ResultSetMetaData没有直接提供方法返回查询所返回的行数。然而,在JDBC2.0中,可以调用last将游标定位到ResultSet的最后一行,然后调用getRow获取当前的行号。

DocumentBuilderFactory
DocumentBuilder
Document
Element
NodeList
Node

如果需要多次执行类似的语句,使用参数化(预备)语句要比每次都执行原始的查询更有效率。这种做法的主旨是,首先按照标准的格式创建参数化语句,在实际使用之前先发送到数据库进行编译。

然而性能并非是预备语句的唯一优点。安全是预备语句的另一项优点。
聪明的攻击者可能提交特殊的表单值,如果它们得到执行,那么攻击者可能会对数据库做出不适当的访问和修改。经常将这种安全风险称为SQL注入攻击(SQL Injection Attack)。除此之外,预备语句还能正确地处理嵌入在字符串中的引号以及处理非字符数据(比如向数据库发送序列化后的对象)。

存储过程有许多优点。例如,语法错误可以在编译时找出来,而非在运行期间;数据库过程的运行可能要比常规SQL查询快得多;程序员只需知道输入和输出参数,不需了解表的结构。另外,数据库语言能够访问数据库本地的一些功能(序列、触发器、多重游标)。
缺点。一:需要学习新的数据库专有语言。二:存储过程的商业逻辑在数据库服务器上执行,而非客户机或Web服务器。
行业的发展均势是尽可能多地将商业逻辑移出数据库,将它们放入JavaBean组件(或在大型系统中,Enterprise JavaBean组件)中,在Web服务器上执行。在Web构架中采用这种方式的主要动机是,数据库访问和网络I/O常常是性能的瓶颈。

调用数据库中的存储过程:
(1)定义对数据库过程的调用。
无参数过程  { call procedure_name }
仅有输入参数过程  { call procedure_name(?, ?, ...) }
有一个输出参数的过程  { ? call procedure_name }
既有输入参数又有输出参数的过程  { ? = call procedure_name(?, ?, ...) }

procedure_name 指数据库中存储过程的名称。注意,过程可能返回多个输出参数,并且,参数 的索引值从输出参数开始。因此,前面最后一个例子中,第一个输入参数的索引值是2(不是1).
(2)为这个过程准备CallableStatement。
用prepareCall方法,从Connection获得CallableStatement,如下所示。
String procedure = "{ ? call procedure_name( ?, ? ) }";
CallableStatement statement = connection.prepareCall(procedure);
(3)注册输出参数的类型。
必须使用registerOutParameter注册每个输出参数的JDBC类型,如下所示。
statement.registerOutParameter(n, type);
其中n对应输出参数的序号(使用以1为基的索引),type对应java.sql.Types类中定义的常量。
(4)为输入参数提供值。
在执行存储过程之前,需要调用与所要设置的项以及参数的类型相对应的setXxx(例如setInt, setString),替换标记出来的输入参数。如
statement.setString(2, "name");
statement.setFloat(3, 26.0F);
切记,如果过程拥有输出参数,输入参数的索引从第一个输出参数开始计起。
(5)执行这个存储过程。
statement.execute();
(6)访问返回的输出参数。
如果过程返回输出参数,那么在调用execute之后,可以通过调用getXxx(getDouble, getDate等 等)访问每个对应的输出参数,其中的Xxx对应返回参数的类型。例如,
int value = statement.getInt(1);

事务管理:
事务管理可以帮助确保数据库中表的一致性。数据库连接的默认设置为自动交付:即每个已执行的语句都自动提交给数据库。为了进行事务管理,首先要调用setAutoCommit(false)关闭连接的自动提交。
Connection conn = DriverManager.getConnection(url, username, password);
boolean autoCommit = conn.getAutoCommit();
Statement statement;
try{
conn.setAutoCommit(false);
statement = conn.createStatement();
statement.execute(...);
...
conn.commit();
}catch(SQLException sqle){
conn.rollback();
}finally{
statement.close();
conn.setAutoCommit(autoCommit);
}

你可能感兴趣的:(Servlet,JSP,Web,多线程,SQL)