习题一
python2.7
1 | # -*- coding: utf-8 -*- |
python3.7
1 | # -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*-
这行用在脚本开头可以设定脚本编码格式
Tips:python 2.7与python 3.7的区别就是后者更加严格规范,必须带上括号!
python中使用(#)符号单行注释
为BUG而生
1 | # -*- coding: utf-8 -*- |
1 | # -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*-
这行用在脚本开头可以设定脚本编码格式
Tips:python 2.7与python 3.7的区别就是后者更加严格规范,必须带上括号!
python中使用(#)符号单行注释
dagger2 官方地址:http://google.github.io/dagger/
Dagger 是为 Java 和 Android 设计的静态编译期注入框架。是为了替换由 Square 公司开发的早期版本 dagger1, 目前由谷歌维护。
Dagger 旨在解决之前基于反射的解决方案所带来的开发和性能问题。更多细节可查看由+Gregory Kick 推出的视频 DAGGER 2 - A New Type of dependency injection。
一个项目中最重要的类有这些, 比如 BarcodeDecoder, KoopaPhysicsEngine, AudioStreamer 等等。 这些类也许会需求这些依赖, 如一个 BarcodeCameraFinder, DefaultPhysicsEngine 和 HttpStreamer。
相比而言, 这些类就显得占了位置却不起什么作用, 如 BarcodeDecoderFactory, CameraServiceLoader, MutableContextWrapper 等等。 这些类就如同拙劣的粘着剂将重要的类联系在一起。
Dagger就是为了解决依赖注入模式中, 编写这些没有实际作用的工厂类模板文件的负担。 它使你把精力投入到更有意义的编码工作中, 例如声明依赖, 提供依赖, 然后走起你的 app ~
基于标准的 javax。inject (JSR 330)注解库, 每个类将会很容易测试。 你不再需要仅仅为了替换一个业务 Service ,而写一堆没用的模板文件。
Dagger 构造你的应用程序的类对象并自动注入它们的依赖。 它使用 javax.inject.Inject 下的注解, 来标记哪些构造器或者全局变量需要 Dagger 注意。
使用 @Inject 来标记一个构造器, 表示 Dagger 指定此构造器来构造相应类的实例。 当某处需要这个类的实例, Dagger 将自动调用此构造器构造实例, 并注入到对应位置。 如果构造器有参数要求, Dagger 也会查找需要的参数实例注入进去。
1 | // 热虹吸抽水器 |
使用 @Inject 直接标记一个全局变量, 在下面的示例中, Dagger 自动查找 Heater 和 Pump 的实例并注入
1 | class CoffeeMaker { |
如果某个类只有全局变量被标记了 @Inject, 但构造器没有被标记, 那么 Dagger 会注入这些全局变量, 但不再构造该类的实例(?)。 使用 @Inject 来标记一个无参构造器来表明 Dagger 也会构造它的实例。
Dagger 也支持方法注入, 但是往往偏爱构造器或全局变量的注入。
没有 @Inject 注解的类, 将不会被 Dagger 创建。
默认的, 如之前所述, Dagger 通过构造相应的类型来满足每个标注的依赖。 比如当你需要一个 CoffeeMaker 实例, Dagger 会调用 new CoffeeMaker() 并注入好它的 @Inject 标记的全局变量来获取一个 CoffeeMaker 实例。
但是 @Inject 并非万能:
在以下这些场景中, 使用 @Inject 是多余且别扭的, 应该使用 @Provides 来标注一个方法来满足对应的依赖, 这个方法的返回值标注了它提供的类型, 也就是它可以响应哪一种类型的依赖请求。
如下面这个案例, 当请求一个 Heater 对象时, provideHeater() 方法会被调用
1 | @Provides static Heater provideHeater() { |
@Provides 注解的方法也可以有它的依赖请求:
1 | @Provides static Pump providePump(Thermosiphon pump) { |
所有 @Provides 注解的方法必须属于一个 module, 也就是一个被 @Module 标记的类:
1 | @Module |
按照惯例, @Provides 注解的方法都应该以 “provide” 为前缀, @Module 注解的类都应该以 “module” 为后缀。
这些拥有 @Inject 和 @Provides 注解的类会组成一个对象图, 通过他们的依赖连接起来。 调用代码就像 main 方法, 通过定义好的一些根节点, 访问这个对象图。 在 Dagger2 中, 这些根节点由一个拥有一些没有参数且返回需要的类型的方法构成的接口所定义。
通过 @Component 标注这样的接口, 并将一些 module 类赋值给 @Component 注解的 modules 参数, Dagger2 会依据这些接口来生成它们的实现类。
1 | @Component(modules = DripCoffeeModule。class) |
实现类的类名是接口名称加上 “Dagger” 的后缀, 调用它的 builder() 静态方法, 使用其返回的建造者实例, 设置依赖, 再调用 build() 来构建一个新的实例。
1 | CoffeeShop coffeeShop = DaggerCoffeeShop。builder() |
提示: 如果 @Component 标记的类不是顶层类(是一个内部类), 那么生成的实现类名将带上它的外部类名, 使用下划线分隔开。 例如:
1 | class Foo { |
生成的类名是 DaggerFoo_Bar_BazComponent
任何一个自带可访问默认构造器的 module 类, 会被禁用, 由其建造方法取代:
1 | CoffeeShop coffeeShop = DaggerCoffeeShop.create(); |
对于一个所有的 @Provides 的方法都是静态的 module 类, 就无需构造它的实例; 如果所有的依赖都无需由用户创建, 那么实现类依然会提供 create() 方法, 可用于获取实例
然后就可以很方便的使用 Dagger 生成的实现来获取一个完全配置并可用的实例:
1 | public class CoffeeApp { |
上面的例子演示了如何使用一些典型的绑定构造一个 component, 但还有多种机制来形成向对象图的绑定; 下面的内容可以作为依赖和生成完整格式的 component:
使用 @Singleton 注解一个 @Provides 注解的方法或可以注入的类, 对象图会对其客户端使用单例模式:
1 | @Provides @Singleton static Heater provideHeater() { |
标记在可注入的类上的 @Singleton 注解也可以作为文档注解。 提醒了维护者这个类可能共享在多个线程。
1 | @Singleton |
由于 Dagger2 关联了对象图定义了作用域的实例与 component 的实现实例, components 需要声明它们所代表的作用域。 例如在一个 component 中使用了一个单例模式的绑定, 又使用了另外某个作用域绑定, 这就没有意义了。 因为这些作用域拥有不同的生命周期并因此处在不同的拥有不同生命周期的 component。 声明一个关联了指定的作用域的 component, 只要添加一个 @Scope 注解。
1 | @Component(modules = DripCoffeeModule.class) |
有时候你需要一个类延迟实例化, 比如一个 T 类型的绑定, 你可以使用 Lazy
1 | class GridingCoffeeMaker { |
有时候你需要多个实例而不是仅仅一个值, 这时候你也许有多种选择, 例如工厂, 建造者等等。 一个选择就是注入 Provider
1 | class BigCoffeeMaker { |
提示: 注入 Provider
有时一个单独的类不足以识别一个依赖。 比如一个成熟的咖啡机 app 也许为了加热水和加热盘子, 会需要不同的 Heater 对象。
在这样的场景下, 我们添加了标识器(qualifier)注解。 任何注解都可以拥有一个 @Qualifier 注解, 下面是 @Named 注解的声明, 一个包含在 javax.inject 中的标识器注解:
1 | @Qualifier |
你可以创建自己的标识器注解, 或者也可以用 @Named。 可以注解成员变量或参数。 类型和标识器注解共同标记一个依赖:
1 | class ExpensiveCoffeeMaker { |
设置标识器的值并设置在 @Provides 注解的方法上:
1 | @Provides @Named("hot plate") static Heater provideHotPlateHeater() { |
依赖可能不能拥有多个标识器注解。
Dagger 注解处理机制是严格的, 并且当出现无效或不完整的绑定会报编译错误。 例如这个 module 装载在一个缺少 Executor 的绑定的 component :
1 | @Module |
编译期会报异常:
1 | [ERROR] COMPILATION ERROR : |
解决这个错误只要在 component 下添加一个 @Provides 注解的返回类型是 Executor 的方法。 @Inject, @Module 和 @Provides 这些注解都会独立验证, 所有绑定关系的验证都发生在 component 层级上。 Dagger 1 是依靠在 Module 层级上的严格验证, 但是 Dagger 2 为了更完整的对象图层面上的验证, 省略了这些过程。
Dagger 的注解处理机会自动生成一些命名诸如 CoffeeMaker_Factory.java 或者 CoffeeMaker_MembersInjector.java 的源代码文件, 这些文件就是 Dagger 实现的细节。 你不需要直接使用它, 尽管你很容易在注入的时候在其内部单步调试。 这些生成的类中, 唯一你要关心的是哪些组件名加上 “Dagger” 前缀的哪些类。
你需要在你的项目环境中引入 dagger-2.0.jar。 为了能够实现生成代码的功能, 还需要在编译环境下引入 dagger-compiler-2.0.jar。
在一个 Maven 项目中:
1 | <dependencies> |
Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:
包括了Bean本身调用的方法和通过配置文件中
包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法
包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”
InstantiationAwareBeanPostProcessor 接口本质是BeanPostProcessor的子接口,一般我们继承Spring为其提供的适配器类InstantiationAwareBeanPostProcessorAdapter来使用它
同beanFactory中bean的生命周期的区别在于
除了包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法,多加了ApplicationContextAware接口
包括了BeanFactoryPostProcessor或者CustomEditonConfiurer, PropertyPlaceholderConfigurer等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后调用一次。
如果配置文件中定义了多个工厂后处理器,最后让他们实现Ordered接口,以便Spring确定调用他们的顺序。
ApplicationContext和BeanFactory另一个最大的不同之处在于:
ApplicationContext会利用Java反射机制自动识别出配置文件中定义的
BeanPostProcessor,InstantiationAwareBeanPostProcessor和
BeanFactoryPostProcessor,并自动将他们注册到应用上下文中;而
BeanFactory需要在代码中通过手工调用addBeanPostProcessor()方法> 进行注册。
${expression}
EL 提供.和[]两种运算符来存取数据。
当要存取的属性名称中包含一些特殊字符,如.或?等并非字母或数字的符号,就一定要使用 []。例如:
${user.My-Name}应当改为${user["My-Name"] }
如果要动态取值时,就可以用[]来做,而.无法做到动态取值。例如:
${sessionScope.user[data]}中data 是一个变量
EL存取变量数据的方法很简单,例如:${username}。它的意思是取出某一范围中名称为username的变量。
因为我们并没有指定哪一个范围的username,所以它会依序从Page、Request、Session、Application范围查找。
假如途中找到username,就直接回传,不再继续找下去,但是假如全部的范围都没有找到时,就回传null。
属性范围在EL中的名称
Page PageScope
Request RequestScope
Session SessionScope
Application ApplicationScope
pageScope : 用于获取page范围的属性值。
requestScope : 用于获取request范围的属性值。
sessionScope : 用于获取session范围的属性值。
applicationScope : 用于获取application范围的属性值。
param : 用于获取请求的参数值。该内置对象的类型是Map<String,String>,可以用来获取值为单值的请求参数,其中key指的是请求参数的名称,value指的是请求参数的值,使用param获取请求参数与request.getParameter()方法一样。
paramValues : 用于获取请求的参数值。与param区别在于,该对象用于获取属性值为数组的属性值。该内置对象的类型Map<String,String[]>,可以用来获取值为多值的请求参数,其中key是参数名,value是多个参数值组成的字符串数组。
header : 用于获取请求头的属性值。该内置对象的类型是Map<String,String>。用法${header.key}
headerValues : 用于获取请求头的属性值。与header区别在于,该对象用于获取属性值为数组的属性值。该内置对象的类型是Map<String,String[]>。用法${headerValues.key[0…]}
initParam : 用于获取Web应用的初始化参数。
cookie : 用于获取指定的cookie值。该内置对象的类型为Map<String,Cookie>。用法${cookie.key}可以获得cookie对象本身
然后获得通过cookie.value获得cookie里面存储的value,简化方法
${cookie.key.value}。pageContext : 代表该页面的pageContext对象,与JSP的pageContext内置对象相同。用法${pageContext.request},类似pageContext.getRequest()方法,返回一个request对象。用法二${pageContext.request.contextPath},获取当前工程的名字。
当然,使用pageContext内置对象还可以获取session对象的id值,如:${pageContext.session.id}。pageContext对象可以获取jsp的其他内置对象,所以通过pageContext对象可以获取其他内置对象的任意的属性值。
此方法可参考JSP自定义标签文档中例子
步骤
1.开发函数处理类,函数处理类就是普通类,这个普通类包含若干个静态方法,每个静态方法都可以定义成一个函数。
2.使用标签库定义函数,定义函数的方法与定义标签的方法大致相似。在<taglib../>元素下增加<function../>元素用于定义自定义函数。
<function../>子元素
name:指定自定义函数的名称
funciton-class:指定自定义函数的处理类
funciton-signature:指定自定义函数对应的方法。
代码如下:
<?xml version="1.0" encoding="GBK"?>
<taglib 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
web-jsptaglibrary_2_0.xsd" version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>crazyit</short-name>
<!-- 定义该标签库的URI -->
<uri>http://www.crazyit.org/tags</uri>
<!-- 定义第一个函数 -->
<function>
<!-- 定义函数名:reverse -->
<name>reverse</name>
<!-- 定义函数的处理类 -->
<function-class>lee.Functions</function-class>
<!-- 定义函数的实现方法-->
<function-signature>
java.lang.String reverse(java.lang.String)</function-signature>
</function>
<!-- 定义第二个函数: countChar -->
<function>
<!-- 定义函数名:countChar -->
<name>countChar</name>
<!-- 定义函数的处理类 -->
<function-class>lee.Functions</function-class>
<!-- 定义函数的实现方法-->
<function-signature>int countChar(java.lang.String)
</function-signature>
</function>
</taglib>
调用代码如下:
......
<%@ taglib prefix="crazyit" uri="http://www.crazyit.org/tags"%>
......
<table border="1" bgcolor="aaaadd">
<tr>
<td><b>表达式语言</b></td>
<td><b>计算结果</b></td>
<tr>
<tr>
<td>\${param["name"]}</td>
<td>${param["name"]} </td>
</tr>
<!-- 使用reverse函数-->
<tr>
<td>\${crazyit:reverse(param["name"])}</td>
<td>${crazyit:reverse(param["name"])} </td>
</tr>
<tr>
<td>\${crazyit:reverse(crazyit:reverse(param["name"]))}</td>
<td>${crazyit:reverse(crazyit:reverse(param["name"]))} </td>
</tr>
<!-- 使用countChar函数 -->
<tr>
<td>\${crazyit:countChar(param["name"])}</td>
<td>${crazyit:countChar(param["name"])} </td>
</tr>
</table>
......
JSP自定义标签分为三步
1.开发自定义标签处理类;
2.建立一个*.tld文件,每个*.tld文件对应一个标签库,每个标签库可包含多个标签;
3.在JSP文件中使用自定义标签;
首先我们需要大致了解开发自定义标签所涉及到的接口与类的层次结构(其中SimpleTag接口与SimpleTagSupport类是JSP2.0中新引入的)。
####1.1开发自定义标签类
自定义标签类应该继承一个父类:SimpleTagSupport类
如果标签类包含属性,每个属性都要有对应的getter和setter方法
重写doTag()方法,这个方法负责生成页面内容。
public class HelloWordTag extends SimpleTagSupport
{
//重写doTag方法,该方法负责生成页面内容
public void daTag() throws JspException,IOException
{
//获取页面输出流,并输出字符串
getJspContext().getOut().write("Hello Word!");
}
}
####1.2建立TLD文件
<?xml version="1.0" encoding="GBK"?>
<taglib 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 web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>mytaglib</short-name>
<!-- 定义该标签库的URI -->
<uri>http://www.crazyit.org/mytaglib</uri>
<!-- 定义第一个标签 -->
<tag>
<!-- 定义标签名 -->
<name>helloWorld</name>
<!-- 定义标签处理类 -->
<tag-class>lee.HelloWorldTag</tag-class>
<!-- 定义标签体为空 -->
<body-content>empty</body-content>
</tag>
<!-- 定义第二个标签 -->
<tag>
<!-- 定义标签名 -->
<name>query</name>
<!-- 定义标签处理类 -->
<tag-class>lee.QueryTag</tag-class>
<!-- 定义标签体为空 -->
<body-content>empty</body-content>
<!-- 配置标签属性:driver -->
<attribute>
<name>driver</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<!-- 配置标签属性:url -->
<attribute>
<name>url</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<!-- 配置标签属性:user -->
<attribute>
<name>user</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<!-- 配置标签属性:pass -->
<attribute>
<name>pass</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<!-- 配置标签属性:sql -->
<attribute>
<name>sql</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
<!-- 定义第三个标签 -->
<tag>
<!-- 定义标签名 -->
<name>iterator</name>
<!-- 定义标签处理类 -->
<tag-class>lee.IteratorTag</tag-class>
<!-- 定义标签体不允许出现JSP脚本 -->
<body-content>scriptless</body-content>
<!-- 配置标签属性:collection -->
<attribute>
<name>collection</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<!-- 配置标签属性:item -->
<attribute>
<name>item</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
<!-- 定义接受动态属性的标签 -->
<tag>
<name>dynaAttr</name>
<tag-class>lee.DynaAttributesTag</tag-class>
<body-content>empty</body-content>
<!-- 指定支持动态属性 -->
<dynamic-attributes>true</dynamic-attributes>
</tag>
<tag>
<name>fragment</name>
<tag-class>lee.FragmentTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>fragment</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
</taglib>
调用方法如下:
<h2>下面显示的是自定义标签中的内容</h2>
<!-- 使用标签 ,其中mytag是标签前缀,根据taglib的编译指令,mytag前缀将由http://www.crazyit.org/mytaglib的标签库处理 -->
<mytag:helloWorld/><br/>
taglib下的子元素
Element:Description
tlib-version:Tag库的版本
jsp-version:Tag库所需要的jsp的版本
short-name:助记符,tag的一个别名(可选) uri:用于确定一个唯一的tag库(重要)display-name:被可视化工具(诸如Jbuilder)用来显示的名称(可选)
small-icon:被可视化工具(诸如Jbuilder)用来显示的小图标(可选)
large-icon:被可视化工具(诸如Jbuilder)用来显示的大图标(可选)
description:对tag库的描述(可选)
listener:一个tag库可能定义一些类做为它的事件侦听类,这些类在TLD中被称为listener 元素,jsp服务器将会实例化这些侦听类,并且注册它们。Listener元素中有一个叫listener-class的子元素,这个元素的值必须是该侦听类的完整类名。
tag:参见下面tag 元素
tag元素下子元素
name:该标签库的名称,JSP页面中就是根据这个名称来使用此标签。(重要) tag-class:Tag标签对应的tag处理类(重要)tei-class:javax.servlet.jsp.tagext.TagExtraInfo的子类,用于表达脚本变量(可选)
body-content:Tag标签body的类型,指定标签体的内容,可以是如下几个(重要)
> tagdependent-指定标签处理类自己负责处理标签体
> empty-指定该标签体为空,只能作为空标签使用
> scriptless-指定该标签体可以试静态HTML元素,表达式语言,但不允许出现JSP脚本
> JSP-指定该标签的标签体可以使用JSP脚本
> dynamic-attributes-指定该标签是否支持动态属性,只有当定义动态属性标签时才需要该子元素’(重要)display-name:被可视化工具(诸如Jbuilder)用来显示的名称(可选)
small-icon:被可视化工具(诸如Jbuilder)用来显示的小图标(可选)
large-icon:被可视化工具(诸如Jbuilder)用来显示的大图标(可选)
description:此tag标签的描述
variable:提供脚本变量的信息(同tei-class)(可选)
attribute:Tag标签的属性名,有如下子元素(重要)
name : 设置属性名,子元素的值是字符串内容。
required : 设置该属性是否为必须属性,该子元素的值是true或false。
fragment : 设置该属性是否支持JSP脚本,表达式等动态内容,子元素的值是true或false。
1.使用taglib编译指令导入标签库。
2.使用标签,在JSP页面中使用自定义标签。
引入自定义标签语法如下
<%@ taglib uri="tagliburi" prefix="tagPrefix"%>
使用自定义标签语法如下
<tagPrefix:tagName tagAttribute="tagValue" ...>
<tagBody/>
</tagPrefix:tagName>
<!--没有标签体的-->
<tagPrefix:tagName tagAttribute="tagValue" .../>
<h2>下面显示的是查询标签的结果</h2>
<!-- 使用标签 ,其中mytag是标签前缀,根据taglib的编译指令,mytag前缀将由http://www.crazyit.org/mytaglib的标签库处理 -->
<mytag:query
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javaee"
user="root"
pass="32147"
sql="select * from news_inf"/><br/>
//省略了其他内容
......
//标签的处理方法,简单标签处理类只需要重写doTag方法
public void doTag() throws JspException, IOException
{
//从page scope中获取属性名为collection的集合
Collection itemList = (Collection)getJspContext().
getAttribute(collection);
//遍历集合
for (Object s : itemList)
{
//将集合的元素设置到page 范围
getJspContext().setAttribute(item, s );
//输出标签体
getJspBody().invoke(null);
}
}
......
上面代码每次遍历都调用了getJspBody()方法,该方法返回该标签所包含的标签体:JspFragment对象,执行该对象的invoke()方法,即可输出标签体内容。
标签处理类中定义了类型为JspFragment的属性,该属性代表了‘页面片段’。
使用标签库时,通过jsp:attribute../动作指令为标签库属性指定值。
参考如下代码
public class FragmentTag extends SimpleTagSupport
{
private JspFragment fragment;
//fragment属性的setter和getter方法
public void setFragment(JspFragment fragment)
{
this.fragment = fragment;
}
public JspFragment getFragment()
{
return this.fragment;
}
@Override
public void doTag() throws JspException, IOException
{
JspWriter out = getJspContext().getOut();
out.println("<div style='padding:10px;border:1px solid black'>");
out.println("<h3>下面是动态传入的JSP片段</h3>");
//调用、输出“页面片段”
fragment.invoke( null );
out.println("</div");
}
}
tld文件如下
......
<tag>
<!--定义标签名-->
<name>fragment</name>
<!--定义标签处理类-->
<tag-class>lee.FragmentTag</tag-class>
<!--指定该标签不支持标签体-->
<body-content>empty</body-content>
<attribute>
<name>fragment</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
......
标签调用代码如下:
......
<h2>下面显示的是自定义标签中的内容</h2>
<mytag:fragment>
<!-- 使用jsp:attribute标签传入fragment参数 -->
<jsp:attribute name="fragment">
<!-- 下面是动态的JSP页面片段 -->
<mytag:helloWorld/>
</jsp:attribute>
</mytag:fragment>
<br/>
<mytag:fragment>
<jsp:attribute name="fragment">
<!-- 下面是动态的JSP页面片段 -->
${pageContext.request.remoteAddr}
</jsp:attribute>
</mytag:fragment>
......
动态属性标签比普通标签多了如下两个要求
标签处理类还需要实DynamicAttributes接口,实现setDynamicAttribute方法。
配置标签时通过<dynamic-attribute../>子元素指定该标签支持动态属性。
代码如下
public class DynaAttributesTag extends SimpleTagSupport implements DynamicAttributes
{
//保存每个属性名的集合
private ArrayList<String> keys = new ArrayList<String>();
//保存每个属性值的集合
private ArrayList<Object> values = new ArrayList<Object>();
@Override
public void doTag() throws JspException, IOException
{
JspWriter out = getJspContext().getOut();
//此处只是简单地输出每个属性
out.println("<ol>");
for( int i = 0; i < keys.size(); i++ )
{
String key = keys.get( i );
Object value = values.get( i );
out.println( "<li>" + key + " = " + value + "</li>" );
}
out.println("</ol>");
}
@Override
public void setDynamicAttribute( String uri, String localName,
Object value )
throws JspException
{
//添加属性名
keys.add( localName );
//添加属性值
values.add( value );
}
}
tld文件如下:
<!-- 定义接受动态属性的标签 -->
<tag>
<name>dynaAttr</name>
<tag-class>lee.DynaAttributesTag</tag-class>
<body-content>empty</body-content>
<!-- 指定支持动态属性 -->
<dynamic-attributes>true</dynamic-attributes>
</tag>
调用方法如下:
<h2>下面显示的是自定义标签中的内容</h2>
<h4>指定两个属性</h4>
<mytag:dynaAttr name="crazyit" url="crazyit.org"/><br/>
<h4>指定四个属性</h4>
<mytag:dynaAttr 书名="疯狂Java讲义" 价格="99.0" 出版时间="2008年" 描述="Java图书"/><br/>
通过定义.tag文件定义标签
使用tag file无需定义标签处理类和tld文件,命名规则需要问tagName.tag,其中tagName文件名即为标签名,将赶文件放在WEB应用某个路径下,这个路径相当于标签库的URI名,可以统一放在/WEB-ING/tags目录下。
引入标签库的时候就可以按照 <%@ taglib prefix="tags" tagdir="/WEB-ING/tags">
方式来引入标签库。
tag file具有如下5个编译指令
taglib:用于导入其他标签库
include:用于导入其他JSP或静态页面
tag:作用类似于JSP文件中的page指令,有pageEncoding,body-content等属性,用于设置页面编码等属性
attribute:用于设置自定义标签的属性,类似于自定义标签处理类中的标签属性。
variavle:用于设置自定义标签的变量,这些变量将传给JSP页面使用。
示例代码如下:
文件名为iterator.tag
<%@ tag pageEncoding="GBK" import="java.util.List"%>
<!-- 定义了四个标签属性 -->
<%@ attribute name="bgColor" %>
<%@ attribute name="cellColor" %>
<%@ attribute name="title" %>
<%@ attribute name="bean" %>
<table border="1" bgcolor="${bgColor}">
<tr>
<td><b>${title}</b></td>
</tr>
<%List<String> list = (List<String>)
request.getAttribute("a");
//遍历输出list集合的元素
for (Object ele : list){%>
<%=ele%>
%=ele%>
<%}%> <="" table>="" <="" code="">%}%>>
调用代码如下:
//使用自定义标签
<tags:iterator bgColor="#99dd99" cellColor="#9999cc" title="迭代器标签" bean="a" />
该方法实际是EL表达式的自定义函数方法
SUN公司的<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
就是采用的这种方法,是用于在EL表达式中可以使用期定义的函数库。
引入方式示例:
<%@ taglib prefix="fns" uri="/WEB-INF/tlds/fns.tld" %>
代码示例:
<?xml version="1.0" encoding="UTF-8" ?>
<taglib 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-jsptaglibrary_2_0.xsd"
version="2.0">
<description>JSTL 1.1 functions library</description>
<display-name>JSTL functions sys</display-name>
<tlib-version>1.1</tlib-version>
<short-name>fns</short-name>
<uri>http://java.sun.com/jsp/jstl/functionss</uri>
<!-- DictUtils -->
<function>
<description>获取字典对象列表</description>
<name>getDictList</name>
<function-class>com.sdyy.base.sys.utils.DictUtils</function-class>
<function-signature>java.util.List getDictList(java.lang.String)</function-signature>
<example>${fns:getDictList(typeCode)}</example>
</function>
<function>
<description>获取字典对象列表</description>
<name>getDictListJson</name>
<function-class>com.sdyy.base.sys.utils.DictUtils</function-class>
<function-signature>java.lang.String getDictListJson(java.lang.String)</function-signature>
<example>${fns:getDictListJson(typeCode)}</example>
</function>
<function>
<description>对象变json</description>
<name>toJSONString</name>
<function-class>com.alibaba.fastjson.JSON</function-class>
<function-signature>java.lang.String toJSONString(java.lang.Object)</function-signature>
</function>
</taglib>
function-class就是该方法的实体所在类路径,
function-signature就是该方法的方法名,值得一提的是,这个方法必须是个static方法。
example就是使用方法示例
spring在J2EE中应用最多的属于WebApplicationContext类,此类是spring IOC容器ApplicaionContext的子类,专门为web应用准备,它允许从相对于web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,而且WebApplicationContext对象也将作为属性放到ServletContext中,可以使用WebApplicationContextUtil的getWebApplicationContext(ServletContex sc)方法获得WebApplicationContext的实例。
WebApplicationContext wac=(WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
此方法可以从ServletContext属性中通过,WebApplicationContext类定义的常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE来获得相应的实例,此方法也是WebApplicationContextUtil获得Web应用上下文实例的内部实现方法。
WebApplicationContext的初始化需要ServletContext的引用,所以必须在拥有Web容器的前提下才能完成启动工作。
因此可以通过定义自启动Servlet或者定义Web容器监听器来,完成WebApplicationContext的初始化工作。
Spring提供了ContextLoaderServlet或者ContextLoaderListener来辅助WebApplicationContext的初始化,这两类中都实现了启动WebApplicationContext实例的逻辑,我们只需要在Web.xml中配置就可以了。
<!--指定配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/baobaotao-dao.xml,/WEB-INF/baobaotao-service.xml
</param-value>
</context-param>
<!--声明Web容器监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener通过Web容器上下文参数contextConfigLocation获取Spring配置文件位置,可以指定多个配置文件,用逗号,空格,或冒号分开均可,对于未带资源类型前缀的配置文件路径,会默认这些路径相对于Web应用部署的根路径,对于配置了资源类型前缀的路径,和上面是等效的,例如 classpath*:/baobaotao-*.xml
<!--指定配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/baobaotao-dao.xml,/WEB-INF/baobaotao-service.xml
</param-value>
</context-param>
<!--声明自启动Servlet-->
<servlet>
<servlet-name>springContextLoaderServlet</listener-class>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
指令 | 描述 |
---|---|
< %@ page … % > | page指令是针对当前页面的指令,比如脚本语言,错误页面和缓冲要求。 |
< %@ include … % > | include指令用于指定包含另一个页面 | < %@ taglib … % > | taglib指令用于定义和访问自定义标签 |
page编译指令常用于JSP页面顶端,用于定义页面一些属性,一个页面可以有多个page指令。
<%@page
[language="java"]
[extends="package.class"]
[import="package.class | package.* , ..."]
[session="ture | false"]
[buffer=" 8kb | none | size kb "]
[autoFlush="true | false"]
[isThreadSafe="true | false"]
[info="text]
[errorPage="relativeURL"]
[contentType="mimeType[;charset=characterSet]" | "text/html;charSet=ISO-8859-1"]
[pageEnconding="ISO-8859-1"]
[isErrorPage="true | false"]
%>
include指令可以将一个外部文件键入到当前JSP文件中,同时解析这个JSP页面。但是这是个静态的include语句,他会把目标页面的其他编译指令也包含进来,如果包含的编译指令冲突,那么页面会出错,静态指令会把目标页面的所有内容都包含进来合成新的页面,因此目标页面可以不需要是完整的页面。
<%@include file="relativeURLSpec"%>
注意区别于JSP动作指令中的jsp:include,动态包含指令,这个指令不会嵌入目标页面的编译指令
taglib指令用于定义和访问自定义标签
JSP动作指令和编译指令不同,编译指令是在JSP变成成servlet是起作用,而动作指令指在运行时动作,其实际只是JSP脚本片段的标准化写法。
forward指令用于将页面响应转发到另外的页面,浏览器请求地址栏不会改变,页面会转发到目标页面,所以这是一个请求转发。他只是采用了新页面来对用户生成响应,实际http请求只有一次,和重定向的多次请求不同,无论转发多少次,但是该次的请求参数,请求属性都不会改变。
<jsp:forward page="{relativeURL | <%=expreesion%>}">
[<jsp:param name="parameterName" value="patameterValue"/>]
</jsp:forward>
forward指令可以添加param指令,设置请求参数,然后在转发的目标页面可以用request内置对象调用getParamter()方法获得请求参数的属性值。
include指令是一个动态include指令,可以包含某个页面,但是不同于静态include编译指令,它只包含目标页面的body部分。
<jsp:include page="{relativeURL | <%=expreesion%>}" flush="true | false">
[<jsp:param name="parameterName" value="parameterValue"/>]
</jsp:include>
flush属性用于指定输出缓存是否转移到被导入文件中。
include也可以对添加请求参数,同forward指令一样,在指定的被包含页面可以调用request内置对象调用getParamter()方法获得请求参数的属性值。
无论是include动作指令还是forward动作指令,其实际效果是在servlet的service方法中调用了相应的方法,forward指令调用的是_jspx_page_context的forward()方法来引入目标页面,include指令是调用JspRuntimeLibrary的include()方法来引入目标页面。区别在于,被forward的页面将完全替代原有页面,被include的页面只是插入到原有页面。
useBean是实例化一个javaBean对象,setProperty是给javaBean对象属性设置值,getProperty是输出javaBean对象的属性值。
useBean语法如下:
<jsp:useBean id="name" class="className" scope="page | request | session | application"/>
- page:该JavaBean实例紧在该页面有效。
- request:该JavaBean实例在本次请求有效。
- session:该JavaBean实例在本次session有效。
- application:该JavaBean实例在本应用内一直有效。
setProperty语法如下:
<jsp:setProperty name="BeanName" property="porpertyName" value="value"/>
getProperty语法如下:
<jsp:getProperty name="BeanName" property="porpertyName"/>
关于setProperty和getProperty指令实际上是调用了javaBean类中的setter和getter两个方法,这两个指令他们都需要根据属性名来操作javaBean的属性,其实不然,他们与java类中定义的属性有一定区别,比如setProperty需要使用name属性,但javaBean中是否真正定义了这个成员name属性并不重要,重要的是一定要存在setName这个方法。
plugin指令主要用于下载服务器端的JavaBean或Applet到客户端执行,由于程序在客户端执行,因此客户端必须安装虚拟机,此指令用的场景不多。
param指令必须和forward,include,plugin指令联合使用,用于传递参数。
<jsp:param name="parameterName" value="parameterValue"/>
<%-- 注释内容 --%> ---JSP注释语法
<!-- 注释内容 --%> ---HTML注释语法
JSP注释标志着JSP容器应该忽略的文本或者语句,可以隐藏相应的JSP语句段,当你不想让其出现在html页面中。当使用浏览器查看源码功能时,html注释内容会显示在浏览器中,而JSP注释这不会,会被隐藏。
<%! declaration; [ declaration; ]+ ... %>
等价于XML格式
<jsp:declaration>
code fragment
</jsp:declaration>
JSP声明用于声明变量和方法。相当于JAVA内容的成员变量和成员方法的声明。可以使用public,private等访问控制修饰,也可以使用static修饰,但是不能使用abstract修饰,因为抽象方法会导致JSP对应的Servlet变成抽象类,从而导致无法实例化。
<%= expression %> ---expression后面不能有分号
等价于XML格式
<jsp:expression>
expression
</jsp:expression>
JSP 表达式元素包含一个脚本语言表达式,该表达式被赋值,转换成一个字符串,并插入到表达式出现在 JSP 文件中的位置。其等价于使用了out.println输出语句。
根据 JAVA 语言规范,表达式元素可以包含任何有效的表达式,但你不能使用分号来结束一个表达式。
<% code fragment %>
等价于XML格式
<jsp:scriptlet>
code fragment
</jsp:scriptlet>
JSP脚本中的变量应该为局部变量,相当于_jspService的方法内部来执>行JSP脚本片段,所以不能在里面定义方法和成员变量,但是可以使用方法与成员变量。
学习是一个无止境的过程,尤其进入了软件这个行业每天面对的就是漫天的新技术,那么只有不停的学习才能不再这个行业中失去重要的地位,就目前而言,个人的能力还不足以在这个行业中获得更多的回报,那么只有不停的努力,才能铺好美好的明天。
因此一个良好的学习计划,决定着是否能脚踏实地的学到真的本领,于是,必须列出一个详细的计划,将未来的路线规划清楚。
以上是2016年度7月份以后下半年的学习计划,希望今年能够做到自律,真正的把知识学给自己。