Peanut's Book Shelf
Why Ruby amazes me? PRuby Chap 6 方法调用
Gem Gatherer 发表于 2008-05-02 17:01:19
用def可以定义一个方法,方法必须以一个小写字母开头
** 如果使用大写字母,不会出错,但是Ruby会优先猜测这是一个常量而不是一个方法调用,解析可能出错
指定形参默认值
def cool_dude(arg1="Miles", arg2="Coltrane", arg3="Roach")
"#{arg1}..."
end
** 不能在方法体内定义非单件类或模块
** 可以嵌套定义方法,但此方法只有在外部方法执行的时候才得到定义
** 方法的返回值是执行的最后一个表达式的值,或者return显式返回的值
可变参数列表
def varargs(arg1, *rest)
...
end
这样,第一个参数赋值给arg1,其它剩余的参数都会被装入到一个新的Array中,然后赋值给第二个参数
** 它还有个有趣的逆操作:)
def five(a,b,c,d,e)
end
<-- five(1,2,3,*['a','b'])
<-- five( *(10..14).to_a)
看见了么?可以传递一个前面加*的数组,这样,数组元素就会自动被视为单独的参数。
方法和block
调用一个方法时,可以提供一个block与其关联,在方法内可以用yield调用这个block
** 如果方法定义的最后一个参数前缀是&,那么所关联的block会被转换为一个Proc对象,然后赋值给这个参数
def initialize(name,&block)
@name, @block=name, block
end
def get_tax(amount)
"Tax = #{@block.call(amount)} " # 这样便于传递代码片段对象。。。很方便不是么:)
end
调用方法
** 如果没有二义性,在调用方法时,可以省略参数列表两侧的括号。
** 如果在方法名和左括号之间有空格,将得到一个警告
返回值
Ruby可以轻易的返回多个值.
def meth
return 1,2
end
<-- meth
--> [1, 2]
更加动态的block : 让lambda来辅助block!
if d > 0
calc = lambda {|n| n*number}
else
calc = lambda {|n| n+number}
<-- puts((1..10).collect(&calc).join(", "))
在实参里加&的参数是代表什么?Ruby将认为它是一个Proc对象,会将其从参数列表中删除,并将Proc对象转化为一个block,然后关联到该方法
散列参数
当我们要传送动态创建的散列表作为参数时,我们可以不用一般创建散列表的方式(那样容易和block混起来)...
list.create_search('short jazz songs',
'genre' => 'jazz',
'duration_less_than' => 270)
当然,这样要散列表是形参表的最后一个才能有效。
** 上面的调用还有一个形式
list.create_search('short jazz songs',
:genre => :jazz,
:duration_less_than => 270)
Why Ruby amazes me? PRuby Chap 5 标准类型
Gem Gatherer 发表于 2008-05-02 01:03:14
所有的数字也是对象。可以对各种形式的消息做出响应.
整数也支持几种有用的迭代器。
3.times { print "X" }
1.upto(5) { |i| print i, " "}
9.downto(7) { |i| print i, " "}
50.step(80,5) { |i| print i, " "}
** 注意,只包含数字的字符串在表达式运算时不会被自动转换成数字
<-- "34" + "56"
--> "3456"
** 但是可以这样
<-- Integer("34") + Integer("56")
--> 90
** Ruby的字符串是8比特字节的序列
可以用#{expr}序列把任何ruby代码的表达式放进字符串中
<-- "#{'Ho!'*3}, Mike"
--> "Ho!Ho!Ho!, Mike"
** 甚至可以将一句或多条语句加入#{}
puts "now is #{def the(a)
'the ' + a
end
the('Time')
} for me."
其它构建字符串的方式...
%q : 代表单引号引起的字符串(%q就相当于一个')
%Q : 代表单引号引起的字符串(%Q就相当于一个")
%q/general single-quoted string/ --> 'general single-quoted string'
%Q!general single-quoted string! --> "general double-quoted string"
** %q和%Q后面的第一个字符是分界符。此字符串直到下一个分界符出现为止(不包括分界符本身)
String的一些方法
String#split将一个字符串分割开来,可以接受正则表达式
<-- "1 | 2".split(/\s*\|\s*/)
--> ["1", "2"]
String#chomp将字符串前后的空格去掉
String.squeeze将字符串中重复并连续的字符合并。
<-- "Heeeeeeello world".squeeze("e")
--> "Hello world" # 如果不带参数则unique所有字符
** 有一些函数,例如squeeze,会返回处理后的字符串,但它还有一个版本squeeze!,是在字符串本身上做修改
String#scan可以从字符串中抽取匹配正则表达式的元素
<-- "112|2223".scan(/\d+/)
--> ["112", "2223"]
区间 : Ruby用区间实现了序列(sequence),条件(conditions)和间隔(intervals)
区间作为序列:序列是有起点和终点,并可以在序列中产生连续值的方法
** 序列可以用..和...来产生 ..创建闭区间(包括右端点),而...是开区间(不包括右端点)
1..10
'a'..'z'
** 序列可以用to_a方法转换成列表
<-- (1..3).to_a
--> [1, 2, 3]
<-- ('bar'..'bat').to_a
--> ["bar", "bas", "bat"] #没做任何额外声明就产生这结果了。。。真够智能的-_-
区间的一些方法
<-- d = 0..9
--> true
<-- d.include?(5)
--> 0
<-- [d.min, d.max]
--> [0, 9]
<-- d.reject { |i| i < 5}
--> [5, 6, 7, 8, 9]
** 可以用自己的对象来创建区间,但必须满足两个条件
1. 这些对象可以用<=> (spaceship操作符)来比较大小
class a
def <=>(other)
self.val <=> other.val
end
end
2. 要能用succ产生下一个值
class a
def succ
raise(IndexError, "Too Large") if @val >= 9
a.new(@val.succ)
end
end
这样a就可以作为区间了
as = a.new(2)..a.new(8)
区间作为条件
while line = gets
puts line if line=~/start/ .. line=~/end/
end
这段代码打印从标准输出得到的若干行的集合,每个集合都满足以下条件
1. 集合第一行包含"start"
2. 集合最后一行包含"end"
区间作为间隔
** 可以用===来测试一些值是否落入区间表达式的间隔内
<-- (1..10) === 3.14
--> true
正则表达式:Ruby内建支持。
创建的方式有三种,都是等效的:
a = Regexp.new('/^\s*[a-z]')
b = /^\s*[a-z]'/
c = %r{'/^\s*[a-z]'}
匹配的方式有两种, Regexo#match(string)或者=~(肯定匹配),!~(否定匹配)
name = "Fats Waller"
<-- name =~ /a/
--> 1
** 匹配返回匹配繁盛的字符位置
** 匹配会设置一些ruby线程变量
*** $& 得到与模式匹配的字符串
*** $` 得到匹配之前的字符串
*** $' 得到匹配之后的字符串
*** $~ MatchData对象,后有详述
*** - , 后有详述
** ^和$分别匹配行首和行尾
<-- "a op b" =~ /^op/
--> nil
** \A匹配字符串开头
** \z匹配字符串结尾。若字符串结尾为\n,则会无视\n
** \Z匹配字符串结尾。不会无视\n
** \b匹配词的边界 (组词字符是字母,数字和下划线的任意组合)
** \B匹配非词的边界
[]可以匹配在其之间的任何字符
** 特殊字符.|()[{+^$*?在方括号内被视为普通字符
** 字符串替换仍正常进行,例如\b代表回退空格,\n代表换行
** []内可以使用Posix字符类
字符类
实际内容
含义
\d
[0-9]
数字
\D
[^0-9]
非数字
\s
[ \t\r\n\f]
空格
\S
[^ \t\r\n\f]
非空格
\w
[A-Za-z0-9_]
组词字符
\W
[^A-Za-z0-9_]
非组词字符
| 字符类 | 含义 |
| [:alnum:] | 字母和数字 |
| [:alpha:] | 字母(大小写) |
| [:blank:] | 空格和制表符 |
| [:cntrl:] | 控制字符(至少包括0x00-0x1f,0x7f) |
| [:digit:] | 数字 |
| [:graph:] | 除了空格之外的可打印字符 |
| [:lower:] | 小写字母 |
| [:print:] | 任何可打印字符 |
| [:punct:] | 除字母数字和空格外的可打印字符 |
| [:space:] | 空格 |
| [:upper:] | 大写字符 |
| [:xdigit:] | 16进制数字(0-9,a-f,A-F) |
重复: 若r代表任何模式,那么
r* 代表0或多个
r+ 代表1或多个
r? 代表0或1个
** r*和r+的匹配都是贪心的
编组收集:小括号在正则表达式中有重要的作用
<-- show_regexp('banana', /(an)*/)
--> <<banana>>
括号的作用一是改变优先级,二是收集括号匹配的结果。
Ruby计算开始括号的数目,保存每个开始括号和相应的关闭括号之间部分匹配的结果。可以在模式的剩余部分和Ruby程序中使用这种部分匹配
** 在模式内部,指的是第一个组的匹配,是第二个,...
** 在模式外,则有,等特殊变量。
** 这可以让你轻易寻找各式各样的重复,例如
<-- show_regexp("Mississippi", /(\w+)/)
--> M<<ississ>>ippi
基于模式的替换
String#sub替换一次,而String#gsub对所有满足的子串都加以替换
** 函数的第一个参数是正则表达式,第二个参数可以是String或者Block。如果是block,匹配的子串会传递给block,block的结果会替换到原先的子串中
MatchData对象
** 每次调用Regexp#match时候,如果失败则返回nil,如果成功,返回MatchData类的一个实例。MatchData对象让你访问关于这次匹配的所有可用信息。
MatchData ma;
ma[0] ---> $&
ma[1]..ma[9] ---> ..
ma.pre_match ---> $`
ma.post_match ---> $'
Way to Webdev:) Head First Servlet & JSP Chap14: 模式和Struts
Mystra 发表于 2008-04-25 21:26:19
Web应用可能很大,为了使我们的项目成功,我们必须使我们的网站做到下面几点:
1. 可维护性
2. 性能
3. 模块性
4. 灵活性,可扩展性
以下是一些宏观准则...
1. 使用接口编写代码 (不用关注实现)
2. 关注点分离和内聚 (让一个模块尽量只关注一件事情)
3. 隐藏复杂性 (如果一个类需要查找服务,而这件事情又很复杂,我们应该用一个组件来专门完成这件事情)
4. 松耦合 (一个类在和另一个类进行通讯时,不应对对方内部实现了解太多)
5. 远程代理 (隐藏复杂性的一种,让用户用操作一个本地对象的类似手法来操纵一个远程对象)
6. 增强声明性控制 (以声明方式控制应用,这样我们通过修改能改变尽量多的系统行为,而无需修改代码)
专题:远程对象:怎样调用在其它JVM里面对象的方法?
Java和J2EE为我们提供了一些机制,可以解决最常见的两个难题: 查找远程对象,以及处理本地和远程对象之间的底层网络I/O通信
JNDI(Java Naming and Directory Interface,Java命名和目录接口):这是访问命名和目录服务的API
** 利用JNDI我们可以在网络上的某个位置查找远程对象
** 如果我们希望某个对象能被JNDI查找到,那么可以向JNDI注册这些对象
RMI(Remote Method Invocation,远程方法调用)
** 利用RMI我们可以假装在调用本地对象方法一样去调用远程对象方法
** RMI创建了一个代理,并把这个对象注册到某种注册表。我们用这个代理做调用,它能处理所有通信细节,套接字,I/O流,TCP/IP,对参数和返回值串行化和反串行化,处理异常等等
不用RMI的客户端
public void foClient() {
try {
// 得到一个新Socket
// 得到一个OutputStream,链到一个ObjectOutputStream
// 发送opCode和参数,刷新输出OS
// 得到InputStream,用于接受返回值
// 读取返回值,处理异常
// 完成清理
} // 捕获处理远程异常
}
用RMI的客户端
public void foClient() {
try {
// 查找远程对象(桩)
// 调用远程对象的方法
// 捕获和处理远程异常
}
}
** 可是,查找远程对象这个JNDI查找还是很复杂。我们要想点办法...
** 这时候我们应该使用业务委托.
一个典型的业务委托的伪代码
// 得到请求,并完成一个JNDI查找
// 得到一个桩
// 调用业务方法
// 处理并抽出所有远程异常
// 把返回值发送回给控制器
** 可以看到,在查找服务时,有重复代码。那么我们就用服务定位器来简化业务委托
** 多个业务委托可能使用相同的JNDI服务
** 业务委托也需要提高内聚。不应同时处理注册查找服务
** 服务定位器可以封装查找不同服务的复杂性。要知道,企业bean和普通远程对象所用的查找代码是完全不一样的
** JSP端每请求一次网络对象的属性,就要做一次远程调用。而这个调用是非常耗时的。
有时我们需要权衡,是响应时间更重要,还是数据的实时性?如果是前者,我们可以利用传输对象传送一个串行化的Java对象。一旦传输,传输对象就和其来源完全失去联系了。
MVC控制器:
为了隐藏复杂性,提高内聚度,Struts帮助我们分离了本该在MVC的“控制器”层解决的表单验证和转发/包含等动作处理都分离开来.
以下失Struts的关键组件.
1. Action Servlet : 每个应用一个。Struts会自己提供
2. 表单bean :对于应用需要处理的各个HTML表单,现在要为它们提供一个Java Bean.Action Servlet会自动填充数据和调用validate(),所以可以把数据转换和错误处理逻辑放在这里
3. Action对象: 动作映射到用例中的一个活动.它有一个关键动作execute(),可以在其中获得验证表单参数,并调用模型组件
4. struts-config.xml : 特定的部署文件。需要完成以下映射:
** 请求URL到Action
** Action到表单Bean
** Action到视图
Struts为前端控制器增加的8个特性
** 声明式控制 : Struts允许在请求URL,验证对象,调用模型的对象以及在视图之间建立映射
** 自动请求分派 : Action.execute()返回一个指示性的ActionForward,它告诉ActionServlet要分派到哪个视图
** 数据源管理
** 数十个新的定制标记
** 国际化支持:针对错误类和定制标记
** 声明式验证:将表单的验证剥离开来
** 全局异常处理
** 插件:可以自己改进struts应用
我们来看一下某个实际例子的Struts-config.xml
<struts-config>
<form-beans>
<form-bean name="SelectBeerForm" type="com.example.web.BeerSelectForm" /> <!-- 表单bean对象的符号名和类 -->
</form-beans>
<action-mappings>
<action path="/SelectBeer" type="com.example.web.BeerSelectAction" name="SelectBeerForm" scope="request" validate="true" input="/form.jsp">
<forward name="show_results" path="/result.jsp"/>
</action> <!-- 将URL路径映射到控制器类 -->
</action-mapping>
<message-resources parameter="ApplicationResources" null="false" />
</struts-config>
** 在Web.xml里要指定ActionServlet,拦截所有的请求
Way to Webdev:) Head First Servlet & JSP Chap13: 过滤器和包装器
Mystra 发表于 2008-04-24 21:54:32
过滤器: 它有两个作用。
1. 请求发送到Servlet之前,可以用过滤器截获和处理请求
** 可以完成安全检查,重新格式化请求首部或体,建立请求审计或日志,等等
2. 在Servlet结束工作之后,在响应发回给客户之前,可以用过滤器处理响应
** 压缩响应流,追加或修改响应流,创建一个完全不同的响应(不要惊讶,这是很有用的:) )
过滤器在某些方面很像Servlet
1. 过滤器有自己的API.当一个类实现了Filter接口,这个类被允许访问ServletContext,而且可以和其它过滤器链接
2. 容器管理过滤器的生命周期(过滤器有init和destroy)
3. 过滤器也要在DD中声明
一个例子...
package com.example.web;
import java.io.*;
import javax.servlet.*; // Filter和FilterChain都在这个里面
import javax.servlet.http.*;
public class BeerRequestFilter implements Filter {
private FilterConfig fc;
public void init(FilterConfig config) throws ServletException {
this.fc = config; // 这几乎是惟一需要做的一件事
}
public void doFilter( ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest httpReq = (HttpServletRequest) req; // 注意这里是ServletRequest,但是可以确定HttpServletRequest,目前似乎没有非Http的ServletRequest...
String name= httpReq.getRemoteUser();
if ( name != null) {
fc.getServletContext().log("User" + name + "is updating");
}
chain.doFilter(req, resp);
}
public void destroy() {
} // 清理工作,一般为空
}
过滤器是“可入栈”的。过滤器可以通过FilterChain串起来(先进先出),并且在Chain里面可以加入Servlet
声明过滤器
<filter>
<filter-name>BeerRequest</filter-name>
<filter-class>com.example.web.BeerRequestFilter</filter-class>
<init-param>
<param-name>LogFileName</param-name>
<param-value>UserLog.txt</param-value>
</init-param> <!-- 可以有多个init-param -->
</filter>
声明对应URL模式的过滤器映射
<filter-mapping>
<filter-name>BeerRequest</filter-name>
<url-pattern>*.do</url-pattern> <!-- 对于这些URL都要过滤一把 -->
</filter-mapping>
声明对应servlet名的过滤器映射
<filter-mapping>
<filter-name>BeerRequest</filter-name>
<servlet-name>AdviceServlet</servlet-name> <!-- 仅对于此Servlet要过滤 -->
</filter-mapping>
** 如果多个过滤器指定了同样的资源,这些过滤器将以声明顺序来串联处理这些资源
** 我们有必要小心考虑过滤器的顺序. 例如,给JPG加水印的过滤器应该在压缩JPG的过滤器之前
** 坏消息:这样只能过滤来自客户的请求,但是还有一类请求是从转发或包含,请求分派或者错误处理器产生的。
** 好消息:在Servlet规范2.4版本中,过滤器可以应用于请求分派器
例子:
<filter-mapping>
<filter-name>MonitorFilter</filter-name>
<url-pattern>*.do</url-pattern>
<dispatcher>REQUEST</dispatcher> <!-- 来自用户请求 -->
<dispatcher>INCLUDE</dispatcher> <!-- 来自包含 -->
<dispatcher>FORWARD</dispatcher> <!-- 来自转发 -->
<dispatcher>ERROR</dispatcher> <!-- 来自错误处理器 -->
</filter-mapping>
响应过滤器:
现在我们可以理解那个栈了。在过滤器的栈中,先压栈的是请求过滤器,然后是Servlet,再然后是响应过滤器(注意一般是在doFilter最后才执行chain.doFilter的!)
但是问题在于,等到过滤器想去处理输出时,Servlet已经将输出flush掉,再也来不及处理了.
为了解决这个问题,我们可以把一个虚拟的输出流传给Servlet(Servlet可不知道这是个假输出,这个输出流不会把响应刷新,而是缓存起来)我们可以由输出流继续处理这些输出
为了不要实现我们自己的HttpServletResponse(太复杂了)...我们使用几个Wrapper(这就是包装器!)
** ServletRequestWrapper / HttpServletRequestWrapper
** ServletResponseWrapper / HttpServletResponseWrapper
一个压缩输出流过滤器的例子
class CompressionResponseWrapper extends HttpServletResponseWrapper {
public ServletOutputStream getOutputStream throws... {
...
servletGzipOS = new GzipSOS(resp.getOutputStream());
return servletGZipOS;
}
}
class MyCompressFilter implements Filter {
public void init(FilterConfig) cfg { }
public void doFilter (request,response chain) {
CompressionResponseWrapper wrappedResp = new CompressionResponseWrapper(response);
wrappedResp.setHeader("Content-Encoding", "gzip");
fc.doFilter(request, wrappedResp);
GZIPOutputStream gzos = wrappedResp.getGZIPOutputStream();
gzos.finish(); // 由于Servlet不再担任刷新输出的责任,我们这里要主动刷新.
chain.doFilter(request, wrappedResp); // 现在沿过滤器链接,传递的就是这个伪输出流了...
}
public void destroy(){}
}
** HTTP拿到首部信息为Content-Encoding : gzip,它会在显示数据之前将其解压缩。
Way to Webdev:) Head First Servlet & JSP Chap12: Web应用安全
Mystra 发表于 2008-04-23 20:44:19
Servlet安全的四大要素:
1. 认证(抵挡假冒者) : 确认你的身份
2. 授权(抵挡非放升级者) : 确认你所在的身份有访问响应资源的权限
3. 机密性(抵挡窃听者 ) : 确认你发送的数据不会被窃听
4. 数据完整性(抵挡篡改者) : 确认你发送的数据不会被篡改
认证的基本过程 ...
1. 浏览器请求一个Web资源
2. 服务器确定此资源是受限的
3. 容器发回一个HTTP401(Unauthorized)
4. 浏览器得到401,要求用户提供用户名和口令
5. 浏览器再次请求同一资源,但还包括一个安全HTTP首部以及用户名和口令
6. 容器检验用户名和口令是否匹配
7. 如果不匹配,返回401.如果匹配,检查此角色是否被允许访问此资源,如果可以,把资源返回给客户
** Servlet中可以实现安全,但不好。安全应有部署人员管理
授权:
Tomcat的安全领域
tomcat-users.xml用于将用户映射到角色。当然,这个设置是容器实现相关的
<tomcat-users>
<role rolename="Guest"/>
<role rolename="Member"/>
<user username="Bill" password="coder" roles="Member,Guest">
....
</tomcat-users>
1. 授权的第一步就是像上面的片断一样定义角色。
2. 定义资源/方法约束
<web-app>
<security-constraint>
<web-resource-collection>
<web-resource-name>UpdateRecipes</web-resource-name> <!--内部名称-->
<url-pattern>/Beer/AddRecipe/*</url-pattern> <!-- 这里的命名规则和Servlet映射一样 -->
<url-pattern>/Beer/ReviewRecipe/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<web-resource-collection>
<auth-constraint> <!-- 不要被名字迷惑,这里是可以访问的角色列表 -->
<role-name>Admin</role-name>
<role-name>Member</role-name>
</auth-constraint>
</security-constraint>
</web-app>
** 这里的受限资源的实质是"对于HTTP GET来说,这是一个受限资源"
** 如果指定一个<http-method>元素,那么未指定的所有HTTP方法都是不受约束的。当然,如果一个http-method都没有指定,那么全部http方法都不能访问
** 别误会...这些资源只是客户不能直接访问而已,Web应用内的其它页面还是可以访问的
** 如果没有auth-constraint,容器允许不经认证就访问这些URL
<auth-constraint>规则:
** 如果没有说哪些角色受约束(即根本没有auth-constraint标签),那么任何角色都不受约束
** 如果放了一个空auth-constraint标签,那么任何角色都不能访问
** 如果两个web-resource-collection指定了相同的资源,却设定了不同的role访问权限,那么结果是两个auth-constraint元素中所有角色的并集都可以访问
** 上一条规则有一个特例,如果至少一个auth-constraint元素是空元素,那么它对应的资源肯定不能被任何人访问(即使其它auth-constraint设定它可以被访问)
程序式安全
// 在Servlet里面
if ( request.isUserInRole("Manager")) { // 硬编码,当然是不好的,没有泛用性!
...
}
** getRemoteUser : 可以用于检查认证状态
在DD中,我们可以把Servlet中硬编码的角色映射到我们自己定义的角色
<web-app...>
<servlet>
<security-role-ref>
<role-name>Manager</role-name>
<role-link>Admin</role-link>
</security-role-ref>
</servlet>
</web-app>
<web-app...>
<security-role>
<role-name>Admin</role-name>
</security-role>
</web-app> ....
** 就算程序设的角色名确实与一个实际的用户匹配,容器也会使用<security-role-ref>映射
认证
容器可以支持4种认证类型。它们的主要区别是所传输的用户名和口令信息有多安全.
1. BASIC : 以一种未加密的编码(base64)传输
2. DIGEST : 以加密形式传输
3. CLIENT-CERT : 非常安全,但是客户要有证书
4. FORM :允许自定义登陆表单,但是最不安全。用户名和口令都在HTTP请求中发回,而且没有加密
要使用认证,只要在DD中声明认证机制就可以
<web-app...>
...
<login-config>
<auth-method>BASIC</auth-method> <!-- 或者DIGEST,CLIENT-CERT -->
</login-config>
</web-app>
表单略微复杂一些
<web-app...>
...
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/loginPage.html</form-login-page>
<form-error-page>/loginError.html</form-login-page>
</form-login-config>
</login-config>
</web-app>
同时,我们要创建DD里声明的两个HTML。。。
<!-- loginPage.html -->
<form method="POST" action="j_security_check">
<input type="text" name="j_username">
<input type="password" name="j_password"> <!-- 这三个以j打头的input都是系统要求如此命名的 -->
<input type="submit" value="Enter">
</form>
** loginError.html就是普通的HTML
保护正在传输的数据,解决之道是使用HTTPS
** HTTPS协议是基于TCP协议之上的SSL来传输的。即使被监听,也无法读取出有效的信息。
** 以声明方式防御式的实现数据传输的安全性
<web-app>
<security-constraint>
<web-resource-collection>
<web-resource-name>UpdateRecipes</web-resource-name> <!--内部名称-->
<url-pattern>/Beer/AddRecipe/*</url-pattern> <!-- 这里的命名规则和Servlet映射一样 -->
<url-pattern>/Beer/ReviewRecipe/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<web-resource-collection>
<auth-constraint> <!-- 不要被名字迷惑,这里是可以访问的角色列表 -->
<role-name>Admin</role-name>
<role-name>Member</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
</web-app>
** transport-guarantee可以取3个值:
NONE:默认值,没有数据保护
INTEGRAL:数据在传输过程中不能更改
CONFIDENTIAL:数据在传输过程中不能被看到(当然更不能改)
** 容器会在适当的时候让浏览器以https协议发送用户名/密码等关键信息
** 这个动作会以容器向用户发送一个301(REDIRECT)的方式实现。
Way to Webdev:) Head First Servlet & JSP Chap11: Web应用部署
Mystra 发表于 2008-04-21 20:43:39
先复习一下各类文件放在哪里...
1. 静态内容和JSP : 应用目录的任意子目录(可以直接访问)或者WEB-INF的任何子目录(客户不能直接访问,但可以由其它文件包含)
2. Servlet : 应用目录/classes
3. 定制标记处理器类 : 应用目录/classes
4. TLD: WEB-INF的子目录或者应用目录/lib/任意jar文件的META-INF及其子目录下
5. JavaBean类 : 应用目录/classes
6. web.xml : WEB-INF下
7. Jar文件 : 应用目录/lib
WAR(web archive,web归档) : WAR实际就是一个JAR.
WAR中可以在META-INF/MANIFEST.MF中声明库和类的依赖性,这样在部署时就能检查容器能否找到应用依赖的包和类
** 如果容器需要寻找一个类,会先查找WEB-INF/classes下面的类,然后再寻找WEB-INF/lib中的jar文件
Servlet映射方式
完全匹配 : <url-pattern>/Beer/SelectBeer.do</url-pattern>
目录匹配 : <url-pattern>/Beer/*</url-pattern> <!-- 仍然必须要以/开头!! -->
扩展名匹配 : <url-pattern>*.do</url-pattern> <!-- 不能以/开头!! -->
如果一个URL同时可以被几种方法匹配,那么顺序是:完全匹配-->目录匹配-->扩展名匹配
** 记住,http://.../test/fooStuff/bar/和http://.../test/fooStuff/bar是不一样的!!前者可以被<url-pattern>/fooStuff/bar/*</url-pattern>匹配,后者不行!
DD中配置欢迎文件:
<web-app....>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
在用户访问一个路径,但是没有直接映射时,容器会按list中的顺序查找该路径是否有welcome-file-list中声明的页面。如果list中的页面都没有存在,那么行为是容器自定义的,可能抛出一个404或者显示该目录下文件等等
DD中配置错误文件
1. 声明普遍性的错误页面,应用于应用内所有的资源,而不只是JSP
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/errorPage.jsp</location>
</error-page>
2. 针对特定异常的的错误页面
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/arithmeticErrorPage.jsp</location>
</error-page>
3. 针对特定HTTP错误码的错误页面
<error-page>
<error-code>404</error-code>
<location>/notFoundErrorPage.jsp</location>
</error-page>
** 异常要使用完全限定类名
** 不能同时指定error-code和exception-type
** 可以用HttpServletResponse.sendError来人为制造异常
配置Servlet的初始化
一旦Servlet有一个<load-on-startup>的子元素,且其值为非0,那么Servlet将在启动时而非第一次访问时被加载
** load-on-startup越接近1,越先被加载
** 如果load-on-startup值一样,那么按照servlet被声明的顺序加载
JSP文档:
为了使JSP完全符合XML格式,采取了以下映射
指令(除Taglib) <%@ page import="java.util.*" %> --> <jsp:directive.page import="java.util.*" />
声明 <%! int y=3; %> --> <jsp:declaration> int y=3; </jsp:declaration>
Scriptlet <% list.add("Fred"); %> --> <jsp:scriptlet>list.add("Fred");</jsp:scriptlet>
文本 There is no spoon. --> <jsp:text>There is no spoon</jsp:text>
脚本表达式 <%= it.next() %> --> <jsp:expression>it.next()</jsp:expression>
** 整个jsp必须被包围在一个<jsp:root>中,taglib指令在<jsp:root>里面
