您的位置:首页 > 编程语言 > Java开发

Java Web高级编程(Spring框架)

2016-03-20 00:00 483 查看
=========================================================================
---Spring Framework容器以一个或多个应用上下文的形式存在 由org.springframework.context.ApplicationContext接口表示
一个应用上下文管理着一组bean等

---bean的作用域
singleton 默认 每个容器只有一份对象实例
prototype 每一次程序向Spring框架申请一次IoC的时候就new一次 创建一个实例
request(session) 一个request一个实例 当且仅当在这个request(session)

---有许多接口继承了 ApplicationContext
ConfigurableApplicationContext是可配置的 基本的 ApplicationContext 只是可读的
org.springframework.web.context.WebApplicationContext和ConfigurableWebApplicationContext接口被设计用于Servlet容器中运行的Java EE 他们提供了对底层ServletContext和ServletConfig的访问

具体类ClassPathXmlApplicationContext和FileSystemXmlApplicationContext被设计用于在独立运行的程序中从XML文件加载Spring配置 XmlWebApplicationContext被设计用于在Java EE Web应用程序中实现相同的目标
AnnotationConfigApplicationContext和AnnotationConfigWebApplicationContext 是使用注解的

---每个DispatcherServlet实例都有自己的应用上下文 包含了对Web应用程序的ServletConfig和它自己的ServletContext的引用

没有一个DispatcherServlet可以访问其他DisepatcherSrvlet的应用上下文 所以通常在一个公共的根应用上下文中共享特定的 bean Web

应用程序的全局应用上下文是所有DispatcherServlet的父亲 通过 org.springframework.web.context.ContextLoaderListener 创建 它也有ServletContext的引用 但是他不属于任何特定的Servlet所以他没有ServletConfig的引用

---通用规则 总是创建一个根应用上下文 所有其他的应用上下文都通过一种或另一种方式继承

---!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!启动 为什么要写rootContext和servletContext两个配置呢?
原因 一种典型的模式 将把所有'业务逻辑类'放在rootContext 根应用上下文中 把所有控制器放在'servletContext' Servlet应用上下文中


<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/rootContext.xml</param-value> </context-param>


将在Web应用程序启动时被初始化 在所有的Servlet之前初始化 注意这里是作用于整个Servlet上下文,而后者(下面的那个)只作用于它所指定的Servlet

<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>


!!!由监听器创建的根应用上下文将被自动设置为所有通过DispatcherServlet创建的应用上下文的
父上下文 '!!!它创建的是整个Web应用程序的全局应用上下文'


<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/resource/'*'</url-pattern> </servlet-mapping> <servlet> <servlet-name>springDispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/servletContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springDispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>


---@Inject完全是@Autowired的同义词 @Resouce也是 使用javax.*的注解 有利于将应用程序和spring解耦 使得切换框架简单

@Autowired 指定自动装配

1.@Autowired 标注setter方法 默认采用byType


@Autowired public void setAxe ( Axe axe ) {}


Spring将会自动搜索容器容器中类型为Axe的Bean实例 并将该Bean实例作为setAxe()的参数传入 找到多个的话会引发异常 没有找到什么都不做 没有异常

2.@Autowired标注多个参数的普通方法


@Autowired public void prepare ( Axe axe, Dog dog) {}


Spring会找类型匹配的Bean 如果恰好为每个参数都找到一个类型匹配的Bean Spring会自动以这些Bean作为参数来调用该方法

3.@Autowired修饰构造器和实例变量


@Autowired private Axe axe; @Autowired public Chinese ( Axe axe, Dog dog) {}


修饰实例变量时 Spring将会把容器中与该实例类型匹配的Bean设置为该实例变量的值 比如例子
Spring会搜索容器中类型为Axe的Bean 如果找到就会将该Bean设置成axe实例变量的值

可以使用@Qualifier修饰实例变量 实现精准的自动装配


@Autowired @Qualifier ( "steelAxe" ) //搜索id为steelAxe的Axe实例 private Axe axe;


还可以做方法的形参


public void setAxe ( @Qualifier( "steelAxe" ) Axe axe ) {}


---

<mvc:annotation-driven /> <!--开启spring mvc的annotation--> <context:annotation-config /> <!--开启spring的annotation--> <context:component-scan base-package="com.wrox"> <context:exclude-filter type="annotation"  <!-- 扫描所有的@Conponent 但@Controller除外-->  expression="org.springframework.stereotype.Controller" /> </context:component-scan>


要是写了两个应用上下文的话 有些组件会被扫描两次 我们真正希望的是分离bean 根应用上下文 保存服务 仓库和其他业务逻辑片段 而DispatcherServlet的应用上下文应该包含Web控制器

---
告诉Spring实例化GreetingServiceImpl

@Service public class GreetingServiceImpl implements GreetingService {}


告诉Spring将GreetingService注入到HelloController中 使用@Autowired标注设置方法

@Autowired public void setGreetingService(GreetingService greetingService) {}


===============================Spring的传值================================

小总结:

@RequestParam("username")是取从JSP或者URL传过来的请求
然后JSP从后台通过Model拿值 通过Model 建议用Model传值
public String printWelcome(@RequestParam("username") String username, Model model)
{
model.addAttribute("username", username);
}

public String printWelcome(ModelMap model)
{
model.addAttribute("message", "Hello world!");
//${message} 取出来就是"Hello world! 其实就像是一个字典
...
}

============================RequestMapping=================================

---RequestMapping
1.URL限制
: 最直接 缩小了URL的请求范围 匹配到多个不同的URL映射 最具体的胜出
2.HTTP请求方法限制: method=RequestMethod.GET
3.请求参数限制: @RequestMapping(value="can", params={"employee", "confim=true"})
只有在employee参数存在并且confirm参数存在而且为true的时候 才调用这个方法

4.请求头限制: @RequestMapping(value="user", headers={"X-Client", "content-type=text/*"})
只匹配包含了 X-Client头并且Content-Type头为任意文本类型的请求

5.内容类型限制: @RequestMapping(value="song", consumes={ "text/json", "application/json"},
produces={"text/json", "application/json" })
内容类型限制是有用的 但是不是好方法

---@RequestMapping 的params特性 作用请求参数限制
@RequestMapping(value="can", params={"employee", "confim=true"})
RequestParam 也是请求参数(?name=aaa)
eg.
@RequestMapping ("user")
public String user ( @RequestParam( "id" ) long userId,
@RequestParam( vlaue = "name" , required = false) String name )

=========================控制器的方法参数==============================

---!!!控制器的方法参数 就是public String user (...) 里面...的那些参数(形参)
1.标准Servlet类型
HttpServletRequest/Response
HttpSession 所以要用session 直接传进去就好了
Input/OutputStream
Reader/Writer

2.注解请求属性

@RequestParam( value="name", required=false ) String name,
写了required=false之后就不用强制这个参数了

i.@RequestMapping ("user")
public String user ( @RequestParam( "id" ) long userId,
@RequestParam( vlaue = "name" , required = false) String name )

ii.@PathVariable用作URI的模板
@RequestMapping ( value = "user/{userId}", method = RequestMethod.GET )
public String user ( @PathVariable ( "userId" ) long userId ) {}

可以用Map<String, String>的单个方法标注@PathVariable 他将包含URL中所有的URI
@RequestMapping ( value="bar/{var1}/foo/{var2}" )
public String barFoo ( @PahtVariable Map<String, String> variables ) {...}

iii.@RequestHeader
@RequestMapping( "foo" )
public String foo ( @RequestHeader( "Content-Type" ) String contentType,
@RequestHeader( value = "X-Custom-Header", required = false )
Date customHeader ) {...}

@RequestMapping( "bar" )
public String bar ( @RequestHeader MultiValueMap< String, String > headers) {...}

@RequestMapping( "baz" )
public String baz ( @RequestHeader HttpHeaders headers) {...}
foo将按照名称获得两个头的数据 放到定义的contentType和customeHeader中
bar和baz将获得所有头的数据 放到定义的变量中

3.输入绑定表单对象(jsp->后台)
不需要实现接口和写注解进行标记
从表单拿的话 Spring会自动吧模型上的UserForm绑定到视图中的表单字段 它将在提交时自动把请求参数转换为表单对象(由FormHttpMessageConverter自动转换)
eg.有一个UserRegistrationForm类 里面有username password email字段和他们的get set方法 然后Spring将在UserRegistrationForm类中寻找set开头的方法 然后它将使用参数名称吧请求参数映射到表单对象属性 例如:调用setUsername方法时将使用请求参数username的值作为参数 如果不匹配 就忽略

4.请求正文转换和实体
@RequestBody
例如:在RESTful Web服务中 POST或者PUT请求可能包含了 一个JSON或者XML格式的请求正文 用于代表比x-www-form-urlencoded更复杂的数据
当该数据代表某种对象时 他通常会被引用为请求实体或者HTTP实体 通过@RequestBody注解
Spring自动把一个请求实体转换为控制器方法参数

public class Account { public long acconutId; public String accountName; public String emailAddress; ... } @RequestMapping("acconut") public class AccountController { @RequestMapping(vlaue="update", method=RequestMethod.POST) public String update ( @RequestBody Account acconut ) {...} }


请求实体必须使用特殊的消息转换器 该转换器可以理解源格式(JSON XML 二进制等) 和 目标格式(POJO或者其他复杂对象) 17章会详解HTTP消息转换器

5.Multipart请求数据
@RequestPart

6模型类型

---@PathVariable是路径变量
@RequestMapping( value="user/edit/{userId}", method = RequestMethod.POST )
public String viewProduct( @PathVariable Long id, Model model ) {}

---从表单和从请求参数里面拿数据 都是拿到后台 后台拿到前台是model
从表单拿的话 Spring会自动吧模型上的UserForm绑定到视图中的表单字段 它将在提交时自动把
请求参数转换为表单对象(由FormHttpMessageConverter自动转换)

=========================控制器方法的返回类型==============================

---控制器方法的返回类型
1.模型类型
Map<String,Object> ModelMap 或Model

2.视图类型
View(或任意实现了View的类 如RedirectView) 返回一个显式的视图对象
ModelAndView 该类同时返回了View和模型类型 或者 字符串视图名称 和 模型类型的能力

3.响应正文实体
4.任意返回类型

返回任何其他对象

@RequestMapping ( value = "/user/home", method = RequestMethod.GET ) @ModelAttribute ( "currentUser" ) public User userHome () { User user = new User(); user.setUserId( 1234987234L ); user.setUsername( "adam" ); user.setName( "Adam Johnson" ); return user; }


@ModelAttribute ( "currentUser" ) 不写这个 默认类名 驼峰

5.异步类型

======================使用Spring的模型和视图模式=============================

---使用显示的视图和视图名称
1.使用重定向试图


model.put{"dashboardUrl", "dashboard"} return new RedirectView( "/{dashboardUrl}", true )


2.配置视图解析
ServletContextConfiguration.java

@Bean public ViewResolver viewResolver () { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setViewClass( JstlView.class ); resolver.setPrefix( "/WEB-INF/jsp/view/" ); resolver.setSuffix( ".jsp" ); return resolver; }


3.创建JSP视图 可以用@elvariable类型提示

---!!!使用含有模型特性的隐式视图
1.配置视图名称转换 一般默认的就可以了
例如URL http:/localhost:8080/mvc/foo 被转化为视图名称foo
URL http:/localhost:8080/mvc/foo/bar.html 被转化为视图名称foo/bar

@Bean public RequestToViewNameTranslator viewNameTranslator () { return new DefaultRequestToViewNameTranslator(); }


2.使用@ModelAttribute


@RequestMapping ( value = "/user/home", method = RequestMethod.GET ) @ModelAttribute ( "currentUser" ) public User userHome () { User user = new User(); user.setUserId( 1234987234L ); user.setUsername( "adam" ); user.setName( "Adam Johnson" ); return user; }


@ModelAttribute ( "currentUser" )告诉Spring返回的User应该被加到模型的特性键currentUser上
如果没有该注解 Spring将使用默认的特性键user(根据返回类型的类名 驼峰命名)
在JSP中使用

<%--@elvariable id="currentUser" type="com.wrox.site.User"--%> ID: ${currentUser.userId}<br /> Username: ${currentUser.username}<br /> Name: ${currentUser.name}<br />


---!!!返回响应实体
通常是RESTful Web服务或者其他自动化任务的保留任务 大多数情况只有js的应用程序发出Ajax GET或者POST请求时游览器才会牵涉进来 因此 有关HTTP实体和内容协商 在17章讲解 这里只讲解处理响应实体 配置消息转化器和内容协商的基础知识

1.配置消息转换器
2.配置内容协商
3.使用@ResponseBody

=============================使用表单简化开发===============================
x-www-form-urlencoded请求正文(表单提交)
org.springframework.http.converter.FormHttpMessageConverter负责将x-www-form-urlencoded消息转化为Controller可以处理的表单对象 将进入的消息转化为Java对象 或者将Java对象转换为发出的消息

---有业务对象和表单对象 User和UserForm

1.在模型中添加表单对象


@RequestMapping ( value = "create", method = RequestMethod.GET ) public String create ( Map< String, Object > model ) { model.put( "ticketForm", new Form() ); return "ticket/add"; }


ticket/add.jsp

<form:form method = "post" enctype = "multipart/form-data"  modelAttribute = "ticketForm" > <form:label path = "subject" >Subject</form:label ><br /> <form:input path = "subject" /><br /><br /> <form:label path = "body" >Body</form:label ><br /> <form:textarea path = "body" rows = "5" cols = "30" /><br /><br /> <b >Attachments</b ><br /> <input type = "file" name = "attachments" multiple = "multiple" /><br /><br /> <input type = "submit" value = "Submit" /> </form:form >


2.获得被提交的表单数据 自动接收从请求参数转换成的被提交的Form对象

Principal principal可以换成HttpSession session

@RequestMapping ( value = "create", method = RequestMethod.POST ) public View create ( Principal principal, Form form ) throws IOException { Ticket ticket = new Ticket(); ticket.setCustomerName( principal.getName() ); ticket.setSubject( form.getSubject() ); ticket.setBody( form.getBody() ); for ( MultipartFile filePart : form.getAttachments() ) { log.debug( "Processing attachment for new ticket." ); Attachment attachment = new Attachment(); attachment.setName( filePart.getOriginalFilename() ); attachment.setMimeContentType( filePart.getContentType() ); attachment.setContents( filePart.getBytes() ); if ( ( attachment.getName() != null && attachment.getName().length() > 0 ) ||  ( attachment.getContents() != null && attachment.getContents().length > 0 ) ) { ticket.addAttachment( attachment ); } } this.ticketService.save( ticket ); return new RedirectView( "/ticket/view/" + ticket.getId(), true, false ); }


=========================================================================

---转发比重定向快 重定向经过客户端 转发没有 而且转发能保持request内的对象
如果需要跳转到一个其它服务器上的资源,则必须使用重定向
转发是服务器行为,重定向是客户端行为


在重定向的过程中 传输的信息会被丢失。地址变了 产生了一个新的请求 所以经过一次重定向后request内的对象将无法使用

转发过程:客户浏览器发送http请求——》web服务器接受此请求——》调用内部的一个方法在容器内部完成请求处理和转发动作——》将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。

重定向过程:客户浏览器发送http请求——》web服务器接受后发送302状态码响应及对应新的location给客户浏览器——》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址——》服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。

在jsp页面中你也会看到通过下面的方式实现转发:
<jsp:forward page="apage.jsp" />

当然也可以在jsp页面中实现重定向:
<%response.sendRedirect("new.jsp"); %> //重定向到new.jsp

一个使用重定向的场景是避免用户重新加载页面时再次调用同样的动作
return "redirect:/product_view/" + saveProduct.getId();
spring3.1通过了Flash属性提供了一种重定向传值的方法

---spring mvc一些传递

1.在后台一个controller跳转到另一个controller

新增在后台完成之后我要跳转到列表页面,不需要传递参数,列表页面默认查询所有的

方式一:使用ModelAndView
return new ModelAndView("redirect:/toList");
这样可以重定向到toList这个方法

方式二:返回String
return "redirect:/ toList ";

2.列表页面有查询条件,跳转后我的查询条件不能丢掉,需要带参数的了,带参数可以拼接url


方式一:自己手动拼接url
new ModelAndView("redirect:/toList?param1="+value1+"¶m2="+value2);
这样有个弊端,就是传中文可能会有乱码问题。

方式二:用RedirectAttributes,这个是发现的一个比较好用的一个类
这里用它的addAttribute方法,这个实际上重定向过去以后你看url,是它自动给你拼了你的url。
使用方法:

attr.addAttribute("param", value);
return "redirect:/namespace/toController";
这样在toController这个方法中就可以通过获得参数的方式获得这个参数,再传递到页面。过去的url还是和方式一一样的。

3.带参数不拼接url页面也能拿到值(重点)
redirectAttributes.addFlashAttribute("param", value);
return "redirect:/namespace/toController";
原理是放到session中,session在跳到页面后马上移除对象。所以刷新一下后这个值就会丢掉。

---
return "/list";和return "list";是一样的 都会加上prefix
return new RedirectView( "/list", true, false );的话
写list 是到http:/localhost:8080/user/edit/list
写/list 是到http:/localhost:8080/list
写/user/list 就是对的 所以用RedirectView的时候 一定要加上'/' 而且要写全路径

---不同的用户界面将共享相同的服务 这样业务逻辑将在所有的用户界面之间保持不变 所以 不应该在Web应用上下文中管理服务和仓库 而是应该在根应用上下文中 他是所有Web应用上下文的父亲

---Spring的编程配置

Bootstrap.java

@SuppressWarnings ( "unused" ) public class Bootstrap implements WebApplicationInitializer { @Override public void onStartup ( ServletContext container ) throws ServletException { container.getServletRegistration( "default" ).addMapping( "/resource/*" ); AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register( RootContextConfiguration.class ); container.addListener( new ContextLoaderListener( rootContext ) ); AnnotationConfigWebApplicationContext servletContext = new AnnotationConfigWebApplicationContext(); servletContext.register( ServletContextConfiguration.class ); ServletRegistration.Dynamic dispatcher = container.addServlet( "springDispatcher", new DispatcherServlet( servletContext ) ); dispatcher.setLoadOnStartup( 1 ); dispatcher.addMapping( "/" ); } }


RootContextConfiguration.java

@Configuration @ComponentScan ( basePackages = "com.wrox.site", excludeFilters = @ComponentScan.Filter ( Controller.class ) ) public class RootContextConfiguration {}


ServletContextConfiguration.java

@Configuration @EnableWebMvc @ComponentScan ( basePackages = "com.wrox.site", useDefaultFilters = false, includeFilters = @ComponentScan.Filter ( Controller.class ) ) public class ServletContextConfiguration {}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: