分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
一、 JavaWeb基础
第一天:
1.Eclipse详解:
(1).Bad versionnumber in .class file:编译器版本和运行(JRE)版本不符合。高的JRE版本兼容低版本的编译器版本。
(2).当程序有错误的时候,使用Debug as 运行程序。双击语句设置断点。程序运行到此处停止。点击跳入方法的内部代码。点击跳过,执行下一条代码,点击跳出,跳出方法。观察变量的值,选中变量右击选择watch. 跳入下一个断点。查看断点,调试完后一定要清除断点。结束运行断点的jvm.
2.HashSet和hashCode和equals方法
java系统首先调用对象的hashCode()方法获得该对象的哈希吗,然后根据哈希吗找到相应的存储区域,最后取出该存储区域内的每个元素与该元素进行比较.两个equals相等,hashCode()相等。需要重写equals,hashCode()方法.更改数据的值,hashCode()的值也更改了,并未删除.内存泄露.有个东西不在被用,但是未被删除,导致内存泄露.
3.Junit测试框架
(1).在测试类,方法前加注解:@Test,不然出现初始化异常。
(2).方法before,after前加@Before,@After注解。在测试方法之前和之后运行方法。
(3).静态方法beforeClass,afterClass方法前加上注解@BeforeClass,@AfterClass,类加载的时候运行
(4).Assert断言。判断两个对象是否相等。期望值和实际值相等。
4.得到配置文件的路径
通过类加载器 reflect.class.getClassLoader.getResourceAsStream();在class指定目录下查找指定的类文件进行加载.编译器把src中的.java文件编译成class文件,所有非.java文件,原封不动的搬过去.但是这种方法是只读的.
通过类的信息reflect.class.getResourceAsStream();相对路径
一般用绝对路径,使用配置文件告诉用户路径.
一定要记住要用完整的路径,但是完整的路径不是硬编码的,是计算出来的.
5.反射
(1).反射主要用于框架开发
(2).一个类有多个组成部分,例如:成员变量,方法,构造方法等,反射就是加载类,并解析类的各个组成部分。
(3).加载类使用Class.forName()静态方法,给类的完整名称,包名和类名。
(4).Class提供了解析public的构造方法,方法,字段等方法以及private。字段封装数据,方法执行功能
(5).静态方法无需传递对象,method.invoke()
(6).升级时保持兼容性,main函数的解析有点麻烦,反射解析数组参变量的时候的问题。启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按照jdk1.5的语法,整个数组是一个参数,而按照jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,newString[]{"xxx"}),javac只把它当做jdk1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。解决的方法:
mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)newString[]{"xxx"});编译器会做特殊处理,编译时不把参数当做数组看待,也就不会把数组打散成若干个参数了.
(7).对数组进行反射:相同的维数(不是数组元素的个数,例如都是一维数组,不关心数组的大小),相同的8种基本数据类型时数组有相同的字节码.
6. 泛型
(1).泛型是对象类型,不能是基本类型,泛型存在源代码级别上,给编译器看的,生成class文件就不存在泛型了
(2).参数类型变量,实际类型变量,泛型类型,参数化的类型
(3).自定义泛型方法:public
(4).静态方法public static
7.可变参数
(1).可变参数就看成数组,可以使用增强for循环
(2).可变参数列表为最后一个参数
(3).可以给可变参数传递一个数组
(4).可变参数的类型是基本类型还是对象类型
8.课程介绍
(1).在谷歌心目中,“云”必须具备以下条件:数据都存在网上,而非终端里,软件最终消失,只要你的“云”设备拥有浏览器就可以运行现在的一切,“云”时代的互联网终端设备将不仅仅是PC,手机,汽车,甚至手表,只要有简单的操作系统加个浏览器就完全可以实现,由于数据都在“云”端,企业的IT管理越来越简单,企业和个人用户也不同在担心病毒,数据丢失等问题。
(2).李开复描述了这样一个场景,只要你的PC或手机等终端里安装了一个简单的操作系统和完整功能的浏览器,开机后输入自己的用户名和密码,你存在“云”中的应用软件和数据就会同步到终端里。
9.快捷键
(1).配置快捷键:window->preferences->key
(2).Alt+/ :内容提示
Ctrl+1 :快速修复
Ctrl+Shift+O :快速导入包
Ctrl+Shift+F :格式化代码
Alt+方向键 :跟踪代码
Ctrl+Shift+/ :添加注释
Ctrl+Shift+\ :取消注释
Ctrl+Shift+X :更改为大写
Ctrl+Shift+Y :更改为小写
Ctrl+Shift+向下键 :复制代码
Ctrl+Shift+向上,向下 :改变代码行的顺序
Ctrl+T :查看继承关系
Ctrl+Shift+T :查看源代码
Ctrl+Shift+L :查看所有的快捷键
10.类加载器及其委托机制的深入分析
(1).Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
(2).类加载器也是Java类,因为其他java类的加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap(内嵌到JVM的内核中,使用C++语言编写的)
(3).Java虚拟机中的所有类装载器采用具有父子关系的属性结构进行组织,在实例化每个类转载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载器
(4).
public class ClassLoaderTest{
public static void main(String[] args){
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
//输出为sun.misc.Lanuncher$AppClassLoader;
System.out.println(System.class.getClassLoader());
//输出为null,因为类System是由BootStrap加载的;
}
}
(5).BootStrap->ExtClassLoader->AppClassLoader
ClassLoader loader= ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();//往上顺序打印
}
System.out.println(loader);//最后打印老祖宗
(6).
BootStrap------>JRE/lib/rt.jar
ExtClassLoader----->JRE/lib/ext/*.jar
AppClassLoader------>ClassPath指定的所有jar或目录
用Eclipse的打包工具将ClassLoaderTest打包成itcast.jar,然后放在jre/lib/ext目录下,在eclipse中运行这个类,运行结果显示为ExtClassLoader,此时的环境状态是classpath目录有ClassLoaderTest.class,ext/itcast.jar包中也有ClassLoaderTest.class,这时我们在打印ClassLoaderTest类的类加载名称,发现是ExtClassLoader,而不是AppClassLoader.
(7).类加载的委托机制:
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢:
首先当前线程的类加载器去加载线程中的第一个类
如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类
每个类加载器加载类时,又先委托给其上级类加载器
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那么多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理
(8).Thread类有一个方法setContextClassLoader(ClassLoader classLoader)//加载当前的类.
(9).每个类加载都首先会委托送到BootStrap,那么BootStrap很累,这样,那为什么不多搞几个BootStrap呢,之所以不这样做,是因为,便于统一管理,因为所有的类都会找BootStrap,可能这时有几个相同的类进行加载,那么BootStrap,不会多次将他们的class文件加载内存中,只会加载一份即可.这样效率就高了.
(10).
public class MyClassLoader{
public static void main(String[]args){
String srcPath=args[0];
String destDir=args[1];//得到目录
String destFileName =srcPath.substring(srcPath.lastIndexOf('/')+1);//得到文件名
String destFilePath=destDir+"\\"+destFileName;
FileInputStream fis = newFileInputStream(srcPath);
FileOutputStream fos=new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cyp(InputStreamips,OutputStream ops){
int b =-1;
while((b=ips.read())!=-1){
ops.write(b^0xff);//对内容进行异或处理
}
}
}
class ClassLoader extends Date{
public String toString(){
return "hello,itcast";
}
}
args[0]:ClassLoader.class的绝对路径
args[1]:itcastlib
有包名的类不能调用无包名的类.
(11).编写自己的类加载器:
知识讲解:
自定义的类加载器必须继承ClassLoader(抽象类)
覆盖findClass方法
defineClass方法:得到class文件转换成字节码
编程步棸:
编写一个文件内容进行简单加密的程序
编写了一个自己的类加载器,可实现对加密过的类进行装载和解密
编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以出了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName
(12).
模板设计模式:
父类--->loadClass(相同的)
子类1(自己干的)
子类2(自己干的)
覆盖findClass方法(自己干)
(13).
public class MyClassLoader extendsClassLoader{
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
@Override
protected Class> findClass(Stringname){
String classFileName = classDir +"\\" + name + ".class";
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = newByteArrayOutputStream();
cypher(fis,bos);
fis.close();
byte[] bytes = bos.toByteArray();
defineClass(bytes,0,bytes.length);
return super.findClass(name);
}
public static void main(String[] args){
Class clazz = newMyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
ClassLoaderAttachment d1 =clazz.newInstance();
}
}
(14).windows->showview->problem查看错误.
(15).可以查看Servlet的类加载委托继承树
11. 枚举
(1).枚举的作用限定指定的值,没有枚举前设计一个类,将构造函数设置成私有的,变量设置成静态常量
(2).枚举可以有构造函数(私有的),字段,方法
(3).可以定义set,get方法,获取变量的值。
(4).带有抽象方法的枚举,不能new出新的对象了。在new对象的时候就重写抽象方法,使用匿名内部类
(5).枚举中的每个枚举值代表枚举类的一个对象。
(6).枚举也可以实现接口,或继承抽象类
(7).JDK5中的switch拓展为除了接受int,short,char,byte外,也可以接受枚举类型。
(8).枚举的方法,name,ordial,valueOf,将字符串转换成枚举值。表单提交数据的时候。values返回枚举的所有的枚举值
12.内省
(1).内省:Introspector,专门操作Bean的属性。
(2).Bean的属性:只有字段提供了set/get方法,就是属性。只要有get/set的方法,就有一个属性,所以属性是有get/set方法决定的
(3).任何类都继承了Object类,又因为Object中有一个class属性
(4).内省的入口类:Introspector,方法getPropertyDescriptors()获取Bean的属性
(5).操作Bean的指定属性
(6).BeanUtils框架操作Bean
(7).在工程中新建一个文件夹,将架包复制到文件夹中,还需另外的架包loging,选中架包,右击build path加入到工程的环境中
(8).BeanUtils使用方便,类型自动转化,只支持8中基本数据类型
(9).给BeanUtils注册类型转换器,ConvertUtils.register()
(10).将Map中的数据整合到对象中,BeanUtils.populate()方法
13.配置Java模板代码
window->preferences->java->Editor->Template:编辑模板代码:line_selection,光标cursor,右击选择source with
14.享元模式
相同的对象只实例化一个,实例:桌面上的图标,word中字符,有很多小的对象,有很多相同的属性,不同的属性叫做外部行为,相同的属性叫做内部行为integer的缓冲池
15.注解
(1).@SuppressWarning("deprecation")过时注解
(2).@Deprecated注解,表示该方法是否过时,架包升级时添加的注解
(3).注解相当于一种标记,通过反射了解你的类及各种元素上有无何种标记
(4).注解相当于一个类:@interface Annotation{};注解类,应用注解类的类,对应用了注解类的类进行反射操作的类
(5).AnnotationTest.class.getAnnotation(ItcastAnnotation.class)得到类AnnotationTest上的注解ItcastAnnotation,注解上使用注解叫做元注解,元数据,元信息
(6).@Retention(RetentionPolicy.RUNTIME)(保持到运行阶段),@Retention(RetentionPolicy.SOURCE)(保持在源文件阶 段),@Retention(RetentionPolicy.CLASS)(保持在class文件中):源代码->class文件->(类加载)内存中的文件(字节码)
(7).@Override注解保持到SOURCE,@SuppressWarning注解保持到SOURCE,@Deprecated注解保持到RUNTIME(只有将该类调到内存中才知道该类中的方法是否过时了)
(8).@Target({ElementType.METHOD,ElementType.TYPE})注解只能标记到方法上或类,接口等类型上 (9).注解的属性:String color();类有个color属性,还有一个特殊的属性value,属性的默认值default,数组的属性值,枚举的属性值,注解的属性值
第二天:
1.dom4j解析XML文档
(1).Dom4j是一个简单、灵活的开放源代码的库,Dom4j是由早期开发JDOM的人分离出来而后独立开发的,与JDOM不同的是,dom4j使用接口和抽象基类,虽然Dom4j的api相对要复杂一些,但是他提供了比JDOM更好的灵活性
(2).Dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使用的特点,现在很多软件采用的Dom4j,例如hibernate,包括sun公司自己的JAXM也使用了Dom4j
(3).使用Dom4j开发,需要下载dom4j相应的jar文件
2. XML语法
(1).编写XML文档时,需要先使用文档声明,声明XML文档的类型,使用IE校验XML文档的正确性.
(2).XML文档中的"中国"保存为本地的码表的"中国"
(3).在XML文档中,空格和换行都作为原始内容被处理
(4).XML区分大小写,一个标签可以有多个属性,每个属性都有它自己的名称和取值,在XML技术中,
标签属性所代表的信息也可以被表示子标签表示
(5).XML文件中的注释采用:"使用反射技术创建一个servlet实例对象(servlet第一次访问的时候被创建)-->调用servlet.init()完成对象的初始化->调用servlet.service()响应客户端的请求,服务器创建request,response容器,service方法执行->向response容器中写入客户机请求的数据->服务器从response中取出数据构建一个HTTP响应回写给客户机-->回写http响应
客户机第一次访问服务器,生成一个servlet,第二次在访问同一个servlet就不会再创建了,当关闭服务器时servlet就消亡了
7. Servlet的线程安全
(1).当多个客户端并发访问同一个servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用servlet的service方法,因此service方法如果访问了同一个资源的话,就有可能引发线程安全的问题,如果某个servlet实现了SingleThreadModel接口,那么servlet引擎将以单线程模式来调用其service方法。SingleThreadModel接口中没有定义任何方法,只要在servlet类的定义中增加实现SingleThreadModel接口的声明即可.对于实现了SingleThreadModel接口的servlet,servlet引擎仍然支持对该servlet的多线程并发访问,其采用的方式是产生多个servlet实例对象,并发的每个线程分别条用一个独立的servlet实例对象。实现SingleThreadModel接口并不能真正解决servlet的线程安全问题,因为servlet的引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个servlet实例对象被多个线程同时调用的问题,事实上,在servlet api2.4中,已经将SingleThreadModel标记为Deprecated(过时的).标准的解决方案是同步方式sychronized
(2).如果线程向person的静态list集合中加入了数据(aaa),数据用完后,一般要移除静态集合中的数据(aaa),否则集合中的数据越来越多,就会导致内存溢出。对象销毁了,静态资源的字节码仍然驻留在内存中.
(3).web中的异常一般不能抛,要catch住.
8. Servlet开发的一些重要细节
(1).由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中,使用
(2).
一个
(3).进入myeclipse->Web更改Web-Content-root
(4).一个servlet可以映射到多个对外访问路径.只需多复制个
(5).同一个Servlet可以映射到多个URL上,即多个
(6).在servlet映射到的URL中可以使用*通配符,但是只能有两种固定的格式:一种格式是:"*.扩展名",另一种格式是以正斜杠/开头并以"/*"结尾的.
匹配所有后缀名为do的访问路径(也可以不需要后缀名, 匹配所有的文件)
(7).servlet1 映射到 /abc/*
servlet2 映射到 /*
servlet3 映射到 /abc
servlet4 映射到 *.do
问题:
1.当请求URL为"/abc/a.html","/abc/*"和"/*"都匹配,但是servlet引擎会调用servlet1
2.当请求URL为"/abc"时,"/abc/*"和"/abc"都匹配,但是servlet引擎会调用servlet3
3.当请求URL为"/abc/a.do"时,"/abc/*"和"*.do"都匹配,但是servlet引擎会调用servlet1
4.当请求URL为"/a.do"时,"/*"和"*.do"都匹配,但是servlet引擎会调用servlet2
5.当请求URL为"/xxx/yyy/a.do"时,"/*"和"*.do"都匹配,但是servlet引擎会调用servlet2
总结:谁长的最像,谁先匹配,同时*的优先级最低.
(8).Servlet是一个供其他Java程序(Servlet引擎:服务器端调用servlet的程序)调用的Java类,它不能独立运行,它的运行完全由servlet引擎来控制和调度.针对客户端的多次servlet请求,通常情况下,服务器只会创建一个servlet实例对象,也就是说servlet实例对象一旦创建,他就会驻留在内存中,为后续的其他请求服务,直至web容器退出,servlet实例对象才会销毁.在servlet的整个生命周期内,servlet的init方法只会被调用一次,而对一个servlet的每次访问请求都导致servlet引擎调用一次servlet的service方法,对于每次访问请求,servlet引擎都会创建一个新的httpservletrequest请求对象和一个新的httpservletresponse响应对象,然后将这两个对象作为参数传递给她调用的servlet的service方法,service方法再根据请求方式分别调用doXXX方法.
(9).右击,可以选择父类的方法,进行重写父类的方法
(10).服务器启动时,servlet并未创建,只有当访问web资源时会创建一个servlet,调用init()方法.一个servlet为多个请求服务,当服务器停了,servlet就被摧毁了,调用destory()方法.
(11).针对客户端的每一次请求,服务器端都会为用户创建一个request和reponse,他们的生命周期很短,如果是并发的请求,大量的请求可能创建大量的request和reponse对象,可能导致服务器运行变慢,但是不是并发的话,可能不会创建那么多个对象
(12).如果在
(13).如果某个servlet的映射路径仅仅为一个正斜杠/,那么这个servlet就成为当前web应用程序的缺省servlet;凡是在web.xml文件中找不到匹配的
(14).配置文件修改了,不需要重启服务器,但是源程序修改了,要重启服务器.
9. Servlet开发入门
(1).jsp就是servlet,servlet是sun公司提供的一门用于开发动态web资源的技术,编写一个java类,实现servlet接口.获得servlet启动信息,通过getServletConfig(),service的两个参数,ServletRequest req,ServletResponse res,这个方法是服务器调用的,获取客户机的对象,只需找request,输出信息找response.getOutputStream();
(2).进入服务器的目录,新建一个web应用,在应用中新建一个java,class,lib,在文件夹java下新建一个servlet文件,
package cn.itcast
public classFistServlet extendsGenericServlet{
public void service(
ServletRequest req,ServletResponseres)throws ServletException,java.io.IOException{
OutputStream out = res.getOutputStream();
out.write("Servlet".getBytes());
}
}
(3).servlet所需的架包导入,tomcat能运行servlet,则在服务器的lib目录下含有架包,为servlet配置对外访问路径web.xml,代码可以到服务器的web.xml文件中拷贝
第五天:
1. HttpServletResponse简介
(1).web服务器收到客户端的http请求,会对每一次请求,分别创建一个用于代表请求的request对象,和代表响应的reponse对象.request和reponse对象既然代表请求和响应,那我们要获取客户及提交过来的数据,只需要找request对象就行了,要向客户机输出数据,只需要找response对象就行了.
(2).reponse的相关方法,构建一个http响应的方法,getStatus(),setHeader(),getWriter(),getOutputStream();字节流可以写任何数据
2.request获取请求头和请求数据
(1).用户点击链接和表单提交向服务器提交数据
(2).在服务器端获取数据:
request.getParameter("username");
request.getParmeterName();
得到所有的名称的枚举.
request.getParameterValues();
得到名称相同的值
(3).在用户提交的数据中,可能抛空指针异常,加强代码的健壮性.
(4).Mapmap=request.getParameterMap();将用户的数据封装到对象中User,先获取Map,再用JavaBeans将Map转化成对象User,其中map关键字是请求参数名称,关键字的类型是String,值的类型是String[],因为可能有多个相同名称对应的值就有多个,所以是个数组;Map将值填充对象User,迭代Map,得到关键字和对应的值,利用反射技术将这些数据填充到User对象中.设计一个对象代表表单.BeanUtils.copyProperties(user,formbean);将表单beans拷贝到user对象beans中;BeanUtils.populate(user,map);对象上的字段必须是八种基本类型,不是的话,要自己写个转换器.
(5).服务器以断点的方式运行进行调试,
(6).request.getInputStream();获取输入流,进行获取数据,进行文件上传.
3. request简介.txt
(1).HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息.
(2).getMethod();获取请求方式,getRequestURI();获取客户想我访问的资源;getHeader();getDateHeader();getHeaderNames();获取所有的头信息,getHeaders();与getHeaderNames();不同的是:可能有同名的头信息,此时就要用getHeaders()方法,a-xx;y-xx;getParameter获取客户带来的数据name="aaa";password="rot";getParameterNames();getParameterValues();getInputStream():将用户的数据当做流读入,如:客户进行文件上传;
(3).getRequestURL():/day07/servlet/demo1;
getRequestURI():http://localhost:8080/day07/servlet/demo1
用在权限拦截上,不同的网页有不同访问权限,做个拦截器,过滤器,还有是在记录页面的访问次数.getQueryString()客户携带的查询信息,地址栏后的类容,getRemoteAddr();得到访问者的ip地址;getRemoteHost():主机在DNS注册了,打印了主机名.getRemotePort();来访者的端口,ie也是一个程序,需要一个端口getLocalAddr();返回web服务器的ip地址,getMethod():获取请求方式,默认是get方式
4.request乱码
(1).在文本框中敲入中文,浏览器提交数据的码表就是浏览器使用哪个码表打开.request容器也是默认码表是iso8859码表,所以在获取数据时,request.setCharsetsEncoding("UTF-8");这种改变码表的方式只对post方式有效,对get方式无效.这种方式解决的是post方式提交的数据.
(2).但是这种方式不能用于get方式,如果是get提交,必定是乱码,所以必须在servlet中手动的改变乱码,newString(username.getBytes("iso8859"),"UTF-8");
(3).用户使用超链接提交数据时,带有中文,其实超链接就是一个get方式,所以要使用get方式去解决乱码问题.
(4).更改服务器的配置,解决乱码问题,但是在开发中严禁这种方式的使用,从浏览器中进入tomcat,修改连接器,HTTP->URLEncoding,然后到tomcat目录下的server.xml中修改连接器Connector,在其中添加一个属性:URLEncoding="UTF-8";还有一种方法,在连接器中添加一个属性,useBodyEncodingForURI="true";是将post方式解决乱码的方法可以用于解决get方式乱码.所以还要事先设置好request容器的码表.
(5).request.setCharacterEncoding("UTF-8");
String username =request.getParameter("username");
response.setCharacterEncoding("gb2312");
response.setContentType("text/html";charset="gb2312");
response.getWriter.write(username);不会有乱码
5.request实现请求转发和mvc设计模式.txt
(1).request是个域对象,request作用的范围是请求范围中,主要用于设计mvc中.
(2).servlet中转发到jsp显示数据,
request.getRequestDispatcher("/mesage.jsp").forward(request,response);
在开发中一般都是用这种方式,而不是用ServletContext的转发方式.每一次请求都对应一个request,正好,request的作用域是一个请求,所有request的转发,都是一个请求,都在这个作用域中.
(3).request.getParameter()方法是获取客户机带来的数据,而request.getAttribute()方法是获取request域中获取数据.
(4).forward方法用于将请求转发到requestDispatcher对象封装的资源,如果在调用forward方法之前,在servlet程序中写入的部分内容已经被真正的传送到了客户端(关闭response流),forward方法将抛出IlegalStateExcepiton异常,如果在调用forward方法之前向servlet引擎的缓冲区(response)中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法就可以被正常执行,原来写入到输出缓冲区中的内容将被清空,但是,已经写入到HTTPServletResponse对象中的响应头字段信息保持有效.
(5).if(true){request.getRequestDispatcher("/index.jsp").forward(request,response)}
跳转到index.jsp向客户机写数据
request.getRequestDispatcher("/index.jsp").forward(request,response)也要向用户写数据。所以一个良好的习惯是跳转之后一定要return;
(6).String data ="aaa";response.getWriter().write(data);不要关闭这个流,这是可以request.getRequestDispatcher("/index.jsp").forward(request,response);理论上应该是两部分内容的组合,即data和jsp中的内容,但是实际上只显示jsp中的内容,因为jsp的内容将覆盖data内容,但是消息头不改变,在这个过程中创建了两个servlet,因为jsp也是一个servlet,但是只有一起请求.不会创建两个容器.
6. request实现页面包含
所有的网页都有相同的网头和网脚,此时只需要将网头和网脚设置共同的部分html,request.getRequesetDispatcher("head.jsp").include(request,response);response.getWriter().writer("hhh");request.getRequestDispatcher("foot.jsp").incude(request,response);被包含页面不能出现全局架构标签,因为可能重复.实现页面包含的作用
7.response的outputstream输出数据的问题
(1).在程序中的"中国"变成了UTF-8码表,但是浏览器默认的是gb2312打开内容,在程序中发送一个头,告诉浏览器用哪个码表打开.
response.setHeader("Content-type","text/html;charset=UTF-8");
(2).html中的标签可以模拟一个http响应头.;
(3).当Content-type写成content-type就变成下载了.
(4).out.write(1);在浏览器上显示的并不是1,因为1对应的码表.
8.response的writer输出数据的问题.txt
(1).String data ="中国";PrintWriter out =response.getWriter();out.writer(data);有问题显示乱码
(2).servlet将"中国"写入response中,但是response用的是iso8859码表,对应没有" 中国",所以在浏览器中看到乱码,response.setCharacterEncoding("UTF-8");设置response的码表为UTF-8;同时也要告诉浏览器以utf-8码表打开.
(3).简便的调用response.setContentType("text/html;charset="UTF-8");可以代替以上两条设置码表的代码.
9.response实现请求重定向和response的一些细节
(1).怎么实现请求重定向:发送状态码302,
response.setStatus(302);response.setHeader("location","/day06/index.jsp");
同时sun公司提供了一个方法response.sendRedirect("/day06/index.jsp");重定向的特点是地址栏发生改变.可能加重服务器的负担,因为有多次请求,一般用于用户登录,因为转发,地址栏不变,用户看不到是否跳到首页了,重定向的地址栏变化,可以告诉用户跳到首页,还有一种是购物,点击购买,服务器的一个servlet帮你买成了,servlet会跳到购物车显示页面,显示你购买的东西.当使用转发时,用户点击刷新,可能又买了一次,重新干一次就不好了,所以用重定向,其他情况下都最好使用转发,减轻服务器的负担
(2).getOutputStream和getWriter方法分别用于得到输出二进制数据,输出文本数据的servletoutputstream,printwriter对象,但是这两个方法是互斥的,条用了其中的任何一个方法后,就不能再调用另一个方法,servlet程序向ServletOutputStream或printwriter对象中写入的数据将被servlet引擎从response里面获取,servlet引擎将这些数据当做响应消息的正文,然后再与响应状态行和个响应头组合后输出到客户机,servlet的service方法结束后,servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,servlet引擎调用close方法关闭该输出流对象
(3).当在demo1中使用了getOutputStream()方法,转发到demo2,但是在demo2中使用了getWriter();就是在一条调用链中不能存在这两个方法,当使用重定向了,有两次请求,产生了两个response对象所以不会有问题,但是转发就有问题了.
(4).无需关心流的关闭,servlet引擎会自动关闭,但是你自己定义的流需要自己关闭,引擎不会去关闭的.
10.response实现文件下载
(1).在webroot应用下新建一个download文件夹,存放下载资源.
当下载的文件是中文文件,则文件名需要经过url编
码,URLEncoder.encode(filename,"UTF-8");filename是下载文件名.
11.web工程中各类地址的写法
(1).request.getRequestDispatcher("/form1.html").forward(request,response);给服务器用的
(2).response.sendRedirect("/day05/form1.html");给浏览器用的
(3).this.getServletContext().getRealPath("/form1.html");给服务器用的
(4).this.getServletContext().getResourceAsStream("/form1.html");给服务器用的
(5).
(6).
总结:写地址以斜杠开头,如果地址是给浏览器用的,这个斜杠代表一个网站,如果是给服务器用的,这个斜杠代表web应用.浏览器向服务器发送请求,就是给浏览器用的
12. 防盗链
首先检查你是否从我指定的网页来的,没有的话,String referer =request.getHeader("referer");直接在地址栏上粘贴地址,还有就是refer.startsWith("http://localhost");检查是否从首页跳转过来的.首页上放广告,再有凤姐日记的链接.网站就是靠广告运行的.
13.输出随机认证码图片
(1).BufferedImage:内存中的一幅图片.构造函数指定了图片的长和宽,像图片上写随机数,getGraphics()方法返回一个图形的对象,然后向该图像上写数据.在把内存中的这幅图片输出到浏览器,ImageIO图像的输入输出;write();方法,指定一个和浏览器相关的输出流.
(2).告诉浏览器以图片的方式读取数据.
(3).
(4).刷新后图片为改变,没有控制浏览器是否进行缓存,浏览器默认是进行缓存的,所以每次拿的都是缓存图片,没有改变,所以要控制浏览器不要缓存,
response.setDateHeader("expries",-1);response.setHeader(Cache-Control","no-cache");response.setHeader("Parma","no-cache");必须三个都要设置好,才起作用.刷新有两个作用:重新向服务器发出请求,还有就是把上一次的事情在干一次.(防止表单重复提交);
(5).
14. 用Expires头控制浏览器缓存
(1).控制浏览器缓存,在servlet向客户会送的数据不变,就需要进行缓存,不需要向服务器发送请求,只需要到硬盘的缓存存放文件夹中读取缓存
(2).Internet选项->常规->点击设置->查看文件,浏览器把index.jsp缓存了,缓存时间是当前时间值:System.currentTime()+1000*3600;
15. 用refresh控制浏览器定时刷新
(1).刷新头:response.setHeade("refresh","3;url='/day04/index.jsp'");控制浏览器每隔3秒刷新一次,
(2).假设是一个用于登陆的servlet:提交用户名和密码,需要到数据库查询是否登陆成功,response.getWriter("恭喜你,登陆成功,本浏览器将在3秒后,跳到首页,如果没有调,请点击点击");
(3).this.getServletContext().getRequestDispatcher("/message.jsp").forword(request,response);
(4).servlet怎么告诉jsp定时刷新.字符串写给jsp"
第六天:
1. Cookie的细节
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(name)和设置值(value),一个web站点可以给以一个web浏览器发送多个Cookie,一个web浏览器也可以存储多个web站点提供的Cookie,浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB,如果创建了一个cookie,并将他发送到浏览器,默认情况下它是浏览器之后即被删除,若希望浏览器将该cookie存储到磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间,将最大失效设置为0,则是命令浏览器删除该cookie,注意,删除cookie时,path必须一致,否则不会删除.cookie不能太大,不然可能造成网络拥堵,也可以将cookie删除.当两个cookie的名称相同时,删除其中一个,就是删除两个.即这两个cookie必须相同,setPath();也要设置.可以到ie保存Cookie的文件中查看文件.javascript也可以删除cookie
2.Cookie显示用户上次访问网站的时间
(1).javax.servlet.http.Cookie类用于创建一个Cookie,response接口也定义了一个addCookie方法,它用于在其响应头中增加了一个相应的Set-Cookies方法,它用于获取客户端提交的Cookie,使用Cookie封装用户的数据.
(2).构造函数,Cookie(name,value);给Cookie一个名称name,用户下一次访问时,带着上一次访问的Cookiet,所以在Request.getCookies();返回一个Cookie数组,即用户的所有Cookie,Cookie的有效期默认是浏览器的进程过程,可以通过setMaxAge()方法设置有效期,案例:用户在一段时间内自动登录。方法setPath();用于设置访问哪个web资源时携带Cookie,方法setDomain(),设置访问哪个域名时携带Cookie.IE默认禁止这种第三方Cookie,你访问我的网站,我给你带回一个的网站的Cookie.
(3).用户第一次访问时,没有携带Cookie,Cookie cookie[] =request.getCookies();cookie可能为空,
首先要判断一下是否为空,
for(inti=0;cookies!=null&&i {long cookieValue= Long.parseLong(cookies[i].getValues()}}; 新建一个 Cookie:Cookiecookies = new Cookie("lastAccessTime",System.currentTimeMillis()); 设置Cookie有效期:cookie.setMaxAge(,,,); 设置有效路径(默认的是"/day04/servlet");cookie.setPath("/day04"); 3.session的工作原理 (1).如果把Cookie禁止,则不能购买东西,Internet选项->隐私->高级 (2).URL重写,你访问我的服务器,都是超链接点击过来的,不同用户访问首页时,就帮用户创建session得到session的id号,然后将id号放在超链接URL中携带过来,不是Cookie携带了,所以所有的URL要重写, request.getSession();Stringurl = response.encodeURL("/day03/servlet/SessionDemo")此方法自动将session的id号加入该地址中.点击 (3).如果没有禁止Cookie,浏览器带一个Cookie来了,就不会进行URL重写 4.session的一些细节问题 (1).一个浏览器就占用一个session,开一个新的网页,不创建session,超链接弹出的新窗口也是共享一个session,同时开多个浏览器,创建多个session,这个问题也不是绝对的,不同的浏览器,功能不一样,将session的id号写到浏览器进程, 5.Session简介 (1).默认情况下,一个浏览器独占一个session对象 (2).HttpSessionsession = request.getSession(); session.setAttribute("name","value"); session.getAttribute("name"); (3).写好过滤器,就可以解决web应用中的乱码问题 (4).一个session只为一个会话服务.(只打开一个浏览器窗口) (5).session的生命周期,第一次访问getSession()方法时,session就被创建,session是30分钟没有人用了(即使浏览器不关闭),服务器就将其删除.而不是结束会话服务.当然这个时间可以实现的. (6).request.getSession()方法怎么知道获取指定用户的session,即session的工作原理:servlet1中:session=request.getSession(),产生一个session的id号,以Cookie的形式回写给用户,下一次用户就带着session的id号来,所以该方法就知道获取指定用户的session,但是这个Cookie没有写有效期,会话结束后,这个id号就没有了,下一次访问,服务器又要创建session,买东西的过程中,关闭浏览器,下一次再接着买,上次买的东西,都没有了,所以解决这个问题,只需设置到这个Cookie的有效期即可.sessionid = session.getId();回写一个Cookie,名称为JSESSIONID,值为sessionid,覆盖默认的Cookie. 6.防止表单的重复提交 (1).用javascript防止表单的重复提交, (2).在服务器端防止表单提交,表单是由程序给出,为用户提交的表单提供一个随机数(表单号),服务器端,查看表单的id号,检查表单是否提交过了,如果提交了,删除该表单的id号,产生令牌的发生器class TokenProcessor{}为了保证令牌是独立的,一般讲其设置为单例模式,是一个对象创建的随机数重复性低,还是多个对象创建的随机数重复对象低,是后一种重复性低,为了得到随机数的长短一样,就要用数据的指纹(摘要),数据的指纹都是一样大的,是128位,类MessageDigest dis =MessageDigest.getInstatnce("md5");使用md5码,byte[] md5 = dis.digest(input);input是传过来的数据,用字节数组变成字符串返回,用到新的算法:base64编码,任何数据的base64码变成ASCII码,原理:将数据字节变成四个字节,0011 0010 1100 1101 0010 1001,这三个字节变成四个字节,每六位装到一个字节,少两位,就在前面补两个零,为:00001100 00101100 00110100 00101001,最小数为0,最大数为63,不会超过64,base64自己定义了一个码表,比如:0:a,1:b,3:c ........63: 所以任何数据都可以变成键盘上对应的字符,人们熟悉的字符,应用在数据的传输过程中,需要给数据的带一个结束符号和开始符号,所以把要传输的数据变成base64,开始符号和结束符号使用base64中没有的字符即可,BASE64Encoder encoder=newBASE64Encoder();return encoder.encoder(md5);这个方法可能在api中查找不到, (3).在servlet中产生一个令牌String token 然后跳到jsp中,这是将token存到session域中,而不是request中,因为token是id号,以后还要使用,在jsp中在用户不知道的情况下将令牌带过去, (4).将已经提交过的表单号删除,struts中就是这样防止表单的重复提交的. 7.会话管理 (1).会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话, (2).会话过程中要解决的一些问题:每个用户与服务器进行交互的过程中,各自会有一些数据,程序想办法保存每个用户的数据,例如:用户点击超链接通过一个servlet购买一个商品,程序应该保存用户购买的商品,以便用户点结账servlet时,结账servlet可以得到用户商品,为用户结账。 (3).如果用户的数据存到request中,结账servlet是一个重新的浏览器请求,所以结账servlet与购买servlet是两次请求,不是共享request,所以结账servlet不能得到购买servlet的内容.ServletContext存在并发问题 (4).一般使用Cookie和Session;Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器,当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去,这样,web资源处理的就是用户各自的数据,你找我买个的东西,买完了东西,你带回去(可能将东西cookie放在ie缓存,在本地硬盘中),当你来结账时,把东西在带来,即访问结账请求,浏览器拿缓存(cookie); (5).Session是服务器端技术,利用这个技术,服务器在运行时可以为每个用户的浏览器创建一个其独享的Session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其他web资源时,其他web资源再从用户各自的session中取出数据为用户服务.用户的数据存在服务器端.购买Servlet:session =request.getSession();Session.setAttribute(name,object);结账Servlet:Session=request.getSession();obj=Session.getAttribute(name);不会再为这个用户创建一个session,而去拿服务器中该用户的session 8.三个域对象的总结 (1).request,session,servletContext三个域:request:程序显示完,数据没有用了,就用request(转发);session:程序显示完数据后,该数据还有用,就用session,servletContext:显示完了,除了该自己用,还要给别人用(聊天室) (2).md5码,数据指纹,保存用户名的密码,为了安全性,将密码的指纹保存,根据md5码反推,需要时间很长,也可以暴力破解,因为密码的长度是一定的,所以一般把数据加上一个随机数,还有一种用途是进行数据完整性的校验, 第七天: 1. jsp入门和jsp运行原理 (1).<%Date date= new Date();out.write(date.toLocaleString();%>显示当前时间. (2).jsp的运行原理:首先将jsp中翻译成servlet放在服务器的work目录下,调用servlet的service方法,在这个方法中将jsp中内容通过流out.write()写到浏览器中,jsp中的java代码将原封不动的运行,可以查看jsp的servlet代码,查看所有定义的对象,不仅有out对象,还有其他对象,JspWriter就相当于PrintWriter (3).最佳实践:一种技术可以用在多个地方,但是只有一个最佳的地方就是最佳实践. 2. jsp语法page指令详解 (1).修改jsp的模板,进入到MyEclipse,搜索servlet.java,在这个目录下,点开jsp找到Jsp.vtl (2).jsp指令是为jsp引擎,引擎就是将jsp翻译成servlet的程序,他们并不直接产生任何可见输出,而是告诉引擎如何处理jsp页面中的其余部分,在jsp2.0规范中共定义了三个指令:page指令,include指令,taglib指令 (3).指令的语法:<%@ 指令属性名=“值”%>,不会产生任何输出,举例:<%@ pagecontentType="text/html;charset=gb2312"%>,如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写,例如:<%@ pagecontenType="text/html";charsetgb2312"%><%@ pageimport="java.util.Date"%>也可以写作<%@ pagecontentType="text/html;charset=gb2312" import="java.util.Date"%> (4).page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它的作用都是整个JSP页面,为了保持程序的可读性和遵循良好的变成习惯,page指令最好是放在整个JSP页面的起始位置. (5).JSP2.0规范中定义的page指令的完整语法: <%@ page [language="java"] [extends="package.class"] [import="{package.class|package.*},..."] JSP引擎自动导入下面的包:java.lang.*;javax.servlet.*;javax.servlet.jsp;javax.servlet.http;可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号分隔:<%@ pageimport="java.util.Date,java.sql.*,java.io.*"%> [session="true|false"]:session是否创建,如果为true,则翻译成servlet时,就创建一个session,否则不创建,设置true,后就可以在JSP中片段中使用session对象,否则不能使用,默认值为true,因为session的周期比较长,为了减轻服务器的负载 [buffer=none|8kb|sizeKb]:默认值为none,jsp的out直接写到response中,没有缓冲了 [autoFlush="true|false"]:自动刷新,默认值为true [isThreadSafe="true|false"]:JSP是否是线程安全的,设置true后,将发现翻译后的servlet就继承了SingleThreade接口,一个servlet只为一次请求服务,不会出现线程安全问题 [info="text"]:带信息 [errorPage="relative_url"]:指定JSP错误的处理页面,errorPage属性的设置值必须使用相对路径,如果以"/"开头,表示相对于当前web应用程序的更目录(注意不是站点根目录),否则,表示相对于当前页面,errorPage="/errors/error.jsp";也可以在web.xml文件中使用 [isErrorPage="true|false"]:这个页面是否为错误处理页面,默认值为false,好处是:服务器在翻译成servlet时将异常封装成一个对象,九大隐式对象中包含一个Excepiton,但是这个对象并不是任何时候都有,只有设置这个参数后,才有,记住 [contentType="mimeType[ ;charset=charachterSet]"|"text/html;charset=ISO-8859-1"]页面的类型 [pageEncoding="charachterSet|ISO-8859-1"]:页面的编码 [isELIgnored="true|false"]:是否忽略EL表达式,EL表达式时JSP2.0的新语法,使用EL表达式时需要将其设置为false.默认都支持EL表达式 (6).jsp乱码问题:Jsp程序存在有与Servlet程序完全相同的中文乱码问题,输出响应正文时出现的中文乱码问题,读取浏览器传递的参数信息时出现的中文乱码问题,Jsp引擎将JSP页面翻译成Servlet源文件时也可能导致中文乱码问题,jsp引擎将jsp源文件翻译成servlet源文件默认采用UTF-8编码,而jsp开发人员可以采用各种字符集编码来编写jsp源文件,因此,jsp引擎将jsp源文件翻译成servlet源文件时,需要进行字符编码的转换,如果jsp文件中没有说明他采用的字符集编码,jsp引擎将把它当做默认的ISO8859-1字符集编码处理,通过page指令的contentType属性说明JSP源文件的字符集编码,page指令的pageEncoding属性说明JSP源文件的字符集编码 (7).tomcat6已经不存在乱码问题了,在jsp中是"中国",将"中国"保存到硬盘中,默认的查本机的码表,web服务器,翻译jsp的时候就是用iso8859-1码表,所以翻译后的servlet中的"中国"将是乱码,所以要改变服务器的编码,所以就用pageEncoding="gb2312"设置服务器翻译的码表,翻译到servlet后"中国"是正常的,但是此时还要设置浏览器以哪个码表打开所以需要设置:contentType="text/html;charset=UTF-8",其实最后的一个设置不需要设置了,因为response是以UTF-8编码的 3. jsp中常用标签 (1).jsp标签也称之为Jsp Action(jsp动作)元素,它用于在jsp页面中提供业务逻辑功能,避免在jsp页面中直接编写java代码,造成jsp页面难以维护. (2). 4. div+css (1).盒子模型:每一块数据都用div套起来,可能控制div的边框,属性border,上边框:Border-top;下边框:Border-bottom;左边框:Border-left;右边框:Border-right,Padding-top,Padding-bottom,Padding-left,Padding-right;控制数据在div盒子中的位置,Margin-top,Margin-bottom,Margin-left,Margin-right:控制一个盒子在页面的位置, (2).div是个行级元素,默认的是div单独排在一行,有时想将两个div排成一行,这是就要用到定位技术:Float和Position两种技术 (3). body{margin:4px 3px 2px 1px;} #father{ background-color:#FFFFEE; width:100; height:100px; border:1px dashed green;//边框是绿色的虚线, } #son1{float:left;} #son2{float:left;} #son3{float:left;}
这个代码是有问题的,当添加
(4).position技术就是将#son1{position:relative;left:60%}
5.jsp九大隐式对象
(1).每个jsp页面在第一次被访问时,web容器都会把请求交给JSP引擎(就是一个Java程序)去处理,JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet),然后按照servlet的调用方式进行调用,由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但是第二次访问,JSP引擎如果发现JSP没有变化,就不在翻译,而是直接调用,所以程序的执行效率不会受到影响,JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用,JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用,
(2).HttpServletRequestrequest,HttpServletResponse response,ServletContextapplication,ServletConfig config JspWriter out,Exception,PageContextpageContext,Object page,Session session,具体的可以查看文档
6.jsp映射和jsp常见错误处理
在web.xml中
7.jsp语法
(1).jsp语法:jsp模板元素,jsp表达式,jsp脚本片段,jsp注释,jsp指令,jsp标签,jsp内置对象,如何查找jsp页面中的错误
(2).jsp脚本表达式(expression)用于将程序数据输出到客户端:语法:<%=变量或表达式%>举例:当前时间:<%=new java.util.Date()%>;jsp引擎在翻译脚本表达式时,会将程序数据转成字符串,然后再相应位置用out.print(...)将数据输出给客户端,jsp脚本表达式中的变量或表达式后面不能有分号(;)
(3).jsp脚本片段<% %>,中写入java代码,原封不动的变成java代码
(4).变量可以再多个脚本片段中是可以相互访问的.
(5).jsp声明:jsp页面中编写的所有代码,默认会翻译到servlet的service方法中,而jsp声明中的java代码被翻译到_jspService方法的外面,语法:<%! java代码%>(多个感叹号),所以,jsp声明可用于定义jsp页面转换成的Servlet程序的静态代码块、成员变量和方法,多个静态代码块、变量和函数可以定义在一个jsp声明中,也可以分别单独定义在多个jsp声明中,jsp隐式对象的作用范围仅限于Servlet的_jspService方法,所以在jsp声明中不能使用这些隐式对象,<% public void run(){} %>这种语法是错误的,<%! public void run(){}%>这种语法是正确的.
(6).注释:<%-- --%> jsp中的注释不会打给浏览器,但是html注释会打给浏览器.
8. out隐式对象
(1).out隐式对象用于向客户端发送文本数据,out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter()方法返回的PrintWriter对象非常相似,JSP页面中的OUT隐式对象的类型为JspWriter,JspWriter相当于一种带有缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存,只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用servletresponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到servlet引擎提供的缓冲区中:设置page指令的buffer属性关闭了out对象的缓存功能,out对象的缓冲区已经满了,整个JSP页面结束.
(2).out.write("hahahahhahha");response.getWriter().write("wowowowo");理论上是先看到hahahah在看到wowowowowwo,但是实际是wowowowowowo hahahahhaha,原因就是out有缓存,JspWriter向缓冲区中写入内容,respose的缓冲中是wowowwowowwo,当jsp结束后,response缓冲区发现JspWriter的缓冲区中有内容,就将其刷新到response,所以先看到wowowowowwo,然后就是hahahahahhaha,就是response写入的内容最先显示,所以在开发过程中不要两种方式都使用,一般使用out.write();
9.pageContext对象
(1).pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其他8大隐式对象的应用,它自身还是一个域用来保存数据,并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其他资源,检索其他对象中的属性等
(2).它的方法:getException()返回exception隐式对象,getPage()方法返回page隐式对象,getRequest()返回request隐式对象,getResponse()返回response隐式对象,getServletConfig返回config隐式对象,getServletContext返回application隐式对象,getSession()方法返回session隐式对象,getOut()方法返回out隐式对象,pageContext封装了其他8中内置对象,用途是在自定义标签中,jsp中最好不要出现java代码,有时想显示数据,又不能有java代码,String data -(String)request.getAttribut("data");out.write(data);在页面中写入一个标签
(3).pageContext也是一个域对象,pageContext.setAttribut("data","aaa");域的生命周期是整个页面的周期,作用域是整个页面,四个域中,作用范围最小的一个,request就是一个请求范围内,session就是在一个会话范围内,servletContext整个web应用
(4).代表各个域的常量:
PageContext.APPLICATION_SCOPE,PageContext.SESSION_SCOPE,PageContext.REQUEST_SCOPE,PageContext.PAGE_SCOPE
(5).封装了访问其他域的方法,方法public java.lang.Object getAttribute(java.lang.Stringname,int scope)是个重载的方法:Stringdata=(String)pageContext.getAttribut("data",PageContext.REQUEST_SCOPE);管理所有域的入口
(6).最重要的一个方法:findAttribute:查找各个域中的属性,pageContext.findAttribut("data")首先从page,request,session,application逐级查找,找不到返回空.用于el表达式,el的执行就是按照这个方法的原理执行的.
(7).引入和跳转到其他资源,PageContext类中定义了一个forward方法和两个include方法来分别简化和代替RequestDispatcher.forward方法和include方法,方法接受的资源如果以"/"开头,就代表当前的web应用
10.jsp语法include指令详解
(1).include指令用于引入其他JSP页面,如果使用include指令引入了其他JSP页面,那么JSP引擎将把这两个JSP翻译成一个Servlet,所以include指令引入通常称之为静态引入
(2).语法:<%@ include file="relativeURL"%>其中的file属性用于指定被引入文件的路径,路径以"/"开头,表示代表当前web应用,细节:被引入的文件必须遵循JSP语法,被引入的文件可以使用任意的扩展名,即使其扩展名为html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用jspf(JSP fragments)作为静态引入文件的扩展名,由于使用include指令将会涉及到2JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的指令不能冲突(除了pageEncoding和导入包除外);
(3).查看源文件,不是格式良好的html页面,就将被包含的页面的头删除
(4).动态包含:request.getRequestDispatcher("/public/head.jsp").inlude(request,response);它会把三个JSP翻译成三个servlet,静态包含只翻译一个Servlet,
(5).在开发过程中两种包含都用到:静态包含编译时包含,性能高,动态包含是运行时包含
第八天:
1. sun公司的jstl标签库
(1).JSTL标签库:核心标签,国际化标签,数据库标签,XML标签,JSTL函数(EL函数)
(2).sun公司的所有标签都放在standard.jar架包中,
(3).
(4).
(5).
(6).
(7).
2.标签案例-打包自己的标签库
(1).首先建一个java工程,将所有的标签处理器类考到这个java工程中,然后将tld文件存放到meta-inf文件夹中,然后将java工程导出为jar
3.标签案例-开发foreach
(1).List list =new ArrayList();然后迭代输出list中的数据
(2).
(3).在doTag()方法中,List list = (List)items;Iteratro it =list.iterator();while(it.hasNext()){Object value =it.next();this.getJspContext().setAttribute(var,value);this.getJspBody().invoke(null);标签中的内容执行回到pagecontext的域中拿去属性var的值,然后输出}
(4).将foreach功能加强可以迭代数组,Map,Collection判断是不是Map,还是Collection,还是Object[],但是要进行类型转换,可能有很多重复代码,所以都直转型Collection collection即可,if(items instanceof Collection){collection =(Collection)items;}if(itemsinstanceof Map){Map map (Map) items;collection = map.entrySet();} if(itemsinstanceof Object[]){Object obj[] =(Object[])items;CollectionArrays.asList(obj);}当碰到基本数据类型时,就需要迭代数组,将数组中的数据放到collection中,
(5).sun公司的foreach标签在standard架包中,在web-inf,c.tld,查找forEach标签对应的类
(6).反射技术,Class类中有一个方法isArray()判断是否是数组类型,不仅适用于任何类型,在反射类中有一个类Array,它有个方法
getLength(),for(inti=0;i 对任意数组进行操作. 4.标签案例-开发if-else标签 (1). (2).同时在父标签中要让其标签体执行,所以在父标签中的doTag()方法,this.getJspBody().invoke(null); (3).得到父标签的变量,所以在子标签的doTag()方法中Choose parent = (Choose)this.getParent();就可以得到父标签的处理器类 5. 标签案例-开发防盗链标签 就是将前面的request的课程中的方法定义为标签处理类即可 6. 标签案例-开发转义 (1).比如超链接的原始输出, StringWriter sw =new StringWriter();JspFragment jf = this.getJspBody();jf.invoke(sw);Stringcontent = sw.getBuffer().toString();服务器的webapps/examples/web-inf/htmlFilter,java中的方法filter(); 7. 标签简介和开发第一个标签 (1).自定义标签主要用于移除jsp页面中java代码,使用自定义标签移除jsp页面中的java代码,只需要完成以下两个步棸:编写一个实现Tag接口的Java类,把页面java代码移动这个java类中(标签处理类),编写标签库描述符(tId)文件,在tId文件中对标签处理器类描述成一个标签, (2).<%request.getRemoteAddr();out.print(ip);%>,显示用户的IP,使用自定义标签,首先将java代码移动到java类,继承Tag接口,标签有开始标签和结束标签,doEndTag()方法,doStartTag()方法,标签可能有爸爸,所以方法getParent()将父标签作为一个对象传给jsp引擎,方法setPageContext()方法最重要,就是传递一个PageContext对象,他同时也携带了其他8个对象,所以将java代码移动到doStartTag();Tag是个接口,所以使用其继承类,TagSupport,自己编写的类只需要覆盖doStartTag();准备好request,response,所以要得到PageContext,又因为服务器在执行doStartTag()方法之前,默认执行了setPageContext()方法了,所以可以写为:HttpServletRequest request =(HttpServletRequest)this.pageContext.getRequest();还有获取out对象,用于输出.移动完java代码后,第二步:就是在标签库中将标签处理器描述标签,可以到tomcat的webapps中自带的一些例子,web-inf/jsp2/taglib.tld. (3). (4).导入标签<@taglib url="http://www.itcast.cin"prefix="itcast"%>:prefix是前缀,可以容易定位到描述标签的文件 (5). (6).tld文件的位置必须放在web-inf中. 8. 传统标签库功能详解 (1).控制标签体内容是否输出:覆写父类的doStartTag()方法,public int doStartTag(){returnTag.EVAL_BODY_INCLUDE}执行标签体,然后再添加tld文件, (2).控制整个JSP是否输出,就在JSP开始头定义一个标签,doEndTag()的返回值是否继续执行余下的JSP内容,只需覆盖父类的doEndTag()方法,public int doEndTag(){return Tag.SKIP_PAGE}不执行JSP内容,然后添加tld文件,一定要将该定义的标签放在JSP开始的部分.返回Tag.EVAL_PAGE就可以执行余下的JSP内容. (3).控制某一部分内容重复执行,只需将重复类容套在自定义的标签中,Tag接口找不到指定的方法,必须要使用其子接口IterationTag接口,其内部中有个方法:doAfterBody(),这个方法在标签体结束后执行,如果返回EVAL_BODY_AGAGIN,就接着执行,直到返回SKIP_BODY,所以继承IterationTag接口,但是TagSupport继承IterationTag,所以还是继承TagSupport即可,首先覆写方法doStartTag()方法,返回Tag.EVAL_BODY_INCLUDE;然后覆写方法doAfterBody(){x--;if(x>0)return IterationTag.EVALBODY_AGAIN;elsereturn IterationTag.SKIP_BODY;}其中x应该定义为成员变量,而不是在doAfterBody的局部变量,因为doAfterBody()方法是多次执行的,不是只执行一次. (4).修改jsp页面的内容,将显示的内容修改成大写的,将内容套在自定义标签中,接口BodyTag接口,此接口继承了IterationTag接口,覆写BodyTagSupport方法doStartTag()返回EVAL_BODY_BUFFERED,会将标签体最为一个对象通过方法setBodyContent传给标签处理器类,然后在doEndTag()方法中BodyCotned bc = this.getBodyContent();得到标签体,String content = bc.getString();content=content.toUpperCase;然后通过pageContext输出,最后要记得返回EVAL_PAGE,执行余下的jsp内容,就拿到标签体中的内容,然后对内容进行操作. (5).IterationTag接口的子类为TagSupport类,TagSupport类的子类为BodyTagSupport类,同时BodyTagSupport类是实现BodyTag接口,同时BodyTag接口继承了IterationTag接口,IterationTag接口继承了Tag接口,同时Tag接口和SimpleTag接口都继承JspTag接口,但是Tag接口时JSP2.0以前定义的,现在不用了,而现在主要用的就是SimpleTag接口,其子类为SimpleTagSupport类.所以称Tag接口为传统标签,SimpleTagSupport为简单标签 9. 简单标签库功能详解 (1).SimpleTag接口集成了以前的Tag,IterationTag,BodyTag三个传统标签的功能,所以SimpleTag有个默认实现类SimpleTagSupport (2).JspContext就是PageContext,方法,setJspBody(JspFragment jspBody),服务器通过这个方法将PageContext对象传递给标签处理器,然后调用doTag()方法,没有开始和结束标签,doTag()方法中抛出个异常,返回SKIP_PAGE控制整个页面不输出, (3).简单标签的一般逻辑都在doTag()方法中,JspFragment jf = this.getJspBody();jf.invoke(this.getJspContext().getOut());简单标签中写成 (4).如果想让标签体不执行,jf.invoke()方法不执行就可以了 (5).jf就是标签体,可以写成:for(int i=0;i<5;i++)jf.invoke(null);这样写也行,就是默认的也是写给浏览器,重复将标签体 (6).StringWritersw = new StringWriter();jf.invoke(sw);将jf的标签体的内容输入到自己的缓冲,可以自己定义一个缓冲流,String content = sw.toString();content =content.toUpperCase(); (7).控制整个标签是否执行,只需在doTag()方法中抛出异常,throw new SkipPageException();即可.同时这个标签也要放在jsp页面的开始处,jsp余下的内容就不执行了 (8).jsp运行时遇到简单标签,实例化标签处理器类,然后通过调用对象setJspContext()方法,将PageContext对象传递给标签处理器类,接着调用setParent()方法将父标签传递给标签处理器类,然后把标签体的内容封装JspFragment对象传递给标签处理器类,然后调用doTag()方法执行自定义标签,执行完后,标签就变成垃圾了,等待垃圾回收器回收,这一点和传统的自定义标签不同 10.开发带属性的标签 (1).要想让一个自定义标签具有属性,通常需要完成两个任务:在标签处理器中编写每个属性对应的setter方法,在TLD文件中描述标签的属性,为自定义标签定义属性时,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收JSP页面中调用自定义标签时传递过来的属性值,例如属性url,在标签处理器类中就要定义相应的setUrl(String url)方法,在标签处理器中定义相应的set方法后,JSP引擎在解析执行开始标签前,也就是调用doStartTag方法前,会调用set属性方法为标签设置属性 (2).在tld文件中声明: (3).count="5"属性传递过去的是字符串,但是count是int型的,服务器自动转型,但是只支持8中基本类型 11.自定义标签功能概述 (1).自定义标签功能扩展,开发人员在编写JSP页面时,经常还需要在页面中引入一些逻辑例如:控制jsp页面某一部分内容是否执行,控制整个jsp页面是否执行,控制jsp页面内容重复执行,修改jsp页面内容输出, (2).希望有段jsp代码只有权限的人才能访问,就是控制某一部分内容是否执行,格式化页面输出的内容. 12.自定义标签运行原理 (1).ie->web服务器->1.jsp(1.servlet)[ (2).jsp翻译成Servlet中可以看到service方法,调用自定标签的动作放在方法中,首先获取pageContext,将标签处理器类的对象创建出来, 第九天: 1. el表达式和jstl快速入门 (1).EL表达式用于获取数据,在jsp页面中可以使用${标示符}的形式,通知JSP引擎调用pageContext.findAttribute()方法,以标示符为关键字从各个域对象中获取对象,如果域对象中不存在标示符所对应的对象,则返回结果为""(注意不是null),EL表达式中也可以使用$(customerBean.address)的形式来访问JavaBean对象的属性,结合JSTL标签,EL表达式也可以轻松获取各种集合中的元素,EL表达式也可以使用类如${1==1}的新办公室进行简单的逻辑判断 (2).<% Person p= new Person();p.setName("aaa");request.setAttribute("person",p);%> ${person.name} (3).用el表达式在取数据时,通常用.号,若取不出来,就用[],${map['aaa'].name}; (4).${pageContext.request.contextPath}获取当前web应用的路径,点击 (5). 2. jsp和javabean (1).javabean是一个遵循特定写法的Java类,它通常具有如下特点:这个Java类必须具有一个无参的构造函数,属性必须私有化,私有化的属性必须通过public类型的方法暴露给其他程序,并且方法的命名也必须遵守一定的命名规范 (2).javabean在j2ee开发中,通常用于封装数据,对于遵循以上写法的javabean组件,其他程序可以通过反射技术实例化javabean对象,并且通过反射那些遵守命名规范的方法,从而获知javabean的属性,进而调用其属性保存数据 (3).属性的修改器和访问器就是set,get; (4).JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,他们分别是: (5). (6). 3. mvc开发模式 (1).按照三层结构开发流程:servlet,jsp是web层,service,javabean是业务逻辑层(service层),dao是数据访问层(dao层),通常在层与层之间定义接口,所以在dao层和service层之间定义dao接口,同理在web层和service层之间定义一个service接口,dao层使用jdbc,hibernate编写,当dao层修改了,service的代码不需要修改,因为在service中使用的的是dao接口 (2).组织包接口:Cn.itcast.domain:存放javabean类,cn.itcast.dao存放dao层的类,cn.itcast.dao.impl:存放dao层接口,cn.itcast.service:存放service层类,cn.itcast.service.impl:存放service层接口,cn.itcast.web.controller:存放servlet的类,cn.itcast.web.listener:存放监听器类,cn.itcast.web.filter:存放过滤器类,cn.itcast.web.util:存放工具类,cn.itcast.juit.test:存放测试类,jsp页面放在web-inf下的jsp文件夹下. 二、 Struts1 1. ActionForm的工作流程分析 (1).ActionForm的工作原理:处理ActionForm的一般步骤: 第一步:检查Action的映射,确定Action中已经配置了对ActionForm的映射 第二步:根据name属性,查找form-bean的配置信息 第三步:检查Action的form-bean的使用范围,确定在此范围下(request,session),是否已经有此form-bean的实例 第四步:假如当前范围下,已经存在了此form-bean的实例,而是对当前请求来说,是同一种类型的话,那么就重用 第五步:否则,就重新构建一个form-bean的实例(调用构造方法),并且保存在一定作用范围 第六步:form-bean的reset()方法被调用 第七步:调用对应setter方法,对状态属性赋值 第八步:如果validate的属性设置为true,那么就调用form-bean的validate()方法 第九步:如果validate()方法没有返回任何错误,控制器将ActionForm作为参数,传给Action实例的execute()方法并执行 注意:直接从ActionForm类继承的reset()和validate()方法,并不能实现什么处理功能,所以要自己重新覆盖方法 2. ActionForm相关的attribute属性 (1).配置文件简介:使ActionServlet,ActionMapping,Action,ActionForm这几个不同层次的组件相互协调工作,这些配置文件是在系统启动的时候,读入到内存中,供控制器使用的 (2). (3).ActionForm相关的input属性 input属性是用来记录路径:当validate校验不通过,即ActionForm的validate方法返回为ActionErrors对象,且该对象携带一些错误信息,就跳转到指定的错误页面(action)的路径,一般是结合validate=true是结合使用的,当为false时,input属性就没有意义了. ActionForm获取用户的请求参数,其属性的名称必须要和请求参数名相同,必须要定义对应的get/set方法 3. ActionForm相关的validate属性 (1).数据校验:判断用户带来的数据是否符合规定的格式. (2).服务器端的校验:看ActionForm有没有调用validate(),可以返回ActionErrors对象,此对象返回一个错误封装对象,没有错误就返回null,缺省的情况是返回null,所以子类要覆盖这个方法.也可以通过在actionForm子类的配置中设置validate="false"的值,使该方法不调用 复位:是总控制器恢复bean属性的默认值 4. ActionForward的有关问题 (1).ActionForward对象的配置对象,这些配置对象拥有独一无二的标识以允许他们按照name属性等来检索,ActionForward对象封装了向前进的URL路径且被请求处理器用于识别目标视图 (2).其内部的方法就是两种方法:request.Dispatch().forward();和response.sendRedirect(),前面讲到的重定向和转发. ActionForward:Redirect是false,就是容器内跳转,就是Request.Dispathcer.forward();在这种情况下path为相对路径是true,就是容器外跳转,就是Response.sendRedirect(),在这种情况下path为绝对路径,要在域名前加上协议头错误!超链接引用无效。 6.ActionMapping的深入研究 (1).ActionMapping:每个 (2).mapping.getName();mapping.getPath();mapping.getType();mapping.findForwards();返回局部跳转的页面信息,即就在一个action内部. 7.Action的深入研究和分析 (1).怎么测试Action实例化了,根据构造函数即可,public Action(){System.out.println("Action isrunning!");}Action在发出请求时初始化,不是在读取配置时初始化,每个action只会初始化一次,即使在不同的会话中(打开两个浏览器).内存中只有一份,资源可以共享和重用,但是不安全.对以多个请求只创建一个action,含有并发问题.需要进行同步.struts2.x是安全的, (2).安全的话注意两点:第一、不要用类变量或实例变量共享只是针对某个请求的数据,第二、注意资源操作的同步性 (3).一个action被调用多少次?这是就可以使用action的不安全性,定义一个变量count即可 8.bean-message标签的讲解 bean-message:国际化信息,key是信息的关键字,其对应value是资源文件 bean-message的使用: 第一步:定义资源文件, com.itcast.ApplicationResources.properties;com.itcast.ApplicationResources_zh-cn;ApplicationResoruces是基名,后面可以跟不同国家的名称 第二步:在struts-config中添加: 例如: 第三步:在页面中使用:bean:message test e="myKey"key="userName"/> 例如:
第四步:切换浏览器的语言类型,工具-->Internet选项->常规->语言
在struts-config.xml文件内的
9.bean-write标签的讲解
从哪个域中输出指定bean以及bean的一些属性
10. DispatchAction的讲解
(1).实现一个模块,需要对学生信息进行CRUD操作:
AddStudentAction
DeleteStudentAction
UpdateStudentAction
QueryStudentAction
分别调用四个类的execute方法,但是四个定义四个类有点过多.所有可以合并相关的Action
(2).特殊的Action的使用:DispatchAction,起作用是可以减少Action的数量,使用方法:继承DispatchAction,在里面添加上需要使用的所有方法,参数和返回值与原来的execute方法完全一样,配置文件中带上parameter属性,如parameter="opertationType";使用的时候才用如下形式:/*.do?operationType=add
(3).一个Action可以调用CRUD的方法,DispatchAction中的四个方法的参数和返回类型都和原先的execute方法一样,就是将execute复制四份,同时将方法的名称改为addStudent,deleteStudent,updateStudent,queryStudent即可,然后在这四个方法中编写响应的代码.
(4).在地址栏中输入..../DispatchAction.do?operationType=addStudnt,就是访问增加学生页面
(5).第一步:可以开发一个DispatchAction的子类,这里的方法注意必须与原来的execute方法同参数同返回值
第二步:配置parameter属性
11.ForwardAction的讲解
(1).访问jsp访问形式:需要统一访问模式*.do;所有的jsp在web-inf下,所以请求jsp之前需要先访问action
(2).因为每个jsp对应一个Action,可能要许多Action,所以我们只定义一个跳转Action:ForwardAction 访问:/testForwardAction.do,目的是统一以*.do形式访问所有的模块;
12. logic_iterate标签的讲解
(1).逻辑标签:逻辑库的标记能够用来处理外观逻辑而不需要使用scriptlet,struts逻辑标签库包含的标记能够有条件的产生输出文本,在对象集合中循环从而重复的产生输出文本,以及应用程序流程控制,它也提供了一组在jsp页面中处理流程控制的标记,这些标记封装在文件名为struts-logic.tld的标记包中,逻辑标记库定义的标记能够执行下列三个功能:条件逻辑,重复,转发/重定向响应
(2).Logic:iterate的使用:单重循环,双重循环,对指定的集合的循环,这个集合必须是一个Iterator,Collection,Map,Array;
(3).
<%String[]usernames={"aa","bb","cc","dd"};
request.setAttribute("usernames",usernames);%>
${username}
name所指代的bean必须是一个集合类型
name+property:每一个人的所有爱好:
User mengfanlong = new User();
mengfanlong.setUsername("mengfanlong");
String[]mengfanlongFavorites={"sports","sleep","study"};
mengfanlong.setFavorites(mengfanlongFavorites);
用ArrayList存储用户
${user.username}
${favorite}
(4).id是迭代时的临时属性,还有其他属性:length,offset,indexId等控制循环变量
13.struts插件的讲解
(1).插件(Plugin)生命周期方法:init,destroy,用户提供setter方法,告诉ActionServlet中心控制器把属性设置
(2).应用:在struts启动时把hibernate加载进来,就是要把hibernate的配置文件读进来,同时打开hibernate的sessionfactory
需要struts.jar包+hibernate.jar包
设计一个类 HibernatePlugin,实现plugin接口,读取hibernate配置文件,打开SessionFactory;
在struts-config.xml配置文件中添加一对
(3).
(4).struts当服务启动时启动,插件在struts启动时读取配置文件时启动.可以在中心控制器ActionServlet的init方法对plugin初始化,destroy销毁了
(5).服务启动-->ActionServlet->读取struts-config.xml->根据各种标签的内容进行一系列的初始化(plugin,ActionMapping)
14.struts的MVC组件
(1).组件:ActionServlet,ActionClasses,ActionMapping,ActionForward,ActionFormBean
(2).struts中的MVC:
第一:模型:本质上来说在struts中model是一个商业逻辑类,开发者实现商业逻辑
第二:视图:View是由与控制器Servlet配合工作的一整套JSP定制标签库构成,利用他们可以快速建立应用系统的界面
第三:控制器:前端控制器是一个Servlet,它将客户端请求转发到相应的后端控制器Action类
15.struts的工作原理
第一步:初始化,读取struts-config.xml,Struts框架总控制器(ActionServlet)是一个Servlet,在web.xml中配置成自动启动的Servlet,读取配置文件(struts-config.xml)的配置信息,为不同的struts模块初始化相应的ModuleConfig对象:ActionConfig、ControlConfig、FormBeanConfig、ForwardConfig、MessageResourceConfig
第二步:等待Http请求:用户提交表单或调用URL向Web应用服务器提交一个请求,请求的数据用HTTP协议上传给Web服务器
第三步:填充FormBean:实例化、复位、ActionServlet拿到用户的数据填充FormBean、校验、保存等,(*.do)从ActionConfig中找到对应该请求的Action子类,如没有对应的Action,控制器直接转发给JSP或静态页面,如有相应的Action且这个Action有一个相应的ActionForm,ActionForm被实例化并用HTTP请求的数据填充其属性,并且保存在ServletContext中(request或session中),这样它们就可以被其它Action对象或者JSP调用
第四步:将请求转换到具体Action处理:控制器根据配置信息ActionConfig将请求派发到具体的Action,相应的FormBean一并传给这个Action的execute()方法.(后台控制器)
第五步:调用后台的业务功能类完成商务逻辑:Action一般只包含一个execute方法,它负责执行相应的业务逻辑(调用其他业务模块),完成后返回一个ActionForward对象,控制器通过该ActionForward对象来进行转发工作.
第六步:返回响应对象:Action根据业务处理的不同结果返回一个目标响应对象给总控制器(前端控制器actionservlet),该目标响应对象对应一个具体的JSP页面或另一个Action
第七步:转换Http请求到目标响应对象(jsp):总控制器根据业务功能action返回的目标响应对象,找到相应的资源对象,通常是一个具体的JSP页面
第八步:Http响应:前几步都在服务器端,这一步在客户端,目标响应对象将结果展现给用户目标响应对象(jsp)将结果页面展现给用户
控件:总控制器ActionServlet的作用最大,Action(execute)ActionForm,配置文件,将各个模块连接起来
注意web.xml和struts-config.xml的区别,总控制器ActionServlet要在web.xml中注册,ActionForm中的属性,Action等的信息都在struts-config.xml中注册.
16.Struts相关基础理论介绍
(1).案例:实现一个用户登录功能,如果用户输入的用户和密码都正确,就跳转到登录成功页面,否则跳转到登录错误页面
(2).知识点:为什么要使用Struts,FrameWork的概念,Struts的概念和体系结构,Struts的工作原理,Struts的组件,Struts配置文件简介,Struts标记库,错误处理框架,校验框架,高级特性,Struts优缺点
(3).框架(Framework):人们用于解决相同或者相似类型问题的方案,可重用性,可扩展性,可收缩性
(4).Struts是Apache组织的一个开源项目,主要是采用了servlet和jsp技术来实现的,是基于Sun JavaEE平台开发的
17.struts中的异常处理
(1).异常的作用:增加健壮性,模块间传递信息
(2).配置异常:定制异常有两种:全局异常(所有Action使用的)和局部异常(一个Action使用的),
(3).全局异常的定义方法:
局部异常的定义方法:
(4).当Action的execute方法抛出异常时,调用异常处理器类ExceptionHandler
(5).怎么使用:
第一步:配置
第二步:在相应的action中的execute方法抛出异常
第三步:在异常处理页面(path所指页面)使用html:errors标签打印提示信息
18.struts注册程序的组件设计和流程分析
(1).编写action,actionform,首先编写actionform,因为action中要用到actionform,在struts-config.xml中注册
actionform,action
,还有跳转页面:
(2).对action的编写要控制线程安全
(3).ActionServlet将请求参数封装成FormBean的属性时进行转换而导致的结果,ActionServlet内部调用BeanUtil这个工具包来将字符串类型的请求参数转换成FormBean中对应的属性的类型,然后再将转换结果装配到FormBean中,这时候可以打印出FormBean中各个属性的结果看看,就大概清楚BeanUtil转换后的结果了,我们在以后的项目实战课程中有对此问题的详细分析和巧妙的解决方案
19.搭建struts开发环境
导入jar包,使用相关类,建立一个配置文件:struts-config.xml 放在web-inf下,web.xml注册struts中心控制器---ActionServlet,注意事项:struts配置文件的位置,预先加载控制器,可以到struts自带的例子程序,从这些例子程序中的web-inf项目下拷贝struts-config.xml文件
20.动态FormBean的讲解
(1).动态form不需要用户自己写代码ActionForm
(2).
需要告诉form-bean的名称,属性名,属性的类型等信息,其中type必须为包装类型.
(3).提交一个表单数据,用数据填充动态表单,在Action中的execute方法中,
DynaActionForm addStudentForm =(DynaActionForm)form;
String sname =(String)addStudentForm.get("sname");
java.sql.Date birth =(java.sql.Date)addStudentForm.get("birth");
21.分析struts程序的执行流程
(1).ie->服务器->控制器ActionServlet(前端控制器)(送ActionForm给后端控制器)->LoginAction(后端控制器,第一句要进行转型LoginForm loginForm = (LoginForm)form;)->前端控制器->显示页面
(2).if(loginForm.getUsername().equals("itcast")){returnURLKeyWord= "loginSuccess";}else{returnURLKeyWord="loginFailure";}
(3).前端控制器是根据struts-config.xml配置文件找到后端控制器,跳转到成功或者失败的页面也是根据struts-config.xml配置文件的.后端控制器才是真正执行代码.
(4).问题:谁来填充From?,什么时候填充?,根据什么内容来填?
ActionServlet怎样把请求派发给Action?
Action运行完后怎样跳转?
22.分析自己写struts框架的思路
开发以下类:
ActionServlet:读取配置文件dom4j,填充form,派发请求:调用对应的action的execute方法,查找响应,跳转
ActionForm{reset(),validate()}
Action{execute(ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse}
ActionMapping{path,name,type,validate}使用HashMap存储
ActionForward{name,path}使用HashMap存储
配置文件struts-config.xml
23.配置全局跳转
ActionA----->Error.jsp
ActionB----->Error.jsp
ActionC----->Error.jsp
以前的方法是在三个Action中都写上forward
全局跳转:Action A,B,C---->Error.jsp,在struts-config.xml文件中添加标签:
24.通过bean_define标签入门struts标签库
(1).JSP视窗组件所使用的struts标记库由四类标记组成:Bean标记:用来在JSP页面中管理bean,Struts-bean.tld;逻辑标记:用来在JSP页面中控制流程,Struts-logic.tld;HTML标记:用来生成HTML标记,在表单中显示数据,使用会话ID对URL进行编程Struts-html.tld;tiles标记:使用动态模板构造普通格式的页struts-tiles.tld
(2).Bean标记,这个标记库中包含用于定义新bean、访问bean及其属性的标记,Bean标记库将标记定义在四个子类别中:创建和复制bean的标记,脚本变量定义标记,bean翻译标记,消息国际化标记.
(3).Bean:define:从已经有的变量或者变量的属性定义一个新的变量:id,name,property,scope(老bean),toScope(新bean),
查找:到scope中查找name的变量获取它的property,
定义:定义一个新变量的名字(id),同时这个属性是必须的
保存:将新变量保存到toScope
25.通过代码了解ActionForm的基本工作流程
(1).通过ActionForm的构造函数来观察是否被实例化了,通过reset()方法来观察是否执行了复位方法.set()方法,观察他们三者的运行顺序,
(2).首先调用构造方法,然后调用reset方法,然后在调用set方法,复位只需要只对请求复位.
26.用struts开发简单的登陆示例程序
(1).建一个类,继承ActionForm,注册,修改配置文件struts-config.xml
(2).public classLoginForm extends ActionForm{
private String username = null;
private String password = null;
}
(3).在struts-config-xml文件中添加一个标签
(4).开发Action,建立一个类,继承Action,覆盖execute方法,需要强制转型,调用其他模块,跳转(根据关键字,关键字参照action中forward标签中的name属性),注册:修改配置文件struts-config.xml,Path:指明调用者(jsp)能通过中心控制器ActionServlet找到Action,Type:指明该action类全名,Name:该action引用的form的名称
(5).public classLoginAction extends Action{覆盖execute方法}
27.用监听器探索ActionForm如何被存储
(1).怎样检查ActionForm被存储了,有两种方法:一种是从过程去查看,另一种从结果去查看.
(2).从过程查看,通过监听器,从结果查看execute方法
(3).制作监听器:定义一个类,AttributeListener,然后实现两个接口,HttpSessionAttributeListener,HttpRequestAttributeListener,在web.xml中写入:
28.在execute方法中分析ActionForm的问题
(1).从结果查看,
AddStudentFormaddStudentFormInSession
=(ActionForm)request.getSession().getAttribute("addStudentForm");
或者
AddStudentFormaddStudentFormInScope
=null;if(mapping.getScope().equals("request")){addStudentFormInScope
=(AddStudentForm)request.getAttribute("addStudentForm");}else{addStudentFormInScope
=(AddStudentForm)request.getSession().getAttribute("addStudentForm");}从不同的域中获取ActionForm
(2).参数form是和存储在域中的form是相同的(ActionForm);
(3).没有sname成员变量,但是有setName()方法,所以准确来说是看是否有setName();页面中的控件中的name是否和ActionForm中成员变量的名称一样,其实不是看成员变量,而只是看标准的set方法,通过反射技术执行.
三、 Struts2
1. Action名称的搜索顺序
(1).获取请求路径的URL,例如URL是:http://server/struts2/path1/path2/path3/test.action
(2).首先寻找namespace为/path1/path2/path3的package,如果不存在这个package则执行步棸3,如果存在这个package,则在这个package中寻找名字为test的action,当在该package下寻找不到action时就会直接跑到默认namespace的package里面中寻找action(默认的命名空间为空字符串),如果在默认namespace的package里面还寻找不到该action,页面提示找不到action
(3).寻找namespace为/path1/path2的package,如果不存在这个package,则转至步棸4,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认的namespace的package里面还寻找不到该action,页面提示找不到action
(4).寻找namespace为/path1的package,如果不存在这个package则执行步棸5,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认namespace的package里面还寻找不到该action,页面提示找不到action
(5).寻找namespace为"/"的package,如果存在这个package,则在这个package中寻找名字为test的action,当在package中寻找不到action或者不存在这个package时,都会去默认namespace的package里面寻找action,如果还是找不到,页面提示找不到action
(6).在struts2中默认的处理后缀是.action,不加后缀也可以
2. Action配置的各项默认值
(1).struts1中:
(2).在struts2中,
(3).Action配置中的各项默认值:
如果没有为action指定class,默认是ActionSupport,可以查看ActionSupport的源代码,首先交给ActionSupport类处理.
如果没有为action指定method,默认执行action中的execute()方法,这个方法的返回值为"success";
(4).如果没有指定result的name属性,默认值为success,正好和execute方法的返回值相同,所以可以实现视图的转发
3.OGNL表达式
(1).OGNL表达式:OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目,struts2框架使用OGNL作为默认的表达式语言
第一:相对EL表达式,它提供了平时我们需要的一些功能,如:
支持对象方法的调用,如:xxx.sayHello();
第二:支持类静态方法调用和值访问,表达式的格式为@[类全名 (包括包路径) ]@[方法名|值名],例如:@java.lang.String@format('foo %s,'bar')或@cn.itcast.Constant@APP_NAME
第三:操作集合对象
(2).OGNL有一个上下文(Context)概念,说白了上下文就是一个MAP结构,他实现了java.utils.Map接口,在struts2中上下文(context)的实现为ActionContext
(3).struts2中的OGNLContext是实现者为ActionContext,它的结构为:OGNL Context:ValueStack(值栈,他是根对象),parameters,request,session,application,attr,当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action,然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问
(4).当要访问某个对象只需在其前面加上一个'#',例如:#request,当然有一个特殊的例子,就是根对象,会省略'#',OGNL会设定一个根对象(root对象),在struts2中根对象就是ValueStack(值栈),如果要访问根对象(即ValueStack)中的对象的属性,则可以省略#命名空间,直接访问该对象的属性即可.
(5).在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的root变量,就是使用它存在一组对象,在root变量中处于第一位的对象叫栈定对象(存放action),通常我们在OGNL表达式里直接写上属性的名称即可访问root变量对象的属性,搜索顺序是从栈定对象开始寻找,如果栈定对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。大家注意:struts2中,OGNL表达式需要配合struts标签才可以使用,如:
(6).由于ValueStack(值栈)是struts2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性:
${foo}获得值栈中某个对象的foo属性
如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀
第一:application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username");
第二:session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName");
第三:request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request['userName'],相当于调用request.getAttribut("userName");
第四:parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username");
第五:attr对象:用于按page->request->session->application顺序访问其属性.
(7).为何使用EL表达式能够访问valueStack中对象的属性:原因是struts2对HttpServletRequest做了进一步的封装,简单代码如下
public class StrutsRequestWrapper extends HttpServletRequestWrapper{
publicStrutsRequestWrapper(HttpServletRequest req){
super(req);
}
public Object getAttribut(String s){
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(s)//先从request范围获取属性值
if(ctx !=null){
if(attribute==null){
....
ValueStack stack =ctx.getValueStack();
attribute=stack.findValue(s);//寻找规则就是前面的寻找规则
....
}
}
return attribute
}
}
EL表达式只能访问ValueStack对象中的内容
(8).采用OGNL表达式创建List/Map集合对象,如果需要一个集合元素的时候(例如List对象或者Map对象),可以使用OGNL中同集合相关的表达式,使用如下代码直接生成一个List对象:
Set标签用于将某个值放入指定范围
scope:指定变量被放置的范围,该属性可以接受application,session,request,page或action.如果没有设置该属性,则默认放置在OGNL Context中.
value:赋给变量的值,如果没有设置该属性,则将ValueStack栈顶的值赋给变量
生成一个Map对象:
数字不用任何符号,字符串使用单引号('),对于Map采用的是maps.entrySet()这个方式进行迭代的.
(9).property标签用于输出指定值:
default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值
escape:可选属性,指定是否格式化HTML代码
value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值
id:可选属性,指定该元素的标识
(1)0.对于集合类型,OGNL表达式可以使用in和not in两个元素符号,其中in表达式用来判断某个元素是否在指定的集合对象中,not in判断某个元素是否不在指定集合对象中,如下所示:
in表达式:
在
不在
no in 表达式
不在
在
(11).OGNL表达式的投影功能,除了in和not in之外,OGNL还允许使用某个规则获得集合对象的子集,常用的有以下3个相关操作符:
第一:?:获得所有符号逻辑的元素
第二:^:获得符合逻辑的第一个元素
第三:$:获得符合逻辑的最后一个元素
例如代码:
在上面代码中,直接在集合后紧跟{}运算符表明用于取出该集合的子集,{}内的表达式用于获取符合条件的元素,this指的是为了从大集合books筛选数据到小集合,需要对大集合books进行迭代,this代表当前迭代的元素,本例的表达式用于获取集合中价格大于35的书集合.
public class BookAction extends ActionSupport{
private List
@Override
public String execute(){
books = new LinkedList
books.add(new Book("aadfsd","spring",23));
books.add(newBook("basdfd","ejb3.0",15));
}
}
4.result配置的各种视图转发类型
(1).在struts1中有两种视图转发类型:容器内转发,容器外转发(重定向);
(2).struts2中的视图转发类型:result配置类似于struts1中的froward,但是struts2中提供了多种结果类型,常用的类型有:dispatcher(默认值)、rdierect、redirectAction、plainText;dispatcher对应于struts1中的容器内部请求转发,redirect对应于struts1中的容器外部转发(重定向)
(3).
在result中还可以使用${属性名}表达式访问action中的属性,表达式里的属性名对应的action中的属性,如下:
(4).下面是redirectAction结果类型的例子,如果重定向的action中同一个包下:
如果重定向的action在别的命名空间下:
当添加用户完后,可以回到一个用户列表,此时可以重定向到action
(5).plaintext显示原始文件内容,例如:当我们需要原样显示JSP文件源代码的时候,我们可以使用此类型
在Eclipse中jsp是用UTF-8编码存放的,当读取jsp的内容时,是用本地字符编码的,可能出现乱码,所以要设置读取文件的编码集.
(6).浏览器重定向的JSP不能放在web-inf目录中,而请求转发的JSP可以放在web-inf目录中
(7).struts2中的全局视图:
和struts1中的全局视图是很相似的
我们可以定义一个包,然后将全局视图的配置放到这个包中
5.struts2常用标签
(1).property标签用于输出指定值:
default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值
escape:可选属性,指定是否格式化HTML代码
value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值
id:可选属性,指定该元素的标识
(2).iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组
red
value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合,
id:可选属性,指定集合里元素的id(已被标注为过时)
status:可选属性,该属性指定迭代时的iteratorStatus实例,该实例包含如下几个方法:
int getCount(),返回当前迭代了几个元素
int getIndex(),返回当前迭代元素的索引
boolean isEven(),返回当前被迭代元素的索引是否为偶数
boolean isOdd(),返回当前被迭代元素的索引是否是奇数
boolean isFirst(),返回当前被迭代元素是否是第一个元素
boolean isLast(),返回当前被迭代元素是否是最后一个元素
(3).
23
给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow