Spring框架

什么是Spring:

  是一个开源的、用来简化企业级应用开发的框架。
  简化:spring对很多常用的API做了封装(比如,使用spring jdbc来访问数据库,就不再需要获得连接与关闭连接)。
  解耦:spring可以帮我们管理对象及对象之间的关系,这样一来,软件的可维护性会大大提高。
  集成:spring可以将其它的一些框架(比如quartz)集成进来。

Spring容器:

Spring容器是什么:

  spring框架中的一个重要的模块,用来管理对象。

如何启动Spring容器:

  step1. 导入spring框架相关的jar文件。

  step1. 导入spring框架相关的jar文件。

  step2. 添加spring的配置文件。

  step3. 启动spring容器:ApplicationContext ac =new ClassPathXmlApplicationContext(“applicationContext.xml”);

Javabean的条件:

  - 类的类型是public;

  - 有完整的包结构;

  - 类实现了序列化接口;

  - 类有无参构造器;

  - 属性有对应的get/set方法;

spring将所有被容器管理的Java类称之为一个Javabean。

利用容器创建对象的三种方式:

  1. 无参构造器(重点):   

   - 配置文件:

<!-- 利用无参构造器:创建一个bean实例。 
	id属性:要求唯一; 
	class属性:要写完整的类名;
 -->
<bean id="hb" class="first.HelloBean" />
   - 启动Spring容器:
//配置文件名称可以改,位置也可以改变。
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//启动Spring容器
System.out.println(ac);
		
//getBean方法:通知容器,依据id,获取对应的bean实例。
HelloBean hb = ac.getBean("hb",HelloBean.class);
System.out.println(hb);

  注意:该类必须带有无参构造器。 

2. 静态工厂方法(了解):

<!-- 利用静态工厂方法:创建一个bean实例。
 	factory-method属性:指定要调用的静态方法。
-->
<bean id="cal1" class="java.util.Calendar" factory-method="getInstance" />

  3. 实例工厂方法(了解):

<!-- 利用实例工厂方法:创建一个bean实例。
	 factory-bean属性:一个bean实例的id;
	 factory-method属性: 要调用的方法。
 -->
<bean id="time1" factory-bean="cal1" factory-method="getTime" />

作用域:

  - 默认情况下,容器对于某个bean的配置,只会创建一个实例;

  - 可以通过scope属性来指定作用域。

<!-- scope属性:指定作用域。
     默认值为singleton(单例);
     如果值为prototype(原型),每getBean一次;会创建一个新的对象。
-->
<bean id="eb" class="other.ExampleBean" scope="prototype" />
//启动spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("app2.xml");
//通过容器获得一个bean实例
ExampleBean eb1 = ac.getBean("eb",ExampleBean.class);
ExampleBean eb2 = ac.getBean("eb",ExampleBean.class);
//若scope属性值为prototype,输出flase;若scope属性值为singleton,输出true		
System.out.println(eb1 == eb2);

延迟加载:

  - 默认情况下,容器启动之后,会将所用作用域为singleton的bean创建好。

  - 可以设置lazy-init属性为true,表示延迟加载(即容器启动之后,不会立即创建作用域为singleton的实例)。

<!-- scope属性:指定作用域。
     默认值为singleton(单例);
     如果值为prototype(原型),每getBean一次,会创建一个新的对象。
     lazy-init属性:值为true,表示延迟加载。
-->
<bean id="eb" class="other.ExampleBean" scope="singleton" lazy-init="true"/>
生命周期相关的几个方法:

  - 初始化方法:init-method属性指定。

  - 销毁方法:destroy-method属性指定

  - 注:销毁方法只针对scope=singleton有效。

<!-- init-method属性:指定初始化方法。
     容器创建相应的bean实例之后,会立即调用该实例的初始化方法。
     destroy-method属性:指定销毁方法。容器在删除bean实例之前,会立即调用该实例的销毁方法。
     注:销毁方法只针对"scope=singleton"有效。
-->
<bean id="mb" class="other.MessageBean" 
    init-method="init" destroy-method="destroy" scope="prototype" />

IOC(Inversion Of Controller)控制反转:

什么是IOC:

  对象之间的依赖关系由容器来建立。

DI(Dependency Injection)依赖注入:

  容器通过调用对象的构造器或者set方法来建立对象之间的依赖关系;

  注:IOC是目标,DI是手段。

依赖注入的方式:

   1. set方式:

       - 有无参构造器;

       - 有对应的set方法。


<!-- 调用set方法来建立依赖关系。
     property的name属性:指定要注入的bean的属性名称;
     ref属性:指定要注入的bean的id。 
 -->
<bean id="b1" class="ioc.B"/>
<bean id="a" class="ioc.A">
   <property name="b" ref="b1"/>
</bean>

   2. 构造器方式:

       - 有相应的带参的构造器。 

<!-- 构造器方式注入。
	index属性:指定要注入的参数的下标,下标从0开始。
 -->
<bean id="b1" class="ioc2.B"/>
<bean id="a" class="ioc2.A">
	<constructor-arg index="0" ref="b1"/>
</bean>
自动装配:

  容器默认情况下,禁止自动装配。使用autowire属性来进行自动装配(如果要使用自动装配,优先考虑byName)。autowire的取值:

    - byName:查找id等于属性名的bean,然后调用set方法完成注入。 如果找不到对应的bean,会注入null;

    - byType:查找与属性类型一致的bean,然后调用set方法完成注入。如果找不到对应的bean,会注入null。 找到多个,会报错;

    - constructor:与byType类似,只是调用构造器来完成注入。

<bean id="wt" class="autowire.Waiter"/>
<!-- byName: 查找id等于属性名的bean,然后调用set方法完成注入。如果找不到对应的bean,会注入null。
     注意:a.有无参构造器;b.有set方法
 -->
<bean id="rest" class="autowire.Restaurant" autowire="byName"/>

<!-- byType:查找与属性类型一致的bean,然后调用set方法完成注入。如果找不到对应的bean,会注入null。找到多个,会报错。
     注意:a.有无参构造器;b.有set方法
 -->
<bean id="rest" class="autowire.Restaurant" autowire="byType"/> 

<!-- constructor:与byType类似,只是调用构造器来完成注入。 -->
<bean id="rest" class="autowire.Restaurant" autowire="constructor"/>
注入基本类型的值:

  使用value属性来赋值 ,会将字符串自动转换成相应的数值类型。

注入集合类型的值:

  List . Set . Map . Properties。

//实体类属性
private String name;
private int age;
private List<String> cities;
private Set<String> interest;
private Map<String,Double> score;
private Properties db;
<!-- 注入基本类型的值
     注入集合类型的值
 -->
<bean id="eb" class="basic.ExampleBean">
       <property name="name" value="袁新"/>
       <property name="age" value="22"/>
       <property name="cities">
	  <list>
		<value>西安</value>
		<value>杭州</value>
		<value>舟山</value>
		<!-- 允许重复元素 -->
		<value>舟山</value>
	  </list>
        </property>
	<property name="interest">
	   <set>
		<value>音乐</value>
		<value>阅读</value>
		<value>编程</value>
		<!-- 不允许重复元素 -->
		<value>编程</value>
	   </set>
	</property>
	<property name="score">
	   <map>
		<entry key="english" value="90.5"/>
		<entry key="math" value="99"/>
	    </map>
	</property>
	<property name="db">
	    <props>
		<prop key="username">yuanxin</prop>
		<prop key="password">123456</prop>
	    </props>
	</property>
</bean>
将集合类型当做一个bean来配置:

<!-- 将集合类型的值当做一个bean来配置。
     命名空间(namespace):为了区分同名元素而在元素前面添加的前缀。
 -->
<util:list id="citiesBean">
    <value>北京</value>
    <value>武汉</value>
    <value>济南</value>
</util:list>
<util:set id="interestBean">
    <value>台球</value>
    <value>钓鱼</value>
    <value>做饭</value>
</util:set>
<util:map id="scoreBean">
    <entry key="english" value="80"/>
    <entry key="math" value="90"/>
</util:map>
<util:properties id="dbBean">
    <prop key="username">Tom</prop>
    <prop key="password">1234</prop>
</util:properties>
<bean id="eb2" class="basic.ExampleBean">
    <property name="cities" ref="citiesBean"/>
    <property name="interest" ref="interestBean"/>
    <property name="score" ref="scoreBean"/>
    <property name="db" ref="dbBean"/>
</bean>
	 
<!-- 读取location指定位置的文件的内容。classpath:是spring框架内部的约定。 -->
<util:properties id="config" location="classpath:config.properties"/>
Spring表达式:

  读取bean或者集合的属性值, 语法类似el表达式。

<!-- 使用spring表达式读取其它的bean的属性值。
	#{eb.name}:读取id等于eb的bean的name属性值。
	#{eb.cities[0]}:读取id等于eb的bean的cities属性值(cities是一个List,读取的是下标等于0的元素的值)。
	#{eb.score.math}:读取id等于eb的bean的score属性值(score是一个Map,读取的是key等于math的value值)。
	#{config.pagesize}:读取id等于config的bean的pagesize属性值(pagesize是properties中的key)。
 -->
<bean id="sb" class="basic.SomeBean">
    <property name="name" value="#{eb.name}"/>
    <property name="city" value="#{eb.cities[0]}"/>
    <property name="score" value="#{eb.score.math}"/>
    <property name="pageSize" value="#{config.pagesize}"/>
</bean>
使用注解简化配置文件:

  1. 什么是组件扫描:

   spring容器启动之后,会扫描指定的包及其子包下面的的所有的类,如果该类有特定的注解,则spring容器会将其纳入容器进行管理。也就是说,相当于在配置文件当中,添加了一个bean的配置。

  2. 如何使用组件扫描:

     step1. 在配置文件当中,添加<context:component-scan base-package="annotation"/>;

     step2. 在类名前,添加注解:

          a. Component 通用;

          b. Service 业务层;

          c. Repository 持久层;

          d. Controller 控制层。

     注意:以上注解,只有语义上的差异,是等价的。

  3. 两个注解:

    a. @Lazy(true): 延迟加载;

    b. @Scope("prototype"):指定作用域,默认值是singleton。

  4. 生命周期相关的注解:

    a. @PostConstruct: 指定初始化方;

    b. @PreDestroy: 指定销毁方法。

  5. 依赖注入相关的注解:

    a. Autowired和Qualifier支持set方式注入和构造器注入;

    b. Resource 只支持set方式注入。

    注意:

       - @Autowired和@Qualifier也可以加到属性前面。等介于加到set方法前面;

       - 默认情况下,容器会按照byType的方式进行注入,有可能找到多个符合要求的bean。建议使用@Qualifier来指定要注入的bean的id。

@Autowired
public Bar(@Qualifier("wt") Waiter wt) {
   System.out.println("Bar的有参构造器...");
   this.wt = wt;
}
//name属性指定注入的bean的id。
@Resource(name="wt")
public void setWt(Waiter wt) {
    System.out.println("School的setWt方法...");
    this.wt = wt;
}

Servlet MVC:


Spring MVC:


 1. spring mvc是什么:

   是一个mvc框架,便于开发基于mvc架构的web应用程序。

 2. 五大核心组件:

   a. DispatcherServlet (前端控制器)

   b. HandlerMapping (处理映射

   c. Controller (处理器)

   d. ModelAndView (处理结果)

   e. ViewResolver (视图解析器)

  工作过程:请求先发送给DispatcherServlet,DispatcherServlet会依据HandlerMapping的指示调用对应的Controller来处理。Controller将处理结果封装成ModelAndView并返回给DispatcherServlet。DispatcherServlet会依据ViewResolver的解析,调用对应的视图对象(比如jsp),生成最终的页面。


 3. 编程步骤:

  step1. 导包(spring-webmvc);
  step2. 添加spring配置文件(空白的配置文件);
  step3. 配置DispatcherServlet(web.xml);

<servlet>
  <servlet-name>action</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <!-- DispatcherServlet的初始化方法会启动spring容器,所以需要告诉它spring配置文件的位置。 
     注意:默认情况下,如果没有配置contextConfigLocation(即没有指定spring配置文件的位置),则会查找/WEB-INF/servletname-servlet.xml。-->
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:app.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>action</servlet-name>
  <url-pattern>*.do</url-pattern>
</servlet-mapping>
  step4. 写Controller(业务逻辑);

/** 处理器:处理业务逻辑 */
public class HelloController implements Controller{
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("HelloController的handleRequest方法...");
		/*
		 * ModelAndView有两个构造器。
		 *  1. ModelAndView(String viewName):viewName是视图名。
		 *  2. ModelAndView(String viewName,Map data):data是数据。
		 */
		return new ModelAndView("hello");
	}
}
  step5. 写jsp;
  step6. 在spring配置文件当中,添加HandlerMapping,ViewResolver的配置信息。

<!-- 配置HandlerMapping。 设置请求路径与处理器的对应关系。 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="mappings">
    <props>
	<prop key="/hello.do">helloController</prop>
    </props>
  </property>
</bean>
<!-- 配置处理器。 -->
<bean id="helloController" class="controller.HelloController" />
<!-- 配置视图解析器。 负责将视图名解析成真正的视图对象,比如 将"hello"解析成hello.jsp。 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/" />
  <property name="suffix" value=".jsp" />
</bean>

分层结构:


基于注解的MVC应用:

 编程步骤:

   step1. 导包
   step2. 添加spring配置文件(空白的配置文件)
   step3. 配置DispatcherServlet(web.xml)
   step4. 写Controller(业务逻辑)
       注意: 

          a. 不用实现Controller接口。

          b. 可以添加多个处理方法。

          c. 处理方法的名称可以自定义,方法的返回值可以是 ModelAndView,也可以是String。

          d. 在类名前,添加@Controller注解。

          e. 可以在类名前或者处理方法前面添加@RequestMapping注解。 该注解的作用相当于HandlerMapping。
   step5. 写jsp
   step6. 在spring配置文件当中,添加ViewResolver的配置信息。

   注意:

     - 添加组件扫描(component-scan base-package)。 

     - 添加mvc注解扫描(annotation-driven)。

<!-- 配置组件扫描。 容器才会扫描类名前的四个注解: @Component,@Controller,@Service,@Repository。 -->
<context:component-scan base-package="controller" />
<!-- 配置mvc注解扫描。 容器才会扫描mvc相关的注解,比如 @RequestMapping。 -->
<mvc:annotation-driven />
<!-- 配置视图解析器。 负责将视图名解析成真正的视图对象,比如 将"hello"解析成hello.jsp。 -->
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/" />
    <property name="suffix" value=".jsp" />
</bean>
获取请求参数:

 login.jsp页面:

<%@page pageEncoding="utf-8" contentType="text/html; charset=utf-8" %>
<html>
  <head></head>
  <body style="font-size:30px;">
     <form action="login.do" method="post">
	用户名:<input name="username"/><br/>
	密码:<input name="pwd" type="password"/><br/>
	<input type="submit" value="登录"/>
     </form>
  </body>
</html>

请求参数封装成一个Javabean:

/**
 * 将请求参数封装成一个Javabean。
 * 该类有如下要求:
 * a. 类的属性与请求参数名保持一致(名称一样,类型一致)
 * b. 这些属性应该提供相应的get/set方法。
 */
public class User {
	private String username;
	private String pwd;

        //此处为属性个get/set方法(省略)...

}

index.jsp页面:

<h1>Login success.</h1>
<h1>Welcome ${username}</h1>
<h1>Welcome ${user.username}</h1>

方式一:通过request

  在处理方法里面,添加request作为方法的入参即可。
  注: 前端控制器会将request对象作为参数传进来。

@RequestMapping("/login.do")
/*
 * 读取请求参数值的第一种方式:
 * 在处理方法里面,添加request作为方法的入参即可。前端控制器会将request对象作为参数传进来。
*/
public String login(HttpServletRequest request){
	String username = request.getParameter("username");
	String pwd = request.getParameter("pwd");
	System.out.println("username:" + username + " pwd:" + pwd);
	return "index";
}

方式二:入参与请求参一致

  将处理方法的入参与请求参数名保持一致。
  注: 如果不一致,可以使用@RequestParam(请求参数名)。

@RequestMapping("/login2.do")
/*
 * 读取请求参数值的第二种方式:
 * 将处理方法的入参与请求参数名保持一致。
 * 注:如果不一致,可以使用@RequestParam(请求参数名)。
 */
public String login2(String username,@RequestParam("pwd") String pwd1){
	System.out.println("username:" + username + " pwd:" + pwd1);
	return "index";
}

方式三:Javabean方式

  将请求参数封装成一个javabean。 

  注: 该类有如下要求:
   a. 类的属性与请求参数名保持一致 (名称一样,类型一致)
   b. 这些属性应该提供相应的get/set方法。

@RequestMapping("/login3.do")
/*
 * 读取请求参数值的第三种方式:
 * 将请求参数封装成一个javabean。
 */
public String login3(User user){
	System.out.println("username:" + user.getUsername() + " pwd:" + user.getPwd());
	return "index";
}
向页面传值:

 方式一:通过ModelAndView

   a.处理方法的返回值类型设置为ModelAndView。
   b.将处理结果添加到Map对象里面。
   c.构造ModelAndView对象。

@RequestMapping("/login4.do")
/*
 * 向页面传值的第一种方式:通过ModelAndView。
 * 将处理方法的返回值类型设置为ModelAndView。
*/
public ModelAndView login4(User user){
	System.out.println(user.getUsername() + ":" + user.getPwd());
	/*
	 * ModelAndView(String viewName,Map data);
	 * viewName:视图名。
	 * data:处理结果。
	*/
	//将处理结果封装成一个Map对象。
	Map<String,Object> data = new HashMap<String,Object>();
	//相当于执行了request.setAttribute
	data.put("username", user.getUsername());
	data.put("user", user);
	ModelAndView mav = new ModelAndView("index",data);
	return mav;
}

 方式二: 通过ModelMap

   a.要将ModelMap对象作为方法的入参。

   b.将处理结果添加到ModelMap。

@RequestMapping("/login5.do")
/*
 * 向页面传值的第二种方式:
 * 通过ModelMap对象。要将ModelMap对象作为方法的入参。
*/
public String login5(User user,ModelMap mm){
	System.out.println(user.getUsername() + ":" + user.getPwd());
	//将处理结果添加到ModelMap。
	//相当于request.setAttribute
	mm.addAttribute("user",	user);
	return "index";
}

 方式三: 通过request

   a.将Http ServletRequest作为方法的入参。

   b. 将参数的值设置到request中。

@RequestMapping("/login6.do")
/*
 * 向页面传值的第三种方式:使用request。
*/
public String login6(HttpServletRequest request){
	String username = request.getParameter("username");
	request.setAttribute("username", username);
	return "index";
}

 方式四:通过session

   a.将HttpSession作为方法的入参。

   b.将参数封装的对象存入session。

@RequestMapping("/login7.do")
/*
 * 向页面传值的第四种方式:使用session。
*/
public String login7(User user,HttpSession session){
	System.out.println(user.getUsername());
	session.setAttribute("user", user);
	return "index";
}
重定向:
 1. 处理方法的返回值是String:

     return "redirect:toIndex.do";

@RequestMapping("/login8.do")
/*
 * 重定向的第一种方式:
 * 如果处理方法的返回值是String,则在重定向地址前添加"redirect:"作为前缀。
*/
public String login8(User user){
	System.out.println(user.getUsername());
	return "redirect:toIndex.do";
}

 2. 处理方法的返回值是ModelAndView:

     RedirectView rv = newRedirectView("toIndex.do");
     ModelAndView mav = new ModelAndView(rv);

@RequestMapping("/login9.do")
/*
 * 重定向的第二种方式:
 * 如果处理方法的返回值是ModelAndView。
*/
public ModelAndView login9(User user){
	RedirectView rv = new RedirectView("toIndex.do");
	ModelAndView mav = new ModelAndView(rv);
	return mav;
}

中文参数值如何处理:

 只需要配置springmvc提供的一个过滤器(CharacterEncodingFilter)。

   a.表单必须以post方式提交。
   b.表单所在的页面的编码与配置文件中的编码一致。

<filter>
   <filter-name>encodingFilter</filter-name>
   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
	<param-name>encoding</param-name>
	<param-value>UTF-8</param-value>
   </init-param>
</filter>
<filter-mapping>
   <filter-name>encodingFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

拦截器:

什么是拦截器:

 是springmvc提供的一个组件,前端控制器会先调用拦截器 ,然后再调用处理器。

如何编写一个拦截器:

 step1, 写一个java类,实现HandlerInterceptor接口。
 step2, 将拦截处理逻辑写在对应的方法里。
    preHandle方法: 前端控制器先调用拦截器的preHandle方法,再调用处理器的处理方法。如果该方法的返回值为true,表示继续向后调用。
    postHandle方法:处理器的方法已经执行完毕,在将ModelAndView返回给前端控制器之前执行该方法。所以,可以在该方法里面修改 ModelAndView。
    afterCompletion方法:最后执行的方法。
 step3,配置拦截器(spring配置文件)。

<!-- 配置拦截器:
     配置拦截器可以在interceptors下面配置多个拦截器, 拦截器的执行的先后顺序由配置的先后顺序来决定。 
     注意:如果要拦截多层路径,比如"/abc/hello2.do",应该使用 /**。
 -->
<mvc:interceptors>
   <mvc:interceptor>
	<mvc:mapping path="/**"/>
	<!-- 排除登录相关的请求 -->
	<mvc:exclude-mapping path="/toLogin.do"/>
	<mvc:exclude-mapping path="/login.do"/>
	<bean class="com.tarena.oss.interceptors.SessionInterceptor"/>
   </mvc:interceptor>
</mvc:interceptors>

让Spring来处理异常:

方式一:配置简单异常处理器

    只适合简单的异常处理。

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 
   <property name="exceptionMappings"> 
	<props> 
	   <prop key="java.lang.NumberFormatException">error1</prop> 
	   <prop key="java.lang.StringIndexOutOfBoundsException">error2</prop> 
	</props> 
   </property> 
</bean>

方式二:使用@ExceptionHandler  

    step1. 添加一个异常处理方法,该方法需要使用@ExceptionHander 修饰。
    step2.在异常处理方法内部,依据异常类型分别采取不同的处理。

@ExceptionHandler
/*
 * 这是一个异常处理方法,用来处理其它方法所抛出的异常。
 * ex:其它方法所抛出的异常。
 */
public String execute(HttpServletRequest request,Exception ex){
   //依据异常类型,分别做不同的处理。
   if(ex instanceof NumberFormatException){
	request.setAttribute("msg","请输入数字");
	return "error2";
   }else if(ex instanceof StringIndexOutOfBoundsException){
	request.setAttribute("msg","数组越界");
	return "error2";
   }
   return "error3";
}
Spring jdbc

Spring jdbc 是什么:

  spring对jdbc API的简单的封装,使用spring jdbc 访问数据库,代码会比直接使用jdbc要简洁。

编程步骤:

  step1. 导包 (springjdbc相关的jar文件)
     spring-webmvc,oracle driver,dbcp,spring-jdbc,junit
  step2. 添加spring配置文件,配置JdbcTemplate。

     注:jdbcTemplate提供了很多常用的数据库操作方法。

<!-- 配置文件 -->
	
<!-- 读取db.properties文件的内容 -->
<util:properties id="jdbc" location="classpath:db.properties" />
<!-- 配置DataSource -->
<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
   <property name="driverClassName" value="#{jdbc.driver}" />
   <property name="url" value="#{jdbc.url}" />
   <property name="username" value="#{jdbc.user}" />
   <property name="password" value="#{jdbc.pwd}" />
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
   <property name="dataSource" ref="ds"/>
</bean>
  step3. 调用jdbcTempate提供的方法来访问数据库。

/**
 * jdbc的增删改查操作 
 */
@Repository("empDAO")
public class EmpDAOJdbcImpl implements EmpDAO {
	@Resource(name="jdbcTemplate")
	//注入jdbcTempate对象。
	private JdbcTemplate jt;
	
	/**
	 * jdbcTempate会将底层的异常统一转换成RuntimeException,然后抛出。
	 */
	public void save(Emp emp) {
		jt.update("INSERT INTO emp VALUES(emp_seq.nextval,?,?)", 
		new Object[]{emp.getName(),emp.getAge()});
	}
	
	public List<Emp> findAll() {
		List<Emp> emps = new ArrayList<Emp>();
		String sql = "SELECT * FROM emp";
		emps = jt.query(sql, new EmpRowMapper());
		return emps;
	}
	
	/**
	 * queryForObject方法:如果找不到记录,会抛出异常。
	 */
	public Emp findById(int id) {
		Emp emp = null;
		String sql = "SELECT * FROM emp WHERE id=?";
		Object[] params = new Object[]{id};
		List<Emp> emps = jt.query(sql, params,new EmpRowMapper());
		if(emps != null && emps.size() > 0){
			emp = emps.get(0);
		}
		return emp;
	}

	public void update(Emp emp) {
		String sql = "UPDATE emp "
				+ "SET name=?,age=? "
				+ "WHERE id=?";
		Object[] params = new Object[]{
				emp.getName(),
				emp.getAge(),
				emp.getId()
			};
		jt.update(sql, params);
	}

	public void delete(int id) {
		String sql = "DELETE FROM emp WHERE id=?";
		Object[] params = new Object[]{id};
		jt.update(sql, params);
	}

	//获得总的记录数
	public int getTotal() {
		String sql = "SELECT count(*) FROM emp";
		return jt.queryForObject(sql, Integer.class);
	}
	
	/*
	 * 写一个内部类,该类的作用是将ResultSet转换成相应的实体对象。
	 */
	class EmpRowMapper implements RowMapper<Emp> {
		/*
		 * rs:要遍历的ResultSet。
		 * index:第几条记录。
		 */
		public Emp mapRow(ResultSet rs,int index) throws SQLException {
			Emp emp = new Emp();
			emp.setName(rs.getString("name"));
			emp.setAge(rs.getInt("age"));
			emp.setId(rs.getInt("id"));
			return emp;
		}
	}
}

MyBatis:

什么是MyBatis:

  是一个开源的持久层框架,它对jdbc做了简单的封装。

如何使用MyBatis:

  step1. 导包。
  step2. 添加配置文件。
   注:配置文件主要提供以下信息
     a.数据库的连接信息。
     b.映射文件的位置。

<!-- 数据库的连接信息 -->	
<configuration>
   <environments default="environment">
	<environment id="environment">
		<transactionManager type="JDBC" />
		<dataSource type="POOLED">
			<property name="driver" value="oracle.jdbc.OracleDriver" />
			<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
			<property name="username" value="yuanxin" />
			<property name="password" value="123456" />
		</dataSource>
	</environment>
   </environments>
   <!-- 指定映射文件的位置 -->
   <mappers>
	<mapper resource="entity/EmpMapper.xml" />
   </mappers>
</configuration>

  step3. 实体类。
   注:实体类的属性名一定要与表的字段名一致。原因:mybatis将查询出来的一条记录存放到一个Map对象里面,key是字段名, value是字段值。接下来,mybatis会依据字段名,调用实体对象对应的 set方法,完成赋值。注意:如果属性名与字段名不一致,可以在写sql时, 为字段名指定别名。
  step4. 写一个映射文件(sql语句)。

<mapper namespace="dao.EmpDAO">
	<!-- 插入记录。
	     id属性:要求唯一。
	     parameterType:要写完整的实体类类名。
	 -->
	<insert id="save" parameterType="entity.Emp">
		INSERT INTO emp 
		VALUES(emp_seq.nextval,#{name},#{age})
	</insert>
	
	<!-- 查询记录。
	     resultType属性:查询出来的记录要转换成相应的对象的类型。
	 -->
	 <select id="findAll" resultType="entity.Emp">
	 	SELECT * FROM emp
	 </select>
	 
	 <!-- 查询。
	      parameterType:如果参数值是一个整数,官方用法是 java.lang.Integer,可以简写为"int"。
	  -->
	 <select id="findById" 
	 	parameterType="int" resultType="entity.Emp">
	 	SELECT * FROM emp WHERE id = #{id1}
	 </select>
	 
	
	<!-- 返回Map类型的查询结果。java.util.Map 可以简写为map。 -->
	<select id="findById2" parameterType="int" resultType="map">
		SELECT * FROM emp WHERE id = #{id1}
	</select>
</mapper>
   注:修改配置文件,将映射文件添加进来。
  step5. 使用MyBatis提供的API来访问数据库。
   添加: insert方法
   查询(返回类型为List): selectList方法
   查询(返回单个对象): selectOne方法
   修改:update方法
   删除:delete方法
   注:这些方法都需要提供要操作的sql的id,id应该按照映射文件的设置来写,可以在id前添加相应的命名空间(比如test.findAll)。

public class TestCase {
	
	private SqlSession session;
	
	@Before
	//初始化方法。junit会在测试方法运行之前,先运行初始化方法。
	public void init(){
		SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
		//创建SqlSessionFactory对象。
		SqlSessionFactory ssf = ssfb.build(TestCase.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml"));
		session = ssf.openSession();
	}
	
	@Test
	public void test1(){
		Emp emp = new Emp();
		emp.setName("King");
		emp.setAge(20);
		/*
		 * insert方法:插入记录。
		 * 第一个参数是要执行的sql的id(id是在映射文件当中设置的),第二个参数是要插入的对象。
		 */
		session.insert("save", emp);
		//要提交事务
		session.commit();
		//关闭(会关闭connection)
		session.close();
	}
	
	@Test
	//测试查询(查询所有记录)
	public void test2(){
		List<Emp> emps = session.selectList("findAll");
		System.out.println(emps);
		session.close();
	}
	
	@Test
	//测试查询(查询一条记录)
	public void test3(){
		Emp emp = session.selectOne("findById", 21);
		System.out.println(emp);
		session.close();
	}
	
	@Test
	//修改记录
	public void test4(){
		Emp emp = session.selectOne("findById", 21);
		emp.setAge(emp.getAge() * 3);
		session.update("update", emp);
		session.commit();
		session.close();
	}
	
	@Test
	//测试删除
	public void test5(){
		session.delete("delete", 21);
		session.commit();
		session.close();
	}
	
	@Test
	//测试查询结果为map
	public void test6(){
		Map map = session.selectOne("findById2", 23);
		//oracle数据库默认会将字段名变成大写形式。
		System.out.println(map.get("NAME"));
		session.close();
	}
}

工作原理:


使用Mapper映射器:

  mybatis会按照DAO接口(或者叫Mapper接口)定义,生成相应的DAO实现对象。
  注:
   a. 接口中的方法的名字与映射文件中的sqlId要一致。
   b. 接口中的方法的返回值类型、参数类型要与映射文件一致。
   c. 映射文件的命名空间要等于Mapper接口的完整的名字。
   d. 调用SqlSession提供的getMapper方法获得DAO实现对象。

public class TestCase2 {
	
	private SqlSession session;

	@Before
	public void init(){
		SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
		SqlSessionFactory ssf = ssfb.build(TestCase.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml"));
		session = ssf.openSession();
	}
	
	@Test
	public void test1(){
		//getMapper方法会按照Mapper接口要求,返回一个实现了该接口要求的对象。
		EmpDAO dao = session.getMapper(EmpDAO.class);
		System.out.println(dao.getClass().getName());
		Emp emp = new Emp();
		emp.setName("Eric");
		emp.setAge(22);
		dao.save(emp);
		//对于添加、修改、删除仍然需要提交事务。
		session.commit();
		session.close();
	}
	
	@Test
	public void test2(){
		EmpDAO dao = session.getMapper(EmpDAO.class);
		List<Emp> emps = dao.findAll();
		System.out.println(emps);
		session.close();
	}
	
	@Test
	public void test3(){
		EmpDAO dao = session.getMapper(EmpDAO.class);
		Emp2 emp2 = dao.findById3(22);
		System.out.println(emp2);
		session.close();
	}
	
}

使用resultMap解决实体类的属性名与表的字段名不一致的情况:

  step1.在映射文件当中,使用resultMap元素配置实体类的属性名与表的字段名的对应关系。

<!--使用resultMap来解决实体类的属性名与表的字段名不一致的情况。 
    type属性:实体类的名字。 
    property属性:实体类的属性名。 
    column属性:表的字段名。 
    注:如果属性名与字段名一样,就不用写了。 -->
<resultMap type="entity.Emp2" id="empRM">
   <result property="empNo" column="id" />
   <result property="eName" column="name" />
</resultMap>
<select id="findById3" parameterType="int" resultMap="empRM">
   SELECT *
   FROM emp WHERE id = #{id1}
</select>

  step2.在Mapper接口当中,声明相应的接口方法。

public void save(Emp emp);
public List<Emp> findAll();
public Emp findById(int id);
public void update(Emp emp);
public void delete(int id);
public Map findById2(int id);
//属性名与表的字段名不一致的方法	
public Emp2 findById3(int id);

  step3.调用SqlSession.getMapper方法。

Spring集成MyBatis:

编程步骤:

 step1. 导包。

   包:spring mvc(3.2.8),spring jdbc(3.2.8) dbcp(1.4),oracle driver(ojdbc14),junit(4.12), mybatis(3.2.5),mybatis-spring(1.2.2)
 step2.配置文件。
   注:只需要spring配置文件(mybatis的配置文件中的配置信息放到了spring的配置文件里面)。
 step3.实体类
 step4.映射文件
 step5.Mapper接口
 step6.修改spring配置文件,添加:
  a.SqlSessionFactoryBean
     负责创建SqlSessionFactory对象。需要为该对象注入DataSource,映射文件的位置。

  b.MapperScannerConfigurer
     负责生成符合Mapper接口要求的对象,并将这些对象添加 到spring容器里面(对象的id是Mapper接口名首字母小写)。
     注: 可以为该对象设置annotationClass属性,其作用是,只扫描 带有特定注解的Mapper接口。

<!-- 读取db.properties文件的内容 -->
<util:properties id="jdbc" location="classpath:db.properties" />
<!-- 配置DataSource -->
<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="#{jdbc.driver}" />
	<property name="url" value="#{jdbc.url}" />
	<property name="username" value="#{jdbc.user}" />
	<property name="password" value="#{jdbc.pwd}" />
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 注入DataSource -->
	<property name="dataSource" ref="ds" />
	<!-- 注入映射文件的位置信息 -->
	<property name="mapperLocations" value="classpath:entity/*.xml" />
</bean>
<!-- 配置MapperScannerConfigurer。 扫描指定包下面的所有的Mapper接口, 创建符合Mapper接口要求的对象,并且 
     会将创建好的对象放到spring容器里面。 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!-- 注入要扫描的包名 -->
	<property name="basePackage" value="dao" />
</bean>
//测试类
public class TestCase {
	
	private ApplicationContext ac = null;
	
	@Before
	//@Before修饰的方法会在测试方法执行前先执行。
	public void init(){
		//启动spring容器。
		ac = new ClassPathXmlApplicationContext("app.xml");
	}
	
	@Test
	public void test1(){
		//通过容器得到一个DAO对象。
		//bean的id是Mapper接口首字母小写。
		EmpDAO dao = ac.getBean("empDAO",EmpDAO.class);
		Emp emp = new Emp();
		emp.setName("kitty");
		emp.setAge(19);
		dao.save(emp);
	}
}

使用SqlSessionTemplate:

 编程步骤:前面5步同上。
 step6.修改spring配置文件,添加:
  a.SqlSessionFactoryBean
     负责创建SqlSessionFactory对象。需要为该对象注入DataSource,映射文件的位置。
  b.配置SqlSessionTemplate

  c.配置组件扫描
 step7.写一个DAO类,注入SqlSessionTemplate。
   注:调用SqlSessionTempate方法即可。(类似于JdbcTemplate)。

<!-- 读取db.properties文件的内容 -->
<util:properties id="jdbc" location="classpath:db.properties" />
<!-- 配置DataSource -->
<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="#{jdbc.driver}" />
	<property name="url" value="#{jdbc.url}" />
	<property name="username" value="#{jdbc.user}" />
	<property name="password" value="#{jdbc.pwd}" />
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="ssfb" class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 注入DataSource -->
	<property name="dataSource" ref="ds" />
	<!-- 注入映射文件的位置信息 -->
	<property name="mapperLocations" value="classpath:entity/*.xml" />
</bean>
<!-- 配置SqlSessionTemplate -->
<bean id="sst" class="org.mybatis.spring.SqlSessionTemplate">
	<constructor-arg index="0" ref="ssfb" />
</bean>
<!-- 配置组件扫描 -->
<context:component-scan base-package="dao" />
/**
 * 使用MyBatis提供的SqlSessionTemplate来访问数据库。
 */
@Repository("empDAO")
public class EmpDAOMybatisImpl implements EmpDAO {
	
	@Resource(name="sst")
	//Resource注解来自于javaee(javax.annotation)
	private SqlSessionTemplate sst;
	
	public void save(Emp emp) {
		sst.insert("save", emp);
	}

	public List<Emp> findAll() {
		return sst.selectList("findAll");
	}

	public Emp findById(int id) {
		return sst.selectOne("findById", id);
	}

	public void update(Emp emp) {
		sst.update("update", emp);
	}

	public void delete(int id) {
		sst.delete("delete", id);
	}

	public Emp2 findById3(int id) {
		return sst.selectOne("findById3",id);
	}
}
登录(Spring+MyBatis):

 step1. 导包
 spring mvc,mybatis,mybatis-spring, driver,dbcp,junit,spring-jdbc
 step2. 配置文件 (spring配置文件)
 step3. 配置前端控制器(web.xml)
 step4. 实体类 (Admin)
 step5. 映射文件 (AdminMapper.xml)
   注意:实体类的属性名如果与表的字段表不一致,应该用别名。
 step6.修改配置文件
   添加: datasource,SqlSessionFactoryBean。
   注意:SqlSessionFactoryBean应该指定映射文件的位置。 

 step7. AdminDAO接口
   注意:应该与映射文件保持一致。检查一下映射文件的namespace 是否等于AdminDAO接口的完整类名。
 step8. 修改配置文件
   添加:MapperScannerConfigurer。
   注意:指定要扫描的包名。

Ajax(aschronous javascript and xml:异步的js和xml):
是一种用来改善"用户体验"的技术,其实质是利用浏览器内置的一个对象 (ajax对象)异步地向服务器发送请求。服务器送回部分数据,浏览器利用这些数据对当前页面做部分更新。整个过程,页面无刷新,不打断用户的操作。
注:
异步:指的是当ajax对象向服务器发送请求时,浏览器不会销毁当前页面,用户仍然可以对当前页面做其它的操作。

部分数据:一般不需要返回完整的页面。一般是以文本或者xml的形式返回。


如何获得ajax对象:

 要区分浏览器。

function getXhr(){
	var xhr = null;
	if(window.XMLHttpRequest){
		//非ie浏览器
		xhr = new XMLHttpRequest();
	}else{
		//ie浏览器
		xhr = new ActiveXObject("MicroSoft.XMLHttp");
	}
	return xhr;
}
Ajax对象的几个重要属性:

 a. onreadystatechange: 绑定事件处理函数(处理readystatechange 事件)。
     注:当ajax对象的readyState的值发生了任何的改变(比如从0变成了1) ,就会产生readystatechange事件。
 b. readyState: 有5个值(0,1,2,3,4),表示ajax对象与服务器通信的状态。4表示ajax对象已经获取了服务器返回的所有的数据。
 c. responseText:服务器返回的文本数据。
 d. responseXML:服务器返回的xml数据。
 e. status:服务器返回的状态码。

编程步骤:

 step1. 获取ajax对象。
   比如: var xhr = getXhr();
 step2. 利用ajax对象发请求。
   发送get请求:xhr.open('get','check_username.do?username=Tom',true);
                       xhr.onreadystatechange=f1; xhr.send(null); 

   注:第一个参数:请求方式。
       第二个参数: 请求地址,如果是get请求,请求参数要添加到请求地址后面。
       第三个参数:如果值是true,表示异步。如果值是false,表示同步( 浏览器会锁定当前页面)。

 step3. 编写服务器端代码。
 step4. 编写事件处理函数。
   比如:if(xhr.readyState == 4 && xhr.status == 200){ var txt =xhr.responseText; 更新页面... }

//利用ajax对象异步地向服务器发送请求,
//并且利用服务器返回的数据更新当前页面。
function check_uname(){
   //step1. 获得ajax对象。
   var xhr = getXhr();
   //step2. 利用ajax对象发请求。
   var uri = 'check_uname.do?username=' + $F('username');
   //encodeURI函数会检查uri中的字符,如果
   //是非ASCII字符,都会统一使用utf-8来编码。
   xhr.open('get',encodeURI(uri),true);
   xhr.onreadystatechange=function(){
     //step4. 处理服务器返回的数据
     if(xhr.readyState == 4 && xhr.status == 200){
        //获得服务器返回的数据
        var txt = xhr.responseText;
        //更新页面
        $('username_msg').innerHTML = txt;
     }
   };
   xhr.send(null);
}

发送post请求:

 注:

  a. 请求参数放到send方法里面。
  b. 按照http协议,如果发送的是post请求,应该提供一个 'content-type'消息头。但是,默认情况下,ajax对象并不 会提供这样一个消息头。

function check_uname(){
   //step1. 获得ajax对象
   var xhr = getXhr();
   //step2. 利用ajax对象发请求
   xhr.open('post','check_uname.do',true);
   xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
   xhr.onreadystatechange=function(){
     //step4. 处理服务器返回的数据
     if(xhr.readyState == 4 && xhr.status == 200){
	 var txt = xhr.responseText;
	 $('username_msg').innerHTML = txt;
      }
   };
   xhr.send('username=' + $F('username'));
}
处理ajax应用中的乱码问题:

 1. post请求:

     a. 乱码原因:浏览器提供的ajax对象在发送post请求时,会对中文参数使用utf-8 来编码,而服务器端默认会使用iso-8859-1来解码。

     b. 解决方案:request.setCharacterEncoding("utf-8");

 2. get请求:

     a. 乱码原因:IE提供的ajax对象在发送get请求时,会对中文参数使用gbk来编码,而其它浏览器会使用utf-8来编码。服务器端默认会使用iso-8859-1来解码。

     b. 解决方案:

         step1. 修改服务器端的配置,使用指定的字符集来解码。 比如,修改tomcat的server.xml,添加URIEncoding=utf-8。

                    <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="utf-8"/>

         step2. 可以使用encodeURI函数对中文参数来编码(该函数会使用utf-8来编码)。

json(javascript object notation:js对象声明):

json是什么:

 一种轻量级的数据交换格式。
 注:
  a. 数据交换:将数据转换成一种与平台无关的数据格式(比如xml),然后发送给接收方来处理。
  b.轻量级:相对于xml,json的文档更小,解析速度更快。

语法:

  a. 表示一个对象
   {属性名:属性值,属性名:属性值...}
   注意:
     -属性名必须使用双引号括起来。
     -属性值可以是string,number,true/false,null,object。
     -属性值如果是string,必须使用双引号括起来。
   比如:{"code":"600015","name":"山东高速","price":10}
  b.表示一个由对象组成的数组。
    [{},{},{}....]

如何将java对象转换成json字符串:

 通过使用json官方提供的工具(比如json-lib),也可以使用其它组织或者公司提供的工具(比如google提供的工具),把对象/对象数组集合转化成json字符串;ajax会将服务器返回的json字符串自动转换成相应的js对象或者js对象组成的数组。
 JSONObject: 单个对象的转换。
 JSONArray:对象组成的集合或者数组的转换。

jQuery对ajax技术的支持:

$.ajax():

用法:$.ajax({});
注:{}是一个描述选项参数的对象,常用的选项参数有以下这些:
 url: 请求地址 (比如 check_uname.do)
 data:请求参数(有两种格式,第一种格式是请求字符串的形式,比如'username=Tom&age=22';第二种格式是对象的形式,比如{'username':'Tom','age':22})。
 type:请求方式 (比如get/post)
 dataType: 指定服务器返回的数据类型。常见的有:
    text: 文本。
    html: html文本。
    json: json字符串。
    xml: xml文本。
    script: javascript脚本。
 success:是一个函数,用来处理服务器返回的数据。
 error:是一个函数,用来处理服务器返回的异常。

load函数:

作用:异步地向服务器发送请求,并且将服务器返回的数据直接添加到符合要求的节点之上。
用法:$obj.load(url,[data]);
注:
  $obj:要操作的节点。
  url:请求地址。
  data:请求参数(格式同上)。

------------------------------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值