
斯人若彩虹,遇上方知有。
JNDIUtil、DBCPUtil、C3P0Util,三种数据源的工具类答: 为什么使用JNDIUtil呢?因为它更接近于实际开发,因为它是配置在tomcat里面的,JNDI是一个容器,是一个Map集合, 是key和value的形式。通过配置文件使用。 DBCP相对差一点啦! C3P0更多是用在和Hibernate进行整合的时候,C3P0在Hibernate用的较多。 但是不绝对哦!用啥都行! 详情未完待续。。。。。。我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
win10中,在命令行情况下,Mysql显示各个端所使用的字符集命令: Microsoft Windows [版本 10.0.16299.64](c) 2017 Microsoft Corporation。保留所有权利。C:\Windows\system32>mysql -u root -prootWelcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 1Server version: 5.5.27 MySQL Community Server (GPL)Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> create database day28;Query OK, 1 row affected (0.01 sec)mysql>mysql> use day28;Database changedmysql>mysql> #用户表mysql> CREATE TABLE S_User( -> userID INT NOT NULL AUTO_INCREMENT, #主键ID -> userName VARCHAR(50) NULL, #用户姓名 -> loginName VARCHAR(50) NULL, #登录名 -> loginPwd VARCHAR(50) NULL, #密码# -> gender VARCHAR(10) NULL, #性别(例如:男,女) -> birthday VARCHAR(50) NULL, #出生日期 -> education VARCHAR(20) NULL, #学历(例如:研究生、本科、专科、高中) -> telephone VARCHAR(50) NULL, #电话 -> hobby VARCHAR(20) NULL, #兴趣爱好(例如:体育、旅游、逛街) -> path VARCHAR(500) NULL, #上传路径(path路径) -> filename VARCHAR(100) NULL, #上传文件名称(文件名) -> remark VARCHAR(500) NULL, #备注 -> PRIMARY KEY (userID) -> );Query OK, 0 rows affected (0.02 sec)mysql>mysql> #初始化数据:默认用户名和密码是adminmysql> INSERT INTO s_user (userID,userName,loginName,loginPwd) VALUES (1,'超级管理员','admin','admin');ERROR 1366 (HY000): Incorrect string value: '\xB3\xAC\xBC\xB6\xB9\xDC...' for column 'userName' at row 1 #客户端传来的数据的编码是gbk,而Mysql针对客户端默认使用的是utf8,所以会报错mysql>mysql> show variables like 'character%'; #显示各个端所使用的字符集,只对当前对话窗口有用+--------------------------+---------------------------------------------------------+| Variable_name | Value |+--------------------------+---------------------------------------------------------+| character_set_client | utf8 || character_set_connection | utf8 || character_set_database | utf8 || character_set_filesystem | binary || character_set_results | utf8 || character_set_server | utf8 || character_set_system | utf8 || character_sets_dir | C:\Program Files\MySQL\MySQL Server 5.5\share\charsets\ |+--------------------------+---------------------------------------------------------+8 rows in set (0.01 sec)mysql> set character_set_client=gbk; #设置Mysql针对客户端使用字符集为gbk编码,报错解决Query OK, 0 rows affected (0.00 sec)mysql> INSERT INTO s_user (userID,userName,loginName,loginPwd) VALUES (1,'超级管理员','admin','admin');Query OK, 1 row affected (0.01 sec)mysql> select * from s_user;+--------+-----------------+-----------+----------+--------+----------+-----------+-----------+-------+------+----------+--------+| userID | userName | loginName | loginPwd | gender | birthday | education | telephone | hobby | path | filename | remark |+--------+-----------------+-----------+----------+--------+----------+-----------+-----------+-------+------+----------+--------+| 1 | 瓒呯骇绠$悊鍛? | admin | admin | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |+--------+-----------------+-----------+----------+--------+----------+-----------+-----------+-------+------+----------+--------+1 row in set (0.00 sec) #因为Mysql针对默认结果集使用的是utf8编码,而我们的pc端使用的是gbk编码,所以会报错mysql> set character_set_results=gbk; #设置Mysql针对数据返回结果集使用字符集为gbk编码,报错解决Query OK, 0 rows affected (0.00 sec)mysql> select * from s_user;+--------+------------+-----------+----------+--------+----------+-----------+-----------+-------+------+----------+--------+| userID | userName | loginName | loginPwd | gender | birthday | education | telephone | hobby | path | filename | remark |+--------+------------+-----------+----------+--------+----------+-----------+-----------+-------+------+----------+--------+| 1 | 超级管理员 | admin | admin | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |+--------+------------+-----------+----------+--------+----------+-----------+-----------+-------+------+----------+--------+1 row in set (0.01 sec)mysql> 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
值栈(ValueStack) Struts2将OGNL上下文设置为Struts2中的ActionContext(内部使用的仍然是OgnlContext),并将值栈设为OGNL的根对象。 我们知道,OGNL上下文中的根对象可以直接访问,不需要使用任何特殊的“标记”,而引用上下文中的其他对象则需要使用“#”来标记。由于值栈是上下文中的根对象,因此可以直接访问。那么对于值栈中的对象该如何访问呢?Struts2提供了一个特殊的OGNLPropertyAccessor,它可以自动查找值栈内的所有对象(从栈顶到栈底),直接找到一个具有你所查找的属性的对象,找不到,就报错。也就是说,对于值栈中的任何对象都可以直接访问,而不需要使用“#”。 假设值栈中有两个对象:student和employee,两个对象都有name属性,student有学号属性number,而employee有薪水属性salary。employee先入栈;而student后入栈,位于栈顶,那么对于表达式name,访问的就是student的name属性,因为student对象位于栈顶;表达式salary,访问的就是employee的salary属性。正如你所见,访问值栈中的对象属性或方法,无须指明对象,也不用“#”,就好像值栈中的对象都是OGNL上下文中的根对象一样。这就是Struts2在OGNL基础上做出的改进。 值栈中的Action实例 Struts2框架总是把Action实例放在栈顶。因为Action在值栈中,而值栈又是OGNL中的根,所以引用Action的属性可以省略“#”标记,这也是为什么我们在结果页面中可以直接访问Action的属性的原因。 Struts2中的其他命名对象 Struts2还提供了一些命名对象,这些对象没有保存在值栈中,而是保存在ActionContext中,因此访问这些对象需要使用“#”标记。这些命名对象都是Map类型。 parameters 用于访问请求参数。如:#parameters['id'] 或 #parameters.id ,相当于调用了HttpServletRequest对象的getParameter()方法。 注意,parameters本质上是一个使用HttpServletRequest对象中的请求参数构造的Map对象,一旦对象被创建(在调用Action实例之前就已经创建好了),它和HttpServletRequest对象就没有了任何关系。 request 用于访问请求属性。如:#request['user'] 或 #request.user ,相当于调用了HttpServletRequest对象的getAttribute()方法。 session 用于访问session属性。如:#session['user'] 或 #session.user ,相当于调用了HttpSession对象的getAttribute()方法。 application 用于访问application属性。如:#application 或 #application.user ,相当于调用了ServletContext对象的getAttribute()方法。 attr 如果PageContext可用,则访问PageContext,否则依次搜索request、session和application对象。 先分清楚下ActionContext 、ValueStack 、Stack Context三者: ActionContext 一次Action调用就会创建一个ActionContext。即OGNL上下文。 调用:ActionContext context = ActionContext.getContext() ValueStack 由OGNL框架实现。 可以把它简单的看作一个栈(List)。 存放表单中的值。 Stack Context(map) 是栈上下文,它包含一系列对象,包括request/session/attr/application的Map等。 (就是传说中的大Map) Stack Object 放入stack中的对象,默认是动作类。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
day25_01_学习回顾 1、Struts2框架在三层架构中哪部分进行的再优化? 答: 表现层、MVC模式。2、Struts1和Struts2的一个显著区别是什么? 答: Struts1的核心控制器是一个servlet。 Struts2的核心控制器是一个过滤器。3、Struts2的编写步骤? 答: 1、拷贝必要jar包到classpath中(即拷贝jar包到WebRoot/WEB-INF/lib中),原则是:用哪个拷贝哪个。 2、at the top of classpath(在最顶层的构建路径),建立一个默认名称为struts.xml的配置文件。在struts.xml文件中进行配置。 3、在web.xml中配置控制器。 4、建立动作类和动作方法。 5、建立一个访问视图的.jsp文件和结果视图页面。4、Struts2的执行过程? 答: tomcat启动,加载应用的web.xml --> tomcat实例化并初始化过滤器 --> 加载struts.xml配置文件 --> 客户浏览器发送请求:hello.action --> 请求到达过滤器 --> 截取请求的动作名称hello,并从struts.xml中查找 --> 找到后,实例化HelloAction动作类,每次都会创建新的实例 --> 调用对应的sayHello()动作方法,方法有返回值 --> 根据返回值找对应的结果视图 --> 找到结果jsp页面 --> 响应浏览器,展示结果5、Struts2的配置文件加载时机和加载顺序? 答: Struts2的配置文件的加载时机:当应用被tomcat加载的时候,struts2的配置文件就已经被加载过了。 Struts2的配置文件的加载顺序:default.properties --> struts-default.xml --> struts-plugin.xml --> struts.xml --> struts.properties --> web.xml 存的是常量 拦截器、结果视图、默认的动作类 插件 我们自己写的 一般不用它 我们自己写的 加载文件的顺序必须是web.xml文件先加载的,读到了里面配置了一个Struts的核心控制器--过滤器, 然后该过滤器的init方法才去执行,它在执行的时候会初始化一些常量、拦截器、结果视图、默认的动作类、插件、配置的属性,给对应的属性赋值。6、常量中struts.devMode是什么意思,如何配置? 答: 是否是开发模式。开发模式:改了配置文件,不需要重启。输出更多的错误信息。开发阶段建议为true。 <constant name="struts.devMode" value="true"><constant/>7、struts.xml配置文件中package的四个属性分别什么意思? 答: name属性:指定包的名称 extends属性:指定当前包的父包。 abstract属性:把包声明为一个抽象包。抽象包就是用来被继承的。只有没有<action>元素的包,才能被定义为抽象包。 namespace属性:名称空间。当指定了名称空间之后,访问路径就变成了:访问路径 = 名称空间 + 动作名称8、访问带有名称空间的动作时,是如何查找的?例如: /user/abc/action1.action 答: 先找名称空间 /user/abc 找不到,就找 /user ,找不到,就找 / ,找不到,就报错 有 /user/abc ,再在当前包找动作名称,找不到,就找默认的名称空间中的动作名称,找不到,就报错,其余以此类推 找到了,就执行。9、action元素的三个属性什么意思? 答: name属性:动作的名称。 class属性:指定动作类,即动作类全名。 method属性:指定要执行的动作方法,即动作类中的方法名称。10、result元素的两个属性分别指的是什么? 答: name属性:结果视图名称。 type属性:结果视图类型。11、4个常用结果类型分别是什么? 答: dispatcher:请求转发 ,是默认值(本动作下) redirect:请求重定向(本动作下) chain:请求转发到另一个动作 redirectAction:请求重定向到另一个动作12、访问ServletAPI的两种方式? 答: 第一种方式:使用的是ServletActionContext的对象(此种方式简单明了,推荐此种方式) 第二种方式:使用的是 依赖注入 的形式,把我们想要的对象注入进来,是由一个 ServletConfigInterceptor的拦截器 为我们做的。需要实现3个接口,实现其中的方法。 day25_02_学习回顾 1、如何封装静态请求参数? 答: 在 struts.xml 配置文件中,通过参数注入的方式,给动作类的参数注入值。相当于调用的是该参数的 setter 方法 。 是由默认的 拦截器栈 中的一个 拦截器staticParams 来完成参数注入的。 示例: <param name="username">张三</param>2、动作类和模型分开的动态封装请求参数,set和get方法是怎么调用的? 答: getXxx setXxx getXxx 或者 getXxx getXxx3、使用模型驱动,动态封装请求参数的要求是什么? 答: 要想使用模型驱动,前提:必须使动作类和数据模型分开写。 1、动作类需要实现一个ModelDriver的接口,注意:该接口需要写泛型。 2、实现接口中的抽象方法getmodel()。 3、在使用模型驱动的时候,数据模型必须由我们自己来实例化。4、实际开发中类型转换的两种情况是什么? 答: 实际开发中用户通过浏览器输入的数据都是字符串String或者字符串数组String[]。 写数据:(增、删、改)是String或String[]转换为其他类型。 读数据:(查)是其他类型转换为String。5、Struts2中提供的常用类型转换分几类? 答: 3类: 1、基本数据类型 自动转换 2、日期类型:默认按照 本地日期格式 转换成(yyyy-MM--dd) 3、字符串数组:默认用 逗号+空格 ,连接成一个字符串6、自定义类型转换器是如何注册的?(两种情况) 答: 1、局部类型转换器,按照 属性 来注册的。 在属性所属的javabean包下新建: javabean名称-conversion.properties 文件 要转换的属性名称=类型装换器的全类名 示例:birthday=com.itheima.web.converter.MyTypeConverter 2、全局类型转换器,按照 要转换的数据类型 来注册的。 在顶层目录下新建: xwork-conversion.properties 文件 要转换的数据类型=类型转换器的全类名 示例:java.util.Date=com.itheima.converter.MyTypeConverter7、如何解决编程式验证使得动作类中的全部动作方法都验证? 答: 1、使用 @SkipValidation 注解。 2、重新定义验证方法的名称,格式为:validate+动作名称,动作名称的首字母要大写哦!8、声明式验证的分别可以基于什么? 答: 1、基于字段的声明式验证:验证谁 --> 怎么验证 --> 验证结果 2、基于验证器的声明式验证:怎么验证 --> 验证谁 --> 验证结果9、命名声明式验证xml文件名的两种方式,有什么不同? 答: 通过编写 验证规则 的 xml文件 。需要验证时,编写xml文件,不要验证时,就不编写。 1、针对动作类中的 所有动作方法 进行验证: 在动作类所在的包中,建立一个 ActionClassName-validation.xml 的文件 示例:UserAction-validation.xml 2、针对动作类中的 某个动作方法 进行验证: 在动作类所在的包中建立一个 xml文件 ,名称为 ActionClassName-ActionName-validation.xml 注意:是动作名称,不是动作方法名称 示例:UserAction-register-validation.xml day26_学习回顾 1、国际化的消息资源文件如何命名? 答: 主要文件名_语言代码_国家代码.properties 主要文件名.properties(默认资源包)2、Struts2中全局范围的资源包、包范围的资源包和动作类范围的资源包,哪个加载优先级高?页面上如何读取指定的消息资源包? 答: 全局范围的资源包:message_en_US.properties 包范围的资源包:package_zh_CN.properties 动作范围的资源包:Demo1Action_zh_CN.properties 动作类范围的资源包的优先级最高。 页面上使用标签 <s:text name="xxx" /> 读取指定的消息资源包。3、Struts2中拦截器的执行时机? 答: 执行动作方法之前,正序执行拦截器。 执行结果视图之后,到序执行拦截器。4、自定义拦截器的步骤是什么? 答: a、编写一个普通类,继承AbstractInterceptor类 或者 实现Interceptor接口 。重写其抽象的intercept方法。 b、在struts.xml中配置拦截器,注意拦截器必须先声明、再使用。 5、多个拦截器如何确定执行顺序? 答: 单个拦截器的执行顺序:拦截器 --> 动作方法 --> 结果视图 --> 拦截器 --> 浏览器响应页面 当有多个拦截器的时候,是由使用顺序决定执行顺序,与声明顺序无关。6、自定义拦截器除了继承AbstractInterceptor还可以继承哪个?另一个有什么好处? 答: 还可以 继承MethodFilterInterceptor 并且 重写doIntercept方法。 好处:在struts的配置文件中,通过参数注入的方式,配置需要拦截哪些方法,和需要放过哪些方法。7、文件上传是哪个拦截器为我们做的?如何限定上传文件的大小和类型? 答: 是fileupload拦截器。 限定上传文件的大小: 1、在struts.xml中改变default.properties文件中的常量。常量是:maxSize 2、给Struts2默认的拦截器栈中的fileUpload拦截器注入参数。(此法行不通)8、struts2中文件下载是由哪个结果类型完成的?需要我们提供什么参数? 答: Stream结果类型完成的。 我们需要给Stream结果类型注入的参数是: 1、contextType:文件的MIME类型 2、contextDisposition:文件的下载方式 3、inputName:字节输入流 9、OGNL是什么?使用它能否访问普通方法?能否直接访问静态方法? 答: OGNL:对象图导航语言。 能访问。 不能直接访问,需要开启允许静态方法访问的开关。开关名为:allowStaticMethodAccess10、ActionContext和ValueStack什么时候创建?是否是线程安全的? 答: 每次动作访问时,就会创建。多例,是线程安全的。 因为每次把数据绑定到了线程局部变量(ThreadLocal)上。11、ContextMap中的结构是什么样的? 答: Context Map是OGNL的上下文,里面有两大部分组成:ActionContext + ValueStack 是一个Map集合中封装多个Map结合。 day27_学习回顾 1、ActionContext是什么结构?里面都有哪些数据? 答: OGNL上下文 = 值栈(ValueStack)+ 大的contextMap(包含本身和小的contextMap) List集合 + Map集合 大的contextMap = ActionContext,是大Map中有小Map。 大Map中里面有三大域对象:request、session、application,attr 和 parameters。这些大Map的值都是小Map。 ActionContext的核心结构是维护了一个Map<String, Object>类型的context变量。数据的存储和读取都是通过context对象来实现的。 2、ActionContext是如何保证数据线程安全的? 答: ActionContext的获取是通过它的静态方法getContext()得到。 Struts2会根据每一次的http请求来创建对应的ActionContext,它是与当前线程绑定的线程局部变量(ThreadLocal)。 每一次请求,就是一个线程,对应着一个request; 每一次请求,会创建一个Action,每一个action对应一个ActionContext,每一次请求也对应着一个ValueStack。 request ---> ActionContext --> Action --> ValueStack 它们都对应着一次请求(或一个线程)。 ActionContext把当前线程作为key,当前线程存的,只能当前线程访问,其他线程访问不到。 3、ValueStack是什么结构? 答: List集合。 4、默认栈顶元素是什么? 答: 如果我们在动作类中没有往 值栈(根) 中放入数据的话,那么我们的动作类对象默认是在值栈的栈顶。 5、ValueStack的setValue方法和set方法分别什么意思? 答: setValue(String expr, Object value) 参数说明: String expr:它是一个OGNL表达式 Object value:我们要操作的数据 把数据存到哪里去? 看expr(OGNL表达式)是否使用了# 如果使用了#,把数据存在ContextMap中,大Map中 如果没使用#,把数据存在ValueStack中,值栈中 例如: vs.setValue("#name", "张三"); // 把数据存放到ContextMap中。 key=name valeu=张三 vs.setValue("name", "李四"); // 把ValueStack中第一个name属性的值替换成李四。如果整个ValueStack中都没有一个name属性的对应的setName方法,会报错。 -------------------------- set(String key, Object o); 参数说明: String key:Map的key Object o:Map的value 如果栈顶是一个Map元素,即List集合中放的是Map集合,直接把key作为Map的key,把Object作为Map的value存入,即直接把数据存入Map。存入的是栈顶。 如果栈顶不是一个Map元素,那就创建一个Map对象,把key作为Map的key,把Object作为Map的value,并压入栈顶。 6、Struts2中,EL表达式是如何查找数据的? 答: jsp中,EL表达式的查找顺序:pageScope --> requestScope --> sessionScope --> applicationScope Struts2对EL表达式查找顺序的改变:pageScope --> requestScope --> valueStack(根中) --> 剩余的contextMap(小Map) --> sessionScope --> applicationScope 7、iterator标签中var属性有什么作用? 答: var:取值就是一个字符串 如果写了该属性:Struts2框架就会把var的值作为key,把当前遍历的元素作为value,存到ActionContext这个大Map中。 如果不写该属性:Struts2框架就会把当前遍历的元素压入值栈ValueStack的栈顶。 8、#,$,%分别都有什么作用? 答: 3.1、# a、取contextMap中键key对应的值value时使用,例如:<s:property value="#name"/> b、OGNL中创建Map对象时使用,例如:<s:radio list="#{'male':'男', 'female':'女'}"/> 3.2、$ a、在JSP中使用EL表达式时使用,例如:${name} b、在xml配置文件中,编写OGNL表达式时使用,例如:在文件下载时,文件名编码:struts.xml --> ${@java.net.URLEncoder.encode(filename)} 3.3、% 在struts2中,有些标签的value属性取值就是一个OGNL表达式,例如:<s:property value="OGNL Expression"/> 还有一部分标签,value属性的取值就是普通字符串,例如:<s:textfield value="username"/>, 如果想把一个普通的字符串强制看成是OGNL表达式,就需要使用 %{} 把字符串套起来。例如:<s:textfield value="%{username}"/>。 当然在 <s:property value="%{OGNL Expression}"/> 也可以使用,但一般不会这么用,因为你两次告诉我你是OGNL表达式,不是有病吗! 9、使用url标签中添加param标签是什么意思? 答: s:url 就是创建一个地址 value:输出的就是value的值,注意:value的取值此时不在是一个OGNL表达式,而是普通字符串。 action:输出的是action的请求地址。和${pageContext.request.contextPath}/action1.action是一样的。 但是,它可以随着配置文件中的扩展名改变而改变。而EL表达式的写法是硬编码,不会自动改变。 var:会把action的值存到contextMap中。 s:a 的效果同 s:url 例如: <%--把name作为key,把value作为值,绑定到请求链接地址后面。相当于get方式拼接请求参数 ,如果value的值是中文,会自动进行URL编码 注意:此时的value的取值是一个OGNL表达式。 --%> <s:param name="name" value="'text'"></s:param> 10、模型驱动是如何帮我们动态封装请求参数的? 答: 0、数据模型和动作类分开,动作类中有数据模型的引用。 1、在ModelDriven接口的实现类中,获取当前动作类的引用。 2、判断该动作类是否实现了ModelDriven接口。 3、是,就把该引用的类型强转为ModelDriven类型。 4、如果我们在动作类中没有往 值栈(根) 中放入数据的话,那么我们的动作类对象默认是在值栈的栈顶。 5、获取值栈的引用。 6、获取动作类中定义的数据模型对象的引用。 7、该引用不为空,就把该对象压入栈顶。(1-7步骤是拦截器modeDriven在起作用。) 8、再调用params拦截器的set方法把对象封装好数据(即进行赋值)。动态参数封装。 11、在struts2中,如何防止表单重复提交? 答: 1、使用重定向 2、表单上使用<s:token/>生成令牌,再配合token拦截器,在struts.xml中进行相关的配置 3、表单上使用<s:token/>生成令牌,再配合tokenSession拦截器,在struts.xml中进行相关的配置(该拦截器只会处理第一次请求,当重复提交请求时,不会再处理。) 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、contextMap中的数据操作 1、存数据: 需要熟悉 ActionContext类 和 ValueStack接口 的 API 。框架为我们存数据。 1.1、利用 ActionContext 存数据 我们先看看 ActionContext.class 的源码: 1.2、利用 ValueStack 存数据 a、如何获取ValueStack呢? b、ValueStack中的getRoot()方法。 c、CompoundRoot是什么? d、栈操作。 动作类中相应的代码为: 学生实体类代码: Struts Debug页面查看数据:2、取数据:用Struts2的标签(OGNL表达式)在JSP上(用的最多) 使用OGNL表达式来去,struts2的OGNL表达式必须写在struts2标签中。 2.1、使用s:property取数据 a、取contextMap中的数据,需使用 #key 。 b、取contextMap里面ValueStack中对象的属性:直接写属性名 如果遇有对象的属性的名称相同(属性名同名),可以通过OGNL表达式,选择查找的起始位置。它会从指定起始位置逐个对象查找指定的属性名称,只要找到了,就不再继续查找了。特殊说明:当s:property不给定value属性时,默认取栈顶对象。 OGNL的使用总结: 1.取根(root)中对象的属性,不使用#。 2.取contextMap中key的值,要使用#。 2.2、ValueStack的其他方法 a、setValue方法 b、set方法 c、findValue方法:我们在jsp上调用的其实都是findValue二、Struts2对EL的改变 1、Struts2中使用EL的问题 前提: 我们应该知道,如果我们在动作类中没有往 值栈(根) 中放入数据的话,那么我们的动作类对象默认是在值栈的栈顶。 问题: 我们放到动作类中的属性和请求域中的属性,使用EL表达式取出来了。但是放到应用域中的属性,使用EL表达式没取出来。 2、关于EL问题的分析 分析: 我们知道EL表达式是从四大域对象中依次查找属性。搜索范围是由小到大。 查找顺序:pageScope --> requestScope --> sessionScope --> applicationScope 但是通过测试发现,程序搜索放到应用域中的name属性,搜索完request范围后就没有继续搜索了,原因是:在请求域中没有找到的情况下,去ValueStack中查找了,返回了ValueStack中栈顶对象name属性的值。 问题出现在这个request的包装类上了,该包装类对getAttribute方法进行了改写,如下图所示: 我们来看看这个包装类的源码:3、Struts2中EL查找顺序改变总结 EL表达式的查找顺序:pageScope --> requestScope --> sessionScope --> applicationScope Struts2对EL表达式查找顺序的改变:pageScope --> requestScope --> valueStack(根中) --> 剩余的contextMap(小Map) --> sessionScope --> applicationScope 4、OGNL的特殊说明 当我们不写#号时,OGNL表达式会从值栈的栈顶开始找对应的属性,如果没有该属性,再去contextMap中把value的值作为key去查找对应的值。这个知道就行,我们开发中一般不这么写!三、OGNL配合通用标签的其他使用 1、iterator标签(很重要) 2、OGNL投影(了解) 2.1、使用过滤条件投影 2.2、投影指定属性 如下图所示: 3、Struts2中 `#,$,%` 符号的使用(重要) 3.1、# a、取contextMap中键key对应的值value时使用,例如:<s:property value="#name"/> b、OGNL中创建Map对象时使用,例如:<s:radio list="#{'male':'男', 'female':'女'}"/> 3.2、$ a、在JSP中使用EL表达式时使用,例如:${name} b、在xml配置文件中,编写OGNL表达式时使用,例如:文件下载时,文件名编码: struts.xml --> ${@java.net.URLEncoder.encode(filename)} 3.3、% 在struts2中,有些标签的value属性取值就是一个OGNL表达式,例如:<s:property value="OGNL Expression"/> 还有一部分标签,value属性的取值就是普通字符串,例如:<s:textfield value="username"/>, 如果想把一个普通的字符串强制看成是OGNL表达式,就需要使用 %{} 把字符串套起来。例如:<s:textfield value="%{username}"/>。 当然在 <s:property value="%{OGNL Expression}"/> 也可以使用,但一般不会这么用,因为你两次告诉我你是OGNL表达式,不是有病吗! 4、Struts2中其他标签的使用 4.1、set标签 4.2、action标签4.3、if标签,elseif标签 else标签4.4、url和a标签(很有用)修改动作名称的扩展名四、Struts2的UI标签和主题 1、Struts2中UI标签的优势 自动的数据回显和错误提示功能 自带的简单样式和排版 2、表单标签的通用属性 说明:UI标签中value的取值一般都是字符串。 2.1、UI标签的通用属性 2.2、关于标签checkboxlist的使用 2.3、UI标签的小案例以及模型驱动的分析3、UI标签的模板(主题) 3.1、struts2中默认主题 默认主题的名称是XHTML,都是在struts的默认属性文件中定义着,该文件是:default.properties, 文件位置:\struts-2.3.15.3-all\struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\struts2-core-2.3.15.3\org\apache\struts2\default.properties 3.2、更改默认主题 a、更改表单某个元素的默认主题:使用的是表单元素的theme属性。 b、更改表单所有主题:使用的是form标签的theme属性。 c、更改全站所有表单主题:是在struts.xml配置文件中,覆盖原有主题的设置。 如下图所示: 注意:当我们使用了simple主题后,我们需要 自己添加文本信息 。原来我们在jsp中怎么写,现在还怎么写。 例如: <s:textfield name="name" label="用户名" />改写为: 用户名:<s:textfield name="name" /> 五、防止表单重复提交(使用拦截器) 1、回顾之前的解决办法: 2、Struts2中的解决办法: 2.1、使用重定向 遗留的问题:刷新页面,再提交没有问题,但是防不住后退,再提交。2.2、表单上使用<s:token/>生成令牌,再配合token拦截器,在struts.xml中进行相关的配置 点击后退再提交时,会提示:没有一个invalid.token的结果视图,如下图所示: 那我们就配置一下这个invalid.token的结果视图: 遗留的问题:此种解决方式,是产生了错误之后再去告知用户,你错了。2.3、表单上使用<s:token/>生成令牌,再配合tokenSession拦截器,在struts.xml中进行相关的配置 该拦截器只会处理第一次请求,当重复提交请求时,不会再处理。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
我们导入的jar包后,一般没有关联源码,需要手动进行关联! 详细操作过程如下: 导入成功后,小奶瓶就被贴上“标签”啦!这样在该工程下,查看源码就方便啦!我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、国际化概念(了解) 1、什么是国际化 软件的国际化:软件开发时,要使它能同时应对世界不同地区和国家的访问,并针对不同地区和国家的访问,提供相应的、符合来访者阅读习惯的页面或数据。 2、什么需要国际化 程序:需要国际化。 输入的数据:是什么样的就是什么样的。 比如: 用户注册的表单,有用户名,密码这5个汉字,在zh_CN语言环境,显示的就是用户名和密码。但是在en_US语言环境,显示的就应该是username和password。这就是 程序。 用户名输入的是【张三】,密码输入的是【test】,那无论在什么语言环境都应该是是【张三】和【test】。这就是 数据。 3、固定文本的国际化 例如:消息提示、错误提示和菜单,导航栏等等固定文本。步骤: 3.1、创建一个消息资源包 一个资源包由多个文件组成,这些文件名都有命名规范:主要文件名_语言代码_国家代码.properties。 语言代码:由iso规定的。国家代码:由iso规定的。 当文件只有 主要文件名.properties 时,表明它是 默认资源包。浏览器会根据不同的语言环境找对应语言环境的资源包,当没有找到时,找默认的。 每个资源包的内容都由 相同的key 和 对应语言环境的value 组成。 比如: message_zh_CN.properties 、 message_zh_HK.properties、message_en_US.properties 3.2、读取资源包中的内容 国际化的小测试,如下图所示: jsp中使用国际化:配置文件 message_zh_CN.properties 和 message_en_US.properties 的书写:login.jsp文件:效果如下图所示:但是呢?login.jsp中不允许出现下面的java代码块(jsp2.0以后的规定),需要用jsp的标签来替代。使用jstl的fmt标签,如下图所示: <% // java代码块Locale locale = request.getLocale();ResourceBundle bundle = ResourceBundle.getBundle("com.itheima.resource.message",locale);%> 二、Struts2中的国际化(了解) 1、Struts2中使用国际化的前提 首先,我们要知道,在Struts2中,所有的消息提示都是基于国际化的。 其次,要想在Struts2中使用国际化,动作类必须 继承ActionSupport类。 2、Struts2中使用国际化 2.1、配置资源包a、配置全局范围的资源包 b、配置包范围的资源包 该方式直接新建包范围的资源包,添加配置文件即可,不需要在 struts.xml 中进行配置。 资源包名称命名规范:package_语言代码_国家代码.properties(固定的写法) 。以此种命名方式的资源包能 被该包及其子包中的动作类访问 。 访问优先级:包范围的资源包高于全局范围的资源包。c、配置局部消息资源包(只为动作类来使用的) 该方式直接新建动作类范围的配置文件即可,也不需要在 struts.xml 中进行配置。 资源包名称命名规范:动作类名称_语言代码_国家代码.properties。以此种命名方式的资源包,只为动作类服务。 访问优先级:优先级最高(就近原则)。2.2、读取资源包的内容a、在动作类中的读取资源包内容(实际开发中几乎从来不用,学习时为了演示而已) b、在页面中读取资源包内容Struts2中资源包的搜索顺序,如下图所示: 所在位置:/struts-2.3.15.3-all/struts-2.3.15.3/docs/WW/docs/localization.html 三、Struts2中的拦截器(特别重要) 1、拦截器的重要性 Struts2中的很多功能都是由拦截器完成的。 比如:servletConfig、staticParam、params、modelDriven等等。 是 AOP编程思想 的一种 应用 形式。 2、拦截器的执行时机 执行动作方法之前,正序执行拦截器。 执行结果视图之后,倒序执行拦截器。如下图所示: 3、自定义拦截器 3.1、拦截器的类视图结构(最终版本): 3.2、编写自定义拦截器步骤: a、编写一个普通类,继承AbstractInterceptor类 或者 实现Interceptor接口 。重写其抽象的intercept方法。 b、在struts.xml中配置拦截器,注意拦截器必须先声明、再使用。 效果图如下:这时。拦截器干活啦,拦截成功,哈哈,该我想干什么就干什么啦!(坏笑) 3.3、拦截器的执行顺序 拦截器 --> 动作方法 --> 结果视图 --> 拦截器 --> 浏览器响应页面,如下图所示:3.4、多个拦截器的执行顺序 当有多个拦截器的时候,是由使用顺序决定执行顺序,与声明顺序无关。3.5、intercept方法的返回值是逻辑结果视图的值。打印下该方法的返回值即可,就不放图啦! 4、拦截器的应用 4.1、检查登录的拦截器案例配置文件struts.xml: 动作类:拦截器:页面:4.2、案例中的问题问题:由于我们写了自己的拦截器,默认的拦截器不起作用了。解决办法:a、把默认拦截器也加入到配置文件中b、a中暴露的问题:当有多个拦截器时,需要改写的地方非常多。 解决办法:抽取公共的包,把全局配置放入公共包中。c、b中的问题:还要再每个动作方法中引入拦截器。能不能不写呢? 思路:我们在设置【开发模式】时,覆盖掉了一个default.properties中的常量,那我们能不能把struts-default.xml中的默认拦截器栈的设置给覆盖掉呢?答案是可以的。d、c中出现的问题:当我们重新定义了默认的拦截器栈,这时候三个动作login、showMain和showOther都将被检查登录的拦截器拦截。 解决办法:需要通过 AbstractInterceptor类的子类 入手,通过查看发现,该类还有一个抽象的子类:MethodFilterInterceptor,它里面提供了两个属性:excludeMethods 和 includeMethods。 excludeMethods:表示那些方法不需要拦截 includeMethods:表示哪些方法需要拦截 可以配置多个动作方法,使用,进行分割。如下图所示:所以我们在自定义拦截器时,还可以 继承MethodFilterInterceptor 并且 重写doIntercept方法。这种方式更好!并且在struts的配置文件中,配置需要拦截哪些方法,和需要放过哪些方法。如下图所示:e、d中遗留的问题:我们在声明时配置了哪些方法需要拦截,哪些方法不需要拦截。但是在没有写动作类和动作方法之前,不确定方法名叫什么。 解决办法:哪个动作方法需要使用拦截器,我就给它注入参数。 四、文件的上传(拦截器)和下载(stream结果类型)(需要练一遍) 1、文件上传 必要前提: a. 表单form的请求方式method必须是post。 b. enctype取值必须是multipart/form-data。 c. 提供一个文件选择域。 动作类:效果如下图所示:2、文件上传的配置 2.1、文件上传大小限制(默认大小是2MB) 如果上传文件超过了默认大小,upload拦截器会转向一个input的逻辑视图。如下图所示: 按照input的逻辑视图提示,增加结果视图,和增加显示动作错误提示,再次上传,得到新的页面结果视图,如下图所示:a、改变上传文件大小限制: 思路1:在struts.xml中改变default.properties文件中的常量。 思路2:给Struts2默认的拦截器栈中的fileUpload拦截器注入参数:(行不通)2.2、限制文件上传的类型a、通过限制上传文件的扩展名 思路:给Struts2默认的拦截器栈中的fileUpload拦截器注入参数:当上传非限定扩展名时,有如下错误提示:b、通过限制上传文件的MIME类型 当上传非限定MIME类型时,有如下错误提示3、出错后的错误信息中文提示(Struts2中的所有文本提示都是基于国际化的) 默认信息提示在:项目目录下\Web App Library\struts2-core.jar\org.apache.struts2\struts-message.properties 解决办法:用国际化消息资源包,把对应的key取值改为中文即可。常用的key值,配置如下:修改显示错误的资源文件的信息,如下图所示:效果如下图所示:4、多文件上传 jsp页面: 配置文件struts.xml:动作类:运行结果:没有问题,就不上图啦! 5、文件下载:其实就是一种结果类型(Stream) 动作类: 配置文件:运行结果:没有问题,就不上图啦!问题:文件名不能在配置文件中写死,需要根据实际情况获取。解决办法:动作类: 在下面的讲解中。 五、OGNL简介(非常重要) 1、什么是OGNL? OGNL是 Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个单独的开源项目。 Struts2框架使用OGNL作为默认的 表达式语言 。 2、OGNL的功能 前提:OGNL是struts2整合的一个 开源项目 ,所以在struts2中,要想使用OGNL表达式,必须使用 Struts2标签库。2.1、支持普通方法的调用 (EL表达式只能调用静态方法,详解见此链接:https://www.cnblogs.com/chenmingjun/p/9243454.html)2.2、访问静态成员(静态属性,静态方法)如下图所示: 2.3、操作集合对象a、创建List对象:b、创建Map对象:2.4、OGNL改写文件下载文件名的问题动作类:struts.xml配置文件: 六、context Map(非常重要) 1、动作类的生命周期 明确:动作类是多例的,每次动作访问,动作类都会实例化。所以是线程安全的。与Struts1的区别是,struts1的动作类是单例的。 2、请求动作的数据存放 问题: 每次请求时,都会产生一些请求数据,这些数据存放到哪里去了? 明确: 在每次动作执行前,核心控制器StrutsPrepareAndExecuteFilter 都会创建一个 ActionContext 和 ValueStack对象。且每次动作访问都会创建。 这两个对象存储了整个动作访问期间用到的数据。并且把数据绑定到了 线程局部变量(ThreadLocal) 上了。所以是线程安全的。 3、context Map:存储数据 Struts2的官方文档对context Map的说明: 注意: 除了value stack之外,全是map,而context Map也是一个map。其实就是Map中又封装的Map。 (很像dbutils中KeyedHandler封装数据的结构,注意:只是像其封装数据的结构而已,把结果集封装成一个Map,Map里面又是一个Map,大Map的key是主键,大Map的value是一个小Map,是一条记录) 查看context Map中的数据:在页面上使用 <s:debug/>效果如下图所示:测试存入数据: 未完待续,欲知后事如何,且听下回分解… 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
EL表达式的自定义方法的步骤: 1、编写一个普通类,提供一个实现功能的静态方法 2、在WEB-INF目录中创建一个扩展名为 .tld 的xml文件,进行相关配置,注意:该文件不能放在classes和lib目录中 3、在jsp页面中使用taglib指令引入外部的标签库或者方法库 1、编写一个普通类,提供一个实现功能的静态方法 2、在WEB-INF目录中创建一个 扩展名为 .tld 的xml文件 ,并进行相关配置,注意:该文件不能放在classes和lib目录中3、在jsp页面中使用taglib指令引入外部的标签库或者方法库4、效果如下图所示: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
sql语句的拼接: 链接地址的简写: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、封装请求正文到对象中(非常重要) 1、如何封装静态请求参数? 在 struts.xml 配置文件中,通过参数注入的方式,给动作类的参数注入值。相当于调用的是该参数的 setter方法 。控制台输出的是 张三,18 ,如下图所示: 原因:是由默认的 拦截器栈 中的一个 拦截器staticParams 来完成参数注入的。该拦截器栈在 struts-default.xml 文件中定义。为了演示方便,我们把写在 struts-default.xml 中默认的拦截器写在 struts.xml 中进行显示出来,如下图所示:当我们把黄色框框中的 <interceptor-ref name="staticParams"/> 这句代码注释掉后,再次访问该 action 时,在控制台输出的是 null,0 。 2、如何封装动态请求参数?:开发时用到的 通过用户的表单封装请求正文参数。1、动作类作为实体模型(即:数据模型与动作类写在一起)实体模型:Entity,对应数据库中表的记录(注意:类对应的是表结构,而对象对应的是一条记录) 原因:是由 params拦截器 完成的。2、动作类和实体模型分开3、模型驱动:建立动作类和模型分开的前提下(开发中采用的方式)此处的学习目标:目前先记住怎么写,要想理解,必须等讲完 OGNL表达式 之后。原因:是由一个 modelDriven拦截器 完成的。 二、用户注册案例(重点) 1、数据建模(数据库和实体模型) 2、建立业务层接口 + 建立业务层实现类 3、建立持久层接口 + 建立持久层实现类(暂时没有具体实现) 4、拷贝jar包和配置文件 拷贝数据库工具类jar包:commons-dbutils-1.4.jar 和 mysql-connector-java-5.0.8-bin.jar 到 /WebRoot/WEB-INF/lib 中,再拷贝数据源工具类jar包:commons-dbcp-1.4.jar 和 commons-pool-1.6.jar 到 /WebRoot/WEB-INF/lib 中,再将与之相对应的配置文件 dbcpconfig.properties 拷贝至项目路径中的 /src 目录下,若该配置文件有乱码,则点击该配置文件 右键/Properties/Text file encoding/Other/UTF-8 ,OK即可。 5、在项目中新建一个 util的包 ,将代码 DBCPUtil.java 拷贝 如下图所示: 6、持久层实现类(具体实现) 如下图所示: 7、表现层使用Struts2框架实现 动作类 配置文件 注册界面和结果视图注册页面:结果视图: 三、数据类型的转换(明白原理即可,实际开发中几乎不用) 1、开发中的情况:实际开发中用户通过浏览器输入的数据都是 String 或者 String[] 。 String/String[] ————> 填充模型(set方法) ————> POJO(plain old java object) pojo中有java的数据类型。 POJO ————> 获取(get方法) ————> 页面展示:String 2、类型转换的两种情况: 写数据:(增,删,改)都是String或String[]数组转换为其他类型。 读数据:(查)其他类型转换为String。 3、Struts2提供的常用类型转换: a. 基本数据类型 自动转换 。 b. 日期类型:默认按照 本地日期格式 转换(yyyy-MM-dd)。 c. 字符串数组:默认用 逗号+空格 ,连接成一个字符串。 4、自定义类型转换器(知道即可)示例:把日期格式按照 MM/dd/yyyy的格式转换 4.1、先看看Struts2中的类型转换器的结构: UML图如下:DefaultTypeConverter.javaStrutsTypeConverter.java 4.2、编写自己的类型转换器(编写一个类继承StrutsTypeConverter,实现其抽象方法) 4.3、注册类型转换器局部类型转换器:只能指定javabean中的属性。按照 属性 来注册。在属性所属的 javabean 的包下建立一个 .properties 文件。文件名称为:javabean名称-conversion.properties如下图所示:此时,只能是User能够使用。其余的javabean用不了。 全局类型转换器:(推荐)按照 要转换的数据类型 来注册。at the top on classpath ,建立一个固定名称 xwork-conversion.properties 的属性文件。 5、转换失败后的处理(需要掌握)当转换失败后,页面提示: 上面的显示给客户端的用户看,太难看了,不好。解决办法:配置回显结果视图问题 :配置了回显视图后,当转换失败时,可以回到请求页面,但是表单数据却都没了?肿么办?解决办法:显示错误提示:借助Struts2的标签库。如下图所示:回显数据:使用struts2的标签库生成表单。(建议使用)如下图所示:演示效果如下图所示:错误信息中文提示:使用的是struts2的国际化。效果如下图所示:问题:类型转换器当转换失败后,如何进入input视图的?原因:是由一个叫做 conversionError 的拦截器完成的。注意:要想使用类型转换中的错误处理,在 定义Action 时必须继承 ActionSupport 。 四、数据验证 用户的输入验证,必须做,且工作量巨大。 1、验证的方式 客户端验证:javascript服务端验证:逻辑验证(我们的代码)注意:如果客户端和服务端二选一的话,服务器端的不能省。实际开发中:客户端 + 服务端 2、Struts2的服务端验证 2.1、编程式验证前提:动作类必须继承ActionSupport。在代码中编写验证规则。 a、针对动作类中的 所有动作方法 进行验证: 在动作类中重写 public void validate() 方法。如下图所示: 用到了针对 java.lang包 中String的扩展的 工具类StringUtils :演示视图如下图所示:但是当我们再写一个动作方法时:由此可知,当重写了validate方法后,该验证方法会对动作类中的所有动作方法进行验证。 b、针对动作类中的 某个动作方法 进行验证: 针对上面的问题,解决办法1:给不需要验证的动作方法添加一个 @SkipValidation注解 。 解决办法2:validation方法遵守书写规范。即:定义验证方法的名称:validate+动作名称 ,动作名称的首字符还要大写。解决办法1和解决办法2的使用时机: 需要验证的动作方法少,用解决办法2。 需要验证的动作方法多,用解决方式1。(简言之:挑少的写)所有编程式验证的弊端:硬编码。(写死了) 2.2、声明式验证(推荐)通过编写 验证规则 的 xml文件 。需要验证时,编写xml文件,不要验证时,就不编写。优势:解决了2.1编程式验证的弊端。 a、针对动作类中的 所有动作方法 进行验证:在动作类所在的包中,建立一个 ActionClassName-validation.xml 的文件,内容如下: 图片中的 xwork-validator-1.0.3.dtd文件 在 \struts-2.3.15.3-all\struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\xwork-core-2.3.15.3.jar\xwork-validator-1.0.3.dtd中。注意:当使用ActionClassName-validation.xml来命名文件时,它是一个动作类验证器。会验证动作类中的所有动作方法。 b、针对动作类中的 某个动作方法 进行验证:在动作类所在的包中建立一个 xml文件 ,名称为 ActionClassName-ActionName-validation.xml。内容如下: 注意:动作名称是配置文件struts.xml中package元素的name属性的取值。 2.3、Struts2内置的常用声明式验证器1、所在位置:xwork-core-2.3.15.3.jar\com\opensymphony\xwork2\validator\validator\default.xml ,该文件内容如下图所示: 2、验证器注入参数 例如:我们使用 requiredstring,默认是去空格,当我们不想去空格时,就可以给该验证器注入参数。基于字段的方式和基于验证器的方式如下: 效果如下图所示:3、声明式基于字段的常用验证器案例(1)注册页面 student.jsp ,如下图所示: (2)注册页面效果图,如下图所示:(3)实体模型(数据模型:JavaBean) Student.java ,如下图所示:(4)struts.xml 中的配置(5)动作类 StudentAction.java ,如下图所示:(6)声明式验证文件 StudentAction-addStudent-validation.xml 如下:(7)整体效果,如下图所示: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
原文链接:http://www.w3school.com.cn/xml/xml_cdata.asp 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、框架概述 把重复性的繁琐的代码封装起来。使程序员在编码中把更多的精力放业务需求的分析和理解上面。 特点:封装了很多细节,程序员在使用的时候会非常简单。 三大框架:Struts2、Spring、Hibernate 二、三层架构与Struts2 表现层:M model V view C control 业务层:Service 持久层:Dao data access object Struts2在三层架构中的位置是处于表现层。注意它只是一个表现层框架。 三、MVC与Struts2 M:Model 模型,封装数据。javabeanV:View 视图,展示界面。jspC:Controller 控制器,控制程序流程。Servlet控制器:Servlet init(ServletConfig config) destroy() service(ServletRequest req, ServletResponse resp)过滤器:Filter 它也可以作为控制使用。 init(FilerConfig filterConfig) destroy() doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)Servlet能做的Filter都可以做,并且比Servlet功能更强大,它多了一个放行的功能。即:过滤器同样也适合做控制器。注意:Struts1的核心就是一个控制器,Struts2的核心就是一个过滤器。 四、案例中存在的一些问题 规律: 获取请求正文,用户要做什么事情。 根据不同的请求,做出不同的判断。 执行具体的方法代码(动作)。 转向页面,展示给用户。 缺陷: 重复性劳动太多,具体的执行代码也有冗余代码。 全是硬编码,像用户要做什么事情,对应执行什么代码,可以写在配置文件中。 具体的代码方法放到了控制器中,过于臃肿。 五、Struts2简介 1、Struts2概述 Struts2是Apache发行的MVC开源框架。注意:它只是表现层(MVC)框架。2、Struts2的来历 Struts1:也是apache开发的一套mvc的开源框架。在2005年之前非常流行。 弊端:Struts1的核心控制器就是一个Servlet。随着使用者的增多,弊端开始出现。 Struts2:在long long ago,有一个设计超前的框架XWork,后来推出了XWork1和WebWork2。Struts2就是apache和OpenSymphony组织合并开发出来。 Struts2里面包含了WebWork2的核心及Struts的一些特性和功能。除此之外,和Struts1没有任何关系了。 六、搭建开发环境 Struts2的下载和安装 开发包目录结构 搭建开发环境拷贝必要jar包到classpath中(即拷贝jar包到WebRoot/WEB-INF/lib中),原则是:用哪个拷贝哪个。注意:新老版本的区别:旧版本的struts2 新版本的struts2 建立Struts2的配置文件at the top of classpath(在最顶层的构建路径),建立一个默认名称为struts.xml的配置文件。注意: 文件名大小写。 创建位置。 该文件名称允许修改,但是我们一般不改。 在web.xml中配置控制器a、配置位置:在web.xml中b、配置什么: struts2已经写好了的一个过滤器。结论:struts2比struts1优秀的一个体现就是:它用了更为强大的过滤器作为控制器。 验证是否搭建环境成功部署应用后,启动Tomcat,不报错表示搭建成功。 关于struts.xml没有提示的问题分析原因:没有找到对应的dtd约束文件。 解决办法: a.开发时联网 b.开发时不能上网咋办呢?那就手动添加该约束文件,过程如下: 附上:上述的struts.xml和web.xml文件 struts.xml文件<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"><struts></struts> web.xml文件(注意:新版本的区别)if you are using struts2 version 2.5 you need to change from org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter toorg.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter是的,去掉了中间.ng文件空间名,原因是在整合xwork的时候,同时简化了不必要的包名空间。<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>day25_00_struts2Template</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list></web-app> 七、Struts2入门案例 以下步骤是日后实际开发中经常重复的。 建立一个访问视图的.jsp文件 在struts.xml文件中配置 建立动作类和动作方法 结果视图页面 测试 小问题解决上面的模板我们做好了,就可以复制使用该模班,注意:复制该模板后,必须要修改 Web Context Root的内容,否则部署的时候会报错(会出现跟模板名称一样的应用),步骤如下:在新项目上右键 --> Properties --> 在搜索框中输入 Web --> 在web Context Root 中 --> Config…,如下图所示: 八、第一个案例的执行过程 tomcat启动,加载应用的web.xml --> tomcat实例化并初始化过滤器 --> 加载struts.xml配置文件 --> 客户浏览器发送请求:hello.action --> 请求到达过滤器 --> 截取请求的动作名称hello,并从struts.xml中查找 --> 找到后,实例化HelloAction动作类,每次都会创建新的实例 --> 调用对应的sayHello()动作方法,方法有返回值 --> 根据返回值找对应的结果视图 --> 找到结果jsp页面 --> 响应浏览器,展示结果 如下图所示: struts2的体系结构图如下:九、Struts2的配置文件的加载时机和加载顺序 加载时机:当应用被tomcat加载的时候,struts2的配置文件就已经被加载过了。 加载顺序:default.properties --> struts-default.xml --> struts-plugin.xml --> struts.xml --> struts.properties --> web.xml 存的是常量 拦截器、结果视图、默认的动作类 插件 我们自己写的 一般不用它 我们自己写的 加载顺序 配置文件名 所在位置 说明 1 default.properties struts2-core-2.5.16.jar\org\apache\struts2 不能修改 2 struts-default.xml struts2-core-2.5.16.jar 不能修改 3 strtuts-plugin.xml 在struts2提供的插件jar包中,名为struts2-convention-plugin-2.5.16.jar 不能修改 4 struts.xml 我们的应用中,该文件是web应用默认的struts配置文件 我们修改的:推荐修改这里 5 struts.properties 我们的应用中,该文件是Struts的默认配置文件 我们修改的 6 web.xml 我们的应用中,该文件是Web应用的配置文件 我们修改的,可以给过滤器配置参数 注意:1、Struts2提供了两种配置的方式。一种是key=value的方式,即使用.properties文件。另一种是xml文件配置。我们推荐使用xml文件(因为它能描述层级关系)。2、如果多个文件配置了同一个struts2常量,则后一个文件中配置的常量值会覆盖前面文件配置的常量值。 十、Struts2中的常量 1、常用的常量 常量定义在了default.properties配置文件中,体现形式都是key=value。所有的struts2应用都会用到这些常量。 常量名 常量值(默认值) 说明 struts.i18n.encoding UTF-8 应用中使用的编码 struts.objectFactory.spring.autoWire name 和spring框架整合有关 struts.multipart.parser jakarta 指定文件上传用的组件 struts.multipart.maxSize 2097152 文件上传总文件大小限制:2M struts.action.extension action 能进入Struts2框架内部的url地址后缀名。多个值用逗号分隔。 struts.enable.DynamicMethodInvocation false 是否允许动态方法调用 struts.devMode false 是否是开发模式。开发模式:改了配置文件,不需要重启。输出更多的错误信息。开发阶段建议为true。 struts.ui.theme xhtml 页面展示用的主题 2、在struts.xml中覆盖常量(即修改struts中default.properties定义的常量值) 3、依次类推,三个文件的配置如下图所示:十一、Struts2中配置文件元素的详解 1、package元素: package元素:作用是定义一个struts的包,它是把配置文件按照面向对象的思想来管理。分模块开发。 即在struts2的配置文件中引入了面向对象思想,使用了分包管理。易于管理动作类。便于模块化开发动作类。 name属性:指定包的名称。注意:包的名称在配置文件中唯一。 extends属性:指定当前包的父包。它是按照面向对象的思想管理的体现。 一般情况下需要继承struts-default包,但不是必须的。不过如果不继承的话,将无法使用struts2提供的核心功能。 struts-default.xml 中定义着 struts-default 这个包。而 struts-default.xml 是在我们的 struts.xml 加载之前加载。 abstract属性:把包声明为一个抽象包。抽象包就是用来被继承的。 只有没有<action>元素的包,才能被定义为抽象包。 namespace属性:名称空间。当指定了名称空间之后,访问路径就变成了: 访问路径 = 名称空间 + 动作名称 当不指定该属性时,该属性有默认值,默认值是""。注意:不是"/"!!! 名称空间的搜索顺序: 第一步:先去找对应的名称空间 在指定的名称空间下找到了:就执行第二步。 在指定的名称空间下没找到:按照名称空间结构向上追溯,一直到根名称空间,只要在任何一级找到了,就执行第二步。 第二步:找动作名称 先在指定的名称空间下,搜索对应的动作名称:找到了就执行动作类的动作方法。 在指定的名称空间下没找到对应的动作名称:就前往默认的名称空间下,找动作名称。注意:它只找动作名称。 package的namespace的执行顺序,如下图所示: 示例代码: <package name="p1" extends="struts-default" namespace="/user"> <!-- 名称空间(namespace="/user") --> <!-- <action name="action1" class="com.itheima.web.action.Demo1Action" method="saveUser" > <result name="success">/success.jsp</result> </action> --> </package> <package name="p2" extends="struts-default"> <!-- 默认的名称空间(namespace=""或者不写该属性) --> <action name="action2" class="com.itheima.web.action.Demo1Action" method="saveUser2" > <result name="success">/success.jsp</result> </action> </package>访问:http://localhost:8080/day25_04_struts2_package/user/action2.action,可以找得到,查找过程: 1、先查找名称空间/user,有/user名称空间; 2、再在该名称空间查找动作名称action2.action,没有该动作名称; 3、再去默认的名称空间查找,有默认的名称空间; 4、再在该名称空间查找动作名称action2.action,有该动作名称,则执行该动作。 2、action元素: action元素:是用于定义动作名称,动作类和动作方法的映射,即配置动作用的。以及当出现不同情况时前往指定的结果视图 。 name属性:动作的名称。和jsp页面上的请求url中的名称对应起来。注意在此处不能写.action。 class属性:指定动作类,即动作类全名。 method属性:指定要执行的动作方法,即动作类中的方法名称。默认是public String execute() { } 方法要求: 1. public的 2. 返回值必须是String 3. 没有参数 可以修改默认动作类,注意:我们一般不改 <default-class-ref class="你自己的动作类" /> 默认的动作类是:com.opensymphony.xwork2.ActionSupport 是在struts-default.xml中定义的。 3、编写动作类的三种方式: a.方式一:动作类就是一个POJO(Plain Old Java Object: 原始的老的java对象),是非常简单的JavaBean。示例代码如下: package com.itheima.web.action;/** * 创建动作类的第一种方式: * 创建一个普通的java类。 * 它就是一个POJO,是非常简单的javabean。 * 原始的 老的 java 对象 * Plain Old Java Object * POJO类是指没有实现任何接口以及除了Object类以外,没有继承任何父类。 * struts2通过获取struts.xml获取到完全的类名,然后底层通过反射,执行方法。 * 该创建动作类的方式的特点:一点也看不到有struts2的痕迹。 * * @author cmj */public class Demo1Action { public String hello() { System.out.println("动作类执行了"); return "success"; }} b.方式二:动作类实现com.opensymphony.xwork2.Action接口。示例代码如下: package com.itheima.web.action;import com.opensymphony.xwork2.Action;/** * 创建动作类的第二种方式: * 动作类实现com.opensymphony.xwork2.Action接口。 * 创建一个普通类,实现Action接口,实现接口中的方法。 * * Action接口中的常量: * 常量名 默认常量值 说明 * SUCCESS "success" 当动作执行成功后,前往指定的位置 * NONE "none" 不返回任何结果视图,和 return null; 效果是一样的 * ERROR "error" 当执行动作方法,出现异常后,前往指定的位置 * INPUT "input" 数据回显 * LOGIN "login" 一般用于返回登录页面 * * @author cmj */public class Demo2Action implements Action { public String execute() throws Exception { System.out.println("动作类执行了"); return null; }} c.方式三:动作类继承com.opensymphony.xwork2.ActionSupport类。推荐使用方式三。示例代码如下: package com.itheima.web.action;import com.opensymphony.xwork2.ActionSupport;/** * 创建动作类的第三种方式: * 创建一个动作类,继承com.opensymphony.xwork2.ActionSupport类。推荐使用方式三。 * 意义:提供了一些基本的功能。比如验证和国际化消息提示等。 * 我们在开发中采取这种方式,来创建我们的动作类。 * * @author cmj */public class Demo3Action extends ActionSupport{ // 当我们在该类中什么都不写,一个动作方法都不提供时: // 有一个默认的动作方法:public String execute() throws Exception { return SUCCESS; }} 4、动作的调用: a.使用通配符: 优先级:绝对匹配优先。使用通配符的按照在配置文件中的先后顺序进行匹配的。b.使用动态方法调用: 示例代码如下: struts.xml <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"><struts> <!-- 先设置为开发者模式 --> <constant name="struts.devMode" value="true" /> <!-- 开启动态方法调用 --> <constant name="struts.enable.DynamicMethodInvocation" value="true"/> <package name="p1" extends="struts-default"> <!-- 动作方法调用的配置 <action name="addUser" class="com.itheima.web.action.UserAction" method="addUser"> <result name="success">/addUser.jsp</result> </action> <action name="updateUser" class="com.itheima.web.action.UserAction" method="updateUser"> <result name="success">/updateUser.jsp</result> </action> <action name="deleteUser" class="com.itheima.web.action.UserAction" method="deleteUser"> <result name="success">/deleteUser.jsp</result> </action> <action name="findUser" class="com.itheima.web.action.UserAction" method="findUser"> <result name="success">/findUser.jsp</result> </action> --> <!-- 使用通配符,配置动作方法 *表示的是动作的名称,当有和动作名称相匹配的时候可以用{出现的位置}来代替 <action name="*_*" class="com.itheima.web.action.{2}Action" method="{1}{2}"> <result name="success">/{1}{2}.jsp</result> </action> --> <!-- 动态方法调用的配置 --> <action name="user" class="com.itheima.web.action.UserAction"> <result name="success">/success.jsp</result> </action> </package></struts> inex.jsp <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>title</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <%-- 使用通配符,访问动作类和动作方法 <a href="${pageContext.request.contextPath}/add_User.action" >添加用户</a><br> <a href="${pageContext.request.contextPath}/update_User.action" >更新用户</a><br> <a href="${pageContext.request.contextPath}/delete_User.action" >删除用户</a><br> <a href="${pageContext.request.contextPath}/find_User.action" >查询用户</a><br> --%> <%-- 使用动态方法调用,格式如下: 动作名称!动作方法名称.action 或者 动作名称!动作方法名称 --%> <a href="${pageContext.request.contextPath}/user!addUser.action" >添加用户</a> <a href="${pageContext.request.contextPath}/user!updateUser.action" >更新用户</a> <a href="${pageContext.request.contextPath}/user!deleteUser.action" >删除用户</a> <a href="${pageContext.request.contextPath}/user!findUser.action" >查询用户</a> </body></html> 十二、Struts2中结果视图详解 1、resulst元素: resulst元素:配置逻辑结果视图,即为动作指定结果视图。 name属性:结果视图名称。与动作方法的返回值对应,当一致时前往指定的jsp。 type属性:结果视图类型。不写的时候,有默认值,默认值是dispatcher(请求转发)。 常用取值: dispatcher:请求转发 ,是默认值(本动作下) <result name="success" type="dispatcher">/success.jsp</result> redirect:请求重定向(本动作下) <result name="success" type="redirect">/success.jsp</result> chain:请求转发到另一个动作 请求转发到同包(同名称空间)下的另一个动作 <result name="success" type="chain">action2</result> 请求转发到不同包(不同名称空间)下的另一个动作 <result name="success" type="chain"> <param name="namespace">/n2</param> <param name="actionName">action3</param> </result> 使用的是注入的思想,在执行之重定向之前,会先获取这两个参数的值 调用的就是setNamespace("/n2")和setActionName("action3") redirectAction:请求重定向到另一个动作 请求重定向到同包(同名称空间)下的另一个动作 <result name="success" type="redirectAction">action2</result> 请求重定向不同包(不同名称空间)下的另一个动作 <result name="success" type="redirectAction"> <param name="namespace">/n2</param> <param name="actionName">action3</param> </result> 使用的是注入的思想,在执行之重定向之前,会先获取这两个参数的值 调用的就是setNamespace("/n2")和setActionName("action3") result元素中type的取值,type属性的取值在 struts-default.xml 中定义,如下图所示: 2、result元素中param子元素 在请求转发或者请求重定向到不同包下的动作时,都用到了result元素的子元素param。 param元素的作用:依赖注入(Dependence Injection)思想:我们通过struts-default.xml中的result-types元素中配置可以看出,每个结果类型视图其实都是靠一个类来实现的。而param元素就是将配置的参数,注入到该类中。 调用的是对应类的setter方法进行注入的。例如:setNamespace("/n2")和setActionName("action3") 示例代码如下: <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"><struts> <constant name="struts.devMode" value="true"/> <package name="p1" extends="struts-default"> <action name="action1" class="com.itheima.web.action.Demo1Action"> <result name="success" type="chain"> <!-- 转发到不同包(不同名称空间)下的另一个动作 --> <param name="namespace">/n2</param> <param name="actionName">action3</param> </result> </action> <!-- 没有给定动作类,默认的动作类是:com.opensymphony.xwork2.ActionSupport,没有给定动作方法,默认的动作方法是:execute --> <action name="action2"> <result name="success" > <param name="location">/success.jsp</param> </result> </action> </package> <package name="p2" extends="struts-default" namespace="/n2"> <action name="action3"> <result name="success" >/success.jsp</result> </action> </package></struts> 3、自定义结果类型及其配置 通过前面的内容,我们看出,其实结果类型就是一个类: 这些类都实现了com.opensymphony.xwork2.Result接口。 或者继承自该接口的实现类org.apache.struts2.dispatcher.StrutsResultSupport。 这些类都有一个doExecute方法,用于执行结果视图。综上:我们也可以自己写一个结果视图。例子: 输出CAPTCHA图像的结果类型。 CAPTCHA(Completely Automated Public Turing Test to Tell Computers and Humans Apart: 全自动区分计算机和人类的图灵测试) ————>简称:验证码。步骤: 1.编写一个普通类,继承自StrutsResultSupport的类,并且重写doExcecute方法。此为自定义结果类型的类。 2.在struts.xml中进行配置。 3.在配置action时,type属性指定声明的结果类型名称。 1、编写一个普通类,继承自StrutsResultSupport的类,并且重写doExcecute方法。此为自定义结果类型的类。 package com.itheima.web.result;import javax.servlet.http.HttpServletResponse;import org.apache.struts2.ServletActionContext;import org.apache.struts2.dispatcher.StrutsResultSupport;import cn.dsna.util.images.ValidateCode;import com.opensymphony.xwork2.ActionInvocation;/** * 自定义结果类型 * 第一步:编写一个普通类,继承自StrutsResultSupport的类,并且重写doExcecute方法。 * 第二步:在struts.xml中进行配置。 * 第三步:在配置action时,type属性指定声明的结果类型名称。 * * @author cmj */public class CAPTCHAResult extends StrutsResultSupport { // 通过配置文件,调整生成图片的大小 private int width; private int height; public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } /* * 使用第三方生成验证码的jar包 * 1.拷贝ValidateCode.jar到工程lib目录 * 2.创建ValidateCode的对象 * 3.获取响应对象输出流 * 4.输出到浏览器 */ // Servlet的中原来怎么写,现在还怎么写 @Override protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { // 创建ValidateCode的对象,该对象的构造参数详解:1:图像宽度 2.图像高度 3.数字的格式 4.干扰线条数 ValidateCode code = new ValidateCode(width, height, 4, 10); // 获取响应对象输出流 HttpServletResponse response = ServletActionContext.getResponse(); // 输出到浏览器 code.write(response.getOutputStream()); }} 2、在struts.xml中进行配置,如下所示。 3、在配置action时,type属性指定声明的结果类型名称。 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"><struts> <!-- 自定义结果类型的配置 --> <package name="p3" extends="myDefault"> <!-- 自定义结果类型 --> <result-types> <result-type name="captcha" class="com.itheima.web.result.CAPTCHAResult"></result-type> </result-types> <action name="captchaAction" class="com.itheima.web.action.CaptchaAction" > <result name="success" type="captcha"> <!-- 配置图像的大小 --> <param name="width">240</param> <param name="height">40</param> </result> </action> </package></struts> 4、建立动作类和动作方法 package com.itheima.web.action;import com.opensymphony.xwork2.ActionSupport;public class CaptchaAction extends ActionSupport { // 当我们在该动作类中什么都不写,即一个动作方法都不提供时: // 会有一个默认的动作方法:public String execute() throws Exception { return SUCCESS; }} 5、建立一个jsp页面 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>title</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <form action="" method="post"> 用户名:<input type="text" name="username"/><br/> 密码:<input type="password" name="password"/><br/> 验证码:<input type="text" name="valicateCode"/> <img src="${pageContext.request.contextPath}/captchaAction.action"/> <br/> <input type="submit" value="登录" /> </form> </body></html> 4、局部视图和全局视图 局部视图的配置 全局视图的配置十三、Struts2中调用ServletAPI 获取ServletAPI的两种方式: 第一种方式:使用的是ServletActionContext的对象(此种方式简单明了,推荐此种方式) 第二种方式:使用的是依赖注入的形式,把我们想要的对象注入进来,是由一个拦截器为我们做的。需要实现3个接口,实现其中的方法。 示例代码如下: package com.itheima.web.action;import javax.servlet.ServletContext;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.apache.struts2.interceptor.ServletRequestAware;import org.apache.struts2.interceptor.ServletResponseAware;import org.apache.struts2.util.ServletContextAware;import com.opensymphony.xwork2.ActionSupport;/** * 获取ServletAPI的两种方式: * 第一种方式:使用的是 ServletActionContext 的对象(此种方式简单,推荐此种方式) * 第二种方式:使用的是 依赖注入 的形式,把我们想要的对象注入进来,是由一个拦截器为我们做的。需要实现3个接口,实现其中的方法。 * * @author cmj */public class Demo1Action extends ActionSupport implements ServletRequestAware, ServletResponseAware, ServletContextAware { private HttpServletRequest request; private HttpServletResponse response; private ServletContext application; public String execute() { // 方式一:使用 ServletActionContext 对象 // HttpServletRequest request = ServletActionContext.getRequest(); // HttpServletResponse response = ServletActionContext.getResponse(); // ServletContext application = ServletActionContext.getServletContext(); // HttpSession session = request.getSession(); // System.out.println(request); // org.apache.struts2.dispatcher.StrutsRequestWrapper@500b3f4c // System.out.println(response); // org.apache.catalina.connector.ResponseFacade@1268bfa2 // System.out.println(application); // org.apache.catalina.core.ApplicationContextFacade@1afd2e1f // System.out.println(session); // org.apache.catalina.session.StandardSessionFacade@63df0310 // 方式二:使用依赖注入 HttpSession session = request.getSession(); System.out.println(request); System.out.println(response); System.out.println(application); System.out.println(session); return null; } @Override public void setServletContext(ServletContext application) { this.application = application; } @Override public void setServletResponse(HttpServletResponse response) { this.response = response; } @Override public void setServletRequest(HttpServletRequest request) { this.request = request; }} 十四、分文件编写Struts2的配置文件 1、不分文件开发可能产生的问题 就类似于我们在写java类时,所有代码都写在一个类里,甚至写在一个方法里。 因为当3个人都checkout了struts.xml文件时,第一个人更新提交了,后面的人在没有更新时就提交,第一个人写的可能就白写了。如下图所示:2、分文件编写Struts2的配置文件我们现在常说的组件式开发,如下图所示: 分文件编写Struts2的配置文件,如下图所示: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
参考链接: https://segmentfault.com/a/1190000013001367 先发链接,有空实践后会整理。我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
参考链接: http://www.cnblogs.com/real-me/p/8336741.html https://www.cnblogs.com/xuehaoyue/p/6650533.html http://www.cnblogs.com/houkai/p/3394402.html http://www.cnblogs.com/xdp-gacl/p/3718879.html#2937655 http://www.cnblogs.com/zzqcn/p/4657124.html http://www.cnblogs.com/jiangz/p/3734968.html http://www.cnblogs.com/wangqiguo/p/4355032.html 先发链接,有空实践后再依据个人博客情况进行整理。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
【复习】请求转发与请求重定向的区别: 1.请求转发 一次请求、服务器行为、地址栏不变、携带数据不丢失 2.请求重定向 两次请求、浏览器行为、地址栏变化、携带数据丢失我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
常用的 default.properties 文件,所在位置:\struts-2.3.15.3-all\struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\struts2-core-2.3.15.3.jar\org\apache\struts2\default.properties # # $Id: default.properties 1532467 2013-10-15 18:20:43Z lukaszlenart $ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ### START SNIPPET: complete_file ### Struts default properties ###(can be overridden by a struts.properties file in the root of the classpath) ### ### Specifies the Configuration used to configure Struts ### one could extend org.apache.struts2.config.Configuration ### to build one's customize way of getting the configurations parameters into Struts # struts.configuration=org.apache.struts2.config.DefaultConfiguration ### This can be used to set your default locale and encoding scheme # struts.locale=en_US struts.i18n.encoding=UTF-8 ### if specified, the default object factory can be overridden here ### Note: short-hand notation is supported in some cases, such as "spring" ### Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here # struts.objectFactory = spring ### specifies the autoWiring logic when using the SpringObjectFactory. ### valid values are: name, type, auto, and constructor (name is the default) struts.objectFactory.spring.autoWire = name ### indicates to the struts-spring integration if Class instances should be cached ### this should, until a future Spring release makes it possible, be left as true ### unless you know exactly what you are doing! ### valid values are: true, false (true is the default) struts.objectFactory.spring.useClassCache = true ### ensures the autowire strategy is always respected. ### valid values are: true, false (false is the default) struts.objectFactory.spring.autoWire.alwaysRespect = false ### if specified, the default object type determiner can be overridden here ### Note: short-hand notation is supported in some cases, such as "tiger" or "notiger" ### Alternatively, you can provide a com.opensymphony.xwork2.util.ObjectTypeDeterminer implementation name here ### Note: By default, com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer is used which handles type detection ### using generics. com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer was deprecated since XWork 2, it's ### functions are integrated in DefaultObjectTypeDeterminer now. ### To disable tiger support use the "notiger" property value here. #struts.objectTypeDeterminer = tiger #struts.objectTypeDeterminer = notiger ### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data # struts.multipart.parser=cos # struts.multipart.parser=pell struts.multipart.parser=jakarta # uses javax.servlet.context.tempdir by default struts.multipart.saveDir= struts.multipart.maxSize=2097152 ### Load custom property files (does not override struts.properties!) # struts.custom.properties=application,org/apache/struts2/extension/custom ### How request URLs are mapped to and from actions #struts.mapper.class=org.apache.struts2.dispatcher.mapper.DefaultActionMapper ### Used by the DefaultActionMapper ### You may provide a comma separated list, e.g. struts.action.extension=action,jnlp,do ### The blank extension allows you to match directory listings as well as pure action names ### without interfering with static resources, which can be specified as an empty string ### prior to a comma e.g. struts.action.extension=, or struts.action.extension=x,y,z,, struts.action.extension=action,, ### Used by FilterDispatcher ### If true then Struts serves static content from inside its jar. ### If false then the static content must be available at <context_path>/struts struts.serve.static=true ### Used by FilterDispatcher ### This is good for development where one wants changes to the static content be ### fetch on each request. ### NOTE: This will only have effect if struts.serve.static=true ### If true -> Struts will write out header for static contents such that they will ### be cached by web browsers (using Date, Cache-Content, Pragma, Expires) ### headers). ### If false -> Struts will write out header for static contents such that they are ### NOT to be cached by web browser (using Cache-Content, Pragma, Expires ### headers) struts.serve.static.browserCache=true ### Set this to false if you wish to disable implicit dynamic method invocation ### via the URL request. This includes URLs like foo!bar.action, as well as params ### like method:bar (but not action:foo). ### An alternative to implicit dynamic method invocation is to use wildcard ### mappings, such as <action name="*/*" method="{2}" class="actions.{1}"> struts.enable.DynamicMethodInvocation = false ### Set this to true if you wish to allow slashes in your action names. If false, ### Actions names cannot have slashes, and will be accessible via any directory ### prefix. This is the traditional behavior expected of WebWork applications. ### Setting to true is useful when you want to use wildcards and store values ### in the URL, to be extracted by wildcard patterns, such as ### <action name="*/*" method="{2}" class="actions.{1}"> to match "/foo/edit" or ### "/foo/save". struts.enable.SlashesInActionNames = false ### Disables support for action: prefix struts.mapper.action.prefix.enabled = false ### Blocks access to actions in other namespace than current with action: prefix struts.mapper.action.prefix.crossNamespaces = false ### use alternative syntax that requires %{} in most places ### to evaluate expressions for String attributes for tags struts.tag.altSyntax=true ### when set to true, Struts will act much more friendly for developers. This ### includes: ### - struts.i18n.reload = true ### - struts.configuration.xml.reload = true ### - raising various debug or ignorable problems to errors ### For example: normally a request to foo.action?someUnknownField=true should ### be ignored (given that any value can come from the web and it ### should not be trusted). However, during development, it may be ### useful to know when these errors are happening and be told of ### them right away. struts.devMode = false ### when set to true, resource bundles will be reloaded on _every_ request. ### this is good during development, but should never be used in production struts.i18n.reload=false ### Standard UI theme ### Change this to reflect which path should be used for JSP control tag templates by default struts.ui.theme=xhtml struts.ui.templateDir=template #sets the default template type. Either ftl, vm, or jsp struts.ui.templateSuffix=ftl ### Configuration reloading ### This will cause the configuration to reload struts.xml when it is changed struts.configuration.xml.reload=false ### Location of velocity.properties file. defaults to velocity.properties struts.velocity.configfile = velocity.properties ### Comma separated list of VelocityContext classnames to chain to the StrutsVelocityContext struts.velocity.contexts = ### Location of the velocity toolbox struts.velocity.toolboxlocation= ### used to build URLs, such as the UrlTag struts.url.http.port = 80 struts.url.https.port = 443 ### possible values are: none, get or all struts.url.includeParams = none ### Load custom default resource bundles # struts.custom.i18n.resources=testmessages,testmessages2 ### workaround for some app servers that don't handle HttpServletRequest.getParameterMap() ### often used for WebLogic, Orion, and OC4J struts.dispatcher.parametersWorkaround = false ### configure the Freemarker Manager class to be used ### Allows user to plug-in customised Freemarker Manager if necessary ### MUST extends off org.apache.struts2.views.freemarker.FreemarkerManager #struts.freemarker.manager.classname=org.apache.struts2.views.freemarker.FreemarkerManager ### Enables caching of FreeMarker templates ### Has the same effect as copying the templates under WEB_APP/templates struts.freemarker.templatesCache=false ### Enables caching of models on the BeanWrapper struts.freemarker.beanwrapperCache=false ### See the StrutsBeanWrapper javadocs for more information struts.freemarker.wrapper.altMap=true ### maxStrongSize for MruCacheStorage for freemarker, when set to 0 SoftCacheStorage which performs better in heavy loaded application ### check WW-3766 for more details struts.freemarker.mru.max.strong.size=0 ### configure the XSLTResult class to use stylesheet caching. ### Set to true for developers and false for production. struts.xslt.nocache=false ### Whether to always select the namespace to be everything before the last slash or not struts.mapper.alwaysSelectFullNamespace=false ### Whether to allow static method access in OGNL expressions or not struts.ognl.allowStaticMethodAccess=false ### Whether to throw a RuntimeException when a property is not found ### in an expression, or when the expression evaluation fails struts.el.throwExceptionOnFailure=false ### Logs as Warnings properties that are not found (very verbose) struts.ognl.logMissingProperties=false ### Caches parsed OGNL expressions, but can lead to memory leaks ### if the application generates a lot of different expressions struts.ognl.enableExpressionCache=true ### Indicates if Dispatcher should handle unexpected exceptions by calling sendError() ### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security struts.handle.exception=true ### END SNIPPET: complete_file default.properties 常用的 struts-default.xml 文件,所在位置:\struts-2.3.15.3-all\struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\struts2-core-2.3.15.3.jar\struts-default.xml <?xml version="1.0" encoding="UTF-8" ?> <!-- /* * $Id: struts-default.xml 1485719 2013-05-23 14:12:24Z lukaszlenart $ * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ --> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" /> <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" /> <bean type="com.opensymphony.xwork2.FileManager" class="com.opensymphony.xwork2.util.fs.DefaultFileManager" name="system" scope="singleton"/> <bean type="com.opensymphony.xwork2.FileManagerFactory" class="com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory" name="xwork" scope="singleton"/> <bean type="com.opensymphony.xwork2.FileManagerFactory" class="com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory" name="struts" scope="singleton"/> <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="xwork" class="com.opensymphony.xwork2.DefaultActionProxyFactory"/> <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/> <bean type="com.opensymphony.xwork2.conversion.ObjectTypeDeterminer" name="tiger" class="com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer"/> <bean type="com.opensymphony.xwork2.conversion.ObjectTypeDeterminer" name="notiger" class="com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer"/> <bean type="com.opensymphony.xwork2.conversion.ObjectTypeDeterminer" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer"/> <bean type="com.opensymphony.xwork2.util.PatternMatcher" name="struts" class="com.opensymphony.xwork2.util.WildcardHelper" /> <bean type="com.opensymphony.xwork2.util.PatternMatcher" name="namedVariable" class="com.opensymphony.xwork2.util.NamedVariablePatternMatcher"/> <bean type="com.opensymphony.xwork2.util.PatternMatcher" name="regex" class="org.apache.struts2.util.RegexPatternMatcher"/> <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="struts" class="org.apache.struts2.dispatcher.mapper.DefaultActionMapper" /> <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="composite" class="org.apache.struts2.dispatcher.mapper.CompositeActionMapper" /> <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="restful" class="org.apache.struts2.dispatcher.mapper.RestfulActionMapper" /> <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="restful2" class="org.apache.struts2.dispatcher.mapper.Restful2ActionMapper" /> <bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/> <bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" /> <constant name="struts.multipart.parser" value="jakarta" /> <bean type="org.apache.struts2.views.TagLibrary" name="s" class="org.apache.struts2.views.DefaultTagLibrary" /> <bean class="org.apache.struts2.views.freemarker.FreemarkerManager" name="struts" /> <bean class="org.apache.struts2.views.velocity.VelocityManager" name="struts" optional="true" /> <bean class="org.apache.struts2.components.template.TemplateEngineManager" /> <bean type="org.apache.struts2.components.template.TemplateEngine" name="ftl" class="org.apache.struts2.components.template.FreemarkerTemplateEngine" /> <bean type="org.apache.struts2.components.template.TemplateEngine" name="vm" class="org.apache.struts2.components.template.VelocityTemplateEngine" /> <bean type="org.apache.struts2.components.template.TemplateEngine" name="jsp" class="org.apache.struts2.components.template.JspTemplateEngine" /> <bean type="com.opensymphony.xwork2.conversion.impl.XWorkConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.XWorkConverter" /> <bean type="com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionPropertiesProcessor" /> <bean type="com.opensymphony.xwork2.conversion.ConversionFileProcessor" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor" /> <bean type="com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor" /> <bean type="com.opensymphony.xwork2.conversion.TypeConverterCreator" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator" /> <bean type="com.opensymphony.xwork2.conversion.TypeConverterHolder" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterHolder" /> <bean class="com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter" /> <bean type="com.opensymphony.xwork2.conversion.impl.CollectionConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.CollectionConverter" scope="singleton"/> <bean type="com.opensymphony.xwork2.conversion.impl.ArrayConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.ArrayConverter" scope="singleton"/> <bean type="com.opensymphony.xwork2.conversion.impl.DateConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.DateConverter" scope="singleton"/> <bean type="com.opensymphony.xwork2.conversion.impl.NumberConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.NumberConverter" scope="singleton"/> <bean type="com.opensymphony.xwork2.conversion.impl.StringConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.StringConverter" scope="singleton"/> <bean type="com.opensymphony.xwork2.TextProvider" name="struts" class="com.opensymphony.xwork2.TextProviderSupport" scope="default" /> <bean type="com.opensymphony.xwork2.LocaleProvider" name="struts" class="com.opensymphony.xwork2.DefaultLocaleProvider" scope="singleton" /> <bean type="org.apache.struts2.components.UrlRenderer" name="struts" class="org.apache.struts2.components.ServletUrlRenderer"/> <bean type="org.apache.struts2.views.util.UrlHelper" name="struts" class="org.apache.struts2.views.util.DefaultUrlHelper"/> <bean type="com.opensymphony.xwork2.util.ValueStackFactory" name="struts" class="com.opensymphony.xwork2.ognl.OgnlValueStackFactory" /> <bean type="com.opensymphony.xwork2.util.reflection.ReflectionProvider" name="struts" class="com.opensymphony.xwork2.ognl.OgnlReflectionProvider" /> <bean type="com.opensymphony.xwork2.util.reflection.ReflectionContextFactory" name="struts" class="com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory" /> <bean type="com.opensymphony.xwork2.TextProvider" name="system" class="com.opensymphony.xwork2.DefaultTextProvider" /> <bean type="com.opensymphony.xwork2.conversion.NullHandler" name="java.lang.Object" class="com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler" /> <bean type="com.opensymphony.xwork2.validator.ActionValidatorManager" name="struts" class="com.opensymphony.xwork2.validator.AnnotationActionValidatorManager" /> <bean type="com.opensymphony.xwork2.validator.ActionValidatorManager" name="no-annotations" class="com.opensymphony.xwork2.validator.DefaultActionValidatorManager" /> <bean type="com.opensymphony.xwork2.validator.ValidatorFactory" class="com.opensymphony.xwork2.validator.DefaultValidatorFactory"/> <bean type="com.opensymphony.xwork2.validator.ValidatorFileParser" class="com.opensymphony.xwork2.validator.DefaultValidatorFileParser" /> <bean class="com.opensymphony.xwork2.ognl.OgnlUtil" /> <bean type="com.opensymphony.xwork2.util.TextParser" name="struts" class="com.opensymphony.xwork2.util.OgnlTextParser" scope="singleton"/> <bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" /> <bean type="ognl.PropertyAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.ObjectAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Iterator" class="com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Enumeration" class="com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.List" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Set" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Map" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Collection" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.ognl.ObjectProxy" class="com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor" /> <bean type="ognl.MethodAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor" /> <bean type="ognl.MethodAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" /> <bean class="org.apache.struts2.views.jsp.ui.OgnlTool" /> <bean type="org.apache.struts2.dispatcher.StaticContentLoader" class="org.apache.struts2.dispatcher.DefaultStaticContentLoader" name="struts" /> <bean type="com.opensymphony.xwork2.UnknownHandlerManager" class="com.opensymphony.xwork2.DefaultUnknownHandlerManager" name="struts" /> <!-- Silly workarounds for OGNL since there is currently no way to flush its internal caches --> <bean type="ognl.PropertyAccessor" name="java.util.ArrayList" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.HashSet" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.HashMap" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" /> <package name="struts-default" abstract="true"> <result-types> <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/> <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" /> </result-types> <interceptors> <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/> <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/> <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/> <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/> <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" /> <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" /> <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" /> <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/> <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/> <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/> <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/> <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/> <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/> <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/> <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/> <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/> <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/> <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/> <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/> <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/> <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/> <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/> <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/> <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" /> <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" /> <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" /> <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" /> <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" /> <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" /> <!-- Basic stack --> <interceptor-stack name="basicStack"> <interceptor-ref name="exception"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> </interceptor-stack> <!-- Sample validation and workflow stack --> <interceptor-stack name="validationWorkflowStack"> <interceptor-ref name="basicStack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack> <!-- Sample file upload stack --> <interceptor-stack name="fileUploadStack"> <interceptor-ref name="fileUpload"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <!-- Sample model-driven stack --> <interceptor-stack name="modelDrivenStack"> <interceptor-ref name="modelDriven"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <!-- Sample action chaining stack --> <interceptor-stack name="chainStack"> <interceptor-ref name="chain"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <!-- Sample i18n stack --> <interceptor-stack name="i18nStack"> <interceptor-ref name="i18n"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <!-- An example of the paramsPrepareParams trick. This stack is exactly the same as the defaultStack, except that it includes one extra interceptor before the prepare interceptor: the params interceptor. This is useful for when you wish to apply parameters directly to an object that you wish to load externally (such as a DAO or database or service layer), but can't load that object until at least the ID parameter has been loaded. By loading the parameters twice, you can retrieve the object in the prepare() method, allowing the second params interceptor to apply the values on the object. --> <interceptor-stack name="paramsPrepareParamsStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="i18n"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param> </interceptor-ref> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> <!-- A complete stack with all the common interceptors in place. Generally, this stack should be the one you use, though it may do more than you need. Also, the ordering can be switched around (ex: if you wish to have your servlet-related objects applied before prepare() is called, you'd need to move servletConfig interceptor up. This stack also excludes from the normal validation and workflow the method names input, back, and cancel. These typically are associated with requests that should not be validated. --> <interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="debugging"/> </interceptor-stack> <!-- The completeStack is here for backwards compatibility for applications that still refer to the defaultStack by the old name --> <interceptor-stack name="completeStack"> <interceptor-ref name="defaultStack"/> </interceptor-stack> <!-- Sample execute and wait stack. Note: execAndWait should always be the *last* interceptor. --> <interceptor-stack name="executeAndWaitStack"> <interceptor-ref name="execAndWait"> <param name="excludeMethods">input,back,cancel</param> </interceptor-ref> <interceptor-ref name="defaultStack"/> <interceptor-ref name="execAndWait"> <param name="excludeMethods">input,back,cancel</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="defaultStack"/> <default-class-ref class="com.opensymphony.xwork2.ActionSupport" /> </package> </struts> struts-default.xml 常用的 struts-plugin.xml 文件,所在位置:用到插件时,会有该文件的配置 <?xml version="1.0" encoding="UTF-8" ?> <!-- /* * $Id: struts-plugin.xml 1221225 2011-12-20 12:22:28Z jogep $ * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ --> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts order="20"> <bean type="com.opensymphony.xwork2.UnknownHandler" name="convention" class="org.apache.struts2.convention.ConventionUnknownHandler"/> <bean type="org.apache.struts2.convention.ActionConfigBuilder" name="convention" class="org.apache.struts2.convention.PackageBasedActionConfigBuilder"/> <bean type="org.apache.struts2.convention.ActionNameBuilder" name="convention" class="org.apache.struts2.convention.SEOActionNameBuilder"/> <bean type="org.apache.struts2.convention.ResultMapBuilder" name="convention" class="org.apache.struts2.convention.DefaultResultMapBuilder"/> <bean type="org.apache.struts2.convention.InterceptorMapBuilder" name="convention" class="org.apache.struts2.convention.DefaultInterceptorMapBuilder"/> <bean type="org.apache.struts2.convention.ConventionsService" name="convention" class="org.apache.struts2.convention.ConventionsServiceImpl"/> <bean type="com.opensymphony.xwork2.config.PackageProvider" name="convention.packageProvider" class="org.apache.struts2.convention.ClasspathPackageProvider"/> <bean type="com.opensymphony.xwork2.config.PackageProvider" name="convention.containerProvider" class="org.apache.struts2.convention.ClasspathConfigurationProvider"/> <constant name="struts.convention.actionConfigBuilder" value="convention"/> <constant name="struts.convention.actionNameBuilder" value="convention"/> <constant name="struts.convention.resultMapBuilder" value="convention"/> <constant name="struts.convention.interceptorMapBuilder" value="convention"/> <constant name="struts.convention.conventionsService" value="convention"/> <constant name="struts.convention.result.path" value="/WEB-INF/content/"/> <constant name="struts.convention.result.flatLayout" value="true"/> <constant name="struts.convention.action.suffix" value="Action"/> <constant name="struts.convention.action.disableScanning" value="false"/> <constant name="struts.convention.action.mapAllMatches" value="false"/> <constant name="struts.convention.action.checkImplementsAction" value="true"/> <constant name="struts.convention.default.parent.package" value="convention-default"/> <constant name="struts.convention.action.name.lowercase" value="true"/> <constant name="struts.convention.action.name.separator" value="-"/> <constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/> <constant name="struts.convention.package.locators.disable" value="false"/> <constant name="struts.convention.package.locators.basePackage" value=""/> <constant name="struts.convention.exclude.packages" value="org.apache.struts.*,org.apache.struts2.*,org.springframework.web.struts.*,org.springframework.web.struts2.*,org.hibernate.*"/> <constant name="struts.convention.relative.result.types" value="dispatcher,velocity,freemarker"/> <constant name="struts.convention.redirect.to.slash" value="true"/> <constant name="struts.convention.action.alwaysMapExecute" value="true"/> <constant name="struts.mapper.alwaysSelectFullNamespace" value="true"/> <!-- <constant name="struts.convention.action.includeJars" /> --> <constant name="struts.convention.action.fileProtocols" value="jar" /> <constant name="struts.convention.classes.reload" value="false" /> <constant name="struts.convention.exclude.parentClassLoader" value="true" /> <package name="convention-default" extends="struts-default"> </package> </struts> struts-plugin.xml 常用的 struts.xml 文件,所在位置:\struts-2.3.15.3-all\struts-2.3.15.3\apps\struts2-blank\WEB-INF\src\java <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/" extends="struts-default"> <default-action-ref name="index" /> <global-results> <result name="error">/error.jsp</result> </global-results> <global-exception-mappings> <exception-mapping exception="java.lang.Exception" result="error"/> </global-exception-mappings> <action name="index"> <result type="redirectAction"> <param name="actionName">HelloWorld</param> <param name="namespace">/example</param> </result> </action> </package> <include file="example.xml"/> <!-- Add packages here --> </struts> struts.xml 常用的 struts.properties文件 ,所在位置: 我们编写的文件,一般不用它,一般我们配置struts.xml文件就行。 常用的 web.xml 文件,所在位置:\struts-2.3.15.3-all\struts-2.3.15.3\apps\struts2-blank\WEB-INF\web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Struts Blank</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app> web.xml 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
log4j 是什么 Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。 log4j:WARN 出现的原因 在使用MyEclipse编写java程序的时候很容易出现下面的警告,原因在警告中也说的非常明白,没有正确的初始化log4j,而初始化log4j一般使用的是一个名叫log4j.properties的文件,因此只需要写好log4j.properties文件,然后让MyEclipse重新部署便可。 警告如下图所示: log4j:WARN No appenders could be found for logger (com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. log4j:WARN 解决的办法 在src下面新建file名为 log4j.properties 内容如下: # Configure logging for testing: optionally with log file log4j.rootLogger=WARN, stdout # log4j.rootLogger=WARN, stdout, logfile log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=target/spring.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n 重新发布,OK!没有提示了。加入了这个配置文件后,再次运行程序上面的警告就会消失。 尤其在进行Web 层开发的时候,只有加入了这个文件后才能看到Spring 后台完整的出错信息。 在开发Spring 整合应用时,经常有人遇到出现404 错误但是却看不到任何出错信息的情况,这时你就需要检查一下这个文件是不是存在。 或者可以将Struts2或 Hibernate 等压缩包解压,内有 log4j.properties 文件,将它复制到项目src文件夹或将 log4j.properties 放到 \WEB-INF\classes文件夹 中即可。 参考链接: https://www.cnblogs.com/jbelial/archive/2012/06/05/2536814.html https://blog.csdn.net/u013595419/article/details/77895262我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
在Struts-2.3的配置文件struts.xml中,Caused by: 元素类型为 "package" 的内容必须匹配 "(result-types?,interceptors?,default-interceptor-ref?,default-action-ref?,default-class-ref?,global-results?,global-exception-mappings?,action*)"。 该错误的意思是:package的内容 必须按 result-types interceptors default-interceptor-ref default-action-ref default-class-ref global-results global-exception-mappings action* 这样的顺序放置。 没有的跳过,有的按此顺序书写。我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
问题背景: 在myeclipse中写项目,经常使用复制项目的方式创建新的项目,但是新项目复制后,光改个名字是不能发布到Tomcat中,这会和以前的项目同名,因为它的Web Context Root还是原来的名字,需要修改。 有些时候做项目的测试时,不可避免地要拷贝整个工程,此时不修改这个配置,在浏览器就不能正常访问。Tomcat就是通过这个Web Context Root访问的。 比如一个工程名是product,那么对应的Web Context Root就是/product,在浏览器就可以这样访问:http://localhost:8080/product 如果这个配置有误,显然不能正常访问了。 解决方法: 法一: 项目 --> 右键 --> Properties --> Project Facets --> Web --> 修改Web Context-Root --> OK。此法有时只能修改一次,有时不让编辑。 法二: 项目 --> 右键 --> Properties --> Deployment --> Deployment Assembly --> 修改Web Context-Root --> Apply --> OK。此法非常好用! 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
1、jar包的变动 必需jar包,旧版本: 必需jar包,新版本: 在struts-2.5.16版本的lib目录下没有xwork-core的jar包,原因是被合并到struts-core这个jar里了。所谓core嘛,当然是一个核心,原来两个合并成一个也是有道理的。 2、web.xml文件配置过滤器变动 if you are using struts2 version 2.5 you need to change from org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter to org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter 是的,去掉了中间.ng文件空间名,原因是在整合xwork的时候,同时简化了不必要的包名空间。 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class></filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> 3、在项目根目录空间下创建struts.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> </struts> 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
Struts2的下载 使用Struts2框架,进行Web开发,或者运行Struts2的程序,必须,先下载并安装好Struts2 从Struts2的官网中进行下载 http://struts.apache.org 单击Download按钮,进入Struts2的下载页面 可以看出,在Full Release(完整版本)标题下,有几个链接单击可供下载 具体说明 Full Distribution 下载Struts2的完整版 通常,建议下载该选项,该选项包括Struts2的示例应用、空示例应用、核心库、源代码和文档等 Example Applications 仅下载Struts2的示例应用 这些,示例应用对于学习Struts2有很多的帮助,下载Struts2的完整版时,已经包含了该选项中的全部应用 Essential Dependencies Only 仅下载Struts2的核心库 下载Struts2的完整版时,已经包含该选项下的全部内容 Documentation 仅下载Struts2的相关文档 包含Struts2的使用文档、参考手册和API文档等,下载Struts2的完整版时,已经包含该选项下的全部内容 Source 下载Struts2的全部源代码 下载Struts2的完整版时,已经包含该选项下的全部内容 通常,建议下载Full Distribution 下载历史版本 如果,要下载某个历史版本,可以通过在浏览器中直接访问如下链接,在Struts2的版本库中,进行下载 https://archive.apache.org/dist/struts/ Struts2安装 Struts2安装非常简单 将Struts2目录中的lib文件夹打开,得到Struts2开发中可能用到的所有JAR包 此版本有93个JAR包,复制到Web项目的WEB-INF/lib路径下即可(实际开发中,用哪个jir包就拷贝哪个jar包,不要全部拷贝) 原文链接:https://blog.csdn.net/nangeali/article/details/77435907我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
验证用户名是否存在的示例代码引出该思想: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><script type="text/javascript" src="${pageContext.request.contextPath}/js/myJS.js"></script><title>Insert title here</title> <script type="text/javascript"> // 方式一:使用文本框的onblur事件(失去焦点事件)// function ckName() {// // 通过标签名获取元素节点对象// var name = document.getElementsByTagName("input")[0];// // 创建XMLHttpRequest对象// var xhr = getXMLHttpRequest();// // 处理响应结果,创建回调函数,根据响应状态动态更新页面// xhr.onreadystatechange = function() {// if (xhr.readyState == 4) { // 说明客户端请求一切正常// if (xhr.status == 200) { // 说明服务器响应一切正常// // alert(xhr.responseText); // 得到响应结果,得到页面上面的结果,注意结果为字符串// var msg = document.getElementById("msg");// if (xhr.responseText == "true") {// // msg.innerText = "用户名已存在";// msg.innerHTML = "<font color='red'>该用户名已存在</font>";// } else {// msg.innerHTML = "<font color='green'>该用户名可以使用</font>";// }// }// }// } // // // 建立一个连接// xhr.open("get", "${pageContext.request.contextPath}/servlet/ckNameServlet?name=" + name.value);// // 发送请求// xhr.send(null);// } // 方式二:不使用文本框的事件 // onload 加载完毕的事件,等到页面加载完毕后再执行onload事件所指向的函数。 window.onload = function() { // 通过名获取元素对象 var nameElement = document.getElementsByName("userName")[0]; nameElement.onblur = function() { var name = this.value; // this等价于nameElement // 创建XMLHttpRequest对象 var xhr = getXMLHttpRequest(); // 处理响应结果,创建回调函数,根据响应状态动态更新页面 xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // 说明客户端请求一切正常 if (xhr.status == 200) { // 说明服务器响应一切正常 // alert(xhr.responseText); // 得到响应结果,得到页面上面的结果,注意结果为字符串 var msg = document.getElementById("msg"); if (xhr.responseText == "true") { // msg.innerText = "该用户名已存在"; msg.innerHTML = "<font color='red'>该用户名已存在</font>"; } else { msg.innerHTML = "<font color='green'>该用户名可以使用</font>"; } } } } // 建立一个连接 xhr.open("get", "${pageContext.request.contextPath}/servlet/ckNameServlet?name=" + name + "&time=" + new Date().getTime()); // 发送请求 xhr.send(null); } } </script></head><body> <!-- 文本框的onblur事件(失去焦点事件) --> <!-- 用户名:<input type="text" name="userName" onblur="ckName()"/><span id="msg" ></span></br> 为了使得页面的标签变得干净,事件不写在标签上。即js代码与html代码分离。 --> 用户名:<input type="text" name="userName" /><span id="msg" ></span></br> 密码:<input type="password" name="pwd" /></br></body></html> 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、测试 项目完成后,需要进行测试Test。测试分为: 功能测试(对某一个功能进行测试)**黑盒**: 例如:public int addOrder(Order order); 流程测试(对一套业务进行测试)**白盒**: 例如:public int addOrder(Order order); 返回1: 代表添加成功 返回0: 代表添加失败 返回-1: 服务器问题 压力测试: 前提:在功能测试和流程测试都没有问题的前提下。 压力测试就是测试高并发访问的问题。 如何解决呢? 1. 优化代码,从代码逻辑上和性能上(软件方面)。 2. 更换性能服务器(硬件方面)。 3. Nginx服务器(服务器集群+负载均衡) 二、代理 反向代理方式实际上就是一台负责转发的代理服务器,貌似充当了真正服务器的功能,但实际上并不是,代理服务器只是充当了转发的作用,并且从真正的服务器那里取得返回的数据。这样说,其实nginx完成的就是这样的工作。我们让nginx监听一个端口,譬如80端口,但实际上我们转发给在8080端口的tomcat,由它来处理真正的请求,当请求完成后,tomcat返回,但数据此时没直接返回,而是直接给nginx,由nginx进行返回,这里,我们会以为是nginx进行了处理,但实际上进行处理的是tomcat。 三、Nginx在Windows平台的配置 能够使用Nginx搭建Tomcat集群,并完成负载均衡。 1、什么是Nginx? 经过查阅资料发现,Nginx不是后端的范畴,更多的是运维方面的知识。 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器。Nginx是由俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru站点开发的,它已经在该站点运行超过两年半了。Igor Sysoev在建立的项目时,使用基于BSD许可。 Nginx的特点是占用内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。 2、为什么使用Nginx? 背景:互联网飞速发展的今天,大用户量高并发已经成为互联网的主体。怎样能让一个网站能够承载几万个或几十万个用户的持续访问呢?这是一些中小网站急需解决的问题。用单机tomcat搭建的网站,在比较理想的状态下能够承受的并发访问量在150到200左右。按照并发访问量占总用户数量的5%到10%这样计算,单点tomcat网站的用户人数在1500到4000左右。对于一个为全国范围提供服务的网站显然是不够用的,为了解决这个问题引入了负载均衡方法。负载均衡就是一个web服务器解决不了的问题可以通过多个web服务器来平均分担压力来解决,并发过来的请求被平均分配到多个后台web服务器来处理,这样压力就被分解开来。 负载均衡服务器分为两种,一种是通过硬件实现的负载均衡服务器,简称硬负载,例如:f5。另一种是通过软件来实现的负载均衡,简称软负载,例如apache和nginx。硬负载和软负载相比前者作用的网络层次比较多,可以作用到socket接口的数据链路层对发出的请求进行分组转发但是价格成本比较贵,而软负载作用的层次在http协议层之上,可以对http请求进行分组转发,并且因为是开源的所以几乎是0成本,所以阿里巴巴、京东等电商网站使用的都是Nginx服务器。 3、使用Nginx完成负载均衡 完成Nginx负载均衡,那么需要先来介绍Tomcat的安装和配置,我们首先要来配置Tomcat完成集群的配置。因为我们没有多台服务器运行Tomcat,那么我们可以模拟在一台服务器上运行多个Tomcat程序。 说到上面的方式,也许很多人又会想起来,这样可以把静态文件交由nginx来进行处理。对,很多用到nginx的地方都是作为静态伺服器,这样可以方便缓存那些静态文件,比如CSS,JS,html,htm等文件。 使用Tomcat配置Tomcat集群:1、下载Tomcat:https://tomcat.apache.org/download-90.cgi,我下载的是apache-tomcat-9.0.7这个版本。2、安装和配置Tomcat:直接将下载后的Tomcat解压在本地磁盘,解压两个分别命名为tomcat1和tomcat2。3、配置tomcat环境变量: 4、需要将tomcat带有端口号的地方改成不同的端口即可,分别打开两个tomcat的安装目录下conf下的server.xmltomcat1/conf/server.xml中的文件修改如下:tomcat2/conf/server.xml中的文件修改如下:5、然后cmd分别进入tomcat1和tomcat2的安装目录下/bin,运行startup.bat。 Nginx的安装和部署:1、Nginx官网下载地址:http://nginx.org/en/download.html2、将nginx-1.14.0.zip解压包某个盘符下。3、打开文件夹,双击nginx.exe即可运行。4、我们会看到一个窗口一闪而过。此时我们打开任务管理器,可以看到两个nginx.exe在那里运行着,这说明我们已经启动了。 5、 打开浏览器 http://localhost:80 显示如下页面,表示安装成功。6、关闭nginx需要使用:相当于找到nginx进程kill。 DOS命令为:nginx -s stop7、修改nginx的配置文件后,需要重新加载配置文件:可以在不关闭nginx的情况下更新配置文件。 DOS命令为:nginx -s reload8、如果不想直接加载,而只是想看看自己的配置文件有没有问题,可以直接输入:nginx -t Nginx的负载均衡的配置:1、打开D:\learn\JavaWeb\nginx-1.14.0\conf这个文件: 2、修改:C:\Windows\System32\drivers\etc\hosts文件,可以配置访问本机的域名。通过以上的配置我们已经可以通过访问到不同的tomcat来分担服务器端的压力了。 配置Tomcat的session共享可以有三种解决方案:请求负载过程中会话信息不能丢失.那么需要在多个tomcat中session需要共享。第一种:是以负载均衡服务器本身提供的session共享策略,每种服务期的配置是不一样的并且nginx本身是没有的。第二种:是利用web容器本身的session共享策略来配置共享。针对于weblogic这种方式还是靠谱的。但是针对于tomcat这种方式存在很大的缺陷,主要因为是依靠广播方式来实现的session复制,会浪费很多带宽导致整个网络反映缓慢。官网也建议这种方式最好不要超过4台tomcat,具体的内容可参考/webapps/docs/cluster-howto.html里面有详细的说明。下面是具体的配置过程:第三种:是Tomcat集群加redis的Session共享配置方法。在这里我们以第二种方式为例:1、先配置Tomcat的虚拟目录: 2、修改D:\learn\JavaWeb\tomcat1\conf\server.xml文件,最简单的集群配置只需要将节点中注释掉的下面这句取消注释即可: Xml代码: <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> 使用这样方法配置的集群会将Session同步到所在网段上的所有配置了集群属性的实例上(此处讲所在网段可能不准确,因为Membership 是使用address和port来区分的。tomcat集群的实例如果在Membership配置中有相同的address和port值的tomcat被分到同一个集群里边。他们的session是相互共享的,同一个session的集群被称为一个cluster。可以配置多个cluster,但是cluster和cluster之间的session是不共享的)。也就是说如果该广播地址下的所有Tomcat实例都会共享Session,那么假如有几个互不相关的集群,就可能造成Session复制浪费,所以为了避免浪费就需要对节点多做点设置了,如下:Xml代码 <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> </Channel> </Cluster> 加了一个Channel,里面包了个Membership,咱们要关注的就是membership的port属性和address属性,不同的集群设置不同的port值或address值,从目前的使用来看,基本上是隔离开了。 3、修改项目的web.xml文件:web.xml文件的修改很简单:只需要在节点中添加这个节点<distributable/>就可以了。有了这二步就实现了Tomcat的集群和Session的共享了。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
bookStore商城开发文档 一、项目演示 看课堂笔记。 二、需求分析 2.1、系统体系结构 基于BS结构进行开发。(浏览器与服务器:瘦客户端) 2.1、系统总体流程 2.3、功能描述 对于本系统,用户一共分成三种,普通用户(游客)、注册会员(注册用户)、管理员。 用户通过访问http://www.bookStore.com页面可能访问到书城首面。 对于普通用户可以浏览商品,查找商品,也可以注册成会员。 注册成会员后的用户,不仅可以完成普通用户具有的功能,还可以添加商品到购物车,并对购物车中的商品进行操作,并可以下订单。通过会员操作页面,查看与修改会员信息,对没有支付的订单进行支付操作及取消订单操作。 管理员可以添加查看商品,并修改商品信息,.可以查看所有订单,并对订单进行管理。并能下载销售榜单。 下面我们通过一个用例图来描述每一个角色可以具有的功能,如下图所示: 2.4、系统界面 美工:第一版的页面可能是美工做的,用ps做的,会经过多次修改。 前端:前端开发人员做成静态网页。 后端:后端开发人员实现动态网页。 2.4.1、前台界面 商城首页 注册页面 登录页面 图书浏览页面 图书详细信息页面 购物车页面 用户管理页面 用户信息修改页面 订单管理页面 2.4.2、后台页面 后台首页 商品操作页面 商品添加页面 下载榜单页面 订单管理页面 三、概要设计 3.1、运行环境 操作系统:Windows环境下运行 软件需求:MySql5.x Tomcat7.x 3.2、基本功能需求 本系统在实现上应该具有以下功能: 普通用户可以通过系统浏览商品信息 普通用户可以进行查询完成商品的查找 普通用户可以进行注册成为会员 会员可以浏览及查找商品 会员可以添加商品到购物车 会员可以查看购物车中商品并进行操作 会员可以下订单 会员可以浏览自己的商品 会员可以查看及修改个人信息 管理员可以添加商品 管理员可以下载销售榜单 管理员可以查看并管理订单 3.3、功能模块设计 项目一共有以下几个模块: 用户模块 用户注册 用户登录 用户激活 用户信息修改 商品模块 商品浏览 商品查找 商品添加 商品删除 商品修改 订单模块 订单创建 订单查看 订单删除 购物车模块 添加商品到购物车 购物车商品数量修改 购物车商品删除 3.4、程序流程图 3.4.1、用户模块 注册流程 登录流程 激活流程 用户信息修改流程 3.4.2、商品模块 商品浏览 商品查找 商品添加 商品删除 商品修改 3.4.3、购物车模块 添加商品到购物车 购物车商品数量修改 购物车商品删除 3.4.4、订单模块 订单创建 订单查看 订单删除 3.5、数据库设计 3.5.1、ER图(实体关系图) 3.5.2、表设计 根据ER图(实体关系图),我们分析当前系统具有以下几个模块: 用户,商品,订单,购物车 用户与订单之间存在一对多关系 (一个用户可以有多个订单) 商品与订单之间存在多对多关系 (一个订单中包含多个商品,一个商品被多个订单下单) 购物车我们暂时不将信息存储到数据库中,所以不用生成表。 创建数据库 create database productstore; 使用数据库 USE productstore; 用户表 CREATE TABLE `user` ( `id` INT(11) AUTO_INCREMENT, `username` VARCHAR(20) , `PASSWORD` VARCHAR(20) , `gender` VARCHAR(10) , `email` VARCHAR(50) , `telephone` VARCHAR(20) , `introduce` VARCHAR(100), `activeCode` VARCHAR(50) , `state` INT(11) , `role` VARCHAR(10) DEFAULT '普通用户', `registTime` TIMESTAMP , PRIMARY KEY (`id`) ) 商品表 CREATE TABLE `products` ( `id` VARCHAR(100) , `name` VARCHAR(40) , `price` DOUBLE , `category` VARCHAR(40) , `pnum` INT(11) , `imgurl` VARCHAR(100) , `description` VARCHAR(255) , PRIMARY KEY (`id`) ) 订单表 CREATE TABLE `orders` ( `id` VARCHAR(100) , `money` DOUBLE , `receiverAddress` VARCHAR(255) , `receiverName` VARCHAR(20) , `receiverPhone` VARCHAR(20) , `paystate` INT(11) , `ordertime` TIMESTAMP , `user_id` INT(11) , PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) 订单项表 CREATE TABLE `orderitem` ( `order_id` VARCHAR(100) , `product_id` VARCHAR(100), `buynum` INT(11) , PRIMARY KEY (`order_id`,`product_id`), FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`), FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ) 四、系统设计 4.1、开发环境 开发本系统我们所使用的工具与技术有: Myeclipse10 tomcat7.x mysql5.x jst标签库 EL表达式 jsp servlet javabean Filter Listener javaMail commons-dbutils commons-fileupload commons-beanutils c3p0 jdk1.6 jdbc 4.2、架构设计 采用javaweb开发的三层架构 Web层 Service层 Dao层 五、详细设计 5.1、问题处理机制 5.1.1、功能问题 使用自定义异常来描述问题。 UserException来描述关于用户的操作问题。 ProductException来描述关于商品操作问题。 OrderException来描述关于订单操作问题。 5.1.2、权限问题 对于普通用户(游客),不可以操作购物车、下订单。 对于会员(注册用户),不可以添加商品、下载销售榜单。 5.2、前台功能 5.2.1、用户操作: 用户注册 用户激活 用户登录 用户信息修改 5.2.2、商品操作 查看全部商品(分页) 根据分类查看商品(分页) 与查看全部商品信息操作步骤一样,只是需要在传递一个类别信息进行查找。 搜索商品 查看商品详细信息 5.2.3、购物车操作 添加商品到购物车 查看购物车中商品 修改购物车中商品数量 删除购物车中商品 删除购物车商品,我们可以通过修改购物车中商品数量来完成,只需要将数量设置为0就可以。 5.2.4、订单操作 生成订单 查看订单 删除订单(未支付订单) 删除订单(已支付订单) 对于已经支付的订单,我们在删除订单时,不需要再修改商品的数量。 订单的在线支付 5.3、后台功能 5.3.1、商品操作 查看商品列表 商品添加 修改商品信息 修改商品信息分成两个步骤: 1. 根据id查找商品信息回显 2.修改商品信息 多条件商品查找 商品删除 下载销售榜单 5.3.2、订单操作 查看订单列表(查看所有订单) 订单查询(多条件查询) 订单详细信息查看 订单的删除(只能删除支付后的订单) 六、编码实现 6.1、环境搭建 导入jar包 导入mysql驱动 导入c3p0 导入dbutils 导入beanutils 导入fileupload 导入javamail mail.jar 导入jstl jstl.jar standard.jar 建立package结构 按照JavaEE 三层结构 cn.itcast.bookStore.dao cn.itcast.bookStore.service cn.itcast.bookStore.web.servlet cn.itcast.bookStore.web.filter cn.itcast.bookStore.web.listener cn.itcast.bookStore.domain cn.itcast.bookStore.util cn.itcast.bookStore.exception domain类编写 User类 private int id; // 用户编号 private String username; // 用户姓名 private String password; // 用户密码 private String gender; // 用户性别 private String email; // 用户邮箱 private String telephone; // 用户联系电话 private String introduce; // 用户介绍 private String activeCode; // 激活码 private String role; // 用户角色 private int state; // 用户状态 private Date registTime; // 注册时间 Product类 private String id; // 商品编号 private String name; // 商品名称 private double price; // 商品价格 private int pnum; // 商品数量 private String category; // 商品分类 private String description; // 商品描述 private String img_url; // 商品图片路径 Order类 private String id; // 订单编号 private double money; // 订单总价 private String receiverAddress; // 送货地址 private String receiverName; // 收货人姓名 private String receiverPhone; // 收货人电话 private int paystate; // 订单状态 private Date ordertime; // 下单时间 private User user; private List<OrderItem> orderitems; // 表示一个order对象,对应多个orderitem OrderItem类 private Order order; // 订单 private Product product; // 商品 private int buynum; // 购物数量 PageBean类 private int currentPage; // 当前页码 private int totalPage; // 总页码数 private int pageSize; // 每页条数 private int count; // 总条数 private List<Product> products; // 每页显示的数据 private String category; //分类 6.2、功能实现 用户注册 用户激活 用户登录 商品添加 商品显示 商品修改 商品删除 添加商品到购物车 购物车商品数量修改 购物车商品删除 生成订单 订单查看 订单删除 下载榜单 权限控制 系统重构 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
day20_day23课堂笔记 一、概述 做一个项目:bookStore图书商城 目的:将前期学过的知识进行整合。 1. 商城首页 index.jsp 2. 在首页上点击类别链接时会跳转到 product_list.jsp 页面展示相关商品信息 3. 新用户注册页面 4. 当我们点击我的帐户 如果用户没有登陆,则跳转到 login.jsp 页面 如果用户登陆了,角色是普通用户,则跳转到 myAccount.jsp 页面 如果用户已经登陆,并且它的角色是管理员,则跳转到 /admin/login/home.jsp 页面 5. 点击购物车,跳转到 cart.jsp 页面展示出购物车中商品信息 6.搜索操作 会对输入的信息与商品的名称进行 模糊匹配 ,将查询出的数据在 product_list.jsp 页面展示出来 二、分述 1. 关于用户的操作(普通注册的用户) 1.1 用户信息的修改操作 1.2 用户订单查询的操作 1.3 用户退出(注销)的操作 用户信息的修改操作: 对于普通注册用户,它可以修改除了 会员邮箱 与 会员名 之外其它信息 用户订单查询的操作 用户可以查询出自己的订单,可以查看订单详情,可以删除已支付的订单 用户退出 本质就是将存储到 session 中的 User对象 invalidate(); 销毁掉 1.4 关于商品查看 查看所有商品信息 查看类别商品信息 查看类似名称的商品信息 查看某个商品的详细信息 1.5 关于购物车 将商品添加到购物车 查看购物车中商品信息 对购物车中商品信息修改及删除 1.6 关于订单操作 生成订单 当前用户所有订单的查看 查看订单的详细信息 删除订单 如果订单为支付,我们可以点击进行支付操作 1.7 支付操作 支付页面是 pay.jsp 2. 关于后台商城管理的操作 2.1 商品管理 商品查看 a) 查看所有商品信息 b) 多条件查询 商品编辑 对商品信息修改(上传操作) 商品删除 a) 删除一件商品 b) 批量删除 商品添加 (上传操作) 下载销售榜单 2.2 订单管理 查看所有的订单 多条件查询订单 查看订单详细信息 订单删除 3. 数据库设计 通过ER图来描述(实体关系图) 只画了简单的实体关系图(属性没有添上) 数据库的架构设计图如下: 三、功能具体实现(部分功能) 1. 注册用户 register.jsp ——> RegistServlet ——> UserService ——> UserDao 注册成功后,需要发送一封激活邮件。 在UserService中调用dao中addUser方法扣,执行下面代码: 我们需要创建一个 ActiveUserServlet 这样一个 servlet。 注意:在激活时,需要考虑激活码的时间限制。 2. 登陆用户 login.jsp ——> LoginServlet ——> UserService ——> UserDao 在登陆时,需要考虑用户的状态是否激活,如果没有激活,用户名与密码正确,也不能登陆。 3. 关于点击在 product_list.jsp 页面上点击“我的帐户”的操作 注意:在其他页面上的点击“我的帐户”没有做,目前只做了这一个页面的。 注意:如果用户没有登陆,则 session 中就不会有User对象,那么会跳转到 login.jsp。 如果用户已经登陆了,则 session 中就会有User对象,会根据用户的角色跳转。 1. 在 head.jsp 中将我们的帐户的链接: <a href="login.jsp">我的帐户</a> 修改成访问 <a href="${pageContext.request.contextPath}/myAccount">我的帐户</a> 2. 创建一个 MyAccountServlet 4. 关于注册用户登陆后的操作 用户信息修改操作 从数据库中将用户信息查询出来,回显 <a href="${pageContext.request.contextPath}/findUserById?id=${user.id}">用户信息修改</a> 创建一个 FindUserByIdServlet 完成用户查找回显功能。 对数据进行修改操作 创建 ModifyUserServlet 完成修改操作。 注意:修改后,要将user从session中销毁,重新登陆。 用户退出操作 <a href="${pageContext.request.contextPath}/logout">用戶退出</a> 在LogOutServlet完成退出操作 5. 关于首页上的商品类别查询的操作 根据类别查询后的数据最终要在product_list.jsp页面展示,并且是分页显示: select * from products limimt m,n where category=? 如果点击的是全部商品目录就是查询所有: select * from products limimt m,n 代码实现: 在menu_seacher.jsp页面上类别连接 <a href="${pageContext.request.contextPath}/pageServlet?category=文学">文学</a> 需要创建一个pageServlet 这个servlet中与我们之前写的分页的servlet唯一的区别就是需要再获取一个类别属性 String category=request.getParameter(“category”); 再调用service,dao查询出数据返回 PageBean 6. 关于订单的操作 前台-订单操作 a) 生成订单 b) 查看订单(查看当前用户所有订单) c) 查看订单详情(查看某一个订单的信息) d) 删除订单 后台-订单操作 a) 查看订单(查看所有用户的订单) b) 按条件查询订单(1. 收货人查询 2. 订单号查询) c) 删除订单 a) 生成订单 将订单数据 insert 到 orders 表中(批处理) 将订单相关数据 insert 到 orderitem 表中(批处理) update修改 products 表中的 pnum 在 order.jsp 页面上 点击提交订单,就应该创建一个订单。 订单生成思路图: 创建一个 CreateOrderServlet ,它是用于接收 order.jsp 页面的请求的。 在CreateOrderServlet中完成什么操作? 1. 收集订单相关数据封装成一个 Order 对象 2. 调用 OrderService 完成订单生成操作。 注意:在 OrderService 中完成订单的生成需要事务控制。 b) 查看订单(查看当前用户所有订单) 查询订单详情 可以有两种写法: 1. 写一条sql语句,得到信息 2. 写n条sql语句,得到信息 代码如下图所示: 7. 在线支付操作 方式一:直接与各个银行做接口 优点:快捷。 缺点:需要针对于不同银行做实现,并且银行接口变动,你也需要变动。 维护工作量极大。 方式二:使用第三方支付 优点:不需要考虑银行,只与第三方支付进行交互。开发工作量极低。 缺点:第三方支付需要一定的时间,并且会收取一定的费用。 常见第三方支付公司 易宝支付开发所需工具 本次案例我们使用第三方支付 我们使用的是易宝支付http://www.yeepay.com/ 前提条件: 1. 必须申请一个商家用户商家帐户: 10001126856 2. 需要网上银行 3. 真实ip地址 支付流程: pay.jsp提交时会访问一个PayOnlineServlet,在这个PayOnlineServlet中我们需要将传递给第三方支付公司的信息获取到封装成表格,并发送给第三方支付公司。 问题1:发送信息有哪些? 查看接口文档。 问题2:信息向哪发送? 正式请求地址:https://www.yeepay.com/app-merchant-proxy/node 问题3:第三方支付公司反馈信息怎样处理? 按照接口文档来处理。 第三方支付公司反馈信息为: http://localhost:8080/product/callBackServlet?p1_MerId=10001126856&r0_Cmd=Buy&r1_Code=1&r2_TrxId=914221214141971I&r3_Amt=0.01 &r4_Cur=RMB&r5_Pid=&r6_Order=2cab9734-e148-44c8-a167-eed72e4abca0&r7_Uid=&r8_MP=&r9_BType=1&ru_Trxtime=20150531161117 &ro_BankOrderId=2767595753150531&rb_BankId=BOC-NET&rp_PayDate=20150531161116&rq_CardNo=&rq_SourceFee=0.0&rq_TargetFee=0.0 &hmac=7f60261451a3f8f51cacaa1bcd5592c4 8. 权限控制 1. url级别的控制,使用Filter,来拦截指定的路径,控制资源访问。 粗粒度权限控制。 2. 自定义标签。 后续会更新。。。 3. Annotation(注解)+动态代理+工厂模式(扩展)。细粒度权限控制。 我们可以对行为拦截(拦截service行为)。 使用 动态代理 与 Annotation 来完成。 原理:我们在Userservice中使用自定义注解,这个自定义注解是用于定义当前行为访问需要的权限。(注意:我们这个项目把接口省略了,直接写的实现类,虽不规范,但是简洁!) 步骤: 1. 我们先自定义一个注解类(注意:自定义注解不是类,本质上是一个接口,但是叫法上我们以类相称,虽不正确,但是亲切!) 2. 我们在Userservice中使用自定义注解 3. 后面不知道该怎么做了。。。 9. 代码重构(优化Servlet) 对servlet进行重构,在实际开发中,一般情况下是一个功能模块对应一个servlet,简单说,就是UserServlet、ProductServlet、OrderServlet、CartServlet。 步骤: 1. 创建一个 UserServlet,配置 web.xml <url-pattern>/user</url-pattern> 2. 获得发送请求的参数URI URL: 统一资源定位符(俗称:网址) URI: 统一资源标识符 http://localhost:8080/day07_03_myApp1/1.html URL= 协议 主机IP(端口号) URI(当前应用的资源路径) 例如:http://localhost:8080/product/user?method=register 优化一: 以UserServlet为例 RegisterServlet、LoginServlet、 ActiveUserServlet、LogOutServlet、ModifyUserServlet、MyAccountServlet、FindUserServlet 在UserServlet中完成以下方法对应以上的Servlet完成的功能,把之前的Servlet中的代码粘过来放到每个功能下即可,如下图所示: 问题:这些方法怎样被调用呢?反过来说,当我们点击页面上的登陆或注册或……怎样找到这些方法呢?如下图所示: 1. 先删除之前的web.xml中配置的servlet映射,添加新的servlet映射: 2. 修改其他页面的跳转链接,例如: 3. 在UserServlet中完成下列代码: 同理:可以按照这种方式完成OrderServlet、ProductServlet、CartServlet功能。 注意:添加商品操作不抽取,因为它是文件上传,也就是说,关于商品添加或修改操作的,只要涉及到上传就不抽取。 经过这样抽取(封装)后,Servlet变少了,代码简洁了,但问题又来了,添加功能后,UserServlet就需要修改,这样不好,不够健壮,那就继续封装! 优化二: 反射思想: UserServlet.java中,不就是变量(字段)和方法吗?UserServlet.java编译后,生成UserServlet.class文件, UserServlet.java里面的变量(字段)和方法在内存中就有了相应的位置,我们把这些“位置”看成对象,而整个.class文件我们看成Class对象,直接从内存的高度去获取和使用它们。示例如下: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
web.xml文件添加servlet访问限制后出现如下错误: cvc-complex-type.2.3: Element 'web-app' cannot have character [children], because the type's content type is element- only. 翻译: cvc-complex-type.2.3:元素'web-app'不能包含character [children],因为该类型的内容类型是仅包含元素的。 错误截图如下: 解决办法一: 出错原因为xml头文件中第三行: xmlns="http://java.sun.com/xml/ns/javaee" 改成javaee改为j2ee后解决问题。 解决办法二: 由于是粘贴过来的编码可能有问题,将xml中的文本重新手打输入一遍,一般就会解决这种问题。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
html中让input标签只读不可编辑的方法 方法1:onfocus=this.blur() <input type="text" name="input1" value="晓艺" onfocus=this.blur() > 方法2:readonly <input type="text" name="input1" value="晓艺" readonly ><input type="text" name="input1" value="晓艺" readonly="true" > 方法3:disabled <input type="text" name="input1" value="晓艺" disabled > 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
Win10默认系统下载的壁纸怎么下载?在哪里找出来呢?首先它是要设置为Windows聚焦才会自动从微软的服务器上去下载壁纸。这些都是随机下载的。每个人的都Win10 都有可能不一样。 Win10锁屏壁纸位置: C:\Users\Bruce\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets 步骤: 1. 进入C盘,点击 Users 文件夹。之后再点击相对应的 用户名 文件夹。 2. 下面要做的就是把隐藏的文件夹给显示出来,还有把扩展名也显示出来后面用得到的。 3. 进入Windows聚焦下载壁纸的文夹以后,看到这些文件不是直接的图片格式,需要给它重命名一下。在任意一个文件上点击一下右键,再点击【重命名】,文件名末尾输入.jpg 即可。 注意: Bruce:是你要的用户名文件夹 Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy:这个文件夹是随机的。但是前面 Microsoft.Windows.ContentDeliveryManager 这一段是不会变的。 其他的文件夹都是一样的。 演示gif动画如下图所示: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
方法如下: 1、可以在potplayer界面右键,视频,图像处理,选择需要的翻转方式。 2、也可以按快捷键: 上下翻转:Ctrl+V 左右翻转:Ctrl+Z 如图: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
myeclipse 2017 CI 中如何修改Servlet模板 在实际开发中,这些生成的代码和注释一般我们都用不到的,每次都要手工删除这些注释和代码,很麻烦,因此可以根据开发的实际情况修改Servlet的模板代码,改成符合实际开发需求的模板代码。 下面以MyEclipse 2017为例进行说明如何修改Servlet的模板代码,具体步骤如下: 找到 MyEclipse 2017 CI 安装目录下的 plugins 文件夹,比如我的:D:\learn\Java\MyEclipse\MyEclipse 2017 CI\plugins,然后找到 com.genuitec.eclipse.wizards_13.0.0.me201612231634.jar 这个jar文件, 打开 com.genuitec.eclipse.wizards_13.0.0.me201612231634.jar 这个jar文件后,可以看到里面有一个 templates 文件夹,进入 templates 文件夹,可以看到里面有一个 Servlet.java 文件。 修改里面的代码:删除 doGet 和 doPost 里面的代码和方法注释,在 doPost 方法里面调用 doGet ,这是根据实际情况修改成的模板代码,修改好之后,保存,重启 MyEclipse 2017 CI,使用MyEclipse创建Servlet,此时就是用刚才修改过的模板进行生成了。 (注意:在 MyEclipse 10 安装目录下的 \Common\plugins文件夹 ,注意文件夹的不同哦!) <aw:import> 表示的是要导入的包, <aw:parentClass> 表示该servlet继承的父类, <aw:constructor 表示的是构造器, <aw:method 表示的是方法的声明, 新的 Servlet.java 文件中的内容如下: #---------------------------------------------# # <aw:description>Template for Servlet</aw:description> # <aw:version>1.1</aw:version> # <aw:date>04/05/2003</aw:date> # <aw:author>Ferret Renaud</aw:author> #---------------------------------------------# <aw:import>java.io.IOException</aw:import> <aw:import>java.io.PrintWriter</aw:import> <aw:import>javax.servlet.ServletException</aw:import> <aw:import>javax.servlet.http.HttpServlet</aw:import> <aw:import>javax.servlet.http.HttpServletRequest</aw:import> <aw:import>javax.servlet.http.HttpServletResponse</aw:import> <aw:parentClass>javax.servlet.http.HttpServlet</aw:parentClass> <aw:constructor name="c1"> public <aw:className/>() { super(); } </aw:constructor> <aw:method name="doGet"> public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } </aw:method> <aw:method name="doPost"> public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } </aw:method> 示例 XxxServlet.java 文件 1 package com.itheima.product.web.servlet; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class PayOnlineServlet extends HttpServlet { 11 12 @Override 13 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 14 15 } 16 17 @Override 18 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 19 doGet(request, response); 20 } 21 22 } XxxServlet.java 附上:旧的 Servlet.java 文件中的内容如下: #---------------------------------------------# # <aw:description>Template for Servlet</aw:description> # <aw:version>1.1</aw:version> # <aw:date>04/05/2003</aw:date> # <aw:author>Ferret Renaud</aw:author> #---------------------------------------------# <aw:import>java.io.IOException</aw:import> <aw:import>java.io.PrintWriter</aw:import> <aw:import>javax.servlet.ServletException</aw:import> <aw:import>javax.servlet.http.HttpServlet</aw:import> <aw:import>javax.servlet.http.HttpServletRequest</aw:import> <aw:import>javax.servlet.http.HttpServletResponse</aw:import> <aw:parentClass>javax.servlet.http.HttpServlet</aw:parentClass> <aw:constructor name="c1"> /** * Constructor of the object. */ public <aw:className/>() { super(); } </aw:constructor> <aw:method name="doGet"> /** * The doGet method of the servlet. <br> * * This method is called when a form has its tag value method equals to get. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" This is "); out.print(this.getClass()); out.println(", using the GET method"); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } </aw:method> <aw:method name="doPost"> /** * The doPost method of the servlet. <br> * * This method is called when a form has its tag value method equals to post. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" This is "); out.print(this.getClass()); out.println(", using the POST method"); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } </aw:method> <aw:method name="doPut"> /** * The doPut method of the servlet. <br> * * This method is called when a HTTP put request is received. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Put your code here } </aw:method> <aw:method name="doDelete"> /** * The doDelete method of the servlet. <br> * * This method is called when a HTTP delete request is received. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Put your code here } </aw:method> <aw:method name="init"> /** * Initialization of the servlet. <br> * * @throws ServletException if an error occurs */ public void init() throws ServletException { // Put your code here } </aw:method> <aw:method name="destroy"> /** * Destruction of the servlet. <br> */ public void destroy() { super.destroy(); // Just puts "destroy" string in log // Put your code here } </aw:method> <aw:method name="getServletInfo"> /** * Returns information about the servlet, such as * author, version, and copyright. * * @return String information about this servlet */ public String getServletInfo() { return "This is my default servlet created by Eclipse"; } </aw:method> 具体操作如下图所示: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
软件开发文档是软件开发使用和维护过程中的必备资料。它能提高软件开发的效率,保证软件的质量,而且在软件的使用过程中有指导、帮助、解惑的作用,尤其在维护工作中,文档是不可或缺的资料。 软件开发文档可以分为开发文档和产品文档两大类。开发文档包括:《功能要求》、《投标方案》、《需求分析》、《技术分析》、《系统分析》、《数据库文档》、《功能函数文档》、《界面文档》、《编译手册》、《 QA 文档》、《项目总结》等。 产品文档包括:《产品简介》、《产品演示》、《疑问解答》、《功能介绍》、 《技术白皮书》、《评测报告》。用户文档《安装手册》、《使用手册》、《维护手册》、 《用户报告》、《销售培训》等。 开发文档 《功能要求》 -- 来源于客户要求和市场调查,是软件开发中最早期的一个环节。客户提出一个模糊的功能概念,或者要求解决一个实际问题,或者参照同类软件的一个功能。有软件经验的客户还会提供比较详细的技术规范书,把他们的要求全部列表书写在文档中,必要时加以图表解说。这份文档是需求分析的基础。 《投标方案》 -- 根据用户的功能要求,经过与招标方沟通和确认,技术人员开始书写《投标方案》,方案书一般包括以下几个重要的章节: 前言 -- 项目背景、公司背景和业务、技术人员结构、公司的成功案例介绍等。 需求分析 -- 项目要求、软件结构、功能列表、功能描述、注意事项等。 技术方案 -- 总体要求和指导思想、技术解决方案、软件开发平台、网络结构体系等。 项目管理 -- 描述公司的软件开发流程、工程实施服务、组织和人员分工、开发进度控制、软件质量保证、项目验收和人员培训、软件资料文档等。 技术支持 -- 公司的技术支持和服务介绍、服务宗旨和目标、服务级别和响应时间、技术服务区域、技术服务期限、授权用户联系人等。 系统报价 -- 软、硬件平台报价列表、软件开发费用、系统维护费用等。 项目进度 -- 整个项目的进度计划,包括签署合同、项目启动、需求分析、系统分析、程序开发、测试维护、系统集成、用户验收、用户培训等步骤的时间规划。 《需求分析》 -- 包括产品概述、主要概念、操作流程、功能列表和解说、注意事项、系统环境等。以《功能要求》为基础,进行详细的功能分析 ( 包括客户提出的要求和根据开发经验建议的功能 ) ,列出本产品是什么,有什么特殊的概念,包括哪些功能分类,需要具备什么功能,该功能的操作如何,实现的时候该注意什么细节,客户有什么要求,系统运行环境的要求等。这里的功能描述跟以后的使用手册是一致的。 《技术分析》 -- 包括技术选型、技术比较、开发人员、关键技术问题的解决、技术风险、技术升级方向、技术方案评价,竞争对手技术分析等。以《需求分析》为基础,进行详细的技术分析 ( 产品的性能和实现方法 ) ,列出本项目需要使用什么技术方案,为什么,有哪些技术问题要解决 ,估计开发期间会碰到什么困难,技术方案以后如何升级,对本项目的技术有什么评价等。 《系统分析》 -- 包括功能实现、模块组成、功能流程图、函数接口、数据字典、软件开发需要考虑的各种问题等。以《需求分析》为基础,进行详细的系统分析 ( 产品的开发和实现方法 ) ,估计开发期间需要把什么问题说明白,程序员根据《系统分析》,开始在项目主管的带领下进行编码。 《数据库文档》 -- 包括数据库名称、表名、字段名、字段类型、字段说明、备注、字段数值计算公式等。以《系统分析》为基础,进行详细的数据库设计。必要时可以用图表解说,特别是关系数据库。 《功能函数文档》 -- 包括变量名、变量初植、功能,函数名,参数,如何调用、备注、注意事项等。以《系统分析》为基础,进行详细的说明,列出哪个功能涉及多少个函数,以便以后程序员修改、接手和扩展。 《界面文档》 -- 包括软件外观、界面素材、编辑工具、文件名、菜单、按钮和其它界面部件的要求,这里与软件完成后的运行界面是一致的。 《编译手册》 -- 包括服务器编译环境、操作系统、编译工具、 GNU 的 C++ 编译器版本信息、目录说明、程序生成、源程序文件列表、 Makefile 配置及其相关程序的对应关系列表。客户端的编译过程、编译结果、编译示例、编译环境、操作系统、编译工具、源文件列表和制作安装程序的过程。 《 QA 文档》 -- 包括产品简介、产品原理、产品功能列表、功能描述、功能流程、执行结果、数据库结构、测试要求等,提供给软件测试人员使用。 《项目总结》 -- 包括项目简介、项目参与人员和开发时间、项目风险管理过程、项目功能列表、项目结构特点、技术特点、对项目的升级建议、对以后的项目的建议、人员素质情况等。 产品文档 《产品简介》 -- 包括公司背景、产品概念、适用范围、产品功能、功能特点、运行要求和公司联系地址。 《产品演示》 -- 包括公司简介、产品背景、产品描述、产品特点、产品作用、适用范围、使用分析、功能模块、解决问题、合作伙伴、成功案例等。一般用 Power point 或者 VCD 录制软件实现。 《疑问解答》 -- 列出用户关心的问题和处理方法。用于解答软件的操作功能和解决用户的疑难问题。 《功能介绍》 -- 以《需求分析》为书写基础,包括软件介绍、软件结构、功能列表、功能描述和公司联系地址。 《技术白皮书》 -- 以《技术分析》为书写基础,包括功能实现、技术选型、关键技术问题的解决、技术方案特点、技术升级方向等。 《评测报告》 -- 第三方权威评测报告。包括评测目的、评测范围、评测环境、评测内容、实测数据、性能表现、结果分析和评测总结等。 《安装手册》 -- 包括系统环境、运行平台、产品安装过程、初始环境设置、安装记录等。 《使用手册》 -- 包括产品简介、功能列表、功能描述和解释、功能操作、客户服务和联系方式等。 《维护手册》 -- 包括产品简介、系统须知、初始环境设置、系统配置、数据管理和备份、技术问题解答和联系方式等。 《用户报告》 -- 包括产品简介、购买时间、使用目的、使用时间、使用地点、实施过程、出现问题和解决、产品总结和建议等。 《销售培训》 -- 包括项目简介、产品功能、产品特点、商业优势、系统运行环境、适用范围、目标客户等。 软件开发文档 第一、需求分析文档 什么是用户需求分析文档呢?也就是在和客户进行沟通时,把用户所要求的信息记录下来,根据用户的要求进行需求分析,规划出我们要开发的软件所要实现哪些功能。 第二、概要设计文档 概要设计:顾名思义,就是对我们所要开发的软件进行一个整体的概括,把这个软件所包含的功能模块作一个设计,以后我们在开发的时候就有目标,有方向了。 第三、系统设计文档 系统设计:就是对概要的一个详细的实施,就是分析我们所要开发软件各大功能模块中所包含的小模块,把这些小模块都一一列举出来,然后再对软件开发人员进行有条理的进行开发任务的分配。 第四、详细设计文档 详细设计文档:主要是把我们每个小模块,小功能的业务逻辑处理用文字的方式表达出来,让程序员在编码的时候有一个依据和参照;同时,在进行详细文档设计的时候,有的软件公司也会根据不同的项目作出相应的《软件开发代码规范》性文档。以保障我们所做工作的统一性。 第五、软件测试文档 当我们参照软件详细设计文档编码完成后,接着就会根据我们所实现的功能,进行软件测试文档的编写;大多测试文档有两类,一类是软件单体测试文档,一类是软件结合测试文档;顾名思义,单体测试:就是对软件中每个小的方法,一个独立的方法进行测试的文档;结合测试:就是把多个功能模块组合到一起进行测试,主要是为了检测每个功能模块之前的交互性和功能的结合实现性。 第六、软件完成后的总结汇报型文档 不管所开发软件的规模大小,在一个软件开发结束后,我们都会把开发过中的问题和项目开发总结一起记录下来,以防以后在开发过程中再有类似问题出现,提高我们的开发效率。 根据软件开发公司的规模、标准和客户的需求不同,开发文档的种类和数量也不同,我在这里和大家讨论的软件开发相关文档都是最基础的。在软件行业有一句话:一个软件能否顺利的完成并且功能是否完善,重要是看这个软件有多少文档。软件开发文档是一个软件的支柱,如果你的开发文档漏洞百出,那么你所开发出来的软件也不可能会好;开发文档的好坏可以直接影响到所开发出来软件的成功与否。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
java基础加强 1、JDK5.0新特性介绍 jdk5.0的代号为:tiger(老虎) 2、静态导入 作用:静态导入类的某个静态成员(包括静态属性和静态方法)。语法:import static java.lang.System.out;import static java.util.Arrays.*;import static java.lang.Math.*;应用:实际开发中一般不用,因为比较抽象。示例代码:import static java.lang.System.out;import static java.util.Arrays.*;import java.util.List;public class Demo1 {public static void main(String[] args) {// out.println("aa");// out.println("bbb");// out.print("ccccc"); String[] ss = {"cc", "bb", "aa"}; List<String> list = asList(ss); // 把数组转成集合 Arrays.asList(ss); sort(ss); // 排序 Arrays.sort(ss);// 遍历数组for (int i = 0; i < ss.length; i++) { out.println(ss[i]); } } } 3、自动装箱和拆箱 装箱:基本类型-->包装类型拆箱:包装类型-->基本类型 Object o = 10; // 自动装箱+自动转换int i = (Integer) o; // 强制转换+自动拆箱小面试题: Integer i = null; // 没有new,说明堆里是空的。int j = i; 答:语法无错误,但是不能运行,运行会报空指针异常。NullPointerExceptionJava的规范指出:特定的基本类型一定会被装箱成相同的包装类型。这些对象会被高速缓存以重复使用,并且会被当做一般对象使用。 这些特殊的值是boolean值的true和false、所有的byte值、介于-128至127的short、int、long值,以及介于\u0000与\u007F之间的任何一个char。 因此这些基本类型的包装类的对象,是同一个对象。 4、增强for循环 增强for循环设计的目的:替换迭代器。增强for循环只能用在数组、或实现Iterator接口的集合类上。注意:增强for循环,只适合遍历集合数据。不适合修改或删除元素。如果要修改或删除使用普通for循环。语法:for (元素类型 元素名 :数组或实现了IteraTable接口的集合) { System.out.println(元素名); } 5、可变参数 注意:可变参数实际就是数组。如果需要其它参数,则要把可变参数写在最后。即:一个方法的参数列表中,只能有一个可变参数,且必须放在最后。在一个方法的参数列表中,不能使用多个可变参数。语法:参数(数据类型 ... 变量名)public int sum(int ... nums) { // 把可变参数当成数组int sum = 0;for (int i = 0; i < nums.length; i++) { sum += nums[i]; }return sum; } 6、枚举 enum关键字用于定义一个枚举类。应用场景:当给一个变量赋值时,值只能是特定或有一定范围时使用。特点:1. 枚举类也是一种特殊形式的Java类。2. 枚举类中声明的每一个枚举值代表枚举类的一个实例对象。3. 与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的(这点不难理解)。4. 枚举类也可以实现接口、或继承抽象类。5. JDK5中扩展了swith语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型。6. 若枚举类只有一个枚举值,则可以当作单态设计模式使用。Java中声明的枚举类,均是java.lang.Enum类的孩子,它继承了Enum类的所有方法。常用方法: name() 返回当前对象的名称 ordinal() 返回当前对象的索引 values() 返回枚举类中的所有对象,此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便。 valueOf(Class enumClass, String name) 把其它类型数据转换为枚举类型 7、反射 反射是框架设计的灵魂!!!反射就是把Java类中的各种成分映射成一个个的java对象(加载类时,解剖出类的各个组成部分)。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。Class类用于表示.class文件。如何得到某个class文件对应的字节码文件对象:1. Class clazz = Class.forName(""); // 根据完整类名,得到类的字节码文件对象。2. 类名.class;3. 对象名.getClass();与构造函数相关的方法: 得到public类型的构造函数 Constructor getConstructor(Class ... parameterTypes) 根据可变参数类型,得到指定的公共的构造函数。 Constructor[] getConstructors() 得到所有的公共的构造函数。 示例代码: clazz.getConstructor(); // 得到无参的公共的构造函数 clazz.newInstance(); // 创建此 Class 对象所表示的类的一个新实例。 clazz.getConstructor(String.class, int.class); // 得到含有两个参数的公共的构造函数 clazz.newInstance("tom", "18"); // 创建此 Class 对象所表示的类的一个新实例。 总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int,voi,... 8、内省 为什么要学内省? 开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦, 所以SUN公司开发了一套API,专门用于操作java对象的属性。什么是JavaBean和属性的读写方法? 通过内省技术访问(java.beans包提供了内省的API)JavaBean的两种方式。1. 通过PropertyDescriptor类操作Bean的属性2. 通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ), 通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。Beanutils工具类: 导入jar文件: commons-beanutils-1.8.3.jar commons-logging-1.1.1.jarapache开源组织 9、泛型 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
动态代理详解 动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。 动态代理与代理模式原理是一样的,只是它没有具体的代理类,直接通过反射生成了一个代理对象。 动态代理生成技术: 1. 基于jdk提供一个Proxy类,可以直接给实现某接口的实现类直接生成代理对象。 2. 基于cglib (spring框架会学习) java.lang.reflect.Proxy; 该类可以直接生成一个代理对象。 Proxy类的方法: public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例。 仅能代理实现至少一个接口的类(即目标对象需要至少有一个接口)。 ClassLoader:类加载器。固定写法,和被代理类使用相同的类加载器即可。 Class[]:代理类要实现的接口。固定写法,和被代理类使用相同的接口即可。 InvocationHandler:策略(方案)设计模式的应用。如何去具体实现代理,由我们自己决定。 InvocationHandler接口中的invoke方法:调用代理类的任何方法,此方法都会执行。 Object invoke(Object proxy, Method method, Object[] args) Object proxy:代理对象本身的引用。一般用不着。 Method method:当前调用的方法。 Object[] args:当前方法用到的参数。 动态代理的应用: 在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了被代理对象的方法调用)。 并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求, 例如:拦截用户的访问请求,以检查用户是否有访问权限、动态地为目的对象添加额外的功能。 静态/动态代理图解: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
父类委托机制详解(全盘负责委托机制 ) 例如:用eclipse的打包工具将TestClassLoader输出成jre/lib/ext目录下的itcast.jar包,再在eclipse中运行这个类,运行结果显示为ExtClassLoadr。此时的环境状态是classpath目录有TestClassLoader.class,ext/itcast.jar包中也有TestClassLoader.class,我们知道,使用一个类,应该只出现一个字节码文件,现在却出现两个了,肿么办?这时候我们就需要了解类加载的具体过程和原理了。如下所示: bootstrap classloader:引导(也称为原始)类加载器,它负责加载Java的核心类。这个加载器是非常特殊的,它实际上不是java.lang.ClassLoader的子类,而是由JVM自身实现的(底层是c代码)。因为JVM在启动的时候就自动加载它们,所以不需要在系统属性CLASSPATH中指定这些类库。 extension classloader:扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中的JAR包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的JAR类包对所有的JVM和system classloader都是可见的。 system classloader:系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径。可以通过静态方法ClassLoader.getSystemClassLoader();找到该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。 classloader 加载类用的是全盘负责委托机制。 全盘负责:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的其它Class通常也由这个classloader负责载入。 委托机制:先让parent(父)类加载器 寻找,只有在parent找不到的时候才从自己的类路径中去寻找。 类加载还采用了cache机制:如果cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、动态代理 1.1、代理模式 什么是代理模式及其作用? Proxy Pattern(即:代理模式),23种常用的面向对象软件的设计模式之一。 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 优点: (1) 职责清晰,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。 (2) 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了保护目标对象的作用。 (3) 高扩展性。 结构: 一个是你要访问的真正的对象(目标类),另一个是代理对象,真正对象(目标对象)与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。 其实装饰(包装)设计模式就是静态代理。 静态代理模式简单案例: KindWomen.java(接口) package com.itheima.demo;public interface KindWomen {public void throwEye();public void doSomething();} PJL.java(目标类1 实现 接口,并具体实现) package com.itheima.demo;public class PJL implements KindWomen {@Overridepublic void throwEye() { System.out.println("潘金莲抛媚眼"); }@Overridepublic void doSomething() { System.out.println("潘金莲XOXO"); }} YPX.java(目标类2 实现 接口,并具体实现) package com.itheima.demo;public class YPX implements KindWomen {@Overridepublic void throwEye() { System.out.println("阎婆惜抛媚眼"); }@Overridepublic void doSomething() { System.out.println("阎婆惜XOXO"); }} WP.java(代理类 也实现接口,但是空实现) package com.itheima.demo;public class WP implements KindWomen {private KindWomen kw;public WP(KindWomen kw) {this.kw = kw; }@Overridepublic void throwEye() { kw.throwEye(); }@Overridepublic void doSomething() { kw.doSomething(); }} XMQ.java(我) package com.itheima.demo;public class XMQ {public static void main(String[] args) { PJL pjl = new PJL(); YPX ypx = new YPX(); KindWomen wp1 = new WP(pjl); KindWomen wp2 = new WP(ypx); wp1.throwEye(); wp1.doSomething(); wp2.throwEye(); wp2.doSomething();} 1.2、动态代理 动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。 动态代理与代理模式原理是一样的,只是它没有具体的代理类,直接通过反射生成了一个代理对象。 动态代理生成技术: 1. 基于jdk提供一个Proxy类,可以直接给实现某接口的实现类直接生成代理对象。 2. 基于cglib (spring框架会学习) java.lang.reflect.Proxy; 该类可以直接生成一个代理对象。 Proxy类的方法: public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例。 仅能代理实现至少一个接口的类(即目标对象需要至少有一个接口)。 ClassLoader:类加载器。固定写法,和被代理类使用相同的类加载器即可。 Class[]:代理类要实现的接口。固定写法,和被代理类使用相同的接口即可。 InvocationHandler:策略(方案)设计模式的应用。如何去具体实现代理,由我们自己决定。 InvocationHandler接口中的invoke方法:调用代理类的任何方法,此方法都会执行。 Object invoke(Object proxy, Method method, Object[] args) Object proxy:代理对象本身的引用。一般用不着。 Method method:当前调用的方法。 Object[] args:当前方法用到的参数。 动态代理模式简单案例: KindWomen.java(接口) package com.itheima.demo;public interface KindWomen {public void throwEye(double money);public void doSomething(double money);} PJL.java(目标类1 实现 接口,并具体实现) package com.itheima.demo;public class PJL implements KindWomen {@Overridepublic void throwEye(double money) { System.out.println("潘金莲拿了" + money + "元钱,抛媚眼"); }@Overridepublic void doSomething(double money) { System.out.println("潘金莲拿了" + money + "元钱,XOXO"); }} XMQ.java(我) package com.itheima.demo;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class XMQ {public static void main(String[] args) {final KindWomen kw = new PJL(); // 真实对象(目标对象)// final YPX ypx = new YPX();// PJL pjl = new PJL();// YPX ypx = new YPX();// KindWomen wp1 = new WP(pjl);// KindWomen wp2 = new WP(ypx);// wp1.throwEye();// wp1.doSomething();// wp2.throwEye();// wp2.doSomething();// public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) // 返回一个指定接口的代理类实例。仅能代理实现至少一个接口的类(即目标对象至少有一个接口)。 KindWomen proxy = (KindWomen) Proxy.newProxyInstance(kw.getClass().getClassLoader(), kw.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理帮你做事情之前,可以做别的事情"); Object o = method.invoke(kw, new Object[] { (Double) args[0] / 2 }); // 这是真实对象执行的方法(做的事情) System.out.println("代理帮你完做事情之后,仍可以做别的事情");return o; } }); proxy.throwEye(5); proxy.doSomething(10); }} 静态/动态代理图解: 1.3、AOP(面向切面编程)编程思想(Spring框架中的核心思想之一) Spring框架中的核心思想包括: Dependency Injection(依赖注入) Inverse of Control(控制反转) Aspect Oriented Programming(面向切面编程) AOP编程思想解决的问题: 1、问题:业务的方法日后会很多,这样会有很多重复的代码。 2、问题:业务已经存在很多的方法,并没有考虑到事务的问题,现在要求加上。那么业务的重心就变了。 动态代理实现AOP示例代码:(转账功能) 完成业务的代码: package com.itheima.service.impl;import com.itheima.dao.AccountDao;import com.itheima.dao.impl.AccountDaoImpl;import com.itheima.domain.Account;import com.itheima.service.AccountService;public class AccountServiceImpl implements AccountService {@Override // 运用AOP思想,转账功能就是转账功能,不要加其他的东西,比如事务控制、日志、权限控制等,这样代码健壮性好,优美。public void transfer(String fromname, String toname, double money) throws Exception {// ad.updateAccount(fromname, toname, money); AccountDao ad = new AccountDaoImpl();// 分别得到转出和转入账户对象 Account fromAccount = ad.findAccountByName(fromname); Account toAccount = ad.findAccountByName(toname);// 修改账户各自的金额 fromAccount.setMoney(fromAccount.getMoney() - money); toAccount.setMoney(toAccount.getMoney() + money);// 完成转账操作 ad.updateAccout(fromAccount);// int i = 10 / 0; ad.updateAccout(toAccount); }}/*package com.itheima.service.impl;import java.sql.Connection;import java.sql.SQLException;import com.itheima.dao.AccountDao;import com.itheima.dao.impl.AccountDaoImpl;import com.itheima.domain.Account;import com.itheima.service.AccountService;import com.itheima.util.C3P0Util;import com.itheima.util.ManagerThreadLocal;public class AccountServiceImpl implements AccountService { @Override public void transfer(String fromname, String toname, double money) { // ad.updateAccount(fromname, toname, money); AccountDao ad = new AccountDaoImpl(); try { ManagerThreadLocal.startTransacation(); // 开启事务 // 分别得到转出和转入账户对象 Account fromAccount = ad.findAccountByName(fromname); Account toAccount = ad.findAccountByName(toname); // 修改账户各自的金额 fromAccount.setMoney(fromAccount.getMoney() - money); toAccount.setMoney(toAccount.getMoney() + money); // 完成转账操作 ad.updateAccout(fromAccount); // int i = 10 / 0; ad.updateAccout(toAccount); ManagerThreadLocal.commit(); // 提交事务 } catch (Exception e) { try { ManagerThreadLocal.rollback(); // 回滚事务 } catch (Exception e1) { e1.printStackTrace(); } } finally { try { ManagerThreadLocal.close(); // 关闭 } catch (Exception e) { e.printStackTrace(); } } }}*/ 产生代理对象的工厂代码: package com.itheima.util;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import com.itheima.service.AccountService;import com.itheima.service.impl.AccountServiceImpl;public class ObjectFactory {// 该方法返回一个代理对象public static AccountService getAccountService() {final AccountService as = new AccountServiceImpl(); // 真实对象(目标对象) AccountService proxy = (AccountService) Proxy.newProxyInstance(as.getClass().getClassLoader(), as.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object invoke = null;try { ManagerThreadLocal.startTransacation(); // 开启事务// 执行的是真实对象的转账方法 invoke = method.invoke(as, args); ManagerThreadLocal.commit(); // 提交事务 } catch (Exception e) {try { ManagerThreadLocal.rollback(); // 回滚事务 } catch (Exception e1) { e1.printStackTrace(); } } finally {try { ManagerThreadLocal.close(); // 关闭 } catch (Exception e) { e.printStackTrace(); } }return invoke; } });return proxy; }} 测试类代码: package com.itheima.test;import com.itheima.service.AccountService;import com.itheima.util.ObjectFactory;public class TestTransfer {/** * @param args * @throws Exception */public static void main(String[] args) throws Exception {// AccountService as = new AccountServiceImpl();// as.transfer("aaa", "bbb", 100); AccountService as = ObjectFactory.getAccountService(); as.transfer("aaa", "bbb", 100); }}/*package com.itheima.test;import com.itheima.service.AccountService;import com.itheima.service.impl.AccountServiceImpl;import com.itheima.util.ObjectFactory;public class TestTransfer { public static void main(String[] args) throws Exception { AccountService as = new AccountServiceImpl(); as.transfer("aaa", "bbb", 100); }}*/ 二、注解 注解不是注释,注释是程序员写的,给程序员看的。注解给程序看,用于描述程序如何运行及在什么阶段来运行。现在在实际开发中,注解最大的功能是用于替换配置文件。 注解是jdk1.5的新特性。可以通过反射来让注解具有功能。注解格式: @Xxxx 2.1 JDK中的三个基本的注解: a. @Override 检查子类确实是覆盖了父类的方法。 b. @Deprecated 说明已经过时了。 c. @SuppressWarnings({ "unused", "deprecation", "rawtypes" }) 抑制程序中的警告。unused(变量未使用)、deprecation(过时)和rawtypes(泛型)表示警告的类型。{}数组。 d. @SuppressWarnings("all") all抑制所有警告。 示例代码如下: package com.itheima.demo1;import java.util.ArrayList;import java.util.Date;import java.util.List;public class Demo1 {// @SuppressWarnings({ "unused", "deprecation", "rawtypes" }) @SuppressWarnings("all")public void ff() {int i = 0; System.out.println("abc"); List list = new ArrayList(); System.out.println(new Date().toLocaleString()); }public void HaHa() { }}class Demo2 extends Demo1 {@Overridepublic void ff() { }@Deprecatedpublic void HaHaHa() { System.out.println(new Date().toLocaleString()); }} 2.2 自定义注解的语法(注解的肉体) 研究一下注解的本质: 声明一个注解(或者创建一个注解类): @interface 注解名{…} public @interface MyAnnotation {} 通过反编译可知,注解它的本质就是一个接口,这个接口需要继承 Annotation接口。但是不能按照下述的代码格式写,需要按照上述代码格式写。 public interface MyAnnotation extends java.lang.annotation.Annotation {...} 分析注解类中的成员: 注解本质上就是接口,我们知道接口中可以有属性和方法。 属性:public static final int age; 方法:public abstract void show(); 但是在注解中也可以有属性,也可以有方法。但是,注解中一般不使用属性。 在注解的开发中,我们只写方法。 我们管注解中的方法,叫做注解的属性。 在注解中我们管方法的返回值叫做注解的类型。 注解定义属性的格式 : 例如:int age(); 关于注解的属性类型可以有哪些? 1. 基本类型 2. String 3. 枚举类型 4. 注解类型 5. Class类型 6. 以上类型的一维数组类型 注解:就是在你的程序代码中的某个位置加了一个标记而已。 示例代码如下图所示: 2.3 注解的反射(注解的灵魂) 模拟 Junit 的 @Test 方法 a、反射注解类 java.lang.reflect.AnnotatedElement; < T extends Annotation > T getAnnotation(Class< T > annotationType) 得到指定类型的注解引用。没有,返回null。 Annotation[] getAnnotations() 得到所有的注解,包含从父类继承下来的。 Annotation[] getDeclaredAnnotations() 得到自己身上的注解。 boolean isAnnotationPresent(Class< ? extends Annotation > annotationType) 判断指定的注解有没有。 Class、Method、Field、Constructor等实现了AnnotatedElement接口。 例如: Class.isAnnotationPresent(MyTest.class); 判断类上面有没有@MyTest注解。 Method.isAnnotationPresent(MyTest.class); 判断方法上面有没有@MyTest注解。 b、反射注解中的属性 如下图元注解图片中所示: 2.4 元注解 a、自定义注解的存活范围(生命周期):默认是CLASS。 什么是元注解? 答:只能用在注解类上的注解叫做元注解。(即:用于修饰注解的注解) @Retention 作用:改变自定义注解的存活范围。 RetentionPolicy SOURCE CLASS RUNTIME 例如:@Retention(RetentionPolicy.RUNTIME) @Target 作用:指定该注解能用在什么地方。 ElementType TYPE METHOD FIELD ANNOTATION_TYPE 例如:@Target({ ElementType.METHOD, ElementType.TYPE }) @Documented 作用:使用了@MyTest的注解的类,如果@MyTest注解类上面有@Documented注解,那么使用了@MyTest的注解的类的API文档中会出现@MyTest的身影。 @Inherited 作用:说明使用了该注解的类的子类可以继续使用该注解。 示例代码如下图所示: 三、简单介绍Servlet3.0中的几个注解 增加对注解的支持。 Servlet3.0 要求:Tomcat7+ JDK6.0+ Servlet3.0:web.xml已经不是必须的了。替代它的就是注解。 四、类加载器 1、作用:类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class。 2、JVM中的类加载器:(当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构: ) BootStrap:是老大。类加载器的祖先。 打印它会得到null。 负责加载 JRE/lib/rt.jar(JDK中绝大部分的类) ExtClassLoader: 负责加载 JRE/lib/ext/*.jar AppClassLoader: 负责加载在 classpath环境变量中的所有类 类加载器之间的父子关系图和管辖范围图 3、父类委托机制 示例代码: package com.itheima;import javax.xml.crypto.dsig.keyinfo.KeyName;public class TestClassLoader {public static void main(String[] args) {// BootStrap:是老大。类加载器的祖先。 打印它会得到null。负责加载 JRE/lib/rt.jar(JDK中绝大部分的类) ClassLoader cl = KeyName.class.getClassLoader(); System.out.println(cl); // null// System.out.println(cl.getClass().getName()); // java.lang.NullPointerException// ExtClassLoader: 负责加载 JRE/lib/ext/*.jar // 没有找着可以测试的类,因为该目录下需要导入源码包,就没有测试啦!但是效果同AppClassLoader。// AppClassLoader: 负责加载在 classpath环境变量中的所有类 ClassLoader cl1 = Student.class.getClassLoader(); System.out.println(cl1); // sun.misc.Launcher$AppClassLoader@73d16e93 System.out.println(cl1.getClass().getName()); // sun.misc.Launcher$AppClassLoader }} 示例图解01: 示例图解02: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
文件的上传和下载学习小结 一、文件上传 客户端通过浏览器将文件上传至服务器。 对于我们写代码来说,就是IO操作。 二、实现文件上传 对表单有什么要求? 1. 提交方式必须为post 2. enctype="multipart/form-data" 3. <input type="file" name="photo"/> 对于服务器的servlet有什么要求? request.getParameter("name"); 不能用该方式获取表单数据了,若非要用,则返回null。 request.getInputStream(); 使用字节输入流获取数据了。 数据能够获取到了,但是需要解析。大神可以自己解析(就是一顿切割、取值)! 对于小小牛的我,使用 Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),它最主要的工作就是帮我们解析request.getInputStream()。 该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。 Commons-fileupload commons-fileupload.jar commons-io.jar DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload sfu = new ServletFileUpload(); List<FileItem> fileItems = sfu.parseRequest(request); FileItem接口的方法: isFormFiled(); 判断当前字段是否是普通文本字段,如果返回false,则说明是文件字段。 getFileName(); 获取字段名,例如:< input type="text" name="username" />,返回的是username。 getString("UTF-8"); 获取字段值,并解决上传普通文本表单出的乱码问题,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件。 getInputStream(); 获取上传文件对应的输入流。 getName(); 获取文件字段的文件名称(如:a.txt)。 getContentType(); 获取上传的文件的MIME类型,例如:text/plain、image/pjpeg。 getSize(); 获取上传文件的大小。 write(File file) 把上传的文件保存到指定文件中。 delete(); 三、文件上传需要考虑的几个问题 a、保证服务器的安全 把保存上传文件的目录放在用户直接访问不到的地方。 b、避免文件被覆盖 让文件名唯一即可。 c、避免同一个文件夹中的文件过多 方案一:按照日期进行打散存储目录 方案二:用文件名的hashCode计算打散的存储目录:二级目录 d、限制文件的大小:web方式不适合上传大的文件 设置单个文件大小 设置总文件大小 e、上传字段用户没有上传的问题 通过判断文件名是否为空即可。 f、临时文件的问题 如果上传文件过大,就会有临时文件。 FileItem.delete(); 如果自己用IO流实现的文件上传,则要在流关闭后,清理临时文件。 FileItem.write(File file); 把上传的文件保存到指定文件,该方式会自动删除临时文件,注意:实际操作不能够自动删除临时文件,所以需要手动删除(即:使用 FileItem自带的方法上传文件)。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、文件的上传和下载 1、文件上传的原理分析 什么是文件上传? 要将客户端(浏览器)数据存储到服务器端,而不将数据直接存储到数据库中,而是要将数据存储到服务器所在的磁盘上,这就要使用文件上传。为什么使用文件上传? 通过文件上传,可以将浏览器端的数据直接保存到服务器端。不将数据保存到数据库中,而是保存到服务器磁盘上,这样减少了数据库服务器的压力,对数据的操作更加灵活。 1.1 文件上传的必要前提 a、提供form表单,method必须是post提交方式。 b、form表单必须设置为enctype="multipart/form-data"。 c、提供input type="file"类的上传输入域。 1.2 enctype属性 作用:告知服务器请求正文的MIME类型(文件类型)。(与请求消息头中:Content-Type作用是一致的) 可选值: application/x-www-form-urlencoded(默认) 请求消息正文:name=tom&photo=a.txt 服务器获取数据:String name = request.getParameter("name"); multipart/form-data 请求消息正文: 服务器获取数据:request.getParameter(String)方法获取指定的表单字段字符内容,但文件上传表单已经不再是字符内容,而是字节内容,所以失效,所以需要字节流的方式。 文件上传:解析请求正文的每部分的内容。 2、借助第三方的上传组件实现文件上传 2.1 fileupload概述 fileupload是由apache的commons组件提供的上传组件。它最主要的工作就是帮我们解析request.getInputStream()。 使用步骤,导入commons-fileupload相关jar包 commons-fileupload.jar,核心包。 commons-io.jar,依赖包。 2.2 fileupload的核心类有 DiskFileItemFactory、ServletFileUpload、FileItem。 a、解析原理 2.3 fileupload简单应用 使用fileupload组件的步骤如下: 1. 创建工厂类DiskFileItemFactory对象 DiskFileItemFactory factory = new DiskFileItemFactory(); 2. 使用工厂创建解析器对象 ServletFileUpload fileUpload = new ServletFileUpload(factory); 3. 使用解析器来解析request对象 List< FileItem > list = fileUpload.parseRequest(request); FileItem对象对应一个表单项(表单字段)。可以是文件字段或普通字段。 FileItem接口的方法: boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,则说明是文件字段。 String getFieldName():获取字段名称,例如:< input type="text" name="username" />,返回的是username。 String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件。 String getName():获取文件字段的文件名称(如:a.txt)。 String getContentType():获取上传的文件的MIME类型,例如:text/plain、image/pjpeg。 int getSize():获取上传文件的大小。 InputStream getInputStream():获取上传文件对应的输入流。 void write(File file):把上传的文件保存到指定文件中。 void delete(); 3、文件上传时要考虑的几个问题(经验分享) a、保证服务器的安全 把保存上传文件的目录放在用户直接访问不到的地方。 b、避免文件被覆盖 让文件名唯一即可。 c、避免同一个文件夹中的文件过多 方案一:按照日期进行打散存储目录 方案二:用文件名的hashCode计算打散的存储目录:二级目录d、限制文件的大小:web方式不适合上传大的文件 设置单个文件大小: ServletFileUpload.setFileSizeMax(字节); 设置总文件大小:(多文件上传时) ServletFileUpload.setSizeMax(字节); e、上传字段用户没有上传的问题 通过判断文件名是否为空即可。 f、临时文件的问题 DiskFileItemFactory: 作用:产生FileItem对象。 DiskFileItemFactory内部有一个缓存,缓存大小默认是10Kb。如果上传的文件超过10Kb,就用磁盘作为缓存。 存放缓存文件的目录在哪里?答:默认是系统的临时目录。 FileItem.delete(); FileItem.delete(); 如果自己用IO流实现的文件上传,则要在流关闭后,清理临时文件。 FileItem.write(File file); 把上传的文件保存到指定文件,该方式会自动删除临时文件,注意:实际操作不能够自动删除临时文件(即:使用 FileItem自带的方法上传文件)。 完整的示例代码如下: package com.itheima.upload;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;import java.util.UUID;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileUploadBase;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.commons.io.FilenameUtils;public class UploadServlet2 extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// request.setCharacterEncoding("UTF-8"); // 解决服务器接收请求的编码问题,该方式的优先级不高,不好!// 要执行文件上传的操作// 首先判断表单是否支持文件上传。即:判断 enctype="multipart/form-data"boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);if (!isMultipartContent) {throw new RuntimeException("your form is not multipart/form-data"); }// 创建一个DiskFileItemfactory工厂类对象 DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setRepository(new File("e:\\")); // 指定临时文件的存储目录// 使用工厂创建解析器ServletFileUpload核心对象 ServletFileUpload sfu = new ServletFileUpload(factory);// 解决上传文件表单项出现乱码问题 sfu.setHeaderEncoding("UTF-8");// 使用解析器来解析request对象 ,并得到一个表单项的List集合try {// 限制上传文件的大小// sfu.setFileSizeMax(1024 * 1024 * 3); // 表示3M大小的文件// sfu.setSizeMax(1024 * 1024 * 6); // 多个文件上传时 List<FileItem> fileItems = sfu.parseRequest(request);// 遍历表单项数据for (FileItem fileitem : fileItems) {if (fileitem.isFormField()) {// 文本表单项,普通文本字段(字符串) processFormField(fileitem); } else {// 文件表单项,文件字段(使用字节流读取) processUploadField(fileitem); } } } catch (FileUploadBase.FileSizeLimitExceededException e) {// throw new RuntimeException("文件过大,不能超过3M"); System.out.println("文件过大,不能超过3M"); // 自定义异常 } catch (FileUploadBase.SizeLimitExceededException e) { System.out.println("总文件大小不能超过6M"); // 自定义异常 } catch (FileUploadException e) { e.printStackTrace(); } }@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }// 文本表单项,普通文本字段(字符串)private void processFormField(FileItem fileitem) {try { String fieldname = fileitem.getFieldName(); // 获取字段名 String fieldvalue = fileitem.getString("UTF-8"); // 获取字段值,并解决上传普通文本表单出的乱码问题// 解决上传普通文本表单项出现的乱码问题// fieldvalue = new String(fieldvalue.getBytes("iso-8859-1"), "utf-8"); System.out.println(fieldname + "=" + fieldvalue); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }// 文件表单项,文件字段(使用字节流读取)private void processUploadField(FileItem fileitem) {try {// 得到文件输入流 InputStream is = fileitem.getInputStream();// 创建一个文件存盘的目录 String directoryRealPath = this.getServletContext().getRealPath("/WEB-INF/upload"); File storeDirectory = new File(directoryRealPath); // 既代表文件又代表目录if (!storeDirectory.exists()) { storeDirectory.mkdirs(); // 就创建一个指定的目录 }// 获取文件字段的文件名称 String filename = fileitem.getName(); // 文件项框中的值 F:\图片素材\小清新\43.jpg 或者 43.jpg// 处理文件名问题 F:\apache-tomcat-7.0.52\webapps\day18_00_upload\ upload\F:\图片素材\小清新\43.jpgif (filename != null) {// filename = filename.substring(filename.lastIndexOf(File.separator) + 1); filename = FilenameUtils.getName(filename); // 效果同上 }// 解决文件同名的问题 filename = UUID.randomUUID() + "_" + filename;// 法一:按日期打散(创建)目录// String childDirectory = makeChildDirectory(storeDirectory); // 2015-10-19// 法二:用文件名的hashCode计算打散的存储目录:即二级目录 String childDirectory = makeChildDirectory(storeDirectory, filename); // a/b// 在storeDirectory目录下,创建完整目录下的文件 File file = new File(storeDirectory, childDirectory + File.separator + filename); // 绝对目录/日期目录/文件名// 通过文件输出流将上传的文件保存到服务器的磁盘 FileOutputStream fos = new FileOutputStream(file);int len = 0;byte[] b = new byte[1024];while ((len = is.read(b)) != -1) { fos.write(b, 0, len); } fos.close(); is.close(); fileitem.delete(); // 如果自己用IO流实现的文件上传,则要在流关闭后,清除临时文件// 把上传的文件保存到指定文件(使用 FileItem自带的方法上传文件)// fileitem.write(new File(storeDirectory, childDirectory + File.separator + filename));// fileitem.delete(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }// 用文件名的hashCode计算打散的存储目录:二级目录private String makeChildDirectory(File storeDirectory, String filename) {int hashcode = filename.hashCode(); // 返回字符串转换的32位hashcode码 System.out.println(hashcode); String code = Integer.toHexString(hashcode); // 把hashcode码转换为16进制的字符 System.out.println(code); // 例如:abdsaf2131safsd String childDirectory = code.charAt(0) + File.separator + code.charAt(1); // 取前两位,作为二级目录,例如:a/b// 创建指定目录 File file = new File(storeDirectory, childDirectory);if (!file.exists()) { file.mkdirs(); }return childDirectory; }/* // 按日期打散目录 private String makeChildDirectory(File storeDirectory) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String dateDirectory = sdf.format(new Date()); // 创建指定目录 File file = new File(storeDirectory, dateDirectory); if (!file.exists()) { file.mkdirs(); } return dateDirectory; }*/} 一次上传一个文件的form表单代码upload.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body><form enctype="multipart/form-data"action="${pageContext.request.contextPath }/servlet/uploadServlet2"method="post"><input type="text" name="name" /><br /> <input type="file" name="photo" /><br /> <input type="submit" value="上传" /><br /></form></body></html> 一次上传两个文件的form表单代码upload2.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body><form enctype="multipart/form-data"action="${pageContext.request.contextPath }/servlet/uploadServlet2"method="post"><input type="text" name="name" /><br /> <input type="file" name="photo" /><br /> <input type="file" name="photo" /><br /> <input type="submit" value="上传" /><br /></form></body></html> 动态添加上传按钮上传多个文件upload3.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><script type="text/javascript">function addFile() {// 得到div容器var div1 = document.getElementById("div1"); div1.innerHTML += "<div><input type='file' name='photo'/><input type='button' value='删除' onclick='delFile(this)'/></div>"; }function delFile(input) {// 使用input对象的爷爷删除他的爸爸 input.parentNode.parentNode.removeChild(input.parentNode); }</script><body><form enctype="multipart/form-data"action="${pageContext.request.contextPath }/servlet/uploadServlet2"method="post"><input type="text" name="name" /><br /><div id="div1"><div><input type="file" name="photo"/><input type="button" value="添加" onclick="addFile()"/><br /></div></div><input type="submit" value="上传" /><br /></form></body></html> 4、文件的下载 注意:在web开发中,不适合大的数据下载,通过浏览器进行大的数据下载,不合适,此时需要借助下载软件进行下载,比如:迅雷、电驴、百度网盘等等。 web开发中,文件下载的应用场景是:从数据库表里面查找数据,动态的生成所需的文件。 文件下载的示例代码如下: package com.itheima.servlet;import java.io.IOException;import java.io.PrintWriter;import java.net.URLEncoder;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class DownloadServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 设置一个要下载的文件 String filename = "销售榜单.csv"; // csv文件可以用excle打开// 设置文件名的编码if (request.getHeader("user-agent").toLowerCase().contains("msie")) { filename = URLEncoder.encode(filename, "UTF-8"); // 将不安全的文件名改为UTF-8格式 } else { filename = new String(filename.getBytes("UTF-8"), "iso-8859-1"); // 火狐浏览器 }// 告知浏览器要下载的文件 response.setHeader("content-disposition", "attachment;filename=" + filename);// response.setHeader("content-type", "image/jpeg"); response.setContentType(this.getServletContext().getMimeType(filename)); // 告知浏览器根据文件名的后缀自动获得文件类型 response.setCharacterEncoding("UTF-8"); // 设置服务器使用什么编码,去向外输出下面文件输出流的内容// 创建一个文件输出流 PrintWriter out = response.getWriter(); out.write("电视机, 20\n"); // 以后连接数据库即可 out.write("洗衣机, 10\n"); out.write("冰箱, 8\n"); }@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }} 重构添加图书信息的示例代码: package com.itheima.web.servlet;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.beanutils.BeanUtils;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.commons.io.FilenameUtils;import com.itheima.domain.Book;import com.itheima.service.BookServiceImpl;import com.itheima.util.UUIDUtil;public class AddBookServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 创建一个DiskFileItemFactory工厂 DiskFileItemFactory factory = new DiskFileItemFactory();// 创建一个ServletFileUpload对象 ServletFileUpload sfu = new ServletFileUpload(factory);// 解决上传文件的乱码 sfu.setHeaderEncoding("UTF-8"); // 解析request对象,返回所有表单项 List<FileItem> fileItems = new ArrayList<FileItem>(0);// 用于封装普通表单项的数据 Map<String, String[]> map = new HashMap<String, String[]>();try { fileItems = sfu.parseRequest(request);// 迭代遍历fileItems表单项for (FileItem fileItem : fileItems) {if (fileItem.isFormField()) {// 普通表单项 String name = fileItem.getFieldName(); // 得到字段名 String value = fileItem.getString("UTF-8"); // 得到字段值 map.put(name, new String[] { value }); // 向map中赋值 } else {// 文件表单项 InputStream inputStream = fileItem.getInputStream(); String filename = fileItem.getName(); // 得到上传的文件名 String extension = FilenameUtils.getExtension(filename);if (!("jsp".equals(extension) || "exe".equals(extension))) { // 上传的文件不能是jsp、exe// 创建一个文件存盘的目录 File storeDirectory = new File(this.getServletContext().getRealPath("/upload"));if (!storeDirectory.exists()) { storeDirectory.mkdirs(); // 如果该目录不存在,就创建 }// 处理文件名问题if (filename != null) { filename = FilenameUtils.getName(filename); }// 目录打散 String childDirectory = makeChildDirectory(storeDirectory, filename); // a/b// 把上传的文件保存到指定文件(使用 FileItem自带的方法上传文件) filename = childDirectory + File.separator + filename; fileItem.write(new File(storeDirectory, filename)); fileItem.delete(); // 删除临时文件 } map.put(fileItem.getFieldName(), new String[] { filename }); // 将图片表单项的name和value保存到map中 } } Book book = new Book(); BeanUtils.populate(book, map); book.setId(UUIDUtil.getUUID()); // 设置图书编号// 调用业务逻辑 BookServiceImpl bs = new BookServiceImpl(); bs.addBook(book);// 分发转向// 不写/代表相对路径,相对于本类的路径 request.getRequestDispatcher("bookListServlet").forward(request, response); } catch (FileUploadException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }// request.setCharacterEncoding("UTF-8"); // 获取表单数据 // Book book = new Book();// try {// BeanUtils.populate(book, request.getParameterMap());// book.setId(UUIDUtil.getUUID()); // 手动生成一个随机ID// } catch (Exception e) {// e.printStackTrace();// } // // // 调用业务逻辑 // BookServiceImpl bs = newBookServiceImpl();// bs.addBook(book);//// // 分发转向 // // 不写/代表相对路径,相对于本类的路径// request.getRequestDispatcher("bookListServlet").forward(request, response);@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }// 目录打散private String makeChildDirectory(File storeDirectory, String filename) {int hashcode = filename.hashCode(); // 返回字符转换的32位hashcode码 System.out.println(hashcode); String code = Integer.toHexString(hashcode); // 把hashcode转换为16进制的字符 System.out.println(code); // abdsaf2131safsd String childDirectory = code.charAt(0) + File.separator + code.charAt(1); // a/b// 创建指定目录 File file = new File(storeDirectory, childDirectory);if (!file.exists()) { file.mkdirs(); }return childDirectory; }} product_list.jsp <div class="divbookpic"><p><a href="#"><img src="${pageContext.request.contextPath}/upload/${b.img_url}" width="115" height="129" border="0" /> </a></p></div> 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、Listener监听器(了解) Javaweb开发中的监听器,是用于监听web常见对象的。 例如web的常见对象有:ServletContext、HttpServletRequest、HttpSession 监听它们的创建与销毁、属性变化、session绑定javaBean。 1、监听机制 事件:就是一个事情。 事件源:产生这个事情的源头。 监听器:用于监听指定的事件的对象。(关联事件和事件源) 注册监听:要想让监听器可以监听到事件产生,必须对其进行注册。 2、Javaweb开发中常见监听器 2.1、监听域对象的创建与销毁 若要监听ServletContext对象的创建与销毁 则需要写一个监听器类实现 ServletContextListener 接口 若要监听HttpSession对象的创建与销毁 则需要写一个监听器类实现 HttpSessionListener 接口 若要监听HttpServletRequest对象的创建与销毁 则需要写一个监听器类实现 ServletRequestListener 接口 2.2、监听域对象的属性变化 若要监听ServletContext对象的属性变化 则需要写一个监听器类实现 ServletContextAttributeListener 接口 若要监听HttpSession对象的属性变化 则需要写一个监听器类实现 HttpSessionAttributeListener 接口 若要监听HttpServletRequest对象的属性变化 则需要写一个监听器类实现 ServletRequestAttributeListener 接口 2.3、监听session绑定javaBean 若要监听javaBean对象是否绑定到了session域 则需要写一个javaBean实现 HttpSessionBindingListener 接口 若要监听javaBean对象的活化与钝化 则需要写一个javaBean实现 HttpSessionActivationListener 接口 3、监听器的快速入门 关于创建一个监听器的步骤: 1. 创建一个监听器类,实现指定的监听器接口。 2. 重写接口中的方法。 3. 在web.xml文件中对监听器进行注册。 3.1、关于域对象的创建与销毁的演示 1. ServletContext对象的创建与销毁 ServletContext对象是在服务器启动时创建的,在服务器关闭时销毁的。 package com.itheima.listener;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContext对象被创建了"); // 服务器一启动,ServletContext对象就被创建了 }@Overridepublic void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了"); // 服务器一关闭,ServletContext对象就被销毁了 }} 2. HttpSession对象的创建与销毁 HttpSession session = request.getSession(); Session的销毁方式 1. 默认超时30分钟后销毁 2. 关闭服务器时销毁 3. 调用invalidate()方法 4. setMaxInactiveInterval(int interval) 可以设置超时时间 问题:直接访问一个jsp页面时,是否会创建Session? 答:会创建,因为我们默认情况下是在jsp页面中直接使用Session内置对象的。 package com.itheima.listener;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;public class MyHttpSessionListener implements HttpSessionListener {@Overridepublic void sessionCreated(HttpSessionEvent se) { System.out.println("HttpSession对象被创建了"); }@Overridepublic void sessionDestroyed(HttpSessionEvent se) { System.out.println("HttpSession对象被销毁了"); }} index.jsp <body><% session.invalidate(); // 手动使session销毁 %></body> 3. HttpServletRequest对象的创建与销毁 Request对象是发送请求时服务器就会去创建它,当响应产生时,request对象就会被销毁。 package com.itheima.listener;import javax.servlet.ServletRequestEvent;import javax.servlet.ServletRequestListener;public class MyServletRequestListener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent sre) { System.out.println("ServletRequest被销毁了"); }@Overridepublic void requestInitialized(ServletRequestEvent sre) { System.out.println("ServletRequest被创建 了"); }} 3.2、演示Request域对象中的属性变化 在java的监听机制中,它的监听器中的方法都是有参数的,参数就是事件对象,而我们可以通过事件对象直接获取事件源。 package com.itheima.attributelistener;import javax.servlet.ServletRequestAttributeEvent;import javax.servlet.ServletRequestAttributeListener;public class MyServletRequestListener implements ServletRequestAttributeListener {@Overridepublic void attributeAdded(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest添加属性了"); }@Overridepublic void attributeRemoved(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest移除属性了"); }@Overridepublic void attributeReplaced(ServletRequestAttributeEvent srae) { // 参数代表事件对象 System.out.println("ServletRequest替换属性了");// srae.getServletRequest(); // 通过事件对象直接获取事件源// System.out.println(srae.getName() + "\t" + srae.getValue()); // org.apache.catalina.ASYNC_SUPPORTED true }} index.jsp <body> <%// session.invalidate(); // 手动使session销毁 request.setAttribute("name", "tom"); request.setAttribute("name", "luna"); request.removeAttribute("name"); %> </body> 3.3、演示session绑定javaBean 1.javaBean对象自动感知被绑定到session中 HttpSessionBindingListener 这个接口是由javaBean实现的,并且不需要在web.xml文件中进行注册。 package com.itheima.domain;import javax.servlet.http.HttpSessionBindingEvent;import javax.servlet.http.HttpSessionBindingListener;public class User implements HttpSessionBindingListener {private String name;private int age;public String getName() {return name; }public void setName(String name) {this.name = name; }public int getAge() {return age; }public void setAge(int age) {this.age = age; }@Overridepublic void valueBound(HttpSessionBindingEvent event) { System.out.println("向session中绑定了一个user对象"); }@Overridepublic void valueUnbound(HttpSessionBindingEvent event) { System.out.println("从session中将user对象移除"); }} index.jsp <body><% // session.invalidate(); // 手动使session销毁 // request.setAttribute("name", "tom"); // request.setAttribute("name", "luna"); // request.removeAttribute("name");User user = new User();user.setName("luna");user.setAge(25); // 绑定到sessionsession.setAttribute("u", user); // 解除绑定session.removeAttribute("u"); %></body> 2.javabean对象可以活化或钝化到session中 HttpSessionActivationListener,如果javaBean实现了这个接口,那么当我们正常关闭服务器时,session中的javaBean对象就会被钝化到我们指定的文件中。 当下一次再启动服务器,因为我们已经将对象写入到文件中,这时就会自动将javaBean对象活化到session中。 package com.itheima.domain;import java.io.Serializable;import javax.servlet.http.HttpSessionActivationListener;import javax.servlet.http.HttpSessionBindingEvent;import javax.servlet.http.HttpSessionBindingListener;import javax.servlet.http.HttpSessionEvent;public class User implements Serializable, HttpSessionBindingListener, HttpSessionActivationListener {private String name;private int age;public String getName() {return name; }public void setName(String name) {this.name = name; }public int getAge() {return age; }public void setAge(int age) {this.age = age; }@Overridepublic void valueBound(HttpSessionBindingEvent event) { System.out.println("向session中绑定了一个user对象"); }@Overridepublic void valueUnbound(HttpSessionBindingEvent event) { System.out.println("从session中将user对象移除"); }@Overridepublic void sessionDidActivate(HttpSessionEvent se) { System.out.println("钝化"); }@Overridepublic void sessionWillPassivate(HttpSessionEvent se) { System.out.println("活化"); }} 我们还需要个context.xml文件来配置钝化时存储的文件,在META-INF目录下创建一个context.xml文件,输入以下代码: <Context><Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"><Store className="org.apache.catalina.session.FileStore" directory="it315"/></Manager></Context> 4、监听器的案例 4.1、案例_定时销毁session 怎样可以将每一个创建的session全都保存起来呢?答:我们可以做一个HttpSessionListener,当session对象创建时,就将这个session对象装入到一个集合中,然后将集合List<HttpSession>保存到ServletContext域中。 怎样判断session是否过期了呢?答:在HttpSession中有一个方法public long getLastAccessedTime(),它可以得到session对象最后使用的时间。然后可以使用invalidate方法销毁。 我们上面的操作需要使用任务调度功能。 在java中有一个Timer定时器类,定时器测试代码: TestTimer.java package com.itheima.timer;import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class TestTimer {public static void main(String[] args) {// 创建一个计时器对象 Timer t = new Timer();// 调度任务功能 t.schedule(new TimerTask() { // 要执行的任务@Overridepublic void run() { System.out.println(new Date().toLocaleString()); } }, 5000, 1000); // 5秒后开始,每隔1秒执行一次 }} 监听ServletContext代码: package com.itheima.test;import java.util.ArrayList;import java.util.Collections;import java.util.Iterator;import java.util.List;import java.util.Timer;import java.util.TimerTask;import javax.servlet.ServletContext;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.http.HttpSession;public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {// 通过事件对象得到事件源(ServletContext) ServletContext application = sce.getServletContext();// 创建一个集合用于存储所有的session对象(需要考虑并发问题,因为我们在web中,它一定是一个多线程的)final List<HttpSession> list = Collections.synchronizedList(new ArrayList<HttpSession>());// 把集合放到application域中 application.setAttribute("sessions", list);// 创建一个计时器对象 Timer t = new Timer();// 调度任务 t.schedule(new TimerTask() {@Overridepublic void run() { System.out.println("开始扫描了...");for (Iterator iterator = list.iterator(); iterator.hasNext();) { HttpSession session = (HttpSession) iterator.next();long l = System.currentTimeMillis() - session.getLastAccessedTime();if (l > 5000) { // 如果时间大于5秒,就把session销毁 System.out.println("session移除了" + session.getId()); session.invalidate(); // 把session销毁 iterator.remove(); } }/* for (HttpSession session : list) { long l = System.currentTimeMillis() - session.getLastAccessedTime(); if (l > 5000) { // 如果时间大于5秒,就把session销毁 session.invalidate(); // 把session销毁 list.remove(session); // 从集合中移除 } } */ } }, 2000, 5000); // 延迟2秒后执行,每间隔5秒执行一次 }@Overridepublic void contextDestroyed(ServletContextEvent sce) { }} 监听Session代码: package com.itheima.test;import java.util.List;import javax.servlet.ServletContext;import javax.servlet.http.HttpSession;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;public class MySessionListener implements HttpSessionListener {@Overridepublic void sessionCreated(HttpSessionEvent se) {// 通过事件源得到HttpSession对象 HttpSession session = se.getSession();// 得到application对象(ServletContext对象) ServletContext application = session.getServletContext();// 得到session对象,并放入到list集合中 List<HttpSession> list = (List<HttpSession>) application.getAttribute("sessions"); list.add(session); System.out.println("添加了" + session.getId()); }@Overridepublic void sessionDestroyed(HttpSessionEvent se) { }} 配置注册监听代码: <listener><listener-class>com.itheima.test.MyServletContextListener</listener-class></listener><listener><listener-class>com.itheima.test.MySessionListener</listener-class></listener> 二、Filter过滤器(重要) Javaweb中的过滤器可以拦截所有访问web资源的请求或响应操作。 1、Filter快速入门 步骤: 1. 创建一个类实现Filter接口。 2. 重写接口中方法,其中doFilter方法是真正过滤用的。 3. 在web.xml文件中进行配置。 注意:在实现Filter接口的类中重写的doFilter方法内如果没有执行这句代码chain.doFilter(request, response);那么浏览器访问服务器的资源是不会被访问到的。 示例代码如下: package com.itheima.filter;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class MyFilter1 implements Filter{@Overridepublic void init(FilterConfig filterConfig) throws ServletException { }@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter1拦截开始了"); // 对浏览器发送的请求进行过滤// 放行 chain.doFilter(request, response); System.out.println("MyFilter1拦截结束了"); // 对服务器返回的响应进行过滤 }@Overridepublic void destroy() { }} 在web.xml文件中进行配置。 <?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"><display-name>day17_01_filter</display-name><filter><filter-name>MyFilter1</filter-name><filter-class>com.itheima.filter.MyFilter1</filter-class></filter><filter-mapping><filter-name>MyFilter1</filter-name><url-pattern>/*</url-pattern></filter-mapping><servlet><servlet-name>ServletDemo1</servlet-name><servlet-class>com.itheima.servlet.ServletDemo1</servlet-class></servlet><servlet-mapping><servlet-name>ServletDemo1</servlet-name><url-pattern>/servlet/demo1</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list></web-app> 测试代码 测试代码一:http://localhost:8080/day17_01_filter/index.jsp 测试代码二:http://localhost:8080/day17_01_filter/servlet/demo1 package com.itheima.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class ServletDemo1 extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo1"); }public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }} 2、FilterChain过滤器链 FilterChain 是 servlet 容器(即服务器)为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。 过滤器使用 FilterChain 调用链中的下一个过滤器(由我们配置的web.xml的先后顺序决定),如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。 问题:怎样可以形成一个Filter链? 答:只要多个Filter对同一个资源进行拦截就可以形成Filter链。 问题:怎样确定Filter的执行顺序? 答:由web.xml中的<filter-mapping>的先后顺序来确定。 示例代码如下图所示: 3、Filter的生命周期 Servlet的生命周期: 实例化 --> 初始化 --> 服务 --> 销毁 当第一次访问服务器资源的时候,Servlet就进行实例化和初始化,只调用一次。(二者不同的地方) 只要应用在(即应用没有卸载),service服务就一直在。 只要应用卸载了或者服务器停止了,Servlet就销毁了。 Filter的生命周期: 当服务器启动时,会创建Filter对象(即调用构造方法进行实例化),并调用init方法,只调用一次。(二者不同的地方) 当浏览器访问服务器资源时,路径与Filter的拦截路径匹配后,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法。 当服务器关闭或应用卸载时,会调用Filter中的destroy方法来进行销毁操作。 4、FilterConfig过滤器参数 在Filter的init方法上有一个参数,类型就是FilterConfig。 FilterConfig它是Filter的配置对象,它可以完成下列功能: 1. 获取Filter名称。 2. 获取Filter初始化参数。 3. 获取ServletContext对象。 如下图所示: 问题:怎样在Filter中获取一个FIlterConfig对象呢? 答:如下图所示:5、Filter的配置文件书写格式 基本配置 <filter><filter-name>Filter名称</filter-name><filter-class>Filter类的包名.类名</filter-class></filter><filter-mapping><filter-name>Filter名称</filter-name><url-pattern>路径</url-pattern></filter-mapping> 关于其它配置 1. <url-pattern> 完全匹配 以"/demo1"开始,不包含通配符*。 目录匹配 以”/”开始 ,以*结束。 扩展名匹配 *.xxx 不能写成 /*.xxx。</url-pattern> 2. <servlet-name> 它是对指定的servlet名称的servlet进行拦截的。</servlet-name> 3. <dispatcher> 可以取的值有 REQUEST FORWARD ERROR INCLUDE 它的作用是:当以什么方式去访问web资源时,进行拦截操作。 1.REQUEST 当从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置,它也是默认值。 2.FORWARD 它描述的是请求转发的拦截方式配置。 3.ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。(了解即可) 4.INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。跟FORWORD差不多。(了解即可)</dispatcher> 三、自动登陆 1、创建数据库与数据表 CREATE DATABASE day17;USE day17;CREATE TABLE USER(id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(100),PASSWORD VARCHAR(100))INSERT INTO USER VALUES(NULL, "tom", "123"); 2、自动登陆功能实现步骤 当用户登陆成功后,判断是否勾选了自动登陆,如果勾选了,就将用户名与密码持久化存储到cookie中。 做一个Filter,对需要自动登陆的资源进行拦截。 自动登录思路图: 四、MD5加密 在mysql中可以对数据进行md5加密,是不可逆(单向)加密。 Md5(字段) UPDATE USER SET PASSWORD=MD5(PASSWORD) WHERE id=1; 在java中也提供了md5加密。md5工具类(MD5Utils类)示例代码如下: package com.itheima.util;import java.math.BigInteger;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public class MD5Utils {/** * 使用md5的算法给某数据进行加密 */public static String md5(String plainText) {byte[] secretBytes = null;try { secretBytes = MessageDigest.getInstance("md5").digest( plainText.getBytes()); } catch (NoSuchAlgorithmException e) {throw new RuntimeException("没有md5这个算法!"); } String md5code = new BigInteger(1, secretBytes).toString(16);for (int i = 0; i < 32 - md5code.length(); i++) { md5code = "0" + md5code; }return md5code; }} 五、全局的编码过滤器 分析: 我们之前做的过滤操作,只能对post请求是ok的,get请求就不行了。 怎样可以做成一个通用的,可以处理post、get所有的请求的? 在java中怎样可以对一个方法进行功能增强? 法1. 继承该方法所在的类 该方法不好,因为不想要的东西我也得继承。 法2. 装饰设计模式 1). 创建一个装饰类让它与被装饰类实现同一个接口或继承同一个父类 2). 在装饰类中有一个被装饰类的引用(即:定义一个与被包装类相同对象的引用(字段)) 3). 定义一个构造方法,把被包装的对象传过来 3). 重写要增强的方法(即:对于不需要改写方法,直接调用;对于需要改写方法,写自己的方法体) 问题:我们获取请求参数有以下方法: 1. getParameter 2. getPrameterValues 3. getParameterMap 这三个方法都可以获取请求参数。 分析后,我们知道 getParameter 与 getParameterValues 方法可以依赖于 getParamterMap 方法来实现。 login.jsp <form action="${pageContext.request.contextPath }/servlet/loginServlet" method="post"> username:<input type="text" name="username"><br/> username1:<input type="text" name="username1"><br/><input type="submit" value="登录"></form> LoginServlet.java package com.itheima.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class LoginServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// request.setCharacterEncoding("UTF-8"); // 每一个servlet都写这句代码,不好 String name = request.getParameterValues("username")[0]; String name1 = request.getParameter("username1"); System.out.println(name); System.out.println(name1); }@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { doGet(request, response); }} web.xml <?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"><display-name>day17_03_Globalfilter</display-name><filter><filter-name>MyFilter</filter-name><filter-class>com.itheima.filter.MyFilter</filter-class></filter><filter-mapping><filter-name>MyFilter</filter-name><url-pattern>/*</url-pattern> </filter-mapping><servlet><servlet-name>LoginServlet</servlet-name><servlet-class>com.itheima.servlet.LoginServlet</servlet-class></servlet><servlet-mapping><servlet-name>LoginServlet</servlet-name><url-pattern>/servlet/loginServlet</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list></web-app> MyFilter.java package com.itheima.filter;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.Map;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;// import javax.servlet.http.HttpServletResponse;public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException { }@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request;// HttpServletResponse resp = (HttpServletResponse) response;// req.setCharacterEncoding("UTF-8"); // 只能解决post的提交方式// resp.setContentType("text/html; charset=utf-8"); req = new MyRequest(req); // 装饰一把(把处理乱码的动作放在装饰类代码里) chain.doFilter(req, response); }@Overridepublic void destroy() { }}// 装饰设计模式 + 适配器// 实现与被包装对象相同的接口// 定义一个与被包装类相同对象的引用(字段)// 定义一个构造方法,把被包装的对象传过来// 对于不需要改写方法,直接调用// 对于需要改写方法,写自己的方法体class MyRequest extends HttpServletRequestWrapper { HttpServletRequest request;public MyRequest(HttpServletRequest request) {super(request); // 是因为父类没有无参数构造this.request = request; }// 一个一个的乱码处理,不好// @Override public String getParameter(String name) { // name = request.getParameter(name); // 可能会出现乱码 // try { // return new String(name.getBytes("iso-8859-1"), "UTF-8"); // 处理乱码// } catch (UnsupportedEncodingException e) {// e.printStackTrace(); // } // return null; // // }@Overridepublic String getParameter(String name) { Map<String, String[]> map = getParameterMap();return map.get(name)[0]; }@Overridepublic String[] getParameterValues(String name) { Map<String, String[]> map = getParameterMap();return map.get(name); }private boolean flag = true;@Overridepublic Map<String, String[]> getParameterMap() {// 得到原始的map集合 Map<String, String[]> map = request.getParameterMap(); // 可能会出现乱码if (flag) { // 处理乱码// 将map集合中的String[]得到,解决每一个元素的乱码问题for (Map.Entry<String, String[]> m : map.entrySet()) { String[] values = m.getValue();for (int i = 0; i < values.length; i++) {try { values[i] = new String(values[i].getBytes("iso-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } flag = false; }return map; }} 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
ajax学习小结 一、Ajax Asynchronous JavaScript And XML = 异步的 JavaScript 及 XML。 目的:使得整个页面不刷新也能出现效果。没有后退按钮。 二、XMLHttprequest(由浏览器内核创建) 方法: open("get/post", "URL"); send(); 属性: readyState 客户端请求就绪状态码 status 服务器响应状态码 responseText 响应的文本内容 responseXML 响应的XML文档对象(一般不用) 事件处理器: onreadystatechange 示例代码: <script type="text/javascript"> // 方式1 function ckName() { // 获取用户名对象 var name = document.getElementsByTagName("input")[0]; // 创建XMLHttpRequest对象 var xhr = getXMLHttpRequest(); // 处理响应结果,创建回调函数,根据响应状态动态更新页面 xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // 说明客户端请求一切正常 if (xhr.status == 200) { // 说明服务器响应一切正常 // alert(xhr.responseText); // 得到响应结果 var msg = document.getElementById("msg"); if (xhr.responseText == "true") { // msg.innerText = "用户名已存在"; msg.innerHTML = "<font color='red'>该用户名已存在</font>"; } else { msg.innerHTML = "<font color='green'>该用户名可以使用</font>"; } } } } // 建立一个连接 xhr.open("get", "${pageContext.request.contextPath }/servlet/ckNameServlet?name=" + name.value); // 发送请求 xhr.send(null); } // 方式2 window.onload = function() { var nameElement = document.getElementsByName("userName")[0]; nameElement.onblur = function() { var name = this.value; // this等价于nameElement // 创建XMLHttpRequest对象 var xhr = getXMLHttpRequest(); // 处理响应结果,创建回调函数,根据响应状态动态更新页面 xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // 说明客户端请求一切正常 if (xhr.status == 200) { // 说明服务器响应一切正常 // alert(xhr.responseText); // 得到响应结果 var msg = document.getElementById("msg"); if (xhr.responseText == "true") { // msg.innerText = "该用户名已存在"; msg.innerHTML = "<font color='red'>该用户名已存在</font>"; } else { msg.innerHTML = "<font color='green'>该用户名可以使用</font>"; } } } } // 建立一个连接 xhr.open("get", "${pageContext.request.contextPath }/servlet/ckNameServlet?name=" + name + "&time=" + new Date().getTime()); // 发送请求 xhr.send(null); } } </script> 三、使用Ajax 1. 确定使用什么事件调用什么方法 2. 步骤: 获取XMLHttpRequest对象 处理响应结果 建立一个连接 发送请求 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、什么是Ajax Ajax( Asynchronous JavaScript And XML ):指异步 JavaScript 及 XML。不是一种新的编程语言 ,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术,是基于JavaScript、XML、HTML、CSS的新用法。 Ajax:只刷新局部页面的技术。包括以下几种技术: JavaScript:更新局部的网页。 XML:一般用于请求数据和响应数据的封装。 XMLHttpRequest对象:发送请求到服务器并获得返回结果(浏览器内核创建的)。 CSS:美化页面样式。 异步:发送请求后不等待返回结果,由回调函数处理结果。 JavaScript 中 XMLHttpRequest对象是整个Ajax技术的核心,它提供了异步发送请求的能力。 构造方法: 不同浏览器,甚至相同浏览器的不同版本,获取该对象的方式是不同的。 var xmlhttp;if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp = new XMLHttpRequest();} else { // code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");} 方法: open() 该方法有3个参数,"get|post","url?name=tom","true|false",默认为true。 send() 发送请求,可以带参数 或 null。 setRequestHeader() 设置请求消息头。 属性: readyState 类型为short,只读 responseText 类型为String,只读 responseXML 类型为Document,只读(一般不用) status 类型为short,只读 事件处理器: onreadystatechange 二、常用方法 open(method, URL, async) 建立与服务器的连接 method参数:指请求的HTTP方法,典型的值是GET或POST URL参数:指请求的地址 async参数:指是否使用异步请求,其值为true或false,默认值是true,一般这个参数不写 send(content) 发送请求 content参数:指请求的参数 setRequestHeader(header, value) 设置请求的头信息 三、常用属性 onreadystatechange: 指定回调函数 readyState: XMLHttpRequest的状态信息(客户端:浏览器) 就绪状态码 说明 0 XMLHttpRequest对象没有完成初始化,即:刚刚创建。 1 XMLHttpRequest对象开始发送请求,即:调用了open方法,但还没有调用send方法。请求还没有发出。 2 XMLHttpRequest对象的请求发送完成,即:send方法已经调用,数据已经提交到服务器,但没有任何响应。 3 XMLHttpRequest对象开始读取响应,还没有结束,即:收到了所有的响应消息头,但正文还没有完全收到。 4 XMLHttpRequest对象读取响应结束,即:一切都收到了。 status: HTTP的状态码(服务器端) 状态码 说明 200 服务器响应正常 400 无法找到请求的资源 403 没有访问权限 500 服务器内部错误 responseText: 获得响应的文本内容 responseXML: 获得响应的XML文档对象 documednt 注:客户端就绪状态码是4,且服务端状态码是200,才可以处理服务器数据。 示例代码如下: <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><script type="text/javascript" src="${ pageContext.request.contextPath }/js/myJS.js"></script><title>Insert title here</title></head><script type="text/javascript">// 1、获取XMLHttpRequest对象var req = getXMLHttpRequest();// 4、处理响应结果 req.onreadystatechange = function() {// alert(req.readyState); // 查看客户器端就绪状态码if (req.readyState == 4) {// alert(req.status); // 查看服务器端响应状态码if (req.status == 200) { // 说明服务器响应一切正常 alert(req.responseText); } } }// 2、建立一个连接 req.open("get", "${ pageContext.request.contextPath }/servlet/servletDemo1");// 3、发送请求 req.send(null);</script><body></body></html> 使用Ajax验证用户名是否存在的实现步骤: 使用文本框的onblur事件(失去焦点事件) 使用Ajax技术实现异步交互a) 获取用户名a) 创建 XMLHttpRequest 对象b) 处理响应结果,创建回调函数,根据响应状态动态更新页面c) 建立一个连接d) 发送请求 示例代码如下: <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><script type="text/javascript" src="${ pageContext.request.contextPath }/js/myJS.js"></script><title>Insert title here</title><script type="text/javascript">function ckName() {// 获取用户名对象var name = document.getElementsByTagName("input")[0];// 创建XMLHttpRequest对象var xhr = getXMLHttpRequest();// 处理响应结果,创建回调函数,根据响应状态动态更新页面 xhr.onreadystatechange = function() {if (xhr.readyState == 4) { // 说明客户端请求一切正常if (xhr.status == 200) { // 说明服务器响应一切正常// alert(xhr.responseText); // 得到响应结果var msg = document.getElementById("msg");if (xhr.responseText == "true") {// msg.innerText = "用户名已存在"; msg.innerHTML = "<font color='red'>该用户名已存在</font>"; } else { msg.innerHTML = "<font color='green'>该用户名可以使用</font>"; } } } } // 建立一个连接 xhr.open("get", "${ pageContext.request.contextPath }/servlet/ckNameServlet?name=" + name.value + "&time=" + new Date().getTime());// 发送请求 xhr.send(null); }</script></head><body> 用户名:<input type="text" name="userName" onblur="ckName()"/><span id="msg" ></span></br> 密码:<input type="password" name="pwd" /></br></body></html> 验证用户名是否存在改进代码版本(该种方式,使得网页的标签很干净,感觉不到调用事件了): <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><script type="text/javascript" src="${ pageContext.request.contextPath }/js/myJS.js"></script><title>Insert title here</title><script type="text/javascript">window.onload = function() {var nameElement = document.getElementsByName("userName")[0]; nameElement.onblur = function() {var name = this.value; // this等价于nameElement// 创建XMLHttpRequest对象var xhr = getXMLHttpRequest();// 处理响应结果,创建回调函数,根据响应状态动态更新页面 xhr.onreadystatechange = function() {if (xhr.readyState == 4) { // 说明客户端请求一切正常if (xhr.status == 200) { // 说明服务器响应一切正常// alert(xhr.responseText); // 得到响应结果var msg = document.getElementById("msg");if (xhr.responseText == "true") {// msg.innerText = "用户名已存在"; msg.innerHTML = "<font color='red'>该用户名已存在</font>"; } else { msg.innerHTML = "<font color='green'>该用户名可以使用</font>"; } } } } // 建立一个连接 xhr.open("get", "${pageContext.request.contextPath }/servlet/ckNameServlet?name=" + name + "&time=" + new Date().getTime());// 发送请求 xhr.send(null); } }</script></head><body> 用户名:<input type="text" name="userName" /><span id="msg" ></span></br> 密码:<input type="password" name="pwd" /></br></body></html> 四、案例1:实现邮箱验证 my.js // 获取XMLHttpRequest对象function getXMLHttpRequest() {var xmlhttp;if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp = new XMLHttpRequest(); } else { // code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }return xmlhttp;} register.jsp <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><script type="text/javascript" src="${pageContext.request.contextPath }/js/my.js"></script><head><title>bookStore注册页面</title><%--导入css --%><link rel="stylesheet" href="css/main.css" type="text/css" /><script type="text/javascript">function changeImage() {document.getElementById("img").src = "${pageContext.request.contextPath}/imageCode?time=" + new Date().getTime(); }// 验证邮箱是否存在function ckEmail() {// 得到邮箱对象var email = document.getElementByName("email")[0];// 创建XMLHttpRequest对象var xhr = getXMLHttpRequest();// 处理响应结果,创建回调函数,根据响应状态动态更新页面 xhr.onreadystatechange = function() {if (xhr.readyState == 4) {if (xhr.status == 200) { alert(xhr.responseText); // 得到响应结果// 得到邮箱的font标签var font = document.getElementsByTagName("font")[0];if (xhr.responseText == "true") { font.innerHTML = "此邮箱已被使用"; font.style.color = "red"; } else { font.innerHTML = "此邮箱可以使用"; font.style.color = "green"; } } } }// 建立一个连接 xhr.open("get", "${pageContext.request.contextPath}/servlet/ckEmailServlet?email=" + email.value + "&time=" + new Date().getTime());// 发送请求 xhr.send(null); }</script></head><body class="main"><%@include file="head.jsp"%><%--导入头 --%><%@include file="menu_search.jsp"%><%--导入导航条与搜索 --%><div id="divcontent"><form action="${pageContext.request.contextPath}/register.jsp"method="post"><table width="850px" border="0" cellspacing="0"><tr><td style="padding:30px"><h1>新会员注册</h1><table width="70%" border="0" cellspacing="2" class="upline"><tr><td style="text-align:right; width:20%">会员邮箱:</td><td style="width:40%"><input type="text" class="textinput"name="email" onblur="ckEmail()"/></td><td><font color="#999999">请输入有效的邮箱地址</font></td></tr>.......</body></html> CkEmailServlet.java package com.itheima.web.servlet;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class CkEmailServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { PrintWriter out = response.getWriter(); String email = request.getParameter("email");if ("tom@163.com".equals(email)) { out.print(true); } else { out.print(false); } }public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { doGet(request, response); }} 五、案例2:搜索框显示数据 编写显示数据的容器div 实现ajax响应数据// 创建XMLHttpRequest对象// 通过事件调用回调函数处理响应结果// 创建一个服务器连接// 发送请求 示例代码如下: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><script type="text/javascript"src="${pageContext.request.contextPath}/js/my.js"></script><script type="text/javascript">window.onload = function() {// 得到搜索框对象var searchElement = document.getElementById("name");// 得到父div元素对象var div = document.getElementById("context1");// 给文件框注册按键弹起事件 searchElement.onkeyup = function() { // 先获取文本框的值var name = this.value;// 如果文本框没有数据时,就把父div隐藏,且不向服务器发送请求if (name == "") { div.style.display = "none";return; }// 获得XMLHttpRequest对象var xhr = getXMLHttpRequest();// 处理结果 xhr.onreadystatechange = function() {if (xhr.readyState == 4) { // 客户端请求一 切正常if (xhr.status == 200) { // 服务器响应一切正常var str = xhr.responseText; // 得到服务器返回的数据var ss = str.split(","); // 把字符串 1001,1002,1003 切成数组var childDivs = "";// 循环把数据放入子div中for (var i = 0; i < ss.length; i++) { childDivs += "<div onclick='writeText(this)' onmouseover='changeBackground_over(this)' onmouseout='changeBackground_out(this)'>" + ss[i] + "</div>"; // 把数组中的每个元素放到子div中 } div.innerHTML = childDivs; // 把多个子div(childDivs)放入列表父div中 div.style.display = "block"; // 把列表隐藏 } } } xhr.open("get", "${ pageContext.request.contextPath}/servlet/searchBookAJAXServlet?name=" + name + "&time=" + new Date().getTime()); xhr.send(null); } }// 鼠标悬浮在子div时,改变背景色function changeBackground_over(div) { div.style.backgroundColor = "gray"; // backgroundColor js中属性的写法,background-color css中属性的写法 }// 鼠标离开子div时,恢复背景色function changeBackground_out(div) { div.style.backgroundColor = ""; }// 填充文本到搜索框function writeText(div) {// 先得到搜索框var searchElement = document.getElementById("name");// 把div中的文本添加到搜索框中 searchElement.value = div.innerHTML; // 把父div(context1)隐藏 div.parentNode.style.display = "none"; }</script><div id="divmenu"><a href="${pageContext.request.contextPath}/showProductByPage?category=文学">文学</a><a href="${pageContext.request.contextPath}/showProductByPage?category=生活">生活</a><a href="${pageContext.request.contextPath}/showProductByPage?category=计算机">计算机</a><a href="${pageContext.request.contextPath}/showProductByPage?category=外语">外语</a><a href="${pageContext.request.contextPath}/showProductByPage?category=经营">经管</a><a href="${pageContext.request.contextPath}/showProductByPage?category=励志">励志</a><a href="${pageContext.request.contextPath}/showProductByPage?category=社科">社科</a><a href="${pageContext.request.contextPath}/showProductByPage?category=学术">学术</a><a href="${pageContext.request.contextPath}/showProductByPage?category=少儿">少儿</a><a href="${pageContext.request.contextPath}/showProductByPage?category=艺术">艺术</a><a href="${pageContext.request.contextPath}/showProductByPage?category=原版">原版</a><a href="${pageContext.request.contextPath}/showProductByPage?category=科技">科技</a><a href="${pageContext.request.contextPath}/showProductByPage?category=考试">考试</a><a href="${pageContext.request.contextPath}/showProductByPage?category=生活百科">生活百科</a><a href="${pageContext.request.contextPath}/showProductByPage" style="color:#FFFF00">全部商品目录</a></div><div id="divsearch"><form action="${pageContext.request.contextPath}/findProductBySearch"method="post"><table width="100%" border="0" cellspacing="0" ><tr><td style="text-align:right; padding-right:220px"> Search <input type="text" name="name" class="inputtable" id="name" autocomplete="on"/> <!-- 解决中文提交的问题 --><input type="image" src="images/serchbutton.gif"border="0" style="margin-bottom:-4px"></td></tr></table></form></div><div id="context1" style="display:block; border:1px solid red; background-color:white; width:128px; position:absolute; left:860px; top:135px;"></div> 六、json对象的学习 示例代码如下:json.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body><script type="text/javascript">/* json对象有些像java的普通类对象,java是纯面向对象,javascript是基于对象和事件的脚本语言。 // java语言创建的类 public class Person() { private String name; private int age; public void show() { } } *//* // js语言创建的类,大小写均可,一般小写,为了更像一个类,这时我们大写 function Person() { var name = "tom"; // 声明一个局部变量 this.age = 10; // 声明一个成员变量 this.show = function() { alert(name); } } var p = new Person(); document.write(p.name); document.write(p.age); p.show(); */var pp = [100, true, 12.34]; // 这是一个数组var pp = { name:"tom", age:18, show:function() { alert("hello json"); } }; // 这是一个json对象document.write(pp.name);document.write(pp.age); pp.show();var ppp = [ { name:"tom", age:18 }, { name:"lune", age:25 } ];document.write(ppp[1].name)</script></body></html> JsonTest.java package com.itheima.json;import java.sql.SQLException;import java.util.ArrayList;import java.util.List;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanListHandler;import org.junit.Test;import com.itheima.domain.Book;import com.itheima.util.C3P0Util;import net.sf.json.JSONArray;import net.sf.json.JSONObject;import net.sf.json.JsonConfig;public class JsonTest { @Test // 使用 JSONObject对象封装对象数据public void test1() { Book b = new Book(); b.setId("10001"); b.setName("西游记"); b.setPnum(20); String s = JSONObject.fromObject(b).toString(); System.out.println(s); } @Test // 使用 JSONArray对象封装 List<Book>对象数据public void test2() throws SQLException { List<Book> list = new ArrayList<Book>(); Book b1 = new Book(); b1.setId("10001"); b1.setName("西游记"); b1.setPnum(20); Book b2 = new Book(); b2.setId("10002"); b2.setName("三国演义"); b2.setPnum(30); Book b3 = new Book(); b3.setId("10003"); b3.setName("水浒传"); b3.setPnum(40);list.add(b1);list.add(b2);list.add(b3); String s = JSONArray.fromObject(list).toString(); System.out.println(s); } @Test // 使用 JSONArray对象封装 List<Book>对象数据 + 使用 JsonConfig对象过滤掉不想要的数据public void test3() throws SQLException { QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); List<Book> list = qr.query("select * from book", new BeanListHandler<Book>(Book.class)); JsonConfig jc = new JsonConfig(); jc.setExcludes(new String[]{"pnum", "description", "category", "id"}); String s = JSONArray.fromObject(list, jc).toString(); System.out.println(s); }} 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、DBUtils介绍 Apache公司开发的框架。 什么是dbutils?它的作用? DBUtils是java编程中的数据库操作实用工具,小巧简单实用。 DBUtils封装了对JDBC的操作,简化了JDBC操作。可以少写代码。 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装 ,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。因此 dbutils 成为很多不喜欢 hibernate 的公司的首选框架。 对于数据表的 读操作 ,dbutils可以把结果转换成List、Array、Set等java集合,便于程序员操作。 对于数据表的 写操作 ,也变得很简单(只需写sql语句就行)。 可以使用数据源、使用JNDI、数据库连接池等技术来优化性能(即重用已经构建好的数据库连接对象)。 二、DBUtils的三个核心对象 QueryRunner类 ResultSetHandler接口 DBUtils类 QueryRunner类 QueryRunner中提供对sql语句操作的API。 它主要有三个方法 query() 用于执行select语句 update() 用于执行insert、update、delete语句 batch() 批处理 ResultSetHandler接口 用于定义select操作后,怎样封装结果集。 DbUtils类 它就是一个工具类,定义了关闭资源与事务处理的方法。 三、DBUtils快速入门 导入jar包 创建QueryRunner对象 使用query方法执行select语句 使用ResultSetHandler封装结果集 使用DbUtils类释放资源 Dbutils快速入门实现步骤: 创建数据库及表,如下: CREATE DATABASE day14; USE day14; CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR (40) NOT NULL, PASSWORD VARCHAR (40), email VARCHAR (60), birthday DATE ) CHARACTER SET utf8 COLLATE utf8_general_ci; INSERT INTO users(username, PASSWORD, email, birthday) VALUES('tom', '123', 'tom@163.com', '1980-12-04'); 导入jar包 注意: c3p0与mysql驱动jar包也要导入。 示例代码如下: 1 package com.itheima.util; 2 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.ArrayList; 6 import java.util.List; 7 8 import org.apache.commons.dbutils.QueryRunner; 9 import org.apache.commons.dbutils.ResultSetHandler; 10 import org.apache.commons.dbutils.handlers.BeanListHandler; 11 import org.junit.Test; 12 13 import com.itheima.entity.User; 14 15 public class TestSelect { 16 17 @Test 18 public void testSelect() throws SQLException { 19 // 创建一个QueryRunner对象 20 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 21 List<User> list = qr.query("select * from users", new ResultSetHandler<List<User>>() { 22 // 当query方法执行select语句后,将结果集以参数的形式传递过来 23 public List<User> handle(ResultSet rs) throws SQLException { 24 List<User> list = new ArrayList<User>(); 25 while (rs.next()) { 26 User u = new User(); 27 u.setId(rs.getInt(1)); 28 u.setUsername(rs.getString(2)); 29 u.setPassword(rs.getString(3)); 30 u.setEmail(rs.getString(4)); 31 u.setBirthday(rs.getDate(5)); 32 list.add(u); 33 } 34 return list; 35 } 36 }); 37 38 for (User user : list) { 39 System.out.println(user); 40 } 41 } 42 43 @Test 44 public void testSelect2() throws SQLException { 45 // 创建一个QueryRunner对象 46 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 47 // 执行查询语句,并返回结果 48 List<User> list = qr.query("select * from users", new BeanListHandler<User>(User.class)); 49 50 for (User user : list) { 51 System.out.println(user); 52 } 53 } 54 } 四、QueryRunner对象 1.1 构造函数 new QueryRunner(); // 它的事务可以手动控制。 此对象调用的方法(如:query、update、batrch)参数中要有Connection对象。 new QueryRunner(DataSource ds); // 它的事务是自动控制的。一个sql一个事务。 此对象调用的方法(如:query、update、batrch)参数中无需Connection对象。 1.2 方法 <T> T query(String sql, ResultSetHandler<T> rsh) <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh) <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) int update(String sql, Object... params) int update(Connection conn, String sql, Object... params) int batch(String sql, Object[][] params) int[] batch(Connection conn, String sql, Object[][] params) 示例如下: 进行基本的CRUD操作:练习一下,示例代码如下: 1 package com.itheima.util; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.SQLException; 6 import java.util.Date; 7 8 import org.apache.commons.dbutils.QueryRunner; 9 import org.junit.Test; 10 11 public class TestCRUD { 12 13 // 没有使用框架之前 14 @Test 15 public void testInsert0() { 16 Connection conn = null; 17 PreparedStatement ps = null; 18 19 try { 20 conn = C3P0Util.getConnection(); 21 ps = conn.prepareStatement("insert into users(name, money) values('ggg', 2000)"); 22 ps.executeUpdate(); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } finally { 26 C3P0Util.release(conn, ps, null); 27 } 28 } 29 30 // 使用框架之后 31 @Test 32 public void testInsert() throws SQLException { 33 // 创建一个QueryRunner对象 34 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 35 // 执行sql语句 36 qr.update("insert into users(username, password, email, birthday) values(?, ?, ?, ?)", "菜10", "123", "c10@163.com", new Date()); 37 38 } 39 40 @Test 41 public void testUpdate() throws SQLException { 42 // 创建一个QueryRunner对象 43 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 44 qr.update("update users set username=?, password=? where id=?", "周杰杰", "333", 15); 45 } 46 47 @Test 48 public void testDelete() throws SQLException { 49 // 创建一个QueryRunner对象 50 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 51 qr.update("delete from users where id=?", 15); 52 } 53 54 // 批处理,只能执行相同的sql语句 55 @Test 56 public void testBatch() throws SQLException { 57 // 创建一个QueryRunner对象 58 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 59 60 Object[][] params = new Object[10][]; // 高维代表执行多少次sql语句 61 for (int i = 0; i < params.length; i++) { 62 params[i] = new Object[] { "菜10" + i, "123", "c10@163.com", new Date() }; // 给每次执行的sql语句中的?赋值 63 } 64 qr.batch("insert into users(username, password, email, birthday) values(?, ?, ?, ?)", params); 65 } 66 } 五、ResultSetHandler接口 ResultSetHandler接口下的所有结果处理器实现类( 共9个实现类 ): ArrayHandler实现类:适合取1条记录(1行数据)。把该条记录的每列值封装到一个Object[]数组中。 ArrayListHandler实现类:适合取多条记录(多行数据)。把每条记录的每列值封装到一个Object[]数组中,把数组封装到一个List集合中。 ColumnListHandler实现类:适合取某一列的数据。把取到的数据封装到List集合中。 KeyedHandler实现类:取多条记录(多行数据),每一条记录封装到一个Map集合中,再把这个Map集合封装到另外一个Map集合中,key为指定的字段值。 MapHandler实现类:适合取1条记录(1行数据)。把当前记录的列名和列值放到一个Map集合中。 MapListHandler实现类:适合取多条记录(多行数据)。把每条记录封装到一个Map集合中,再把Map集合封装到List集合中。 ScalarHandler实现类:适合取单行单列数据(一个单元格的数据)。 BeanHandler实现类:适合取单行数据。 BeanListHandler实现类:适合取单行数据。示例代码如下: 1 package com.itheima.util; 2 3 import java.sql.SQLException; 4 import java.util.List; 5 import java.util.Map; 6 7 import org.apache.commons.dbutils.QueryRunner; 8 import org.apache.commons.dbutils.handlers.ArrayHandler; 9 import org.apache.commons.dbutils.handlers.ArrayListHandler; 10 import org.apache.commons.dbutils.handlers.BeanHandler; 11 import org.apache.commons.dbutils.handlers.BeanListHandler; 12 import org.apache.commons.dbutils.handlers.ColumnListHandler; 13 import org.apache.commons.dbutils.handlers.KeyedHandler; 14 import org.apache.commons.dbutils.handlers.MapHandler; 15 import org.apache.commons.dbutils.handlers.MapListHandler; 16 import org.apache.commons.dbutils.handlers.ScalarHandler; 17 import org.junit.Test; 18 19 import com.itheima.entity.User; 20 21 public class TestResultSetHandler { 22 @Test // ArrayHandler实现类:适合取1条记录(1行数据)。把该条记录的每列值封装到一个Object[]数组中。 23 public void tese1() throws SQLException { 24 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 25 Object[] arr = qr.query("select * from users", new ArrayHandler()); 26 27 for (Object o : arr) { 28 System.out.println(o); 29 } 30 } 31 32 @Test // ArrayListHandler实现类:适合取多条记录(多行数据)。把每条记录的每列值封装到一个Object[]数组中,把数组封装到一个List集合中。 33 public void tese2() throws SQLException { 34 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 35 List<Object[]> query = qr.query("select * from users", new ArrayListHandler()); 36 37 for (Object[] os : query) { 38 for (Object o : os) { 39 System.out.println(o); 40 } 41 System.out.println("--------------"); 42 } 43 } 44 45 @Test // ColumnListHandler实现类:适合取某一列的数据。把取到的数据封装到List集合中。 46 public void tese3() throws SQLException { 47 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 48 List<Object> list = qr.query("select * from users", new ColumnListHandler(3)); 49 50 for (Object o : list) { 51 System.out.println(o); 52 } 53 } 54 55 @Test // KeyedHandler实现类:适合取多条记录(多行数据)。每一条记录封装到一个Map集合中,再把这个Map集合封装到另外一个Map集合中,key为指定的字段值。 56 public void tese4() throws SQLException { 57 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 58 // 大Map集合的key是表中的第一列数据,小Map集合中的key是表的列名,所以大Map集合的key是Object类型,小Map集合的key是String类型。 59 Map<Object, Map<String, Object>> map = qr.query("select * from users", new KeyedHandler(1)); 60 61 for (Map.Entry<Object, Map<String, Object>> m : map.entrySet()) { 62 System.out.println(m.getKey()); // 默认大Map集合中key值就是id列的值 63 for (Map.Entry<String, Object> mm : m.getValue().entrySet()) { 64 System.out.println(mm.getKey() + "\t" + mm.getValue()); // 取出小Map集合中的列名和列值 65 } 66 System.out.println("--------------"); 67 } 68 } 69 70 @Test // MapHandler实现类:适合取1条记录(1行数据)。把当前记录的列名和列值放到一个Map集合中。 71 public void tese5() throws SQLException { 72 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 73 Map<String, Object> map = qr.query("select * from users where id=?", new MapHandler(), 10); 74 75 for (Map.Entry<String, Object> m : map.entrySet()) { 76 System.out.println(m.getKey() + "\t" + m.getValue()); 77 } 78 } 79 80 @Test // MapListHandler实现类:适合取多条记录(多行数据)。把每条记录封装到一个Map集合中,再把Map封装到List集合中。 81 public void tese6() throws SQLException { 82 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 83 List<Map<String, Object>> list = qr.query("select * from users", new MapListHandler()); 84 85 for (Map<String, Object> map : list) { 86 for (Map.Entry<String, Object> m : map.entrySet()) { 87 System.out.println(m.getKey() + "\t" + m.getValue()); 88 } 89 System.out.println("--------------"); 90 } 91 } 92 93 @Test // ScalarHandler实现类:适合取单行单列数据(一个单元格的数据)。 94 public void tese7() throws SQLException { 95 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 96 Object o = qr.query("select count(*) from users", new ScalarHandler(1)); // 统计有多少行数据 97 System.out.println(o.getClass().getName()); // 注意:行数是long类型,因为表格已经突破了千万行了。 98 } 99 100 @Test // BeanHandler实现类:适合取单行数据。 101 public void tese8() throws SQLException { 102 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 103 User user = qr.query("select * from users where id=?", new BeanHandler<User>(User.class), 1); 104 System.out.println(user); 105 } 106 107 @Test // BeanListHandler实现类:适合取单行数据。 108 public void tese9() throws SQLException { 109 QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); 110 List<User> list = qr.query("select * from users where id=?", new BeanListHandler<User>(User.class), 1); 111 System.out.println(list); 112 } 113 } 六、DBUtils控制事务的开发 1.版本一:转账的具体操作在数据访问层(dao)中实现。不好,因为转账操作属于业务层(service)的东西。 2.版本二:转账的具体操作在业务层(service)中实现。可以,但是,业务层里面出现了数据访问层(dao)的东西。Connection conn = C3P0Util.getConnection(); 3.版本三:使用线程局部变量ThreadLocal 七、ThreadLocal线程局部变量 模拟ThreadLocal类的设计,让大家明白它的作用。 public class ThreadLocal { private Map<Runnable, Object> container = new HashMap<Runnable, Object>(); public void set(Object value) { container.put(Thread.currentThread(), value); // 用当前线程作为key } public Object get() { return container.get(Thread.currentThread()); } public void remove() { container.remove(Thread.currentThread()); } } 总结:调用该类的get方法,永远返回当前线程放入的数据,保证线程安全。所以ThreadLocal叫做线程局部变量。 八、完成案例 项目开发的准备工作 创建项目 添加jar包 添加dao、service、servlet分层代码 创建数据库及表 create database day14; use day14; create table books ( id varchar(200) primary key, name varchar(100) not null, price double, pnum int, category varchar(50), description varchar(200) ) SELECT * FROM books; INSERT INTO books VALUES('1001', 'java编程思想', 98, 100, '计算机', '好书'); INSERT INTO books VALUES('1002', '解忧杂货铺', 10, 20, '文学', '好书'); INSERT INTO books VALUES('1003', '创京东', 20, 30, '科技', '好书'); 显示列表数据 的思路图: 添加/修改列表数据 的思路图: 实现 删除 功能(并添加删除提示): 实现 全选/全不选 功能: 实现 批量删除 功能(方式1:使用js的方式): 实现 批量删除 功能(方式2:通过提交表单的方式): 实现 多条件查询 功能: 实现 分页显示 的功能: 分页显示思路及思路图: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
day13_Mysql事务与数据库连接池学习笔记 一、Mysql的事务 事务: 事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功(数据回滚)。 例如:A给B转帐,对应于如下两条sql语句 : update account set money=money-100 where name='a'; update account set money=money+100 where name='b'; 1、Mysql中的事务 a、mysql引擎是支持事务的。 b、mysql语句默认是自动提交事务。每条语句都处在单独的事务中。 c、手动控制事务(命令行): 开启事务:start transaction; 或者 begin; 提交事务:commit; 回滚事务:rollback; 2、JDBC如何控制事务? 3、事务的特性(ACID)(面试题) 原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。多条语句变成一个不可分割的整体。 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。转账前和转账后的总金额不变。 隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。(多线程) 持久性(Durability):指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。 4、事务的隔离级别 赃读:指一个事务读取了另一个事务未提交的数据。 读到的是假的数据。 不可重复读:在一个事务内读取表中的某一行数据,多次读取的结果不同。一个事务读取到了另一个事务提交后的数据。(update) 每次读到的结果不一样。 虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取的结果不一致。(insert) 读到的数据变多了。 数据库通过设置事务的隔离级别防止以上情况的发生: 1、READ UNCOMMITTED: 赃读、不可重复读、虚读都有可能发生。 read uncommitted 2、READ COMMITTED: 避免赃读。不可重复读、虚读都有可能发生。(oracle默认的) read committed 4、REPEATABLE READ: 避免赃读、不可重复读。虚读有可能发生。(mysql默认的) repeatable read 8、SERIALIZABLE: 避免赃读、不可重复读、虚读。 serializable 级别越高,性能越低,数据越安全。实际开发是以级别2、4为主,其余的用程序控制。 mysql中: 查看当前的事务隔离级别:SELECT @@TX_ISOLATION; 更改当前的事务隔离级别:SET TRANSACTION ISOLATION LEVEL 四个级别之一。 特别注意:设置隔离级别必须在开启事务之前。 5、JDBC控制事务的隔离级别 Connection接口: JDBC代码中设置隔离级别:必须在开启事务之前。 注意:mysql的默认级别,若是操作mysql数据库,可以不用设置,默认设置就是这个。 二、数据库连接池 1、连接池原理:(面试) 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个; 通过释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。 这项技术能明显提高对数据库操作的性能。 目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。 2、编写标准的数据源 自定义的数据库连接池要实现javax.sql.DataSource接口,一般都叫数据源。 实现DataSource接口,并实现连接池功能的步骤: 1. 在MyDataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。 2. 实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。 3. 当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。 Collection保证将自己返回到LinkedList中是此处编程的难点。 3、编写数据源时遇到的问题及解决办法 a、装饰设计模式:使用频率很高 目的:改写已存在的类的某个方法或某些方法,装饰设计模式(包装设计模式) 口诀: 1、编写一个类,实现与被包装类相同的接口。(具备相同的行为) 2、定义一个被包装类类型的变量,即引用,用于接收被包装的对象。 3、定义构造方法,把被包装类的对象注入,给被包装类变量赋值。 4、对于不需要改写的方法,调用原有的方法。 5、对于需要改写的方法,写自己的代码。 // 该类本身就是一个装饰类,对原类的具体装饰饰在该类中实现。(但是需要写很多多余的代码) b、默认适配器模式:是装饰设计模式的一个变体 包装适配器类: 包装适配器实现类: 4、常用的数据源配置(日后都使用数据源,所以一定要配置一下) 4.1、DBCP(数据源开源框架) DBCP:Apache推出的 DataBase Connection Pool 数据连接池(数据源) 使用步骤: > 添加jar包:commons-dbcp-1.4.jar 和 commons-pool-1.5.6.jar 和 mysql-connector-java-5.0.8-bin.jar > 添加属性资源文件(等待时间,最大连接数等配置信息) > 编写数据源操作的工具类 数据源操作的工具类: 测试类: 4.2、C3P0 使用步骤: 1、添加jar包 这次我们使用的是Java Project项目,在该项目下新建lib文件夹,添加jar包:c3p0-0.9.1.2.jar,再添加至构建路径。 2、编写配置文件 c3p0-config.xml,放在classpath中,或classes目录中 3、编写操作数据源的工具类 4、编写测试类 注意:测试类类名TsetCRUD、TestDao、TestJDBC,名字含义都一样。 5、用JavaWeb服务器管理数据源(连接池):比如用Tomcat,此时不需要导入jar包 开发JavaWeb应用,必须使用一个JavaWeb服务器,JavaWeb服务器都内置数据源。 Tomcat:(DBCP) 数据源只需要配置服务器即可。 配置数据源的步骤: 1、拷贝数据库连接的jar包mysql-connector-java-5.0.8-bin.jar到tomcat的lib目录下。 2、配置数据源XML文件: a)如果把配置信息写在tomcat下的conf目录的context.xml中,可以配置多个不同的数据源,那么所有应用都能使用此数据源。 b)如果是在当前应用的META-INF中创建context.xml,编写数据源,那么只有当前应用可以使用此数据源。 演示b) context.xml文件 3、如何使用连接池呢?如下: 在xxx.jsp下加入:<% ...... %> JNDI:java nameing directory interface 有多个<Resource/>,就有多个name,多个name就变成了JNDI。 JNDI容器就是一个Map。 二、自定义JDBC框架(练技术) 1、数据库元信息的获取(很简单、很无聊、很重要) a、元信息:(Meta Data)指数据库或表等的定义信息。 2、自定义JDBC框架 反射: 策略设计模式: 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
java中注解用法详解——@SuppressWarnings 一、前言 注释类型: 当你的编码可能存在警告时,比如安全警告,可以用它来消除。 api中是这样描述的: 指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。 注意,在给定元素中取消显示的警告集是所有包含元素中取消显示的警告的超集。 例如,如果注释一个类来取消显示某个警告,同时注释一个方法来取消显示另一个警告,那么将在此方法中同时取消显示这两个警告。 根据风格不同,程序员应该始终在最里层的嵌套元素上使用此注释,在那里使用才有效。 如果要在特定的方法中取消显示某个警告,则应该注释该方法而不是注释它的类。 在java编译过程中会出现很多警告,有很多是安全的,但是每次编译有很多警告影响我们对error的过滤和修改,我们可以在代码中加上 @SuppressWarnings(“XXXX”) 来解决 例如:@SuppressWarnings("deprecation") 表示不显示使用了不赞成使用的类或方法时的警告。 再比如: 编码时我们总会发现如下:变量未被使用的警告提示 上述代码编译通过且可以运行,但每行前面的“感叹号”就严重阻碍了我们判断该行是否设置的断点了。这时我们可以在方法前添加 @SuppressWarnings("unused") 去除这些“感叹号”。 再比如: 这时我们可以在方法前添加 @SuppressWarnings("resource") 去除这些“感叹号”。 二、 @SuppressWarings注解 作用:用于抑制编译器产生警告信息。 示例1——抑制单类型的警告: @SuppressWarnings("unchecked") public void addItems(String item){ @SuppressWarnings("rawtypes") List items = new ArrayList(); items.add(item); } 示例2——抑制多类型的警告: @SuppressWarnings(value={"unchecked", "rawtypes"}) public void addItems(String item){ List items = new ArrayList(); items.add(item); } 示例3——抑制所有类型的警告: @SuppressWarnings("all") public void addItems(String item){ List items = new ArrayList(); items.add(item); } 三、注解目标 通过 @SuppressWarnings 的源码可知,其注解目标为类、字段、函数、函数入参、构造函数和函数的局部变量。 而专家建议注解应声明在最接近警告发生的位置。 四、抑制警告的关键字 It depends on your IDE or compiler. Here is a list for Eclipse Galileo: all to suppress all warnings boxing to suppress warnings relative to boxing/unboxing operations cast to suppress warnings relative to cast operations dep-ann to suppress warnings relative to deprecated annotation deprecation to suppress warnings relative to deprecation fallthrough to suppress warnings relative to missing breaks in switch statements finally to suppress warnings relative to finally block that don’t return hiding to suppress warnings relative to locals that hide variable incomplete-switch to suppress warnings relative to missing entries in a switch statement (enum case) nls to suppress warnings relative to non-nls string literals null to suppress warnings relative to null analysis restriction to suppress warnings relative to usage of discouraged or forbidden references serial to suppress warnings relative to missing serialVersionUID field for a serializable class static-access to suppress warnings relative to incorrect static access synthetic-access to suppress warnings relative to unoptimized access from inner classes unchecked to suppress warnings relative to unchecked operations unqualified-field-access to suppress warnings relative to field access unqualified unused to suppress warnings relative to unused code List for Indigo adds: javadoc to suppress warnings relative to javadoc warnings rawtypes to suppress warnings relative to usage of raw types static-method to suppress warnings relative to methods that could be declared as static super to suppress warnings relative to overriding a method without super invocations List for Juno adds: resource to suppress warnings relative to usage of resources of type Closeable sync-override to suppress warnings because of missing synchronize when overriding a synchronized method 部分翻译如下: @SuppressWarnings(“unchecked”) // 抑制未检查的转化,例如集合没有指定类型的警告 @SuppressWarnings(“unused”) // 抑制未使用的变量的警告 @SuppressWarnings(“resource”) // 抑制与使用Closeable类型资源相关的警告 @SuppressWarnings(“path”) // 抑制在类路径,原文件路径中有不存在的路径的警告 @SuppressWarnings("deprecation") // 抑制使用了某些不赞成使用的类和方法的警告 @SuppressWarnings("fallthrough") // 抑制switch语句执行到底没有break关键字的警告 @SuppressWarnings("serial") // 抑制某类实现Serializable,但是没有定义serialVersionUID,这个需要但是不必须的字段的警告 @SuppressWarnings("rawtypes") // 抑制没有传递带有泛型的参数的警告 @SuppressWarnings("all") // 抑制全部类型的警告 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
PageContext:pageConext 存放的数据仅在当前页面有效。开发时使用较少。当前页面存放数据用表单标签中的 <input type="hidden" />,且该存值方式用户看不到。 ServletRequest: request 存放的数据在一次请求(转发:可以传数据)内有效。使用非常多。 HttpSession: session 存放的数据在一次会话(多次请求)中有效。使用的比较多。例如:存放用户的登录信息、购物车功能。 ServletContext: application 存放的数据在整个应用范围内都有效。因为范围太大,应尽量少用。用于统计在线人数。 到此为止,web开发接触到了4个域对象,这4个域对象是学习web的重点,也是笔试经常考察的知识点。 pageContext(称之为page域) request(称之为request域) session(称之为session域) servletContext(称之为application域) 明确如下问题: 什么是域?答:作用范围。 这4个对象的生命周期?答:存活时间。 哪种情况下用哪种域对象? 1、request:如果客户向服务器发请求,产生的数据,用户看完就没用了,像这样的数据就存在request域,比如:新闻数据,属于用户看完就没用的。 2、session:如果客户向服务器发请求,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在session域中,比如:购物数据,用户需要看到自己购物信息,并且等一会儿,还要用这个购物数据结帐。 3、servletContext:如果客户向服务器发请求,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在servletContext域中,比如:聊天数据。我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、JavaWeb开发模式 C/S:客户端 / 服务器 (胖客户端)比如:LOL、CS、魔兽世界。.exe安装文件。 B/S:浏览器 / 服务器(瘦客户端)比如:页游。网页版软件。 JavaBean: 其实就是一个普通类(实体bean:用作封装数据),包含三样标准:一个无参构造方法、私有属性、公共的getter和setter方法。 还有一个业务bean:用于封装业务,比如:登录、注册等。 1、Model1模式(了解即可,早已过时) JSP + JavaBean 在网页.jsp代码中通过jsp:useBean、jsp:setProperty、jsp:getProperty,相当于new出来一个类,即jsp代码中嵌入实体类(java代码),不合适实际开发! 因为这样的话,java代码就太多了,喧宾夺主了!但是,这种方式适合教学用。 一般new一个类(实体bean类),是纯的java代码,就放在专门放java代码的项目的src里面比较好,比如:XxxServlet.java中。 2、Model2模式(模拟MVC) JSP + Servlet + JavaBean 把JavaBean放到3个域对象中。 MVC:开发模式 M:Model模型 相当于 JavaBean(即四种作用域) V:view视图 相当于 JSP C:Controller控制器 相当于 Servlet 分层思想:强内聚、弱耦合。 二、案例:用户的注册和登陆 三、开发步骤 开发时注意事项: 约定优于编码。列名、字段名、表单中的属性名要一致。 1、创建数据库及表 2、开发web应用 a、搭建开发环境:添加jar包,按顺序创建包结构。 b、创建实体类(javaBean),用于封装数据。 c、dao层(数据访问层) 接口 实现类 先添加工具类:DBUtils和数据库配置文件。 以后开发中,dao中的代码,有异常的话就全部往外抛,全部抛到service里面的实现类来处理。 因为service属于业务逻辑层,service里面有日志记录,会把异常写入到日志。 所以业务层中的异常就不要向外抛了,在service里面的实现类来处理。 d、service层(业务层) 接口 实现类 e、表示层(JSP、Servlet) index.jsp log.jsp reg.jsp logServlet.jsp regServlet.jsp logoutServlet.jsp 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、jsp概述 JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。 JSP实际上就是Servlet。 JSP这门技术的最大的特点在于:写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。 jsp = html + java html:静态内容。 servlet:服务器端的小应用程序。适合编写java逻辑代码,如果编写网页内容-->太苦逼。 jsp:适合编写输出动态内容,但不适合编写java逻辑。 Servlet负责那些事情? 1. 获取表单数据 2. 处理业务逻辑 3. 分发转向 提问:为什么JSP技术也是一种动态web资源的开发技术? 答:因为JSP技术允许在页面中嵌套java代码,以产生动态数据,并且web服务器在执行jsp时,web服务器会传递web开发相关的对象给jsp, jsp通过这些对象,可以与浏览器进行交互,所以jsp当然也是一种动态web资源开发技术。 强调一个概念: 对现在的用户而言,认为通过浏览器看到的东西都是网页。 但我们程序员心里要清楚,开一个浏览器访问网页,这些网页有可能是一个html页面(即静态web资源),也有可能是一个动态web资源(即用servlet或jsp程序输出的)。 二、jsp的原理 1. IE浏览器在访问JSP页面时,Web服务器是如何调用并执行一个jsp页面的?(Servlet) 答:第一次:转译(翻译) --> 编译 --> 执行 第二次:执行 2. Web服务器在执行jsp页面时,是如何把Jsp页面中的html排版标签发送到客户端的? 答:out.print("<......>"); 或者 out.write("<......>"); 3. Jsp页面中的java代码服务器是如何执行的? 答:服务器 --> .java文件 --> .class文件 --> 结果out回客户端 4. Web服务器在调用jsp时,会给jsp提供一些什么java对象(内置对象)? 答:HttpSession、ServletConfig、ServletContent、request、response、out等等。 三、jsp的最佳实践 Servlet:控制器。重点编写java代码逻辑。(获取表单数据、处理业务逻辑、分发转向) JSP:代码显示模板。重点在于显示数据。(为什么显示数据不直接用.html文件呢?因为.jsp文件中可以插入java代码显示回显消息,简言之,jsp功能更强大) 示例: lojin.jsp doLogin.jsp(该jsp文件,先转译成java文件,注意:该jsp文件说白了其实就是纯的java代码,那么就最好不使用jsp文件了。) success.jsp 但是用户关心的是只有显示给他们的页面,你怎么实现的,跟我(用户)没有关系。所以doLogin.jsp的代码可以放到src中。 四、jsp的基本语法 1、JSP的模版元素:简言之就是网页的静态内容 例如:html标签和普通文本。 2、JSP的脚本 2.1、小脚本 <% java代码 %> 2.1、表达式 <%= 2 + 3 %> 等价于out.print(2 + 3); 2.3、声明 <%! %> 表示在类中定义全局成员和静态块。 3、JSP注释 JSP注释:<%-- 被注释的内容 --%> 特点:安全,省流量 网页注释:<!-- 网页注释 --> 特点:不安全,费流量 4、JSP的3个指令 5、JSP的6个动作 6、JSP的9个内置对象 五、jsp的3个指令 JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。 在JSP 2.0规范中共定义了三个指令: page指令(页面级别) include指令(导入其它页面的信息) taglib指令(标签指令) 例如: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8" contentType="text/html; UTF-8"%> 语法: <%@ 指令名称 属性1="属性值1" 属性2="属性值2 ...... %> 或者: <%@ 指令名称 属性1="属性值1" %> <%@ 指令名称 属性2="属性值2" %> 示例: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %> 或者: <%@ page language="java" %> <%@ page import="java.util.*" %> 1、page 作用:page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面。 为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。 属性: 1.1、import和java代码中的import是一样的。 <%@ page import="java.util.Date, java.util.List" %> 或者: <%@ page import="java.util.Date" %> <%@ page import="java.util.List" %> JSP会自动导入(默认导入)以下的包: import java.lang.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; 1.2、session: 表示是否会自动创建session对象。其默认值是true。 1.3、buffer: JSP中有javax.servlet.jsp.JspWriter输出字符流。设置输出数据的缓存大小,默认大小是8kb。建议最好不要改大小。 1.4、errorPage: 如果页面中有错误,则跳转到指定的资源。(即不让页面弹出404/500等错误) errorPage="/uri" 如果写"/"则代表当前应用的目录下,绝对路径。 如果不写"/"则代表相对路径。 1.5、isErrorPage: 表示是否创建throwable对象。其默认值是false。作用:输出页面错误信息:by zero 1.6、contextType: contentType="text/html; charset=UTF-8" 告诉浏览器使用什么编码来解析文本。等同于 response.setContextType("text/html; charset=utf-8"); 1.7、pageEncoding: 告诉JSP引擎(服务器)要转译(翻译)的文件使用的编码。 例如:<%@ page pageEncoding="gbk" %> 1.8、isELIgnored: 是否支持EL表达式。 默认是false,表示支持EL表达式。 例如:${1+1} 等价于out.print(1+1); pageEncoding说明图: 2、include include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。 静态包含:把其它资源包含到当前页面中。 <%@ include file="/include/header.jsp" %> 动态包含: <jsp:include page="/include/header.jsp"></jsp:include> 两者的区别:转译(翻译)的时间段不同 前者:在翻译时就把两个文件合并 后者:不会合并文件,当代码执行到include时,才包含另一个文件的内容。 使用原则:能用静的就不用动的。原因之一是:可以省略一些代码的书写。 3、taglib 作用:在JSP页面中导入JSTL标签库。替换jsp中的java代码片段。 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> prefix:前缀(相当于重新给uri起个名字) 示例: <% if (5 > 3) { out.print(5); } %> <%-- 上下二种方式是等价的 --%> <c:if test="${5>3}"> aaa </c:if> 下面的这种方式很好用!后面慢慢体会。 六、jsp的6个动作 使用标签的形式来表示一段java代码。 如: <jsp:include page="2.jsp"></jsp:include> <jsp:include > 动态包含 <jsp:forward> 请求转发 <jsp:param> 设置请求转发的参数 <jsp:useBean> 创建一个对象 <jsp:setProperty> 给指定的对象属性赋值 <jsp:getProperty> 取出指定对象的属性值 示例: 七、jsp的9个内置对象 指在JSP的<%=%> 和<% %>中可以直接使用的对象,服务器给我们创建好的对象,直接拿过来用就行了。 例如: <%=request.getParameter("name") %> <% Student stu = new Student(); stu.setName("tom"); out.print(stu.getName()); // request.getRequestDispatcher("/7.jsp").forward(request, response); %> pageContext(重要):做自定义标签的时候会用到它,该域对象用的比较少,因为该域对象只在当前页面有效。 1、本身也是一个域对象:它可以操作其它三个域对象(request、session、application)的数据 void setAttribute(String name, Object value); Object getAttribute(String name); void removeAttribute(String name); 操作其它域对象的方法 void setAttribute(String name, Object value, int scope); Object getAttribute(String name, int scope); void removeAttribute(String name, int scope); scpoe(域)的值: PageContext.PAGE_SCOPE PageContext.REQUEST_SCOPE PageContext.SESSION_SCOPE PageContext.APPLICATION_SCOPE findAttribute(String name); // 自动从page、request、session、application依次查找,找到了就取值,然后结束查找。(很牛的方法,要记住) 2、它可以创建其它的8个隐式对象 在普通类中可以通过PageContext获取其他JSP隐式对象。自定义标签时使用。 3、提供了转发和包含的简易方法 pageContext.forward("2.jsp"); // 请求转发 pageContext.include("2.jsp"); // 包含 八、四大域对象:实际开发如何用? PageContext:pageConext 存放的数据仅在当前页面有效。开发时使用较少。当前页面存放数据用表单标签中的 <input type="hidden" />,且该存值方式用户看不到。 ServletRequest: request 存放的数据在一次请求(转发:可以传数据)内有效。使用非常多。 HttpSession: session 存放的数据在一次会话(多次请求)中有效。使用的比较多。例如:存放用户的登录信息、购物车功能。 ServletContext: application 存放的数据在整个应用范围内都有效。因为范围太大,应尽量少用。用于统计在线人数。 九、EL表达式 1、EL的概述和基本语法 EL表达式:expression language 表达式语言 作用:要简化jsp中java代码开发。 特点:具有很强的容错机制。 它不是一种开发语言,而是jsp中获取数据的一种规范。或者说,它把java底层代码进行了封装。 2、EL的具体功能 a、获取数据 EL表达式只能获取存在4个作用域中的数据。 ${ s } 原理: ${ s } 等价于 pageContext.findAttribute("s"); 注意1:${ s }取不到表单数据,若想取到表单数据,需要 ${ param.s } 这样才可以取到。 注意2:url?name=tom&pwd=123 这种方式就是get提交的表单方式,所以需要 ${ param.name } 才能取到值。 EL获取对于null这样的数据,在页面中表现为空字符串(""),不会报错,说明EL具有很强的容错机制。 ${ s.name } 等价于 s.getName()方法 点(.) 运算符相当于调用了getter方法,点后页面跟的是属性名。 属性导航:非常好用。以后学习框架Hibernate的时候,一个对象中包含其它对象。 []运算符:点能做的,它也能做; 它能做的,点不一定能做。 ${ s.name } == ${ s['name'] } == ${ s["name"] } b、运算 empty 判断null、空字符串、没有元素的集合(即使集合对象本身不为null)都返回true。 三元运算符 c、隐式对象:11个 十、JSTL 1、什么是JSTL JSTL(JavaServerPages Standard Tag Library)JSP标准标签库。 作用是:简化java代码。 EL表达式${...} 只能处理简单java代码,复杂的逻辑java代码就不行了!此时就用JSTL。 2、JSTL的作用 使用JSTL实现JSP页面中逻辑处理。如判断、循环等。 注意:在JSTL中想取值,必须加${值}。 3、使用JSTL 1)在JSP页面中添加taglib指令(prefix:前缀(相当于重新给uri起个名字)) 2)使用JSTL标签 4、常用标签介绍 核心标签库: > 通用标签:set、 out、 remove(了解就行,开发时根本不用个!) > 条件标签:if choose > 迭带标签:forEach 普通for循环 迭带器 注意: <tr></tr> 代表行 <td></td> 代表单元格 <th></th> 相当于<td>,只是内置样式加粗居中 或者使用css选择器中的类选择器的方式改变样式,如下: c:forEach中的varStatus属性。 该属性指向一个字符串,该字符串引用一个对象。map.put("vs", 一个对象); 这个对象记录着当前遍历的元素的一些信息: getIndex() 返回索引,从0开始。 getCount() 返回计数,从1开始。 isFirst() 是否是第一个元素。 isLast() 是否是最后一个元素。我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
一、会话概述 1、什么是会话?如同打电话。 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。 2、会话过程要解决的问题是什么?保持各个客户端自己的数据。 每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。 例如:用户点击超链接通过一个servlet购买了一个商品,程序应该想办法保存用户购买的商品,以便于用户点结帐servlet时,结帐servlet可以得到用户购买的商品为用户结帐。 思考:用户购买的商品保存在request或servletContext中行不行?答:不行。 保存会话数据的两种技术 : Cookie:是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。 当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。 这样,web资源处理的就是用户各自的数据了。 HttpSession:Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的HttpSession对象, 由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中, 当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。 二、Cookie(饼干/小甜饼) 由于Cookie数据是由客户端来保存和携带的,所以称之为客户端技术。 javax.servlet.http.Cookie类用于创建一个Cookie,response接口中定义了一个addCookie方法,它用于在其响应消息头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。 1、属性: name:该名称不能唯一确定一个Cookie。路径可能不同。 value:不能存中文。 path:默认值是写Cookie的那个程序的访问路径。 比如:http://localhost:8080/day10_00_cookie/servlet/ck1写的Cookie path就是:/day10_00_cookie/servlet 即看当前创建cookie的资源(servlet)文件路径。 客户端在访问服务器另外资源时,根据访问的路径来决定是否带着Cookie到服务器。 如果当前访问的路径是以cookie的path开头的路径(包括在该路径下的子路径),浏览器就带Cookie。否则不带Cookie。 maxAge:cookie的缓存时间。默认是-1,-1指示该cookie将保留到浏览器关闭为止。(默认存在浏览器的缓存中)。单位是秒。 负数:表示cookie的数据存在浏览器缓存中。 0:表示删除。注意:路径要保持一致,否则可能删错人。 正数:表示缓存(持久化到磁盘上)的时间。单位是秒。 2、方法: public Cookie(String name, String value) 构造方法(只有一个) setValue与getValue方法 setMaxAge与getMaxAge方法(单位为秒) setPath与getPath方法 setDomain与getDomain方法 getName方法 3、补充知识点: 面试题:Servlet负责那些事情? 1. 获取表单数据 2. 处理业务逻辑 3. 分发转向 案例: 1. 客户端记住用户名 2. 显示用户上次浏览过的商品 三、HttpSession(会话) 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。 因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。 Session和Cookie的主要区别在于: Cookie是把用户的数据写给用户的浏览器。 Session技术把用户的数据写到用户独占的session中。 Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。 1、为什么要学HttpSession? > 它也是一个域对象: servletContext(不同浏览器) > session(同一浏览器的多个会话) > request(一个会话) > 同一个会话下,可以使一个应用的多个资源共享数据。 > cookie是客户端技术,只能存字符串,不安全,存储少量信息,不适合存储敏感信息。HttpSession是服务器端的技术,它可以存对象。 2、常用方法 把数据保存在HttpSession对象中,该对象也是一个域对象。 void setAttribute(String name, Object value) 使用指定名称将对象绑定到此会话。如果具有同样名称的对象已经绑定到该会话,则替换该对象。 Object getAttribute(String name) 返回与此会话中的指定名称绑定在一起的对象,如果没有对象绑定在该名称下,则返回 null。 void removeAttribute(String name) 从此会话中移除与指定名称绑定在一起的对象。如果会话没有与指定名称绑定在一起的对象,则此方法不执行任何操作。 String getId() 返回包含分配给此会话的唯一标识符的字符串。标识符是由 servlet 容器分配的,并且是与实现相关的。 setMaxInactiveInterval(int interval) 设置session的存活时间(默认存活时间是30分钟) void invalidate() 使此会话无效(退出网站时调用) 3、getSession():内部执行原理 HttpSession request.getSession():内部执行原理 1、获取名称为JSESSIONID的Cookie的值。例如:Cookie: JSESSIONID=070BB766FAB03E03DBF28F8040CA616F 2、如果没有这样的Cookie,服务器则创建一个新的HttpSession对象,并分配一个唯一的SessionID,并且向客户端写了一个名字为JSESSIONID=sessionID的Cookie。 3、如果有这样的Cookie,服务器则获取Cookie的值(即HttpSession对象的值),从服务器的内存中根据ID找那个HttpSession对象: 找到了:取出继续为你服务。 找不到:从2开始,创建一个新的HttpSession对象。 HttpSession request.getSession(boolean create):返回与此请求关联的当前 HttpSession,如果没有当前会话并且 create 为 true,则返回一个新会话。 参数: true:和getSession()功能一样。 false:根据客户端JSESSIONID的Cookie的值,找对应的HttpSession对象,找不到返回null(但不会创建新的,只是查询)。 4、客户端禁用Cookie后的会话数据保存问题 客户端禁用Cookie:浏览器永远不会向服务器发送Cookie的请求消息头。 解决方案: 方案一:在主页上给出提示:请不要禁用您的Cookie。 方案二:URL重写。必须对网站的所有地址都重写。 http://url ---> http://url;JSESSIONID=070BB766FAB03E03DBF28F8040CA616F response.encodeRedirectURL(String url); 用于对sendRedirect方法后的url地址进行重写。 response.encodeURL(String url); 看浏览器有没有发送Cookie请求消息头,没有就重写URL,有就不重写。 用于对表单action和超链接的url地址进行重写。 request.getSession(); // 必须写,虽然没有接收 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
============================================================ 一、HttpServletResponse接口 学好HttpServletResponse与HttpServletRequest的关键是:理解HTTP协议。 Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。 (注意:request和response分别表示的是对象名,实际上不是对象) request和response对象既然代表请求和响应, 那我们要获取客户端提交过来的数据,只需要找request对象就行了。 要向容器(服务器)输出数据,只需要找response对象就行了。 因为每一的请求都会调用一次service(request, response)方法。 方法的形参request和response是由服务器创建的,在service执行之前就已经创建好了,分别是两个对象(是不需要new的,服务器帮你干了)。 1、响应消息行 HTTP/1.1 200 OK HTTP/1.1 200 OK 协议/版本 响应状态码 对响应码的描述(一切正常) HttpServletResponse接口的方法: void setStatus(int sc) 设置响应状态码 response.setStatus(302); response.setHeader("location", "url"); // 上面两句代码等价于下面一句 response.sendRedirect("location"); Location:http://www.it315.org/index.jsp 指示新的资源的位置通常和302/307一起使用,完成请求重定向 请求转发和请求重定向图解: 2、响应消息头 HttpServletResponse接口的方法: void sendRedirect(String location) 请求重定向 void setHeader(String name, String value) 设置响应消息头信息 // 设置客户端(浏览器)使用什么编码(通过响应消息头设置) response.setHeader("content-type", "text/html; charset=UTF-8"); // 告知客户端不缓存 response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); response.setDateHeader("expires", 0); Referesh刷新 3、响应正文(主体) ServletResponse接口的方法: PrintWriter getWriter() 获取字符输出流 ServletOutputStream getOutputStream() 获取字节输出流 void setCharacterEncoding(String charset) 设置服务器使用什么编码 String setContentType(String type) 设置将发送到客户端的响应的内容类型(同时设置客户端和服务端要使用的编码) 例如:response.setContentType("text/html; charset=UTF-8"); URLEncoder是HTML格式编码的实用工具类,该类包含了将 String 转换为 application/x-www-form-urlencoded MIME 格式的静态方法。 案例1:向客户端输出中文数据:用PrintWriter(字符流)发送数据 案例2:向客户端输出中文数据:用OutputStream(字节流)发送数据 案例3:客户端下载文件 案例4:自己编写代码实现客户端网页显示验证码 案例5:通过验证码工具类实现客户端网页显示验证码 案例6:实现定时刷新 4、response的几处细节 1. getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。 2. getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。否则会抛异常。 3. Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取, Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。 4. Serlvet的service方法结束后,Servlet引擎将检查getOutputStream或getWriter方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。 (注:这里的Servlet引擎就是Tomcat服务器) ============================================================ 二、HttpServletRequest 1、请求消息行 Get http://localhost:8080/day09_01_HttpServletRequest/servlet/req1?username=zs&pwd=123 HTTP/1.1 HttpServletRequest接口的方法: String getMethod() 获取请求行的请求方式 String getRequestURL() 返回客户端发出请求时的完整URL String getRequestURI() 返回请求消息行中的资源名部分(8080之后所有的) String getContextPath() 获取当前应用的虚拟目录 例如:/day09_01_HttpServletRequest String getQueryString() 返回请求消息行中的参数部分 2、请求消息头 HttpServletRequest接口的方法: String getHeader(String name) 根据头名称得到头信息值 Enumeration getHeaderNames() 获取所有请求消息头的name Enumeration getHeaders(String name) 获取相同请求消息头名称的信息值 1 package com.itheima.servlet; 2 3 import java.beans.IntrospectionException; 4 import java.beans.PropertyDescriptor; 5 import java.io.IOException; 6 import java.io.UnsupportedEncodingException; 7 import java.lang.reflect.InvocationTargetException; 8 import java.lang.reflect.Method; 9 import java.util.Enumeration; 10 import java.util.Map; 11 12 import javax.servlet.ServletException; 13 import javax.servlet.http.HttpServlet; 14 import javax.servlet.http.HttpServletRequest; 15 import javax.servlet.http.HttpServletResponse; 16 17 import org.apache.commons.beanutils.BeanUtils; 18 19 import com.itheima.entity.User; 20 21 public class ServletDemo3 extends HttpServlet { 22 23 public void doGet(HttpServletRequest request, HttpServletResponse response) 24 throws ServletException, IOException { 25 // 告诉服务器要使用什么码表,注:客户端浏览器使用的是什么码表,传过来给服务器的就该使用什么码表 26 request.setCharacterEncoding("UTF-8"); // 注:只能处理post请求方式 27 28 test1(request); 29 test2(request); 30 test3(request); 31 test4(request); 32 } 33 34 private void test4(HttpServletRequest request) { 35 try { 36 User u = new User(); 37 System.out.println("封装数据前:" + u); 38 39 BeanUtils.populate(u, request.getParameterMap()); 40 41 System.out.println("封装数据后:" + u); 42 } catch (Exception e) { 43 e.printStackTrace(); 44 } 45 } 46 47 private void test3(HttpServletRequest request) { 48 try { 49 User u = new User(); 50 System.out.println("封装数据前:" + u); 51 // 获取表单数据 52 Map<String, String[]> map = request.getParameterMap(); 53 54 for (Map.Entry<String, String[]> m : map.entrySet()) { 55 String name = m.getKey(); 56 String[] value = m.getValue(); 57 58 // 反射 59 // 创建一个属性描述器对象 60 PropertyDescriptor pd = new PropertyDescriptor(name, User.class); 61 // 得到setter属性对象 62 Method setter = pd.getWriteMethod(); 63 64 if (value.length == 1) { 65 setter.invoke(u, value[0]); // 给只有一个值的变量赋值 66 } else { 67 setter.invoke(u, (Object) value); // 给复选框赋值 68 } 69 } 70 71 System.out.println("封装数据后:" + u); 72 73 } catch (Exception e) { 74 e.printStackTrace(); 75 } 76 } 77 78 private void test2(HttpServletRequest request) { 79 // 获取表单提交的所有的name的名称 80 Enumeration names = request.getParameterNames(); 81 while (names.hasMoreElements()) { 82 String name = (String) names.nextElement(); // 得到每一个name名称 83 String[] values = request.getParameterValues(name); // 根据name名称,得到value值 84 for (int i = 0; values != null && i < values.length; i++) { 85 System.out.println(name + "\t" + values[i]); 86 } 87 } 88 } 89 90 private void test1(HttpServletRequest request) throws UnsupportedEncodingException { 91 // 获取表单数据 92 // 根据表单中name属性的名,获取value属性的值方法 93 String username = request.getParameter("username"); 94 String pwd = request.getParameter("pwd"); 95 String sex = request.getParameter("sex"); 96 String[] hobbys = request.getParameterValues("hobby"); 97 String city = request.getParameter("city"); 98 99 // 解决get方式编码 100 username = new String(username.getBytes("iso-8859-1"), "UTF-8"); 101 102 System.out.println(username); 103 System.out.println(pwd); 104 System.out.println(sex); 105 106 for (int i = 0; hobbys != null && i < hobbys.length; i++) { 107 System.out.print(hobbys[i] + "\t"); 108 } 109 System.out.println(); 110 111 System.out.println(city); 112 } 113 114 public void doPost(HttpServletRequest request, HttpServletResponse response) 115 throws ServletException, IOException { 116 doGet(request, response); 117 } 118 119 } 3、请求正文(重要) 与获取表单数据相关的方法: <input type="text" name="username" /> ServletRequest接口的方法: String getParameter(String name) 根据表单中name属性的名,获取value属性的值 String[] getParameterValues(String name) 专为复选框获取值提供的方法 Enumeration getParameterNames() 获取表单提交的所有的name名 Map getParameterMap() 获取表单提交的所有value值 (做框架时用,非常实用) ServletInputStream getInputStream 以字节流的方式得到所有表单数据 与操作非表单数据相关的方法:(request也是一个域对象,也维护了一个Map集合,只是作用的范围较小,在当前请求范围内有效) ServletRequest接口的方法: void setAttribute(String name, Object value) Object getAttribute(String name) void removeAttribute(String name) 与请求转发相关的方法: ServletRequest接口的方法: // 得到请求转发或请求包含的协助对象 RequestDispatcher getRequestDispatcher(String path) RequestDispatcher接口的方法: void forward(ServletRequest request, ServletResponse response) // 转发的方法 void include(ServletRequest request, ServletResponse response) // 请求包含 与请求编码相关的方法: ServletRequest接口的方法: // 解决post提交方式的编码 request.setCharacterEncoding("UTF-8"); // 告诉服务器要使用什么码表,注:只能处理post请求方式 // 解决get提交方式的编码(需要一个一个变量的转换,麻烦,用的不多,一般用在地址栏上明文写的链接上,因为明文是get方式提交) String name = new String(name.getBytes("iso-8859-1"), "UTF-8"); 注:一般而言,默认是get提交方式,所以我们在写表单的html代码时,要立即把表单的属性method="post" 最好。 request与response对象解决乱码的应用: ============================================================ 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
你是否好奇标识浏览器身份的User-Agent,为什么每个浏览器都有Mozilla字样? Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36 Mozilla/5.0 (Linux; U; Android 4.1.2; zh-tw; GT-I9300 Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0 Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0) 故事还得从头说起,最初的主角叫NCSA Mosaic,简称Mosaic(马赛克), 是1992年末位于伊利诺伊大学厄巴纳-香槟分校的国家超级计算机应用中心(National Center for Supercomputing Applications,简称NCSA)开发,并于1993年发布的一款浏览器。 它自称“NCSA_Mosaic/2.0(Windows 3.1)”,Mosaic可以同时展示文字和图片,从此浏览器变得有趣多了。 然而很快就出现了另一个浏览器,这就是著名的Mozilla,中文名称摩斯拉。 一说 Mozilla = Mosaic + Killer,意为Mosaic杀手,也有说法是 Mozilla = Mosaic & Godzilla,意为马赛克和哥斯拉。 而Mozilla最初的吉祥物是只绿色大蜥蜴,后来更改为红色暴龙,跟哥斯拉长得一样。 但Mosaic对此非常不高兴,于是后来Mozilla更名为Netscape,也就是网景。 Netscape自称“Mozilla/1.0(Win3.1)”,事情开始变得更加有趣。 网景支持框架(frame),由于大家的喜欢框架变得流行起来,但是Mosaic不支持框架。 于是网站管理员探测user agent,对Mozilla浏览器发送含有框架的页面,对非Mozilla浏览器发送没有框架的页面。 后来网景拿微软寻开心,称微软的Windows是“没有调试过的硬件驱动程序”。 微软很生气,后果很严重。此后微软开发了自己的浏览器,这就是Internet Explorer,并希望它可以成为Netscape Killer。 IE同样支持框架,但它不是Mozilla,所以它总是收不到含有框架的页面。 微软很郁闷很快就沉不住气了,它不想等到所有的网站管理员都了解IE并且给IE发送含有框架的页面,它选择宣布IE是兼容Mozilla, 并且模仿Netscape称IE为“Mozilla/1.22(compatible; MSIE 2.0; Windows 95)”,于是IE可以收到含有框架的页面了,所有微软的人都嗨皮了,但是网站管理员开始晕了。 因为微软将IE和Windows捆绑销售,并且把IE做得比Netscape更好,于是第一次浏览器血腥大战爆发了,结果是Netscape以失败退出历史舞台,微软更加嗨皮。 但没想到Netscape居然以Mozilla的名义重生了,并且开发了Gecko,这次它自称为: “Mozilla/5.0(Windows; U; Windows NT 5.0; en-US; rv:1.1) Gecko/20020826”。 Gecko是一款渲染引擎并且很出色。Mozilla后来变成了Firefox,并自称: “Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.7.5) Gecko/20041108 Firefox/1.0”。 Firefox性能很出色,Gecko也开始攻城略地,其他新的浏览器使用了它的代码, 并且将它们自己称为: “Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7.2) Gecko/20040825 Camino/0.8.1”,以及 “Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.8) Gecko/20071008 SeaMonkey/1.0”, 每一个都将自己装作Mozilla,而它们全都使用Gecko。 Gecko很出色,而IE完全跟不上它,因此user agent探测规则变了,使用Gecko的浏览器被发送了更好的代码,而其他浏览器则没有这种待遇。 Linux的追随者对此很难过,因为他们编写了Konqueror,它的引擎是KHTML,他们认为KHTML和Gecko一样出色,但却因为不是Gecko而得不到好的页面, 于是Konqueror为得到更好的页面开始将自己伪装成“like Gecko”,并自称为“Mozilla/5.0 (compatible; Konqueror/3.2; FreeBSD) (KHTML, like Gecko)”。自此user agent变得更加混乱。 这时更有Opera跳出来说“毫无疑问,我们应该让用户来决定他们想让我们伪装成哪个浏览器”。 于是Opera干脆创建了菜单项让用户自主选择让Opera浏览器变成 “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.51”,或者 “Mozilla/5.0 (Windows NT 6.0; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.51”,或者 “Opera/9.51 (Windows NT 5.1; U; en)”。 后来苹果开发了Safari浏览器,并使用KHTML作为渲染引擎,但苹果加入了许多新的特性,于是苹果从KHTML另辟分支称之为WebKit,但它又不想抛弃那些为KHTML编写的页面, 于是Safari自称为“Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5”,这进一步加剧了user agent的混乱局面。 因为微软十分忌惮Firefox,于是IE重装上阵,这次它自称为“Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ”,并且渲染效果同样出色,但是需要网站管理员的指令它这么做才行。 再后来,谷歌开发了Chrome浏览器,Chrome使用Webkit作为渲染引擎,和Safari之前一样,它想要那些为Safari编写的页面,于是它伪装成了Safari。 于是Chrome使用WebKit,并将自己伪装成Safari,WebKit伪装成KHTML,KHTML伪装成Gecko,最后所有的浏览器都伪装成了Mozilla,这就是为什么所有的浏览器User-Agent里都有Mozilla。 Chrome自称为“Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13”。 因为以上这段历史,现在的User-Agent字符串变得一团糟,几乎根本无法彰显它最初的意义。 追根溯源,微软可以说是这一切的始作俑者,但后来每一个人都在试图假扮别人,最终把User-Agent搞得混乱不堪。 一句话结论:因为网站开发者可能会因为你是某浏览器(这里是 Mozilla),所以输出一些特殊功能的程序代码(这里指好的特殊功能), 所以当其它浏览器也支持这种好功能时,就试图去模仿 Mozilla 浏览器让网站输出跟 Mozilla 一样的内容,而不是输出被阉割功能的程序代码。 大家都为了让网站输出最好的内容,都试图假装自己是 Mozilla 一个已经不存在的浏览器。 附各大浏览器诞生年表: 1993年1月23日:Mosaic 1994年12月:Netscape 1994年:Opera 1995年8月16日:Internet Explorer 1996年10月14日:Kongqueror 2003年1月7日:Safari 2008年9月2日:Chrome 注:本文转自简明现代魔法。 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】
Servlet学习小结 一、Servlet:服务器端的小应用程序。 作用:处理客户端的请求和响应。 二、Servlet的生命周期 实例化 --> 初始化 --> 服务 --> 销毁 构造方法 int() service() destroy() void init(ServletConfig config); void service(ServletRequest request, ServletResponse response); void destory(); ServletConfig getServletConfig(); 三、Servlet的三种创建方式 1. 实现javax.servlet.Servlet接口 2. 继承javax.servlet.GenericServlet抽象类(适配器模式) 3. 继承javax.servlet.http.HttpServlet抽象类(模板设计模式) 四、Servlet的线程安全 不要使用全局变量,要使用局部变量。 五、ServletConfig对象的作用 1. 可以获取Servlet配置文件的信息。 2. 可以获取ServletContext对象。 六、与Servlet相关的对象 1. Servlet接口-------------->javax.servlet.Servlet; 2. GenericServlet抽象类----->javax.servlet.GenericServlet; 3. HttpServlet抽象类-------->javax.servlet.http.HttpServlet; 4. ServletConfig接口-------->javax.servlet.ServletConfig; 5. ServletContext接口------->javax.servlet.ServletContext; 6. ServletRequest接口------->javax.servlet.ServletRequest; 7. ServletResponse接口------>javax.servlet.ServletResponse; 8. HttpServletRequest接口--->javax.servlet.http.HttpServletRequest; 9. HttpServletResponse接口-->javax.servlet.http.HttpServletResponse; 10.RequestDispatcher接口---->javax.servlet.RequestDispatcher; 我的GitHub地址:https://github.com/heizemingjun 我的博客园地址:http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军【转载文章务必保留出处和署名,谢谢!】