序列化条件
1) 必须实现 java.IO.serializable 对象
2) 该类的所有属性必须是可序列化的,如果不是需要用transient关键词标注
序列化关键词:ObjectOutputStream WriteObject()
反序列化:读取文件流 转换成实体类
ObjectInputStream ReadObject()
java.net 包中提供了两种常见的网络协议的支持:
· TCP:TCP是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。
· UDP:UDP是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。
本教程主要讲解以下两个主题。
· Socket编程:这是使用最广泛的网络概念,它已被解释地非常详细。
套接字使用TCP提供了两台计算机之间的通信机制。客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket对象。客户端和服务器现在可以通过对 Socket对象的写入和读取来进行进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:
· 服务器实例化一个 ServerSocket对象,表示通过服务器上的端口通信。
· 服务器调用 ServerSocket类的 accept()方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
· 服务器正在等待时,一个客户端实例化一个 Socket对象,指定服务器名称和端口号来请求连接。
· Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket对象能够与服务器进行通信。
· 在服务器端,accept()方法返回服务器上一个新的 socket引用,该 socket连接到客户端的 socket。
连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。
服务器应用程序通过使用 java.net.ServerSocket类以获取一个端口,并且侦听客户端请求。
ServerSocket 类有四个构造方法:
序号 |
方法描述 |
1 |
public ServerSocket(int port) throws IOException |
2 |
public ServerSocket(int port, int backlog) throws IOException |
3 |
public ServerSocket(int port, int backlog, InetAddress address) throws IOException |
4 |
public ServerSocket() throws IOException |
创建非绑定服务器套接字。如果 ServerSocket构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。
这里有一些 ServerSocket类的常用方法:
序号 |
方法描述 |
1 |
public int getLocalPort() |
2 |
public Socket accept() throws IOException |
3 |
public void setSoTimeout(int timeout) |
4 |
public void bind(SocketAddress host, int backlog) |
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个Socket对象通过实例化,而服务器获得一个 Socket对象则通过 accept()方法的返回值。
Socket 类有五个构造方法.
序号 |
方法描述 |
1 |
public Socket(String host, int port) throws UnknownHostException, IOException. |
2 |
public Socket(InetAddress host, int port) throws IOException |
3 |
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. |
4 |
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. |
5 |
public Socket() |
当 Socket 构造方法返回,并没有简单的实例化了一个 Socket对象,它实际上会尝试连接到指定的服务器和端口。
下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket对象,所以无论客户端还是服务端都能够调用这些方法。
序号 |
方法描述 |
1 |
public void connect(SocketAddress host, int timeout) throws IOException |
2 |
public InetAddress getInetAddress() |
3 |
public int getPort() |
4 |
public int getLocalPort() |
5 |
public SocketAddress getRemoteSocketAddress() |
6 |
public InputStream getInputStream() throws IOException |
7 |
public OutputStream getOutputStream() throws IOException |
8 |
public void close() throws IOException |
这个类表示互联网协议(IP)地址。下面列出了 Socket编程时比较有用的方法:
序号 |
方法描述 |
1 |
static InetAddress getByAddress(byte[] addr) |
2 |
static InetAddress getByAddress(String host, byte[] addr) |
3 |
static InetAddress getByName(String host) |
4 |
String getHostAddress() |
5 |
String getHostName() |
6 |
static InetAddress getLocalHost() |
7 |
String toString() |
如下的 GreetingClient是一个客户端程序,该程序通过 socket连接到服务器并发送一个请求,然后等待一个响应。
如下的GreetingServer程序是一个服务器端应用程序,使用 Socket来监听一个指定的端口。
编译以上两个 java 文件代码,并执行以下命令来启动服务,使用端口号为 6066:
$ javac GreetingServer.java
$ java GreetingServer 6066
等待远程连接,端口号为:6066...
新开一个命令窗口,执行以上命令来开启客户端:
$ javac GreetingClient.java
$ java GreetingClient localhost 6066
连接到主机:localhost ,端口号:6066
远程主机地址:localhost/127.0.0.1:6066
服务器响应: 谢谢连接我:/127.0.0.1:6066
Goodbye!
主要通过java.mail和java.activation两个jar包
http://www.runoob.com/java/java-sending-email.html
:
JAVA中堆和栈的区别
在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
堆内存用来存放由new创建的对象和数组 。
在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理 。
在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。
引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。
java中变量在内存中的分配
1、类变量(static修饰的变量):在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈 以便于高速访问。静态变量的生命周期--一直持续到整个"系统"关闭
2、实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的"物理位置"。 实例变量的生命周期--当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存
3、局部变量:局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放
附:java的内存机制
Java 把内存划分成两种:一种是栈内存,另一种是堆内存。
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。
堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。
这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针 !
:
线程安全问题都是由全局变量 及静态变量 引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
1) 常量始终是线程安全的,因为只存在读操作。
2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
3)局部变量 是线程安全的 。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
有状态 就是有数据存储功能。
有状态对象 (Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。
无状态就是一次操作,不能保存数据 。
无状态对象( Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
有状态对象:
无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。
Struts2默认的实现是Prototype模式。也就是每个请求都新生成一个Action实例,所以不存在线程安全问题。需要注意的是,如果由Spring管理action的生命周期, scope要配成prototype作用域。
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。有两种方式:
(1)在SpringMVC 的配置文件中定义MyController 的bean 对象。
(2)在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
<beanclass="com.host.app.web.controller.MyController"/>
< context:component-scanbase-package= "com.host.app.web" />//路径写到controller的上一层(扫描包详解见下面浅析)
RequestMapping是一个用来处理请求地址映射的注解(将请求映射到对应的控制器方法中),可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
RequestMapping请求路径映射,如果标注在某个controller的类级别上,则表明访问此类路径下的方法都要加上其配置的路径;最常用是标注在方法上,表明哪个具体的方法来接受处理某次请求。
@Controller
@RequestMapping(value="/book")
publicclass BookController {
@RequestMapping(value="/title")
public String getTitle(){
return"title";
}
@RequestMapping(value="/content")
public String getContent(){
return"content";
}
}
由于BookController类加了value="/book"的@RequestMapping的注解,所以相关路径都要加上"/book",即请求的url分别为:
(1)http://localhost:8080/book/title
(2)http://localhost:8080/book/content
"@RequestMapping"的value值前后是否有“/”对请求的路径没有影响,即value="book" 、"/book"、"/book/"其效果是一样的。
RequestMapping的属性
value:指定请求的实际url
(1)普通的具体值。如前面的value="/book"。
(2)含某变量的一类值。
@RequestMapping(value="/get/{bookId}")
public String getBookById(@PathVariable String bookId,Model model){
model.addAttribute("bookId", bookId);
return "book";
}
路径中的bookId可以当变量,@PathVariable注解即提取路径中的变量值。
(3)ant风格
@RequestMapping(value="/get/id?"):可匹配“/get/id1”或“/get/ida”,但不匹配“/get/id”或“/get/idaa”;
@RequestMapping(value="/get/id*"):可匹配“/get/idabc”或“/get/id”,但不匹配“/get/idabc/abc”;
@RequestMapping(value="/get/id/*"):可匹配“/get/id/abc”,但不匹配“/get/idabc”;
@RequestMapping(value="/get/id/**/{id}"):可匹配“/get/id/abc/abc/123”或“/get/id/123”,也就是Ant风格和URI模板变量风格可混用。
(4)含正则表达式的一类值
@RequestMapping(value="/get/{idPre:\\d+}-{idNum:\\d+}"):可以匹配“/get/123-1”,但不能匹配“/get/abc-1”,这样可以设计更加严格的规则。
可以通过@PathVariable 注解提取路径中的变量(idPre,idNum)
(5)或关系
@RequestMapping(value={"/get","/fetch"} )即 /get或/fetch都会映射到该方法上。
method:指定请求的method类型, GET、POST、PUT、DELETE等;
@RequestMapping(value="/get/{bookid}",method={RequestMethod.GET,RequestMethod.POST})
params:指定request中必须包含某些参数值是,才让该方法处理。
@RequestMapping(params="action=del"),请求参数包含“action=del”,如:http://localhost:8080/book?action=del
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
仅处理请求中包含了名为“myParam”,值为“myValue”的请求。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
@RequestMapping(value="/header/id", headers ="Accept=application/json"):表示请求的URL必须为“/header/id 且请求头中必须有“Accept =application/json”参数即可匹配。
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(value = "/pets", method = RequestMethod.GET, headers="Referer=http://www.ifeng.com/")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
仅处理request的header中包含了指定“Refer”请求头和对应值为“http://www.ifeng.com/
”的请求。
consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html。
@Controller
@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
// implementation omitted
}
方法仅处理request Content-Type为“application/json”类型的请求。
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回。
@Controller
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, produces="application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {
// implementation omitted
}
方法仅处理request请求中Accept头中包含了"application/json"的请求,同时暗示了返回的内容类型为application/json;
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
RequestMapping注解有六个属性,下面我们把她分成三类进行说明(下面有相应示例)。
1、value, method;
value: 指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
method: 指定请求的method类型, GET、POST、PUT、DELETE等;
例:@RequestMapping(value = "/restUser", method = RequestMethod.POST)
2、consumes,produces
consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
cousumes的样例:
@Controller
@RequestMapping(value= "/pets", method = RequestMethod.POST,consumes="application/json")
publicvoid addPet(@RequestBody Pet pet, Model model){
// implementation omitted
}
方法仅处理request Content-Type为“application/json”类型的请求。
produces的样例:
@Controller
@RequestMapping(value= "/pets/{petId}", method = RequestMethod.GET,produces="application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Modelmodel) {
// implementation omitted
}
方法仅处理request请求中Accept头中包含了"application/json"的请求,同时暗示了返回的内容类型为application/json;
3、params,headers
params: 指定request中必须包含某些参数值是,才让该方法处理。
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
params的样例:
@Controller
@RequestMapping("/owners/{ownerId}")
publicclass RelativePathUriTemplateController {
@RequestMapping(value ="/pets/{petId}", method = RequestMethod.GET,params="myParam=myValue")
publicvoid findPet(@PathVariable String ownerId,@PathVariable String petId, Model model) {
// implementation omitted
}
}
仅处理请求中包含了名为“myParam”,值为“myValue”的请求;
headers的样例:
@Controller
@RequestMapping("/owners/{ownerId}")
publicclass RelativePathUriTemplateController {
@RequestMapping(value= "/pets", method = RequestMethod.GET,headers="Referer=http://www.ifeng.com/")
publicvoid findPet(@PathVariable String ownerId,@PathVariable String petId, Model model) {
// implementation omitted
}
}
仅处理request的header中包含了指定“Refer”请求头和对应值为“http://www.ifeng.com/”的请求;
例如:
1 @Service("userService")
2 public class UserServiceImpl implementsUserService {
3 ………
4 }
@Service("userService")注解是告诉Spring,当Spring要创建UserServiceImpl的的实例时,bean的名字必须叫做"userService",这样当Action需要使用UserServiceImpl的的实例时,就可以由Spring创建好的"userService",然后注入给Action:在Action只需要声明一个名字叫“userService”的变量来接收由Spring注入的"userService"即可,具体代码如下:
1 // 注入userService
2 @Resource(name = "userService")
3 private UserService userService;
注意:在Action声明的“userService”变量的类型必须是“UserServiceImpl”或者是其父类“UserService”,否则由于类型不一致而无法注入,由于Action中的声明的“userService”变量使用了@Resource注解去标注,并且指明了其name = "userService",这就等于告诉Spring,说我Action要实例化一个“userService”,你Spring快点帮我实例化好,然后给我,当Spring看到userService变量上的@Resource的注解时,根据其指明的name属性可以知道,Action中需要用到一个UserServiceImpl的实例,此时Spring就会把自己创建好的名字叫做"userService"的UserServiceImpl的实例注入给Action中的“userService”变量,帮助Action完成userService的实例化,这样在Action中就不用通过“UserServiceuserService = new UserServiceImpl();”这种最原始的方式去实例化userService了。如果没有Spring,那么当Action需要使用UserServiceImpl时,必须通过“UserService userService = new UserServiceImpl();”主动去创建实例对象,但使用了Spring之后,Action要使用UserServiceImpl时,就不用主动去创建UserServiceImpl的实例了,创建UserServiceImpl实例已经交给Spring来做了,Spring把创建好的UserServiceImpl实例给Action,Action拿到就可以直接用了。Action由原来的主动创建UserServiceImpl实例后就可以马上使用,变成了被动等待由Spring创建好UserServiceImpl实例之后再注入给Action,Action才能够使用。这说明Action对“UserServiceImpl”类的“控制权”已经被“反转”了,原来主动权在自己手上,自己要使用“UserServiceImpl”类的实例,自己主动去new一个出来马上就可以使用了,但现在自己不能主动去new“UserServiceImpl”类的实例,new“UserServiceImpl”类的实例的权力已经被Spring拿走了,只有Spring才能够new“UserServiceImpl”类的实例,而Action只能等Spring创建好“UserServiceImpl”类的实例后,再“恳求”Spring把创建好的“UserServiceImpl”类的实例给他,这样他才能够使用“UserServiceImpl”,这就是Spring核心思想“控制反转”,也叫“依赖注入”,“依赖注入”也很好理解,Action需要使用UserServiceImpl干活,那么就是对UserServiceImpl产生了依赖,Spring把Acion需要依赖的UserServiceImpl注入(也就是“给”)给Action,这就是所谓的“依赖注入”。对Action而言,Action依赖什么东西,就请求Spring注入给他,对Spring而言,Action需要什么,Spring就主动注入给他。
3) 例如:
4)1@Repository(value="userDao")
5)2publicclass UserDaoImplextendsBaseDaoImpl
6)3 ………
7)4 }
8) @Repository(value="userDao")注解是告诉Spring,让Spring创建一个名字叫“userDao”的UserDaoImpl实例。
9) 当Service需要使用Spring创建的名字叫“userDao”的UserDaoImpl实例时,就可以使用@Resource(name = "userDao")注解告诉Spring,Spring把创建好的userDao注入给Service即可。
10) 1//注入userDao,从数据库中根据用户Id取出指定用户时需要用到
11) 2 @Resource(name = "userDao")
12) 3private BaseDao
@Resource的装配顺序:
(1)、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
(2)、指定了name或者type则根据指定的类型去匹配bean
(3)、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错
然后,区分一下@Autowired和@Resource两个注解的区别:
(1)、@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
(2)、@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了
Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。
@RequestParam用于将请求参数区数据映射到功能处理方法的参数上。
public String requestparam1(@RequestParam Stringusername)
请求中包含username参数(如/requestparam1?username=zhang),则自动传入。
@RequestParam有以下三个参数:
value:参数名字,即入参的请求参数名字,如username表示请求的参数区中的名字为username的参数的值将传入;
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将抛出异常;
defaultValue:默认值,表示如果请求中没有同名参数时的默认值,设置该参数时,自动将required设为false。
public Stringrequestparam4(@RequestParam(value="username",required=false) String username)
表示请求中可以没有名字为username的参数,如果没有默认为null,此处需要注意如下几点:
原子类型:必须有值,否则抛出异常,如果允许空值请使用包装类代替。
Boolean包装类型:默认Boolean.FALSE,其他引用类型默认为null。
如果请求中有多个同名的应该如何接收呢?如给用户授权时,可能授予多个权限,首先看下如下代码:
public Stringrequestparam7(@RequestParam(value="role") String roleList)
如果请求参数类似于url?role=admin&rule=user,则实际roleList参数入参的数据为“admin,user”,即多个数据之间使用“,”分割;我们应该使用如下方式来接收多个请求参数:
public Stringrequestparam7(@RequestParam(value="role") String[] roleList)
或者
public Stringrequestparam8(@RequestParam(value="list") List
4、@ModelAttribute
ModelAttribute可以应用在方法参数上或方法上,他的作用主要是当注解在方法参数上时会将注解的参数对象添加到Model中;当注解在请求处理方法Action上时会将该方法变成一个非请求处理的方法,但其它Action被调用时会首先调用该方法。
4.1 @ModelAttribute注释一个方法
被@ModelAttribute注释的方法表示这个方法的目的是增加一个或多个模型(model)属性。这个方法和被@RequestMapping注释的方法一样也支持@RequestParam参数,但是它不能直接被请求映射。实际上,控制器中的@ModelAttribute方法是在同一控制器中的@RequestMapping方法被调用之前调用的。
被@ModelAttribute注释的方法用于填充model属性,例如,为下拉菜单填充内容,或检索一个command对象(如,Account),用它来表示一个HTML表单中的数据。
一个控制器可以有任意数量的@ModelAttribute方法。所有这些方法都在@RequestMapping方法被调用之前调用。
有两种类型的@ModelAttribute方法。一种是:只加入一个属性,用方法的返回类型隐含表示。另一种是:方法接受一个Model类型的参数,这个model可以加入任意多个model属性。
(1)@ModelAttribute注释void返回值的方法
@Controller
@RequestMapping(value="/test")
public class TestController {
/**
* 1.@ModelAttribute注释void返回值的方法
* @param abc
* @param model
*/
@ModelAttribute
public void populateModel(@RequestParam String abc, Model model) {
model.addAttribute("attributeName", abc);
}
@RequestMapping(value = "/helloWorld")
public String helloWorld() {
return "test/helloWorld";
}
}
这个例子,在获得请求/helloWorld 后,populateModel方法在helloWorld方法之前先被调用,它把请求参数(/helloWorld?abc=text)加入到一个名为attributeName的model属性中,在它执行后helloWorld被调用,返回视图名helloWorld和model已由@ModelAttribute方法生产好了。
这个例子中model属性名称和model属性对象由model.addAttribute()实现,不过前提是要在方法中加入一个Model类型的参数。
(2)@ModelAttribute注释返回具体类的方法
/**
* 2.@ModelAttribute注释返回具体类的方法
* @param id
* @return
*/
@ModelAttribute
public User getUserInfo(String id){
if(id!=null && !id.equals("")){
return userService.getUserInfo(id);
}
return null;
}
这种情况,model属性的名称没有指定,它由返回类型隐含表示,如这个方法返回User类型,那么这个model属性的名称是user。
这个例子中model属性名称有返回对象类型隐含表示,model属性对象就是方法的返回值。它无须要特定的参数。
(3)@ModelAttribute(value="")注释返回具体类的方法
@Controller
@RequestMapping(value="/test")
public class TestController {
/**
* 3.@ModelAttribute(value="")注释返回具体类的方法
* @param abc
* @return
*/
@ModelAttribute("str")
public String getParam(@RequestParam String param) {
return param;
}
@RequestMapping(value = "/helloWorld")
public String helloWorld() {
return "test/helloWorld";
}
}
这个例子中使用@ModelAttribute注释的value属性,来指定model属性的名称。model属性对象就是方法的返回值。它无须要特定的参数。
完整的代码:
package demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import demo.model.User;
import demo.service.IUserService;
@Controller
@RequestMapping(value="/test")
public class TestController {
@Autowired
private IUserService userService;
/**
* 1.@ModelAttribute注释void返回值的方法
* @param abc
* @param model
*/
@ModelAttribute
public void populateModel(@RequestParam String abc, Model model) {
model.addAttribute("attributeName", abc);
}
/**
* 2.@ModelAttribute注释返回具体类的方法
* @param id
* @return
*/
@ModelAttribute
public User getUserInfo(String id){
if(id!=null && !id.equals("")){
return userService.getUserInfo(id);
}
return null;
}
/**
* 3.@ModelAttribute(value="")注释返回具体类的方法
* @param abc
* @return
*/
@ModelAttribute("str")
public String getParam(@RequestParam String param) {
return param;
}
@RequestMapping(value = "/helloWorld")
public String helloWorld() {
return "test/helloWorld";
}
}
Jsp前台取值:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>helloWorldtitle>
head>
<body>
1.The attributeValue is: ${attributeName}
<br/><br/>
2.用户信息:<br/>
姓名:${user.user_name}<br/>
年龄:${user.user_age}<br/>
邮箱:${user.user_email}<br/><br/>
3.The param is: ${str}
body>
html>
页面效果图:
URL格式:http://localhost/SSMDemo/test/helloWorld?abc=text&id=1¶m=aaa 注:当url或者post中不包含参数abc和参数param时,会报错。
(4)@ModelAttribute和@RequestMapping同时注释一个方法
@Controller
@RequestMapping(value="/test")
public class TestController {
@RequestMapping(value = "/helloWorld")
@ModelAttribute("attributeName")
public String helloWorld() {
return "hi";
}
}
这时这个方法的返回值并不是表示一个视图名称,而是model属性的值,视图名称由RequestToViewNameTranslator根据请求"/helloWorld"转换为helloWorld。Model属性名称由@ModelAttribute(value=””)指定,相当于在request中封装了key=attributeName,value=hi。
Jsp页面:
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>helloWorldtitle>
head>
<body>
The attributeValue is: ${attributeName}
body>
html>
4.2 @ModelAttribute注释一个方法的参数
@ModelAttribute注释方法的一个参数表示应从模型model中取得。若在model中未找到,那么这个参数将先被实例化后加入到model中。若在model中找到,则请求参数名称和model属性字段若相匹配就会自动填充。这个机制对于表单提交数据绑定到对象属性上很有效。
当@ModelAttribute注解用于方法参数时,它有了双重功能,即“存/取”。首先,它从模型中取出数据并赋予对应的参数,如果模型中尚不存在,则实例化一个,并存放于模型中;其次,一旦模型中已存在此数据对象,接下来一个很重要的步骤便是将请求参数绑定到此对象上(请求参数名映射对象属性名),这是Spring MVC提供的一个非常便利的机制--数据绑定。
@RequestMapping(value = "/login.htm", method = RequestMethod.GET)
public String doLogin(@ModelAttribute("baseMember") BaseMember member) {
member.setLoginName("loginName");
return "home";
}
上述代码中,如果模型中尚不存在键名为“baseMember”的数据,则首先会调用BaseMember类的默认构造器创建一个对象,如果不存在默认构造器会抛出异常。因此,给实体类提供一个默认构造器是一个好的编程习惯。当请求路径的请求参数或提交的表单与BaseMember的属性名匹配时,将自动将其值绑定到baseMember对象中,非常的便利!这可能是我们使用@ModelAttribute最主要的原因之一。比如:请求路径为http://localhost:8080/spring-web/login.htm?loginName=myLoginName,baseMember对象中的loginName属性的值将被设置为myLoginName。
4.3 @ModelAttribute注解的使用场景
当@ModelAttribute注解用于方法时,与其处于同一个处理类的所有请求方法执行前都会执行一次此方法,这可能并不是我们想要的,因此,我们使用更多的是将其应用在请求方法的参数上,而它的一部分功能与@RequestParam注解是一致的,只不过@RequestParam用于绑定单个参数值,而@ModelAttribute注解可以绑定所有名称匹配的,此外它自动将绑定后的数据添加到模型中,无形中也给我们提供了便利,这也可能是它命名为ModelAttribute的原因。
@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。
@RequestMapping(value="/users/{userId}/topics/{topicId}")
public String test(
@PathVariable(value="userId")int userId,
@PathVariable(value="topicId")int topicId)
如请求的URL为“控制器URL/users/123/topics/456”,则自动将URL中模板变量{userId}和{topicId}绑定到通过@PathVariable注解的同名参数上,即入参后userId=123、topicId=456。
@Responsebody表示该方法的返回结果直接写入HTTP response body中。一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@Responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@Responsebody后,会直接返回json数据。
@RequestBody将HTTP请求正文插入方法中,使用适合的HttpMessageConverter将请求体写入某个对象。
$("#btn2").click(function(){
varurl='<%=request.getContextPath()%>/User/addUserInfo';
vardata={"user_name":$("#userName").val(),"user_sex":$("#userSex").val(),"user_age":$("#userAge").val(),
"user_email":$("#userEmail").val(),"user_telephone":$("#userTelephone").val(),"user_education":$("#userEducation").val(),
"user_title":$("#userTitle").val()};
$.ajax({
type:'POST',
contentType : 'application/json',
url:url,
dataType:"json",
data:JSON.stringify(data),
async:false,
success:function(data){
alert("新增成功!");
},
error: function(XMLHttpRequest,textStatus, errorThrown){
alert(XMLHttpRequest.status);
alert(XMLHttpRequest.readyState);
alert(textStatus);
}
})
})
@RequestMapping(value="/addUserInfo",method=RequestMethod.POST)
@ResponseBody
//将请求中的data写入UserModel对象中
public String addUserInfo(@RequestBody UserModeluser){
System.out.println("user_name--------"+user.getUser_name());
System.out.println("user_sex--------"+user.getUser_sex());
System.out.println("user_age--------"+user.getUser_age());
System.out.println("user_email--------"+user.getUser_email());
System.out.println("user_title--------"+user.getUser_title());
System.out.println("user_education--------"+user.getUser_education());
System.out.println("user_telephone--------"+user.getUser_telephone());
//不会被解析为跳转路径,而是直接写入HTTPresponse body中
return "{}";
}
@RequestBody 将HTTP请求正文转换为适合的HttpMessageConverter对象。
@ResponseBody 将内容或对象作为 HTTP 响应正文返回,并调用适合HttpMessageConverter的Adapter转换对象,写入输出流。
@RequestBody
作用:
i) 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;
ii) 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。
使用时机:
A) GET、POST方式提时, 根据request header Content-Type的值来判断:
application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);
multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);
其他格式,必须(其他格式包括application/json,application/xml等。这些格式的数据,必须使用@RequestBody来处理);
B) PUT方式提交时, 根据request header Content-Type的值来判断:
application/x-www-form-urlencoded, 必须;
multipart/form-data, 不能处理;
其他格式, 必须;
说明:request的body部分的数据编码格式由header部分的Content-Type指定;
@ResponseBody
作用:
该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:
返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
这里有三种指令标签:
指令 |
描述 |
<%@ page ... %> |
定义页面的依赖属性,比如脚本语言、error页面、缓存需求等等 |
<%@ include ... %> |
包含其他文件 |
<%@ taglib ... %> |
引入标签库的定义,可以是自定义标签 |
JSP支持九个自动定义的变量,江湖人称隐含对象。这九个隐含对象的简介见下表:
对象 |
描述 |
request |
HttpServletRequest类的实例 |
response |
HttpServletResponse类的实例 |
out |
PrintWriter类的实例,用于把结果输出至网页上 |
session |
HttpSession类的实例 |
application |
ServletContext类的实例,与应用上下文有关 |
config |
ServletConfig类的实例 |
pageContext |
PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问 |
page |
类似于Java类中的this关键字 |
Exception |
Exception类的对象,代表发生错误的JSP页面中对应的异常对象 |
· getParameter(): 使用 request.getParameter()方法来获取表单参数的值。
· getParameterValues(): 获得如checkbox类(名字相同,但值有多个)的数据。接收数组变量,如checkbox类型
· getParameterNames():该方法可以取得所有变量的名称,该方法返回一个Emumeration。
· getInputStream():调用此方法来读取来自客户端的二进制数据流。
Web.xml 中的 Servlet 过滤器映射(Servlet Filter Mapping)
·
指定一个过滤器。
o
用于为过滤器指定一个名字,该元素的内容不能为空。
o
元素用于指定过滤器的完整的限定类名。
o
元素用于为过滤器指定初始化参数,它的子元素
指定参数的名字,
指定参数的值。
o 在过滤器中,可以使用FilterConfig
接口对象来访问初始化参数。
·
元素用于设置一个 Filter所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径
o
子元素用于设置filter的注册名称。该值必须是在
元素中声明过的过滤器的名字
o
设置 filter所拦截的请求路径(过滤器关联的URL样式)
·
指定过滤器所拦截的Servlet名称。
·
指定过滤器所拦截的资源被 Servlet容器调用的方式,可以是REQUEST
,INCLUDE
,FORWARD
和ERROR
之一,默认REQUEST
。用户可以设置多个
子元素用来指定 Filter 对资源的多种调用方式进行拦截。
·
子元素可以设置的值及其意义
o REQUEST
:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
o INCLUDE
:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
o FORWARD
:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
o ERROR
:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
1) 添加
Cookie c=new Cookie(name,value);
c.setMaxAge(maxAge);
c.setPath(path);
2) 获取
/**
* 将cookie保存到map中 方便读取
* @param request
* @return
*/
public static Map
Cookie[]cookies=request.getCookies();
Map
if(cookies!=null){
for(Cookiecookie:cookies){
map.put(cookie.getName(),cookie);
}
}
return map;
}
/**
* 根据名称获取cookie
* @param request
* @param name
* @return
*/
public static CookieGetCookieByName(HttpServletRequest request, String name){
Map
if(cookieMap.containsKey(name)){
return cookieMap.get(name);
}
return null;
}
1)添加
HttpSession session=request.getSession();
session.setAttribute("user",user);
2)删除
session.removeAttribute("user");
session.invalidate();
Web.XML中配置session过期时间
<session-config>
<session-timeout>15session-timeout>
session-config>
3)获取
Userusers=(User)session.getAttribute("user");
1)保存
Application.setAttribute(“name”,value);
2)获取
Application.getAttribute(“name”);
实现场景:可用来记录访问量,它的生命周期是整个应用程序启动和关闭
使用以上方法,在 web服务器重启后,计数器会被复位为 0,即前面保留的数据都会消失
使用数据库解决或者是将参数放在链接上。
创建一个文件上传表单
下面的 HTML 代码创建了一个文件上传表单。以下几点需要注意:
· 表单 method 属性应该设置为 POST 方法,不能使用 GET 方法。
· 表单 enctype 属性应该设置为 multipart/form-data.
· 表单 action 属性应该设置为在后端服务器上处理文件上传的 Servlet文件。下面的实例使用了 UploadServlet Servlet来上传文件。
· 上传单个文件,您应该使用单个带有属性 type="file"的 标签。为了允许多个文件上传,请包含多个 name属性值不同的 input标签。输入标签具有不同的名称属性的值。浏览器会为每个 input标签关联一个浏览按钮。
引用核心标签库的语法如下:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
JSTL格式化标签用来格式化并输出文本、日期、时间、数字。引用格式化标签库的语法如下:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
标签 |
描述 |
|
用于在JSP中显示数据,就像<%= ... > |
|
用于保存数据 |
|
用于删除数据 |
|
用来处理产生错误的异常状况,并且将错误信息储存起来 |
|
与我们在一般程序中用的if一样 |
|
本身只当做 |
|
|
|
|
|
检索一个绝对或相对 URL,然后将其内容暴露给页面 |
|
基础迭代标签,接受多种集合类型 |
|
根据指定的分隔符来分隔内容并迭代输出 |
|
用来给包含或重定向的页面传递参数 |
|
重定向至一个新的URL. |
|
使用可选的查询参数来创造一个URL |
JSTL格式化标签用来格式化并输出文本、日期、时间、数字。引用格式化标签库的语法如下:
<%@ taglib prefix="fmt"
uri="http://java.sun.com/jsp/jstl/fmt" %>
标签 |
描述 |
|
使用指定的格式或精度格式化数字 |
|
解析一个代表着数字,货币或百分比的字符串 |
|
使用指定的风格或模式格式化日期和时间 |
|
解析一个代表着日期或时间的字符串 |
|
绑定资源 |
|
指定地区 |
|
绑定资源 |
|
指定时区 |
|
指定时区 |
|
显示资源配置文件信息 |
|
设置request的字符编码 |
JSTL SQL标签库提供了与关系型数据库(Oracle,MySQL,SQL Server等等)进行交互的标签。引用SQL标签库的语法如下:
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
标签 |
描述 |
|
指定数据源 |
|
运行SQL查询语句 |
|
运行SQL更新语句 |
|
将SQL语句中的参数设为指定值 |
|
将SQL语句中的日期参数设为指定的java.util.Date 对象值 |
|
在共享数据库连接中提供嵌套的数据库行为元素,将所有语句以一个事务的形式来运行 |
JSTL XML标签库提供了创建和操作XML文档的标签。引用XML标签库的语法如下:
<%@ taglib prefix="x"
uri="http://java.sun.com/jsp/jstl/xml" %>
在使用xml标签前,你必须将XML和 XPath的相关包拷贝至你的
· XercesImpl.jar
下载地址: http://www.apache.org/dist/xerces/j/
· xalan.jar
下载地址: http://xml.apache.org/xalan-j/index.html
标签 |
描述 |
|
与<%= ... >,类似,不过只用于XPath表达式 |
|
解析 XML 数据 |
|
设置XPath表达式 |
|
判断XPath表达式,若为真,则执行本体中的内容,否则跳过本体 |
|
迭代XML文档中的节点 |
|
|
|
|
|
|
|
将XSL转换应用在XML文档中 |
|
与 |
JSTL包含一系列标准函数,大部分是通用的字符串处理函数。引用JSTL函数库的语法如下:
<%@ taglib prefix="fn"
uri="http://java.sun.com/jsp/jstl/functions" %>
函数 |
描述 |
fn:contains() |
测试输入的字符串是否包含指定的子串 |
fn:containsIgnoreCase() |
测试输入的字符串是否包含指定的子串,大小写不敏感 |
fn:endsWith() |
测试输入的字符串是否以指定的后缀结尾 |
fn:escapeXml() |
跳过可以作为XML标记的字符 |
fn:indexOf() |
返回指定字符串在输入字符串中出现的位置 |
fn:join() |
将数组中的元素合成一个字符串然后输出 |
fn:length() |
返回字符串长度 |
fn:replace() |
将输入字符串中指定的位置替换为指定的字符串然后返回 |
fn:split() |
将字符串用指定的分隔符分隔然后组成一个子字符串数组并返回 |
fn:startsWith() |
测试输入字符串是否以指定的前缀开始 |
fn:substring() |
返回字符串的子集 |
fn:substringAfter() |
返回字符串在指定子串之后的子集 |
fn:substringBefore() |
返回字符串在指定子串之前的子集 |
fn:toLowerCase() |
将字符串中的字符转为小写 |
fn:toUpperCase() |
将字符串中的字符转为大写 |
fn:trim() |
移除首位的空白符 |
JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:
· 这个Java类必须具有一个无参的构造函数
· 属性必须私有化。
· 私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
上面我们在index.jsp中使用
找到tomcat服务器下的"work\Catalina\localhost\项目名称\org\apache\jsp"这个目录,就可以看到将index.jsp页面翻译成servlet的java源码了,如下所示:
使用文本编辑器打开index_jsp.java文件,在_jspService方法中可以看到person对象的创建过程,如下所示:
1 gacl.javabean.study.Person person = null;
2 synchronized (_jspx_page_context) {
3 person = (gacl.javabean.study.Person) _jspx_page_context.getAttribute("person", PageContext.PAGE_SCOPE);
4 if (person == null){
5 person = new gacl.javabean.study.Person();
6 _jspx_page_context.setAttribute("person", person, PageContext.PAGE_SCOPE);
7 }
8 }
下面我们来分析一下上述生成的代码:
首先是定义一个person对象,值是null
gacl.javabean.study.Person person = null;//定义一个空的person对象
然后是使用pageContext对象的getAttribute方法获取存储在PageContext.PAGE_SCOPE域中的Person对象
person = (gacl.javabean.study.Person) _jspx_page_context.getAttribute("person", PageContext.PAGE_SCOPE);
如果在PageContext.PAGE_SCOPE域中的Person对象没有找到person对象,那么就创建一个新的person对象,然后使用pageContext对象的setAttribute方法将新创建的person存储在PageContext.PAGE_SCOPE域中
if (person == null){
person = new gacl.javabean.study.Person();
_jspx_page_context.setAttribute("person", person, PageContext.PAGE_SCOPE);
}
也就是说,在index.jsp中使用