《JavaEE框架整合开发入门到实战——Spring+SpringMVC+MyBatis》读书笔记
加油生活,嗯,希望假期可以把这本书刷完,新年快乐,嘻嘻,今天是旧的一年里最后的一天,嗯,除夕一过,就25岁啦。希望新的一年里,学更多的东西,认识优秀的人,希望家人健康平安,希望自己少一些烦恼,总之先学习吧,理想中幻想,之后是很痛苦的经历,所以要多一些自制,不能沉沦其中。
在新的学期,将短期的过度努力变成长期的恰到好处的喜欢和投入,就不会需要那么多的美好来激励自己,不会总是看那么多的鸡汤,但认知范畴的学习方法还是要多看,不会惶恐未来,人家讲时间会改变一切,会淹没掉一切的伤痛,我觉得,只是时间改变了人罢了,我们更多的妥协,更多的遗憾,乃至习惯了自己。
生之幸之,此生尽兴。何其艰难哎!!!
我的头发哎!!!
2019.2.4 除夕夜
嗯。书中源码资源百度云在最后。
一 , Spring入门
Spring概述
Spring框架是一个针对JavaEE的轻量级解决发案,简化了JavaEE企业级应用程序的开发过程。目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题,是一个分层的JavaSE/EE full-stack(一站式)轻量级开源框架。Spring负责基础架构。
Spring起源背景,第一个版本由Rod Johnson 开发,于2003年发布,Spring主要功能包括:
- 基于依赖注入的核心功能,
- 声明式的面向切面编程(AOP)支持;
- 与多种持久层技术的整合
- 独立的Web MVC框架
侵入式概念
Spring是一种非侵入式的框架...
侵入式:对于EJB、Struts2等一些传统的框架,通常是要实现特定的接口,继承特定的类才能增强功能,改变了java类的结构
非侵入式:对于Hibernate、Spring等框架,对现有的类结构没有影响,就能够增强JavaBean的功能
Spring是一个轻量级的IoC(Inversion of Control,控制反转)和AOP( Aspect Oriented Programming 面向切面)的容器框架,具有的特点:
- 轻量:完整的Spring框架可以在一个大小只有1Mb的JAR文件里发布,Spring是非入侵式的,即Spring应用中的对象不依赖与Spring的特定类。
- 控制反转:Spring通过一种称为控制反转的(IoC)的技术促进了松耦合,当应用了IoC时,一个对象依赖的其他对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖的对象,因此可以认为IoC与JNDI相反,不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
- 面向切面:Spring提供了面向切面编程,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发,应用对象可以只实现他们应该做的,即完成业务逻辑,不需要负责其他的系统级关注点。
- 容器:Spring包含并管理应用对象的配置和生命周期,开发者可以配置自己的每个Bean,可以创建一个单独的实例或者每次需要时都可以生成一个新的实例,
- 框架:Spring可以将简单的组件配置,组合成为复杂的应用,在Spring中,应用对象的声明式的组合,一般在xml文件里,Spring也提供了很多基础功能,事务管理,持久化集成,将应用逻辑留给开发者。
Spring体系结构
Spring框架以集成20多个框架,分布在核心容器(Core Container),数据库访问集成(Data Access/Integration)层,Web层,AOP(Aspect Oriented Programming。面向切面编程)模块,植入(Instrumention)模块,消息传输(Messaging)模块和测试(Test)模块;
1.核心容器:
是其它模块建立的基础,由以下组成:
Spring-core模块:提供框架的基本组成部分,包括控制反转(Inversion of Control)和依赖注入,将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述,由IoC容器负责依赖类之间的创建 ,拼接,管理,获取等工作,
Spring-beans模块: 提供了BeanFactory接口,是Spring的核心接口,是工厂模式的一个经典实现,Spring将管理对象称为Bean。
Spring-Context模块:建立在Core和Beans模块的基础之上的,提供一个框架式的对象访问方式,是访问定义和配置的任何对象的媒介,ApplicationContext接口时Context模块的核心。
Spring-Context-support模块:支持整合第三方库到到Spring应用程序的上下文,特别是用于高速缓存(EhCache,JCache)和任务调度(CommonJ,Quartz)的支持。
Spring-excpression模块:提供了强大的表达式语言去支持运行时查询和操作对象图。这是对JSP2.1规范中规定的统一表达式语言(Unified EL)的扩展。该语言支持设置属性和获取属性值,属性分配,方法调用,访问数组,集合和索引器的内容,逻辑和算术运算,变量命名以及从Spring的IoC容器中以名称检索对象,它还支持列表投影,选择以及常见的列表聚合。
2.AOP和Instrumentation:
Spring-aop模块:提供了一个符合AOP要求的面向切面的编程实现,允许定义方法拦截器和切入点,将代码的按照功能进行分离,以便干净的解耦。
Spring-aspects模块:提供了与AspectJ的集成功能,AspectJ是一个功能强大的且成熟的AOP框架。
Spring-instrument模块:提供了类植入(Instrumentation)支持和类加载器的 实现。允许在JVM启动时启用一个代理类,通过代理类在运行时修改类的字节码,改变一个类的功能。
3.消息:
Spring 4.0 以后新增消息模块(Spring-messaging),该模块提供了对消息传递体系结构和协议的支持。
4.数据访问/集成:
数据访问/集成层由JDBC,ORM,OXM,JMS和事务模块组成。Spring在DAO层的抽象层面,建立起一套面向DAO层的统一的异常体系,同时将各种访问数据的检查型异常转换为非检查型异常,为整合各种持久层框架提供基础。
Spring-jdbc模块:提供了一个JDBC的抽象层,消除了繁琐的JDBC编码和数据库厂商特定的错误代码解析、
Spring-orm模块:为流行的对象关系映射提供集成层,包括JPA和Hibernate,使用Spring-orm模块可以将这些O/R映射框架与Spring提供的其他功能集合使用。例如声明式事务管理功能。
Spring-oxm模块:提供了一个支持对象/XML文件映射的抽象层实现,如JAXB,Castor,JiBX和XSteram。
Spring-jms模块(Java Messageing Service):指Java消息传递服务,包含用于生产和使用消息的功能,自Spring-messaging模块的集成。
Spring-tx模块(事务模块):支持用于实现特殊接口的和所有POJO(普通Java对象)类的编程和声明式事务管理。
5.Web及远程操作层:
该层建立在Application Context模块之上,提供了Web应用的各种工具类,如通过Listener或Servlet初始化Spring容器, 将Spring容器注册到Web容器中。由Spring-Web,Spring-webMVC,Spring-websocket和Portlet模块组成。
Spring-web模块:提供了基本的Web开发集成功能,例如多文件上传功能,,使用Servlet监听器初始化一个IoC容器以及Web应用的 上下文。
Spring-webmvc模块:也称Web-Servlet模块,包含用于Web应用程序的SpringMVC和REST Web Service实现,SpringMVC框架提供了领域模型代码和Web表单之间的清晰分离,并与SpringMVC框架,并与Spring Framework 的所有其他功能集成,
Spring-websocket模块:Spring4.0后新增的模块,它提供了WebSoket和SockJS的实现。
Poetlet模块:类似于Servlet的功能,提供例如Protlet环境下的MVC实现。
6.测试:
Spring-test模块支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。
Spring的开发环境的构建:
使用Eclipse开发JavaWeb 应用
Spring的下载及目录结构:在使用Spring框架开发应用程序时,除了需要引用Spring自身的JAR包。还需要引用Commons.logging的JAR包。
Spring核心的配置文件applicationContext.xml
或者叫bean.xml
那这个配置文件怎么写呢??一般地,我们都知道框架的配置文件都是有约束的...我们可以在spring-framework-3.2.5.RELEASE\docs\spring-framework-reference\htmlsingle\index.html
找到XML配置文件的约束
二,Spring IoC
1.基本概念:
.Spring框架的核心功能是控制反转IoC,面向切面AOP和申明式事务都依赖于IoC实现的基础上的,
控制反转(Inversion of Control) 就是依赖倒置原则的一种代码设计的思路,依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。即知道需求的对象去构造底层,不是知道底层需求去构造需求对象。解决的办法是依赖注入:
依赖注入(dependency injection),就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。被依赖对象赋值给调用者的成员变量,相当于为调用者注入所依赖的实例,将“new 被调用对象”的部分有控制反转容器实现,
控制反转容器(IoC Container)的好处:
- 可以自动对你的代码进行初始化,你只需要维护一个Configuration(可以是xml可以是一段代码),而不用每次初始化都要亲手去写那一大段初始化的代码。
- 我们在创建实例的时候不需要了解其中的细节。我们自己手动创建一个对象时,是从底层往上层new的,而IoC Container在进行这个工作的时候是反过来的,它先从最上层开始往下找依赖关系,到达最底层之后再往上一步一步new(有点像深度优先遍历),IoC Container可以直接隐藏具体的创建实例的细节,它就像一个工厂。
Ioc思想的好处:
第一,资源集中管理,实现资源的可配置和易管理。
第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。
IoC容器:消解耦合度,通过容器来控制业务对象的之间的依赖关系,不在代码中实现,控制权代码转到IoC容器,控制权的转移就是反转。无论是创建对象、处理对象之间的依赖关系、对象创建的时间还是对象的数量,Spring使用控制反转来实现对象不用在程序中写死。具体的实现为依赖注入。
依赖注入,dependency injection,Spring容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入所依赖的实例。即在Spring中,控制反转是通过第三方去获取生产特定对象的方式,在Spring中实现控制反转的是IoC容器,其实现方法为依赖注入。
2.Spring IoC容器:
即控制反转的容器,基于两个访问接口:
- org.springframework.beans.factory.BeanFactory:Bean工厂,借助于配置文件 能够实现对JavaBean的配置和管理,用于向使用者提供Bean的实例。
- org.springframework.context.ApplicationContext:ApplicationaaContext构建在BeanFactory的基础上的。
Spring将容器中的对象称为Bean,这与传统的JavaBean不完全相同,只是借用了Bean的名称。
BeanFactory接口:
它提供了完整的IoC服务支持,是一个管理Bean的工厂,主要负责初始化各种Bean,并根据预定的配置文件完成对象之间依赖关系的组装,有多个实现类。
BeanFactory接口中的常用方法及功能
boolean containsBean(String name) | 判断String容器是否包含id为name的Bean对象 |
Object getBean(String name) | 返回容器id为name的Bean对象 |
Object getBean(String name,Class requiredType) | 返回容器中id为name,类型为required的Bean |
Class getType(String name) | 返回容器中id为name的Bean的类型 |
方法一
ClassPathResource resource = new ClassPathResource("applicationContext.xml");//创建BeanFactory对象DefaultListableBeanFactory factory = new DefaultListableBeanFactory();//加载配置文件XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);reader.loadBeanDefinitions(resource);TestDao tt = (TestDao)factory.getBean("test");tt.sayHello();方法二//初始化Spring容器,加载配置文件BeanFactory beanfac = new XmlBeanFactory(new FileSystemResource("C:\\Users\\li rui long\\eclipse-workspace\\SpringDemo\\src\\applicationContext.xml"));//通过容器获取实例TestDao tt = (TestDao)beanfac.getBean("test");tt.sayHello();
AppplicationContext接口:
是BeanFactory的子接口,代表应用的上下文环境。在BeanFactory的基础之上的。该接口除了包含BeanFactory的所有功能以为,还添加了
- 国际化(MessageSource),为应用提供国际化访问资源
- 资源访问(ResourceLocder),根据资源的地址判断资源类型,并返回对应的Resource实现类。
- 事件传播(ApplicationContext)等内容的支持。是最长用的接口。引入了事件机制,包括启动事件,关闭事件等,让容器在上下文中提供了对应用事件的支持。
创建ApplicationContext接口实例通常有三种方法:
- 通过ClassPathXmlApplicartionContext创建:将从类路径中(src根目录)中寻找指定的Xml配置文件。
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");//初始化Spring容器ApplicationContext,加载配置文件。TestDao tt = (TestDao)appCon.getBean("test");//通过容器获取test实例;tt.sayHello();
ClassPathXmlApplicartionContext类实现了java.io.Closeable接口,因此需要使用完毕后调用close()方法关闭。 ApplicationContext的初始化和BeanFactory的初始化的区别,BeanFactory在初始化容器时,并为实例化所有的Bean,直到第一次访问(getBean())某个Bean时才实例化目标Bean,而ApplicationContext在初始化应用上下文时,就实例化所有的单实例的Bean,所以ApplicationContext在初始化时间比BeanFactory稍长一些。但程序后获取的Bean实例时将直接从缓存中调用。性能较好
- 通过FileSystemXmlApplicationContext创建:将从指定的文件绝对路径中寻找XML配置文件,找到并装载完成ApplicationContext的实例化工作。
ApplicationContext appCon = new FileSystemXmlApplicationContext("C:\\Users\\li rui long\\eclipse-workspace\\SpringDemo\\src\\applicationContext.xml");TestDao tt = (TestDao)appCon.getBean("test");//通过容器获取test实例;tt.sayHello();
通过Web服务器实例化ApplicationContext容器:在Web服务器实例化ApplicationContext容器,一般使用基于org.springframwork.web.context.ContextLoaderListener的实现方式(spring-web-5.0.2RELEASE.jar复制到WEB-INF/lib目录中),此方法只需要web.xml中添加代码。
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:appliactionContext.xml</param-value> </context-param> <listener><listener-calss>org.springframework.web.context.ContextLocaderListener</listener-class> </listener>
3.依赖注入类型
在Spring中实现IoC容器的方法是依赖注入,依赖注入的作用是在使用Spring框架的DI/IoC容器创建对象时动态的将其所依赖的对象(例如属性值)注入到Bean组件中,完成bean的组合,Spring框架的依赖注入通常有两种实现方式(以Xml装配)
构造方法注入:
如果bean的属性中有一些是必须赋值的,或者多个属性的赋值顺序有要求,使用setter的方法可能会造成错误,使用构造方法注入可以保证IoC容器提供的Bean实例一定是可用的。
创建Dao包:
package Dao;public interface TestDao {public void sayHello();
}package Dao;public class TestDaoImpl implements TestDao {public TestDaoImpl() {// TODO Auto-generated constructor stub}@Overridepublic void sayHello() {// TODO Auto-generated method stubSystem.out.println("Hello,study hard!");}}
创建service包:
package service;public interface TestService {public void sayHello();
}package service;import Dao.TestDao;public class TestServiceImpl implements TestService {private TestDao testdao;public TestServiceImpl(TestDao testdao) {// 构造方法,用于实现依赖注入接口对象testDaosuper();this.testdao = testdao;}@Overridepublic void sayHello() {// TODO Auto-generated method stubtestdao.sayHello();System.out.println("构造方法注入!!!");}}
编写配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 将指定类配置给Spring,让Spring创建其实例 --><bean id="test" class="Dao.TestDaoImpl"><!-- collaborators and configuration for this bean go here --></bean><!-- 使用构造方法注入 --><bean id = "testService" class = "service.TestServiceImpl"><!-- 用于定义构造方法的参数,index用于定义参数的位置,ref指定某个实例的引用,如果为常量值,ref用value代替,或者 name指定参数名,value指定参数值 --><constructor-arg index = "0" ref = "test"/></bean></beans>
创建test包:
package test;import org.springframework.context.support.ClassPathXmlApplicationContext;import service.TestService;public class Test {public Test() {}public static void main(String[] args) {ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");TestService ts = (TestService)appCon.getBean("testService");ts.sayHello();}}
属性的setter方法注入:
使用属性的setter方法注入是Spring最主流的方式,利用Java Bean 的,使用setter方法来注入,Java反射机制实现。Spring先调用Bean的默认构造方法函数实例化Bean对象,然后通过反射的方式调用setter方法注入属性值。
创建接口实现类TestserviceImpl
package service;import Dao.TestDao;public class TestServiceImpl implements TestService {private TestDao testdao;public TestServiceImpl() {// 构造方法,用于实现依赖注入接口对象testDaosuper(); }public TestDao getTestdao() {return testdao;}public void setTestdao(TestDao testdao) {this.testdao = testdao;}@Overridepublic void sayHello() {// TODO Auto-generated method stubtestdao.sayHello();System.out.println("构造方法注入!!!");}}
将TestServiceImpl类托管给Spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 将指定类配置给Spring,让Spring创建其实例 --><bean id="test" class="Dao.TestDaoImpl"><!-- collaborators and configuration for this bean go here --></bean><bean id = "testService" class = "service.TestServiceImpl"><!-- 使用setter方法注入 --><property name="testdao" ref = "test"/></bean></beans>
在test中测试setter方法注入:
package test;import org.springframework.context.support.ClassPathXmlApplicationContext;import service.TestService;public class Test {public Test() {}public static void main(String[] args) {ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");TestService ts = (TestService)appCon.getBean("testService");ts.sayHello();}}
两种注入方法的对比:使用setter方法与传统的JavaBean写法相似,程序员容易接受。构造方法可以注入可以在构造器中决定依赖关系的注入顺序,对于依赖关系无需变化的Bean,构造注入更好,符合高内聚的原则。
4,注入的值类型
Spring支持三种类型:字面值,其他的Bean的引用,集合类型。
字面值一般指可用字符串表示的值,可以通过<bean>,<Constructor-arg>的<value>子元素或value属性的注入。默认情况下,基本的数据类型,及其封装类,Spring等类型都可以采用字面值的方式注入,Spring会根据参数类型自动将字面值装换为正确的类型,
<constructor-arg name ="" value = ""/><constructor-arg name ="">
<value >值</value>
</constructor-arg><property name = ""value="">
其他Bean的引用:当Bean需要注入的熟悉是对象类型时,则可以引用IoC容器中定义的类型匹配的其他Bean,可以通过<bean>或<constructor-arg>的<ref>子元素或ref属性注入。
集合类型:<array>,<list>,<set>,<map>,<props>元素分别用来设置类型为数组,List,Set,Map,和Properties的集合属性值,
<property name=""><array><value>1234</value><ref bean = ""/><array>
</property ><property name=""><set><value>1234</value><ref bean = ""/></set>
</property ><property name=""><list><value>1234</value><ref bean = ""/></list>
</property ><property name=""><entry key = "1" value ="1234"><entry key ="2" value-ref ="">
</property ><property name=""><props><prop key ="aaa">AAA</prop><prop key = "bbb">BBB</prop></props>
</property >
三 .Spring Bean
在Spring应用中。Spring IoC容器可以创建,装配和配置应用组件的对象,组件对象称为Bean。
Bean的配置:Spring框架支持XML和Properties两种格式的配置文件,实际开发中常用XML格式的配置文件。根元素为<beans>,可以包含多个<bean>子元素,一个bean元素代表一个Bean,常用属性及子元素。
id | Bean在BeanFactory中的唯一标识,在代码中通过BeanFactory获取Bean实例时需要以此作为索引名称; |
class | Bean的具体实现类,使用类的权限定名 |
scope | 指定Bean实例的作用域 |
<constructor-arg> | <bean>元素的子元素,使用构造方法注入时指定参数,index或者name指定序号和参数名,ref指定BeanFactory中其他Bean的引用关系,type属性指定参数类型,value属性指定参数的字面量。 |
<property> | <bean>元素的子元素,使用setter方法注入时指定参数,iname指定参数名,ref指定BeanFactory中其他Bean的引用关系,type属性指定参数类型,value属性指定参数的字面量。 |
<list> | <property>元素的子元素,用于封装List类型的依赖注入。 |
<map> | <property>元素的子元素,用于封装map类型的依赖注入。 |
<array> | <property>元素的子元素,用于封装array类型的依赖注入。 |
<set> | <property>元素的子元素,用于封装set类型的依赖注入。 |
Bean的实例化:
Spring 框架实例化Bean有三种方式:构造方法,静态工厂,实例工厂,最常见的构造方法。
构造方法实例化:
public User(String id, String username) {this.id = id;this.username = username;}
<bean id="user" class="User"><!--通过constructor这个节点来指定构造函数的参数类型、名称、第几个--><constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg><constructor-arg index="1" name="username" type="java.lang.String" value="zhongfucheng"></constructor-arg></bean>
静态工厂实例化:
public class Factory {public static User getBean() {return new User();}}
<!--静态工厂方法创建对象,使用class指向静态类,factory-method指定静态方法--><bean id="user" class="Factory" factory-method="getBean" />
实例工厂实例化:
public class Factory {public User getBean() {return new User();}}
<!--创建工厂对象--><bean id="factory" class="Factory"/><!--factory-bean指定配置工厂对象,factory-method指定工厂的方法--><bean id="user" class="User" factory-bean="factory" factory-method="getBean"/>
Bean的作用域:
在Spring中可以为bean 指定作用域,Spring 5.0 中为Bean定义了如下作用域。作用域将对Bean的生命周期产生影响,Bean的作用域通过<bean>元素的scope属性来指定,request ,session,和application,websocket 作用域是针对WebApplicationContext上下文的,singleton和prototype作用域适用于所有类型。
singleton | 默认的的作用域,使用singleton定义的Bean在Spring容器中只有一个Bean实例。Bean以单实例的模式存在。 |
prototype | 一个Bean定义对应多个实例对象,每次调用getBean时就创建一个新的Bean实例。 |
request | 在一次HTTP请求中容器将返回一个Bean实例,不同的请求返回不同的Bean实例,依据某个Bean定义创建而成,该作用域仅基于Web的Spring ApplicationContext 中使用。 |
session | 在 一个 HTTPSession 中,一个Bean定义对应一个实例,该作用域仅在基于Web的Spring ApplicationContext 中使用。 |
applicataio | 为每个ServletContext对象创建一个实例,即同一个应用共享Bean实例,仅在web spring应用程序上下文中使用。 |
websocket | 为每个WebSocket对象创建一个Bean实例,仅在Web Spring应用程序上下文中使用。 |
singleton作用域:Spring IoC 容器仅生成和管理一个Bean实例,创建之后将被缓存起来,在使用id或name获取Bean时,IoC容器将返回缓存中共享的Bean实例,
语法:<bean id =“” class = “” scope =“”>
prototype作用域:Spring IoC 将为每次请求创建一个新的实例,此时,Spring容器仅仅使用new关键字创建Bean实例,一旦创建成功,容器就不在跟踪实例,也不会维护Bean实例的状态。一般不设置为proptotype
Bean的生命周期:
一个对象的生命周期是指从创建到销毁的过程,Spring IoC 容器的Bean也拥有由容器控制的完整而复杂的生命周期,singleton作用域下Spring可以清楚的知道Bean何时被创建,初始化,销毁。对于prototype作用域只负责创建。
Bean生命周期的整个过程:
- 根据Bean的配置情况实例化一个Bean;
- 根据Bean上下文对实例化的Bean进行注入依赖,即对Bean属性进行初始化。
- 如果实现了BeanNameAwere 接口,将调入它实现的setBeanName(String beanId)方法,参数为当前Spring配置文件中Bean的id。
- 如果Bean实现了BeanFactoryAware接口,将调用setBeanFactory方法,参数传递为当前Spring工厂的实例引用。
- 如果Bean实现了ApplicationContextAware接口,将调用它实现的setApplicationContext(ApplicationContext)方法,此处参数传递的是ApplicationContext实例的引用。
- 如果Bean关联了BeanPostProcessor接口,将调用初始化方法postProcessBeforeInitialization(Object obj,String s)对Bean进行操作。
- 如果Bean实现了InitializingBean接口,将调用afterpropertiesSet方法。
- 如果Bean在String配置文件中配置了init-method属性,将自动调用其配置的初始化方法。,
- 如果Bean关联了BeanPostProcessor接口,将调用postProcessAfterInitialization(Object obj,String s)方法,由于是在Bean初始化结束时调用Aflter方法,也可以用于内存或缓存技术。以上工作完成后可以使用Bean,
- 当Bean不需要时将进入销毁阶段,如果Bean实现了DisposableBean接口,则调用其实现的destory方法将Spring中的Bean销毁。
- 如果在配置文件中通过destroy-method属性指定了Bean的销毁方法。将调用其配置的销毁方法。
package Dao;public class Bean {public Bean() {// TODO Auto-generated constructor stub}public void init() {System.out.println(this.getClass().getName()+"自定义初始化方法");}public void destroy() {System.out.println(this.getClass().getName()+"自定义销毁方法");}}package test;import org.springframework.context.support.ClassPathXmlApplicationContext;import Dao.Bean;public class Tests {public Tests() {// TODO Auto-generated constructor stub}public static void main(String[] args) {// TODO Auto-generated method stubClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");System.out.println("获取对象前");Bean bean = (Bean)ctx.getBean("bean");System.out.println("获取对象后"+bean);ctx.close();}}
<bean id = "bean" class = "Dao.Bean" init-method ="init" destroy-method = "destroy"/>
Bean的装配方式:
将Bean依赖注入到Spring容器中,支持基于XML配置的装配,基于注解的装配以及自动装配等多种装配。
基于Xml基本装配前面以述,由Bean之间的关系,有配置信息的继承机制和依赖机制。
继承机制:可以通过<bean> 元素指定的parpent值重用已有的<bean>元素的配置信息
<bean id = "test1" class = "service.TestServiceImpl">
<bean id ="test2" parent = "test1"/>
依赖机制:Spring为<Bean>元素提供了depends-on属性来指定前置依赖Bean
<bean id = "usrDao" class = ""/>
<bean id = "userService" calss="" depends-on = "userDao"/>
自动装配:在Xml文件中,<Bean>元素使用autowire实现自动装配。属性值为no(不使用自动装配),byName(根据属性名自动装配),byType(根据属性类型自动装配,多个抛出异常),constructor(与bytype类似,应用与构造器参数,没有抛出异常),autodetect(通过Bean的自省机制来决定)。
<bean id = "usrDao" class = "" autowire =“byName”/>
基于注解的装配:
在Spring框架中定义了一系列的注解;创建了Bean实现类后需要 配置注解,
<context:component-scan base-package ="Bean所在包的路径">
@Component:该注解是一个泛化的概念,仅仅表示一个组件对象(Bean),可以作用在任何层次上,
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 使用context命名空间,通过Spring扫描指定包Dao及其子包的所有实现类,进行注解解析 --><context:component-scan base-package ="Dao"/></beans>
package Dao;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component()
/** 相当于@Component("bean")或@Component(value ="bean"),为Bean的id,默认为首字母小写的类名。* */public class Bean {@Value("liruilong")//只能注入简单值;private String name;public Bean() {// TODO Auto-generated constructor stub}public String getName() {return name;}public void setName(String name) {this.name = name;}
}package test;import org.springframework.context.support.ClassPathXmlApplicationContext;import Dao.Bean;public class Tests {public Tests() {// TODO Auto-generated constructor stub}public static void main(String[] args) {// TODO Auto-generated method stubClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");Bean bean = (Bean)ctx.getBean("bean");System.out.println(bean.getName());ctx.close();}}
@Reposittory:该注解用于将数据访问层(DAO)的类标识文Bean ,即注解数据访问层Bean,功能与@Component相同。
@Service:该注解用于标注业务逻辑层组件类(Service层),其功能与@Component相同。
@Contrller:该注解用于标注一个控制器组件类(Spring MVC 的 Controller),其功能与@Component相同。
@Autowired:该注解可以对类成员变量,方法及构造方法进行标注,完成自动装配的工作通过使用@Autowired来消除setter和getter方法,默认按照Bean的类型装配。
@Resource:该注解与@Autowired的功能一样,区别在于该注解默认是按照名称来装配注入的,只有当找不到与名称匹配的Bean时才会按照类型来装配注入,而@Autowired默认按照Bean的类型进行装配,如果想按照类型来装配注入,则需要和@Qualifier主解一起使用。@Resource有两个属性,name和type,name按照名称来注入,type按照Bean类型来注入。
@Qualifier:该注解与@Autowired注解配合使用。当@Autowired注解需要按照名称来装配注入时,需要和该注解一起使用。Bean实例名由@Qualifier注解的参数指定。
为了使类的标注更加清晰,在实际开发中推存使用@Reposittory标注数据访问层,@Service标注业务逻辑层,@Contrller 控制器层。
package Dao.Dao;
import org.springframework.stereotype.Repository;
@Repository()
public class TestDao {public TestDao() {// TODO Auto-generated constructor stub}public void save() {System.out.println("testDao save");}}package Dao.service;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import Dao.Dao.TestDao;
@Service()
public class testService {@Resourceprivate TestDao testdao ;public testService() {// TODO Auto-generated constructor stub}public void save() {testdao.save();System.out.println("testService save");}
}package Dao.Controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import Dao.service.testService;
@Controller
public class testController {@Autowiredprivate testService testservice;public testController() {// TODO Auto-generated constructor stub}public void save() {System.out.println("testController save");}}
配置注解信息不需要改变<context:component-scan base-package ="Dao"/>
Spring和Junit集成测试
- 第一步:在项目导入 spring-test的jar包
- 第二步: 使用 @RunWith注解 和 @ContextConfiguration 注解 集成测试,初始化 spring容器
package com.qst.test;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext2.xml") public class UserServiceImpl_test {@Autowiredprivate UserServiceImpl userServiceImpl;@Testpublic void login() {userServiceImpl.login();} }
- 第三步:注入需要测试对象,进行测试
第四章,Spring AOP
1,Spring AOP的基本概念
AOP概念(Aspect-Oriented Programming):即面向切面编程,与OOP(Object - Oriented Programming,面向对象编程)相辅相成,AOP的基本单元为Aspect(切面),Struts2 的拦截器设计就是基于AOP的思想。
AOP原理:大型系统中的通用的服务型的代码会穿插在各个业务类,方法中,随着系统规模的增大,会造成大量的代码重复,,且与核心代码没有太多的关系。系统中的业务可分为核心关注点和横切关注点,核心关注点是业务处理的主要流程,横切关注点是与核心业务无关的通用业务。如日志权限等,各个横切点离散的穿插与核心业务中。导致系统中的每一个模块代码都与这些业务具有很强的依赖性,当需要添加横切功能时,需要大幅修改已有的代码。AOP即解决这个问题,使用AOP框架,能够将这些影响多个类的通用性服务抽取出来,(即切面),并通过配置的方式明确在那些位置插入这些服务,系统运行后,AOP框架在指定的时机自动运行这些服务,从而达到核心业务逻辑和服务性逻辑分离的目的,减少了重复代码的,提高了系统的可维护性和可扩展性。
AOP术语:
- 连接点(Joinpoint):连接点是指代码中一些具有边界性质的特定位置,或程序运行时的一些时间点,AOP可以针对连接点配置切面,连接点的类型有很多,如类初始化前,类初始化后,类方法调用前后,抛出异常等,Spring框架的AOP功能只支持针对方法的连接点。
- 切入点(Pointcut):即被增强的连接点。当某个连接点满足预定条件时。AOP框架能够定位到这个连接点,该连接点将被添加增强(Advice),该连接点就变成了一个切入点。Spring AOP 框架中,所有的方法执行都是连接点,通过切入点确定哪些连接点需要被处理。
- 增强(Advice):添加特定的连接点上的一段代码程序,增强包含了用于添加到目标连接上的一段代码逻辑,以及用于定位连接点的位置信息,在Spring AOP中提供的增强接口都带有方位名,BeforeAdvice等。
- 目标对象(Target):需要添加增强的目标类。即被通知的对象,如果AOP框架使用运行时代理,借助AOP框架,业务类可以只实现核心业务,而日志,事务管理等横切关注点则可以通过AOP框架添加到特定的连接点上,如果没有AOP框架,只能手工编写框架。
- 引入(Introduction):特殊的增强,可以为目标类添加自定义属性及方法,即使一个业务类没有实现某个接口,通过AOP框架的引入功能,也可以动态的为该业务类添加接口的实现逻辑,让业务类称为这个接口的实现类。
- 织入(Weaving):将增强添加到目标类具体的连接点上的工程,即将切面代码插入到目标对象上,生成代理对象,AOP框架负责将目标类和增强连接在一起,可以实现在编译期(Java 编译器),类装载期(类装载器),动态代理织入,运行期个阶段为目标类添加子类的方式,完成织入过程。Spring AOP 框架默认采用动态代理织入,而AspectJ(基于Java语言框架的AOP框架)采用编译织入,和类装载织入。
- 代理(Prosxy):目标类被AOP框架织入增强后会产生一个代理类,融入了目标类和增强逻辑,根据织入的方式的不同,代理类可能和目标类实现相同的业务接口,也可以直接就是目标类的子类,所以可使用调用目标类的方式来调用该代理类。
- 切面(Aspect):切面由切入点和增强组成,包括增强逻辑的定义和切入点的定义,AOP框架负责实施切面,将切面定义的增强逻辑织入到切面所指定的逻辑中。
使用AOP框架时,开发者主要工作为定义点和增强,通常采用XML配置文件或者注解的方式,配置好增强的信息和切入点后,AOP框架会自动生成AOP代理。
AOP的实现策略:
在java语言的编写的程序中,重源代码到最终运行,会经历编写源代码,编译生出字节码,加载字节码,运行程序几个阶段。各个阶段都可以以特定的方式织入增强。
JDK 动态代理:
JDK动态代理是java.lang.reflect.*包提供的方式(反射)。必须借助接口才能在运行期生成代理对象,对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP。不需要引入第三方包,不能针对类。只要一个类实现了某个接口,就可以通过动态代理机制在运行期动态的构造这个接口的实现对象。
按照JavaSE动态代理的要求:
1,需要编写一个类实现java.lang.reflect.InvocationHandler接口。实现java.lang.reflect.InvocationHandler接口时需要重写invoke()方法:
Object invoke(Object proxy,Method method,Object[] args)throws Exception;
//proxy :自动生成的动态代理对象,与目标对象会实现同一接口;
//method:运行时调用的方法,此方法应为指定接口中定义的方法。
//args:调用method方法被调用时的返回值。
//返回值:代理对象的method方法被调用时的返回值,
2,构造上述实现类的实例,然后调用java.lang.Proxy的newProxyInstance()方法获取自动生成的代理对象,完成业务逻辑的方法调用。
public static Object newProxyInstance(ClassLocader loader,Class<?>[] interfaces,InvocationHandler handler)throws IllegalArgumentException//loader:代理类的加载器
//interfaces:代理类的所有接口,这些接口中的方法都会被拦截。
//handler:动态代理的对象。
//返回值:动态生成代理对象,此对象会实现inferface参数中包括的所有接口。
业务接口及实现类
package dynamic.jdk;public interface TestDao {public void save();public void modify();public void delete();
}package dynamic.jdk;public class TestDaoImpl implements TestDao {public TestDaoImpl() {// TODO Auto-generated constructor stub}@Overridepublic void save() {// TODO Auto-generated method stubSystem.out.println("保存");}@Overridepublic void modify() {// TODO Auto-generated method stubSystem.out.println("修改");}@Overridepublic void delete() {// TODO Auto-generated method stubSystem.out.println("删除");}}
代理类:
package dynamic.jdk;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;import aspect.Myspect;public class JDKDynamicPorxy implements InvocationHandler {//声明目标类对象真实对象)private TestDao testdao;//创建代理对象,建立代理对象和真实对象的代理关系,并返回代理对象。public Object createProxy(TestDao testDao) {this.testdao=testDao;//获取代理类 的构造器ClassLoader cld = JDKDynamicPorxy.class.getClassLoader();//获取目标类的所有接口Class [] clazz = testdao.getClass().getInterfaces();//动态生成代理对象,需要代理类的类构造器,目标类所有接口,即目标类实例。return Proxy.newProxyInstance(cld, clazz, this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// TODO Auto-generated method stub//切面对象Myspect myAspect = new Myspect();//调用切面方法myAspect.check();myAspect.except();//执行目标类方法(被增强的方法)对应的实例对象的该method 方法。Object obj = method.invoke(testdao, args);myAspect.log();myAspect.monitor();return obj;}
}
测试类:
package dynamic.jdk;public class JDKDynamicTest {public static void main(String[] args) {// TODO Auto-generated method stub//实例化代理类JDKDynamicPorxy jdkdynamicProxy = new JDKDynamicPorxy();//实例化目标类TestDao testdao = new TestDaoImpl();//生成代理对象TestDao testDaoAdvice = (TestDao)jdkdynamicProxy.createProxy(testdao);testDaoAdvice.save();System.out.println("++++++");testDaoAdvice.modify();System.out.println("++++++");testDaoAdvice.delete();}}
Method 的invoke方法API文档:
public Object invoke(Object obj,Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException
对带有指定参数的指定对象调用由此 Method
对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。如果底层方法是静态的,那么可以忽略指定的 obj
参数。该参数可以为 null。如果底层方法所需的形参数为 0,则所提供的 args
数组长度可以为 0 或 null。如果底层方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。
参数:
obj
- 从中调用底层方法的对象
args
- 用于方法调用的参数
返回:
使用参数 args
在 obj
上指派该对象所表示方法的结果
抛出:
IllegalAccessException
- 如果此 Method
对象强制执行 Java 语言访问控制,并且底层方法是不可访问的。
IllegalArgumentException
- 如果该方法是实例方法,且指定对象参数不是声明底层方法的类或接口(或其中的子类或实现程序)的实例;如果实参和形参的数量不相同;如果基本参数的解包转换失败;如果在解包后,无法通过方法调用转换将参数值转换为相应的形参类型。
InvocationTargetException
- 如果底层方法抛出异常。
NullPointerException
- 如果指定对象为 null,且该方法是一个实例方法。
ExceptionInInitializerError
- 如果由此方法引起的初始化失败。
在代理实例上处理方法调用并返回结果。当与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
CGLIB动态代理:
JDK动态代理必须要提供接口才可以使用,并且增强的方法只能是接口中申明的方法,对于没有提供接口的类,采用动态字节码的方式,CGLIB为流行的动态字节码生成工具,只能采用CGLIB动态代理。
//目标类
package dynamic.cglib;public class TestDao {public void svae() {System.out.println("保存");}public void modify() {System.out.println("修改");}public void delete() {System.out.println("删除");}}
//代理类:
package dynamic.cglib;import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import aspect.Myspect;public class CglibDynamicProxy implements MethodInterceptor{/** 创建代理类的方法,生成CGLIb代理对象。* target是目标对象,需要增强的对象,* 返回目标对象的CGLIB代理对象。*/public Object createProxy(Object target) {//创建一个动态类对象,即增强类对象,Enhancer enhancer = new Enhancer();//确定要增强的类,设置其父类,enhancer.setSuperclass(target.getClass());//确定代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor的方法。enhancer.setCallback(this);//返回创建的代理对象。return enhancer.create();}/** intercept方法会在程序执行目标方法时被调用,* proxy是CGLIB根据指定的父类生成的代理对象。* method是拦截器* args是拦截器方法的参数组* methodProxy是方法的代理对象,用于执行父类的方法* 返回代理结果* (non-Javadoc)* @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy)*/@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {// TODO Auto-generated method stub//创建一个切面Myspect myaspect = new Myspect();myaspect.check();myaspect.except();Object obj = methodProxy.invokeSuper(proxy, args);//后增强myaspect.log();myaspect.monitor();return obj;}}
//测试类
package dynamic.cglib;public class CGLIBDynamicTest {public static void main(String [] args) {//创建代理对象CglibDynamicProxy cdb = new CglibDynamicProxy();//创建目标类对象TestDao testDao = new TestDao();//获取增强后的目标对象TestDao testDaoAdvice = (TestDao)cdb.createProxy(testDao);//执行方法testDaoAdvice.svae();testDaoAdvice.modify();testDaoAdvice.delete();}}
Spring Aop
Spring框架通过Java SE动态代理和cglib实现了AOP功能:当明确指定目标类的实现的业务接口时,Spring使用动态代理的方式,也可以强制使用cglib,没有指定目标类的接口时,Spring使用cglib进行字节码增强
增强(通知)的类型:根据Spring中通知在目标类中方法中的连接点的位置,分为6种类型。
- 环绕增强:(org.aopalliance.intercept.MethodInterceptor)是在目标方法执行前和执行后实施增强,可以替换其他的增强,可应用与日志记录,事务处理等功能。
- 前置增强:(org.springframework.aop.MethodBeforAedvice)在目标方法执行前实施增强,如果增强不抛出异常,那么连接点一定会被执行,应用于权限管理。
- 返回后增强(后置返回通知):(org.springframework.aop.AfterReturningAdvice)在目标方法成功执行后(即没有抛出异常的情况)实施增强,用于关闭流,删除临时文件等功能,
- 后置增强(后置最终通知):(org.springframework.aop.AfterAdvice)在目标方法执行后增强,即连接点在出现任何情况下都会执行的增强,一般用于释放资源,
- 异常增强::(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后的增强。
- 引介增强:(org.springframework.aop.IntroducyionIntercetor)一种特殊的增强,能够使目标类实现某个指定的接口,可以添加属性和方法,可应用于修改目标类。
基于代理类的AOP实现:
ProxyFactoryBean:ProxyFactoryBean是org.springframework.beans.factory.FactoryBean接口的实现类,FactoryBean负责实例还一个Bean实例,ProxyFactoryBean负责为其他bean实例创建代理实例。
target | 代理的目标对象 |
proxyInterfaces | 代理需要实现的接口列表,如果为多个接口,可以使用一下格式赋值。<list><value></value></list> |
interceptorNames | 需要织入目标的Advice |
proxyTargetClass | 是否对类代理而不是接口,默认为false,使用JDK动态代理,当为true时,使用CGLIB动态代理。 |
singleton | 返回的代理是否为单例模式,默认为true。 |
optimize | 当设置为true时强制使用CGLIB动态代理。 |
package spring.proxyfactorybean;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;/** 切面类* */
public class Myaspect implements MethodInterceptor {public Myaspect() {// TODO Auto-generated constructor stub}@Overridepublic Object invoke(MethodInvocation arg0) throws Throwable {// TODO Auto-generated method stub//增强方法check();except();//执行目标方法;Object obj = arg0.proceed();//增强方法log();moitor();return obj;}private void moitor() {// TODO Auto-generated method stubSystem.out.println("性能检测!!"); }private void log() {// TODO Auto-generated method stubSystem.out.println("模拟日志记录!!");}private void except() {// TODO Auto-generated method stubSystem.out.println("模拟异常处理");}private void check() {// TODO Auto-generated method stubSystem.out.println("模拟权限控制!!");}}
配置切面并 指定代理:
<!-- 定义目标对象 --><bean id = "testDao" class = "dynamic.jdk.TestDaoImpl"/><!-- 创建一个切面 --><bean id = "myAspect" class = "spring.proxyfactorybean.Myaspect"/><!-- 使名用Spring代理工厂定义一个 代理对象--><bean id = "testProxy" class = "org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定代理实现的接口 --><property name = "proxyInterfaces" value = "dynamic.jdk.TestDao"/><!-- 指定目标对象 --><property name = "target" ref = "testDao"/><!-- 指定切面 ,织入环绕增强--><property name = "interceptorNames" value = "myAspect"/><!-- 指定代理方式 --><property name = "proxyTargetClass" value = "true"/></bean>
创建测试类:
package spring.proxyfactorybean;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import dynamic.jdk.TestDao;public class ProxyFactoryBeanTest {public ProxyFactoryBeanTest() {// TODO Auto-generated constructor stub}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext12.xml");//从容器中获得增强后的目标对象System.out.println(applicationContext.getBean("testProxy").getClass().getName());TestDao testdaoAdvice =(TestDao)applicationContext.getBean("testProxy");//执行方法testdaoAdvice.save();System.out.println("++++++++++++++++++++");testdaoAdvice.modify();System.out.println("++++++++++++++++++++");testdaoAdvice.delete();}
}
基于XML配置Spring AOP
基于XML配置开发AspectJ
AspectJ是一个基于Java语言的AOP框架,从Spring2.0后引入AspectJ的支持,建议使用AspectJ框架实现SpringAOP,有两种方式,一是基于XML配置开发AspectJ,二是基于注解开发AspectJ。
基于XML文件配置开发AspectJ通过Xml文件定义切面,切入点及通知,所有的定义都在<aop:config>元素及其子元素中。
<aop:config> | 开发AspectJ的顶层配置元素,在配置文件Bean下可以包含多个该元素 |
<aop:aspect> | 配置(定义)一个切面,<aop:config>元素的子元素,属性ref指定切面的定义, |
<aop:pointcut> | 配置切入点,<aop:aspect>元素的子元素,属性expression指定通知增强那些方法。 |
<aop:before> | 配置前置通知(增强),属性method指定前置通知方法,属性pointcut-ref指定关联的切入点。 |
<aop:after-returning> | 配置返回后增强(后置返回通知),属性method指定后置置通知方法,属性pointcut-ref指定关联的切入点。 |
<aop:around> | 配置环绕通知,属性method方法指定环绕通知方法,属性pointcut-ref指定关联的切入点。 |
<aop:after-throwing> | 配置异常通知,<aop:aspect>元素的子元素,属性method方法指定异常通知方法,属性pointcut-ref指定关联的切入点。没有异常时不会执行 |
<aop:after> | 配置后置(最终)通知:属性method方法指定异常通知方法,属性pointcut-ref指定关联的切入点。 |
<aop:declear-parents> | 给通知引入新的额外的接口,增强功能 |
Spring配置文件中的<beans>元素可以包含多个<aop:config>,一个<aop:config>元素又可以包含属性和子元素,子元素包括<aop:pointcut>、<aop:advisor>、<aop:aspect>,配置时,这3个子元素必须按照此顺序定义。
1.1配置切面
在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素可以将定义好的Spring Bean转换成切面Bean,所以先定义一个普通的Spring Bean。配置<aop:aspect>元素时,通常会指定id和ref两个属性。
id用来定义该切面的唯一标识名称。 ref用于引用普通的Spring Bean。
1.2配置切入点
当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。
定义<aop:pointcut>元素时,通常会指定id和expression两个属性
id用于指定切入点的唯一标识名称。 expression用于指定切入点关联的切入点表达式。
切入点表达式(举例说明)
切入点表达式的基本格式:
Spring支持使用三种逻辑运算组合切入点表达式,||,&&,!;
1.3配置通知
使用<aop:aspect>的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:
属性名称解释
pointcut该属性用于指定一个切入点表达式,Spring将在匹配该表达式的连接点时织入该通知。
pointcut-ref该属性指定一个已经存在的切入点名称,通常pointcut和pointcut-ref两个属性只需要使用其中之一。
method该属性指定一个方法名,指定将切面Bean中的该方法转换为增强处理。
throwing该属性只对<after-throwing>元素有效,用于指定一个形参名,异常通知方法可以通过该形参访问目标方法所抛出的异常。
returning该属性只对<after-returning>元素有效,用于指定一个形参名,后置通知方法可以通过该形参访问目标方法的返回值。
基于注解开发AspectJ:
基于注解开发AspectJ要比基于XML配置开发AspectJ方便,
注解名称解释
@Aspect:用来定义一个切面。
@pointcut:用于定义切入点表达式。在使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称,这个方法签名就是一个返回值为void,且方法体为空的普通方法。
@Before:用于定义前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已有的切入点,也可以直接定义切入点表达式)。
@AfterReturning:用于定义后置通知,相当于AfterReturningAdvice。在使用时可以指定pointcut / value和returning属性,其中pointcut / value这两个属性的作用一样,都用于指定切入点表达式。returning属性值用于表示Advice方法中定义与此同名的形参,该形参可用于访问目标方法的返回值。
@Around:用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。
@After-Throwing:用于定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定pointcut / value和throwing属性。其中pointcut/value用于指定切入点表达式,而throwing属性值用于指定-一个形参名来表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。
@After:用于定义最终final 通知,不管是否异常,该通知都会执行。使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。
@DeclareParents:用于定义引介通知,相当于IntroductionInterceptor (不要求掌握)。
@Repository:用于定义目标类为目标对象,@Repository("Spring Bean ")
1,创建切面类,并进行注解;
//创建切面类,并进行注解
package aspect.xml;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;/** 切面类,在此类中编写各种类型通知* */
public class MyAspect {/** 前置增强,使用JoinPoint接口作为参数获得目标对象信息* */public void before(JoinPoint jb) {System.out.println("前置通知:模拟权限控制");System.out.println("。目标对象:"+jb.getTarget()+"被增强的方法:"+jb.getSignature().getName());}/** 后置返回通知*/public void afterReturning(JoinPoint jp) {System.out.println("后置返回通知:"+"模拟删除临时文件");System.out.println(",被增强处理的方法:"+jp.getSignature().getName());}/** 环绕通知* proceedingJoinPoint是Join的子接口,代表可以执行的目标方法* 返回值必须为Object* 必须有一个参数类型为ProceedingJoinPoint类型* 必须throws Throwable*/public Object around(ProceedingJoinPoint pjp)throws Throwable{//开始System.out.println("环绕开始:执行目标方法前,模拟开启事务");return pjp;}/** 异常通知*/public void except(Throwable e) {System.out.println("异常通知"+"程序执行异常"+e.getMessage());}/**后置(最终)通知 */public void after() {System.out.println("最终通知:模拟释放资源!");}
}
2,注解目标类;@Repository("Spring Bean ")
3,创建配置文件
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 定义目标类对象 -->
<bean id = "testDao" class = "dynamic.jdk.TestDaoImpl"/>
<!-- 配置切面 -->
<bean id = "myaspect" class = "aspect.xml.MyAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 配置切面 --><aop:aspect ref = "myaspect"><!-- 配置切入点,通知增强那些方法 --><aop:pointcut expression = "execution(* dynamic.jdk.*.*(..))" id = "myPointCut"/><!-- 将通知与切入点关联 --><!-- 关联前置切入点 --><aop:before method = "before" pointcut-ref = "myPointCut"/><!-- 关联后置增强点 --><aop:after-returning method ="afterReturning" pointcut-ref = "myPointCut"/><!-- 关联环绕增强点 --><aop:around method = "around" pointcut-ref = "myPointCut"/><!-- 关联异常通知 --><aop:after-throwing method = "except" pointcut-ref = "myPointCut"/><!-- 关联后置(最终)通知,不管目标方法是否执行都要执行 --><aop:after method ="after" pointcut-ref = "myPointCut"/></aop:aspect></aop:config></beans>
4,创建测试类
package aspect.annotaion;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import dynamic.jdk.TestDao;public class annotationTest {public static void main(String[] args) {// TODO Auto-generated method stubApplicationContext app = new ClassPathXmlApplicationContext("/aspect/annotaion/applicationContext.xml");TestDao t1 = (TestDao)app.getBean("testDao");t1.save();}}
第五章 ,spring的事务管理
事务处理是企业级应用的核心需求之一,Spring框架对事务处理提供了良好的支持,支持编程式声明式两种事务管理。
Spring事务支持:
JavaEE应用的事务策略分为全局事务(或称分布式事务,需要使用JTA(Java Trans))和局部事务(只需要JDBC事务支持即可,只考虑关系型数据库),
全局事务通常由JavaEE应用服务器管理,需要使用EJB并得到应用服务器提供的JTA支持,JTA需要通过JNDI获取,因此用户的应用无论是跨多个事务性资源(关系型数据库和消息队列等),还是使用单一的事务性资源,EJB都使用全局事务加一处理。所以基于EJB的应用无法脱离应用服务器的环境。
局部事务是基于单一事务性资源的,与持久化技术有关,当采用JDBC时,需要使用Connection对象操作事务,当采用Hibernate持久化技术时,需要使用Session对象操作事务。
在单一事务性资源的情况下,Spring直接使用底层的数据源管理事务,在面对多个事务性资源时,Spring会寻求Java EE应用服务器的支持。
Spring框架为局部事务提供了两种管理方法,编程式事务和声明式事务。
Spring的数据库编程
数据库编程式互联网编程的基础,Spring框架为开发者提供了JDBC模式模板,即jdbcTemplate,一般使用Hibernate和MyBatis框架进行数据库编程。
Spring数据库编程主要使用SpringJDBC模块的core(JDBC 的核心功能包,包括常用的jdbcTemplate类)包和DataSource(访问数据源的工具类)包。需要进行配置数据源和JDBC模板。
配置JDBC模板时,需要将DataSource注入到jdbcTemplate,而在数据库访问(DAO)层需要使用jdbcTemplate时也需要将jdbcTemplate注入到对应的Bean中。
Spring JDBCTemplate的常用方法:
- public int update(String sql,Object args[]):方法对数据表进行增删改等操作,返回更新的行数。
String insertSQL = "insert into user values(null,?,?)"; Object param1[] = {"liruilong","男"}; jdbcTemplate.update(sql,param1);
public List<T>query(String Sql,RowMapper<T> rowMapper,Object args[]):该方法可以对数据表进行查询操作,rowMapper将结果集映射到用户自定义的类中(前提是自定义类中的属性与数据表的字段对应)。
String selectSql = "select * form user";
Rowapper<Myuser> rowapper = new BeanPropertyPowMapper<Myuser>(Myuser.class);
List<Myuser> list = jdbcTemplate.query(selectSql,rowMapper,null);
实例代码:
1.创建导入Jar包;
2,创建配置文件;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 指定需要扫描的包,使注解生效 --><context:component-scan base-package="Spring_jdbcTemplate"/><!-- 配置数据源 --><bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value = "com.mysql.jdbc.Driver"/><property name="url" value ="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name="username" value = "root"/><property name="password" value ="mysql" /></bean><!-- 配置JDBC模板 --><bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref = "dataSource"/></bean></beans>
3,创建实体类,
package Spring_jdbcTemplate;public class MyUser {private Integer uid;private String uname;private String usex;public MyUser() {// TODO Auto-generated constructor stub}@Overridepublic String toString() {return "MyUser [uid=" + uid + ", uname=" + uname + ", usex=" + usex + "]";}public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public String getUname() {return uname;}public void setUname(String uname) {this.uname = uname;}public String getUsex() {return usex;}public void setUsex(String usex) {this.usex = usex;}}
4,创建数据访问层,
package Spring_jdbcTemplate;import java.util.List;public interface TestDao {public int update(String sql,Object[] param);public List<MyUser>query(String sql,Object[] param);}package Spring_jdbcTemplate;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
@Repository("testDao")
public class TestDaoImple implements TestDao {@Autowired//使用配置文件的JDBC模板private JdbcTemplate jdbcTemplate;@Overridepublic int update(String sql, Object[] param) {// TODO Auto-generated method stubreturn jdbcTemplate.update(sql, param);}@Overridepublic List<MyUser> query(String sql, Object[] param) {// TODO Auto-generated method stubRowMapper<MyUser>rowMappler = new BeanPropertyRowMapper<MyUser>(MyUser.class);return jdbcTemplate.query(sql, param, rowMappler);}}
5,创建测试类。
package Spring_jdbcTemplate;import java.util.List;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSpringJDBC {public static void main(String[] args) {ApplicationContext appCon = new ClassPathXmlApplicationContext("/Spring_jdbcTemplate/applicationContext.xml");//从容器中获取目标对象TestDao testDao = (TestDao) appCon.getBean("testDao");String insertSql ="INSERT INTO user VALUES(NULL,?,?)";String selectSql = "SELECT * FROM USER";Object param1 []= {"li1","男"};Object param12 []= {"li12","男"};Object param13 []= {"li13","男"};Object param14 []= {"li15","男"};Object param15 []= {"li16","男"};Object param16 []= {"li16","男"};testDao.update(insertSql, param16);testDao.update(insertSql, param12);testDao.update(insertSql, param15);testDao.update(insertSql, param14);testDao.update(insertSql, param13);testDao.update(insertSql, param12);List<MyUser> list = testDao.query(selectSql,null );for(int i = 0;i<list.size();i++) {System.out.println(list.get(i));}}
}
编程式事务管理(在代码中显示的调用beginTransaction,commit,rollback等事务处理相关的方法):
Spring框架使用一致的编程模型控制事务,通过TransactionTemplete并配合TransactionCallback回调接口指定具体的持久化操作完成编程式事务操作。
基于底层的API的编程事务管理:根据PlatformTransactionManager,TransactionDefinition和TransactionStatus几个核心接口,通过编程的方式来进行事务处理,
TransactionDefinition接口:用于描述事务的隔离级别(指事务之间的隔离程度),传播规则,超时时间,是否为只读事务属性等,可以编程方式实现,也可以通过XML文件或注解的方式。 事务隔离级别:
- 读未提交(READ_UNCOMMITED):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
- 读以提交(READ_COMMITED):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务以提交的对已有记录的更新。
- 可重复读(REPEATABLE_READ):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他事务对已有记录的更新。
- 序列化(SERIALIZABLE):一个事务只能操作在该事务开始之前提交单独的数据,并且可以在在该事务中操作这些数据。
事务的传播(指参与事务的业务方法发生互相调用时如何控制事务,即一个业务方法A中调用另一个方法B时,B方法采用何种事务控制的问题)。
事务的超时时间是指事务的最长持续时间,通过TransactionDefinition接口中的getTimeout()方法可以获得事务的超时时间。
只读事务是指无法修改数据的事务,通过TransactionDefinition接口中的isReadOnly()方法判断一个事务是否为只读事务。
TransactionStatus接口:用于描述事务状态,事务管理器通过该接口获得事务的运行期状态信息,也可以通过该接口间接的回滚事务,它相比于在抛出异常时回滚事务的方式更具有可控性,方法:
Object createSavepoint() | 创建保存点; |
Boolean rollbackToSavepoint(Object Savepoint) | 事务回滚到指定的保存点。保存点被释放。 |
void releaseSavepoint(Object savepoint) | 释放特定保存点,提交事务时,所有保存点自动释放 |
boolean hasSavePoint() | 当前事务是否在内部创建一个保存点 |
boolean isNewTransaction() | 当前事务是否已经结束,即是否已经回滚或提交 |
void setRollbackOnly() | 当前事务设置为rollback-only、通知事务管理器只能将事务回滚,事务管理器显示调用或者抛出异常的方式回滚事务 |
boolean isRollbackOnly() | 当前事务是否已被标识为rollback——only |
PlatformTransactionManage接口:代表事务管理器,是Spring事务管理的核心接口,其提供了三个控制事务的方法:
Transaction getTransaction(TransactionDefinition def):根据事务定义的信息(TransactionDefinition )从事务环境中返回一个已存在事务,或者创建一个新的事务,并用TransactionStatus来表述事务状态。
void commit(TransactionStatus status):根据事务的状态提交事务,如果标识为rollback——only,将执行回滚操作。
void rollback(TransactionStatus status):回滚事务,当commit方法抛出异常时该方法自动调用。
该接口有很多实现类:xxxTransactionManager,适用于不同的场景。
配置数据源管理器的实现类:org.springframework.jdbc.datasource.DriverManagerDataSource
基于底层的API的编程事务管理具体步骤:
1,给数据源配置事务管理器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 指定需要扫描的包,使注解生效 --><context:component-scan base-package="Spring_jdbcTemplate"/><!-- 配置数据源 --><bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value = "com.mysql.jdbc.Driver"/><property name="url" value ="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name="username" value = "root"/><property name="password" value ="mysql" /></bean><!-- 配置JDBC模板 --><bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref = "dataSource"/></bean><!-- 为数据源配置事务管理器 --><bean id = "txManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name = "dataSource" ref = "dataSource"/></bean></beans>
2,创建数据库访问类,
package Spring_jdbcTemplate;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Repository("CodeTransaction")
public class CodeTransaction {@Autowiredprivate JdbcTemplate jdbcTmeplate;@Autowiredprivate DataSourceTransactionManager txManager;public String test() {//默认事务定义,例如隔离级别等。TransactionDefinition tf = new DefaultTransactionDefinition();//开启事务tsTransactionStatus ts = txManager.getTransaction(tf);String message = "执行成功,没有事件回滚";try {//删除表的数据String De_SQL= "DELETE FROM USER";//添加数据String In_SQL="INSERT INTO user VALUES(NULL,?,?)";Object [] param = {"liruilong","男"};//delete data;jdbcTmeplate.update(De_SQL);//insert datajdbcTmeplate.update(In_SQL, param);//提交事务txManager.commit(ts);}catch(Exception e) {txManager.rollback(ts);message = "主键重复,事务回滚";e.printStackTrace();}return message;}}
3,创建测试类;
package Spring_jdbcTemplate;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestCodeTransaction {public static void main(String[] args) {// TODO Auto-generated method stubApplicationContext app = new ClassPathXmlApplicationContext("/Spring_jdbcTemplate/applicationContext.xml");CodeTransaction ct =(CodeTransaction) app.getBean("CodeTransaction");String result = ct.test();System.out.println(result);}}
基于TransactionTemplate的编程式事务管理:事务管理的代码散落在业务逻辑中,破坏了原有代码的调理性,TransactionTemplate的execute方法由一个TransactionCallback接口类型的参数,该接口中定义一个doInTransaction方法,通常以匿名内部类的方式实现TransactionCallback接口,并在其doInTransaction方法中书写业务逻辑,使用默认的回滚和事务提交规则,在业务代码中不需要显示的调用任何事务处理API,doInTransaction方法有一个TransactionStatus类型的参数,可以在方法任何位置调用该参数的setRrollbackOnly方法将事务标识为回滚,以执行事务操作。
根据默认规则,如果在执行回调的方法中抛出了未检查异常,或者显示调用setRollbackOnly方法,则回滚事务,如果事务执行完或者抛出受检异常。则提交事务。
基于TransactionTempleat的编程式事务:
1,为事务管理器添加事务模板:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 指定需要扫描的包,使注解生效 --><context:component-scan base-package="Spring_jdbcTemplate"/><!-- 配置数据源 --><bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value = "com.mysql.jdbc.Driver"/><property name="url" value ="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name="username" value = "root"/><property name="password" value ="mysql" /></bean><!-- 配置JDBC模板 --><bean id = "jdbcTmeplate" class = "org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref = "dataSource"/></bean><!-- 为数据源配置事务管理器 --><bean id = "txManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name = "dataSource" ref = "dataSource"/></bean><!-- 为事务管理器添加事务模板 --><bean id = "transactionTempleat" class = "org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref = "txManager"/></bean></beans>
2,创建数据库访问类:
package Spring_jdbcTemplate;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;@Repository("TransactionTemplate_Demo")
public class TransactionTemplate_Demo {@Autowiredprivate JdbcTemplate jdbcTmeplate;@Autowiredprivate TransactionTemplate transactionTempleat;String message ;public String test() {transactionTempleat.execute(new TransactionCallback<Object>() {String DELSQL = "DELETE FROM user";String INSSQL = "INSERT INTO user values(1,?,?)";Object [] param = {"lisi","nan"};@Overridepublic Object doInTransaction(TransactionStatus arg0) {// TODO Auto-generated method stubtry {jdbcTmeplate.update(DELSQL);jdbcTmeplate.update(message, param);message = "Success!!";}catch(Exception e) {message ="loser!!";System.out.println(e.getMessage());}return message;}});return message;}}
3,创建测试类:
package Spring_jdbcTemplate;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TransactionTemplateTest {public static void main(String[] args) {// TODO Auto-generated method stubApplicationContext app =new ClassPathXmlApplicationContext("/Spring_jdbcTemplate/applicationContext.xml");System.out.println(app.getBean("TransactionTemplate_Demo").getClass().getName());TransactionTemplate_Demo Tr = (TransactionTemplate_Demo) app.getBean("TransactionTemplate_Demo");System.out.println(Tr.test());}}
声明式事务管理:Spring的申明式事务管理是通过AOP技术实现的事务管理,其本质是对方法前后进行拦截,然后在目标方法开始之前创建一个事务,在执行完目标方法后根据情况提交回滚事务。即通过事务的声明性信息,Spring负责将事务管理增强的逻辑动态的织入到业务方法相应的连接点上,
- 配置数据源
- 配置JDBC模板
- 配置事务增强(通知)
- 配置事务通知的切面
声明式事务管理的优点是不需要通过编程的方式管理业务,即在业务逻辑中没有事务处理的代码,即纯业务代码不被污染,方便后期维护,与编程式事务管理相比,声明式事务的最小粒度只能作用到方法级别,编程式事务可以作用到代码级别,可以将代码块独立为方法改善。
Spring的声明式事务通过两种方式实现:基于XML的方式,和基于@Transactional注解的方式。
基于XML方式的声明式事务管理:通过在配置文件中配置事务规则的相关申明来实现的,
Spring框架提供提供了tx命名空间来配置事务,该命名空间下的<tx:advice>元素可以为一个或一批(通过通配符进行方法名称的匹配)方法匹配事务增强(通知),
id |
提供唯一的id标识, |
transaction-manager | 已经配置事务管理器id |
<tx:advice>元素具有<tx:attributes>子元素,该元素的子元素<tx:method>可以配置需要被事务增强的方法,以及事务的传播,隔离,超时,只读事务,对指定异常回滚,和对指定异常不回滚等属性。
name | 配置需要被增强的通配符 |
propagation | 配置事务的传播类型 |
isolation | 配置事务隔离级别 |
timeout | 配置事务超时时间,默认-1,即由底层决定 |
read-only | 配置是否为只读,默认false |
rollback-for | 配置需要自动回滚的异常类型,默认所有的RuntimeException都会回滚 |
no-rollback-for | 配置不触发自动回滚事务的异常类型,默认所有的CheckedException都不会回滚。 |
1,创建Dao层
package Dao;public interface TestDao_XML {public int save(String sql,Object obj[]);public int delete(String sql, Object obj[]);
}package Dao;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;import service.TestService_XML_Demo;
@Repository("TestDao_XMLs")
public class TestDao_XMLs implements TestDao_XML {@Autowiredprivate JdbcTemplate jdbctemplate;@Overridepublic int save(String sql, Object obj[]) {// TODO Auto-generated method stubreturn jdbctemplate.update(sql, obj);}@Overridepublic int delete(String sql, Object obj[]) {// TODO Auto-generated method stubreturn jdbctemplate.update(sql, obj);}}
创建Service层:
package service;public interface TestService_XML_Demo {public int save(String sql,Object obj[]);public int delete(String sql, Object obj[]);
}package service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import Dao.TestDao_XML;@Service("TestService_XML_Demos")
public class TestService_XML_Demos implements TestService_XML_Demo {@Autowiredprivate TestDao_XML testdao;public TestService_XML_Demos() {// TODO Auto-generated constructor stub}@Overridepublic int save(String sql, Object obj[]) {// TODO Auto-generated method stubreturn testdao.save(sql, obj);}@Overridepublic int delete(String sql, Object obj[]) {// TODO Auto-generated method stubreturn testdao.delete(sql, obj);}}
创建控制器层:
package Dao.Controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;import service.TestService_XML_Demo;@Controller("StatementController")
public class StatementController {@Autowiredprivate TestService_XML_Demo testService;public String test() {String message= "事务正常提交!!!";String del = "DELETE FROM USER";String sav = "INISERT INTO user VALUES(?,?,?)";Object obj[] = {1,"liruiong","nan"};try {testService.delete(del, null);testService.save(sav, obj);}catch (Exception e) {// TODO: handle exceptionSystem.out.println(e.getMessage());message = "事务回滚!!!!";}return message;}}
创建配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 指定需要扫描的包,使注解生效 --><context:component-scan base-package="Dao.Controller"/><context:component-scan base-package="service"/><context:component-scan base-package="Dao"/><!-- 配置数据源 --><bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value = "com.mysql.jdbc.Driver"/><property name="url" value ="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name="username" value = "root"/><property name="password" value ="mysql" /></bean><!-- 配置JDBC模板 --><bean id = "jdbcTmeplate" class = "org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref = "dataSource"/></bean><!-- 为数据源配置事务管理器 --><bean id = "txManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name = "dataSource" ref = "dataSource"/></bean><!-- 为事务管理器添加事务模板 --><bean id = "transactionTempleat" class = "org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref = "txManager"/></bean><!-- 编写通知(增强)申明事务 --><tx:advice id ="myAdvice" transaction-manager="txManager"><tx:attributes><!-- 表示任意方法 --><tx:method name="*"/></tx:attributes></tx:advice><!-- 编写AOP,让Spring自动对目标对象生成代理,需要使用AspectJ的表达式 --><aop:config><!-- 定义切入点 --><aop:pointcut expression = "execution(* service.*.*(..))" id = "myPointCut"/><!-- 切面,将切入点与通知关联 --><aop:advisor advice-ref="myAdvice" pointcut-ref = "myPointCut"/></aop:config></beans>
测试类:
package test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import Dao.Controller.StatementController;public class XMLtest {public static void main(String[] args) {// TODO Auto-generated method stubApplicationContext app = new ClassPathXmlApplicationContext("comstatement.xml");StatementController coll = (StatementController) app.getBean("StatementController");System.out.println(coll.test());}}
基于@Transactional注解的声明式事务管理:
基于注解的方式配置声明式的事务管理时,事务属性是直接写在java代码中的,需要在配置文件中添加<tx:annotation-driven>元素:
transaction-manager | 以配置好的事务管理器的bean的id,没有则查找该名字的id |
proxy-target-class | 是否通过CGLIB创建子类代理对象 |
order | 即除了事务切面类,还需要织入的的切面,可以控制事务切面的在目标连接点的织入顺序 |
Spring框架使用@org.springframework.transaction.annotation.Transactional注解提供配置事务的功能,该注解可以作用于接口,方法,类,即类的方法上,当作用于类时,该类的所有public方法都具有该类型的事务属性,只有在基于接口的代理时才会生效,
propagation | 配置事务的传播类型 |
isolation | 配置事务的隔离级别 |
timeout | 配置事务的超时时间 |
readOnly | 配置是否可读 |
rollbackFor | 配置需要自动回滚的事务的异常类型默认{} |
rollbackForClassName | 配置需要自动回滚的事务的异常类名默认{} |
noRollbackFor | 配不触发自动回滚的事务的异常类型默认{} |
noRollbackForClassName | 配置不触发自动回滚的事务的异常类名默认{} |
package service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import Dao.TestDao_XML;@Service("TestService_XML_Demos")
@Transactional()
public class TestService_XML_Demos implements TestService_XML_Demo {@Autowiredprivate TestDao_XML testdao;public TestService_XML_Demos() {// TODO Auto-generated constructor stub}@Overridepublic int save(String sql, Object obj[]) {// TODO Auto-generated method stubreturn testdao.save(sql, obj);}@Overridepublic int delete(String sql, Object obj[]) {// TODO Auto-generated method stubreturn testdao.delete(sql, obj);}}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 指定需要扫描的包,使注解生效 --><context:component-scan base-package="Dao.Controller"/><context:component-scan base-package="service"/><context:component-scan base-package="Dao"/><!-- 配置数据源 --><bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value = "com.mysql.jdbc.Driver"/><property name="url" value ="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name="username" value = "root"/><property name="password" value ="mysql" /></bean><!-- 配置JDBC模板 --><bean id = "jdbcTmeplate" class = "org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref = "dataSource"/></bean><!-- 为数据源配置事务管理器 --><bean id = "txManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name = "dataSource" ref = "dataSource"/></bean><!-- 为事务管理器添加事务模板 --><bean id = "transactionTempleat" class = "org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref = "txManager"/></bean><!--为事务管理器注册事务驱动--><tx:annotation-driven transaction-manager = "txManager"/></beans>
在事务处理中捕获异常:
申明式事务的处理流程是:
- Spring根据配置完成事务定义,设置事务属性。
- 执行开发者的代码逻辑。
- 如果开发者的代码产生异常。满足事务回滚的配置条件,则事务回滚,否则,事务提交,
- 事务资源释放。
当对于异常代码加入try——catch语句时,Spring就不能在申明式事务处理中正常得到事务回滚的异常信息,Spring只有在发生未捕获的RuntimeException时才回滚事务。
在基于XML的声明式事务管理中,捕获异常,需要补存两个条件。
- 修改申明事务的配置,即将<tx:mwthod name = "*" >添加属性rollback-for = “java.lang.Exception”/>
- 在异常块的catch语句中要将捕获的异常抛出: throw new RuntimeException();
在基于@Transacjution注解的申明式事务管理中捕获异常:
- 修改@Transaction注解,@Transaction(rollback={Exception.class})
- 在catch语句中添加“throw new RuntimeException();”语句。
第四章,MyBatis
MyBatis简介
MyBatis是一个人基于Java的持久层框架,MyBatis提供的持久层框架包括SQL Maps和Data Access Object(DAO),它消除了几乎所有的JDBC的代码和和参数的手工设置,以及结果集的检索MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java得POJOs(plain old Java Objects),映射为数据库的记录。
MyBatids是一种半自动的映射,需要手动匹配PO和映射关系,而Hibernate是一个全表映射的框架,只需要提供POJO和映射关系即可。
MyBatis环境的构建:
在使用MyBatis框架时需要将核心包和依赖包(lib)引入到应用程序中,如果为Web应用,需要将核心包和依赖包复制到/WEB-INF/lib目录中。
MyBatis的工作原理:
- 读取MyBatis配置文件:mybatis-config.xml为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息,例如数据库的连接信息。
- 加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL 语句,需要在MyBatis配置文件mybatis-config.xml中加载,mybatis-config.xml文件可以加载多个映射文件,每个映射文件对应数据库中的一张表。
- 构造会话工厂:通过Mybatis的环境等配置信息构建会话工厂SqlSessionFactory。
- 创建会话对象:由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有的方法。
- Executo执行器:Mybatis底层定义了一个Executor接口来操作数据库,它讲根据SqlSession传递的参数动态的生成需要执行的 SQL语句,同时负责查询缓存的维护。
- MappedStatement 对象:在Executor接口在执行方法中有一个 MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id,参数等信息。
- 输入参数映射: 输入参数类型可以是Map,List等集合类型,也可以是基本的集合类型和POJO类型,输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
- 输出结果映射:输出结果类型可以是Map,List等集合类型,也可以是基本数据类型和POJO类型。输出结果的映射类似于JDBC对结果集的解析。
使用Eclipse开发MyBatis入门程序:
- 创建Web应用,并添加JAR包
- 创建日志文件
# Global logging configuration log4j.rootLogger=ERROR, stdout # MyBatis logging configuration... log4j.logger.org.mybatis.example.BlogMapper=DEBUG # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- 创建持久化类
package com.mybatis.po;public class Myuser {private Integer uid;private String uname;private String usex;public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public String getUname() {return uname;}public void setUname(String uname) {this.uname = uname;}public String getUsex() {return usex;}public void setUsex(String usex) {this.usex = usex;}@Overridepublic String toString() {return "Myuser [uid=" + uid + ", uname=" + uname + ", usex=" + usex + "]";}}
- 创建映射文件 (<mapper>元素是配置文件的根元素,它包含了一个namespace属性,该属性值通常)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace = "com.mybatis.mapper.UserMapper"><!-- 根据ID查询用户信息 --><select id="selectUserById" parameterType = "Integer" resultType = "com.mybatis.po.Myuser">SELECT * FROM user WHERE uid = #{uid}</select><!-- 查询所有的用户信息 --><select id="selectAllUser" resultType = "com.mybatis.po.Myuser">SELECT * FROM user</select><!-- 添加一个用户,#{uname}为com.mybatis.po.MyUser属性值 --><insert id ="addUser" parameterType = "com.mybatis.po.Myuser">INSERT INTO user (uname,usex) VALUES (#{uname},#{usex})</insert><!-- 修改一个用户 --><update id="updateUser" parameterType = "com.mybatis.po.Myuser">UPDATE user SET uname = #{unmae},usex = #{user} where uid = #{uid}</update><!-- 删除一个用户 --><delete id = "delectUser" parameterType = "Integer">DELECT from user WHERE uid = #{uid}</delete> </mapper >
5. 创建Mybatis配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><!-- 配置环境 --><environments default ="development"><environment id = "development"><!-- 使用JDBC的事务管理 --><transactionManager type ="JDBC"/><dataSource type ="POOLED"><!-- Mysql 数据库驱动 --><property name ="driver" value = "com.mysql.jdbc.Driver"/><!-- 连接数据库的URL --><property name = "url" value = "jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name ="username" value = "root"/><property name = "password" value = "mysql"/></dataSource></environment></environments><mappers><!-- 映射文件的位置 --><mapper resource = "com/mybatis/mapper/UserMapper.xml"/></mappers> </configuration>
在mybatis配置文件中,在configuration根节点下面,可配置properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers这些节点。那么本次,就会先介绍properties节点和environments节点。
<configuration> <!-- 方法一: 从外部指定properties配置文件, 除了使用resource属性指定外,还可通过url属性指定url <properties resource="dbConfig.properties"></properties> --><!-- 方法二: 直接配置为xml --><properties><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test1"/><property name="username" value="root"/><property name="password" value="root"/></properties>
当以上两种方法都xml配置优先, 外部指定properties配置其次。
<environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><!--如果上面没有指定数据库配置的properties文件,那么此处可以这样直接配置 <property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test1"/><property name="username" value="root"/><property name="password" value="root"/>--><!-- 上面指定了数据库配置文件, 配置文件里面也是对应的这四个属性 --><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment><!-- 我再指定一个environment --><environment id="test"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><!-- 与上面的url不一样 --><property name="url" value="jdbc:mysql://localhost:3306/demo"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments>
environments元素节点可以配置多个environment子节点:
假如我们系统的开发环境和正式环境所用的数据库不一样(这是肯定的), 那么可以设置两个environment, 两个id分别对应开发环境(dev)和正式环境(final),那么通过配置environments的default属性就能选择对应的environment了, 例如,我将environments的deault属性的值配置为dev, 那么就会选择dev的environment。
typeAliases节点:主要用来设置别名,其实这是挺好用的一个功能, 通过配置别名,我们不用再指定完整的包名,并且还能取别名,例如: 我们在使用 com.demo.entity. UserEntity 的时候,我们可以直接配置一个别名user, 这样以后在配置文件中要使用到com.demo.entity. UserEntity的时候,直接使用User即可。
<configuration><typeAliases><!--通过package, 可以直接指定package的名字, mybatis会自动扫描你指定包下面的javabean,并且默认设置一个别名,默认的名字为: javabean 的首字母小写的非限定类名来作为它的别名。也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user) <package name="com.dy.entity"/>--><typeAlias alias="UserEntity" type="com.dy.entity.User"/></typeAliases>......</configuration>
TypeHandler节点:
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。
<configuration><typeHandlers><!-- 当配置package的时候,mybatis会去配置的package扫描TypeHandler<package name="com.dy.demo"/>--><!-- handler属性直接配置我们要指定的TypeHandler --><typeHandler handler=""/><!-- javaType 配置java类型,例如String, 如果配上javaType, 那么指定的typeHandler就只作用于指定的类型 --><typeHandler javaType="" handler=""/><!-- jdbcType 配置数据库基本数据类型,例如varchar, 如果配上jdbcType, 那么指定的typeHandler就只作用于指定的类型 --><typeHandler jdbcType="" handler=""/><!-- 也可两者都配置 --><typeHandler javaType="" jdbcType="" handler=""/></typeHandlers>......</configuration>
objectFactory节点:
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。默认情况下,我们不需要配置,mybatis会调用默认实现的objectFactory。 除非我们要自定义ObjectFactory的实现, 那么我们才需要去手动配置。自定义ObjectFactory只需要去继承DefaultObjectFactory(是ObjectFactory接口的实现类),并重写其方法即可
<configuration>......<objectFactory type="org.mybatis.example.ExampleObjectFactory"><property name="someProperty" value="100"/></objectFactory>......</configuration
plugin节点
plugins 是一个可选配置。mybatis中的plugin其实就是个interceptor, 它可以拦截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 的部分方法,处理我们自己的逻辑。Executor就是真正执行sql语句的东西, ParameterHandler 是处理我们传入参数的,还记得前面讲TypeHandler的时候提到过,mybatis默认帮我们实现了不少的typeHandler, 当我们不显示配置typeHandler的时候,mybatis会根据参数类型自动选择合适的typeHandler执行,其实就是ParameterHandler 在选择。ResultSetHandler 就是处理返回结果的。
<configuration>......<plugins><plugin interceptor="org.mybatis.example.ExamplePlugin"><property name="someProperty" value="100"/></plugin></plugins>......</configuration>
mappers节点
mappers 节点下,配置我们的mapper映射文件, 所谓的mapper映射文件,就是让mybatis 用来建立数据表和javabean映射的一个桥梁。在我们实际开发中,通常一个mapper文件对应一个dao接口, 这个mapper可以看做是dao的实现。所以,mappers必须配置。
<configuration>......<mappers><!-- 第一种方式:通过resource指定 --><mapper resource="com/dy/dao/userDao.xml"/><!-- 第二种方式, 通过class指定接口,进而将接口与对应的xml文件形成映射关系不过,使用这种方式必须保证 接口与mapper文件同名(不区分大小写), 我这儿接口是UserDao,那么意味着mapper文件为UserDao.xml <mapper class="com.dy.dao.UserDao"/>--><!-- 第三种方式,直接指定包,自动扫描,与方法二同理 <package name="com.dy.dao"/>--><!-- 第四种方式:通过url指定mapper文件位置<mapper url="file://........"/>--></mappers>......</configuration>
- 6,创建测试类
package com.mybatis.test;import java.io.IOException; import java.io.InputStream;import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder;import com.mybatis.po.Myuser;public class MyBatista_test {public static void main(String[] args) {try {//读取配置文件myBatista-config.xmlInputStream config=Resources.getResourceAsStream("mybatis-config.xml"); //根据配置文件构建SqlSessionFactory对象SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);//根据SqlSessionfactory创建SQLSession对象SqlSession ssn = ssf.openSession();//Sqlsession对象执行映射文件中定义的SQL,并返回结果;Myuser mu = ssn.selectOne("com.mybatis.mapper.UserMapper.selectUserById",1);System.out.println(mu);ssn.commit();ssn.close();}catch(IOException e){e.printStackTrace();}} }
MyBatis于Spring的整合
导入相关的JAR包:
- MyBatis框架需要的包。
- spring框架需要的包。
- Mybatis与Spring整合的中间JAR包。
- 数据库驱动JAR包。
- 数据源所需要的JAR包。
在Spring中配置Mybatis工厂:
使用Spring管理Mybatis的数据操作接口
框架整合实例:
- 创建应用并导入相关的JAR包;
- 创建持久层类;
package com.mybatis.po; public class Myuser {private Integer uid;private String uname;private String usex;public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public String getUname() {return uname;}public void setUname(String uname) {this.uname = uname;}public String getUsex() {return usex;}public void setUsex(String usex) {this.usex = usex;}@Overridepublic String toString() {return "Myuser [uid=" + uid + ", uname=" + uname + ", usex=" + usex + "]";} }
- 创建SQL映射文件和Mybatis核心配置文件;
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace = "com.mybatis.dao.UserDao"><!-- 根据ID查询用户信息 --><select id="selectUserById" parameterType = "Integer" resultType = "com.mybatis.po.Myuser">SELECT * FROM user WHERE uid = #{uid}</select><!-- <select id="selectAllUser" resultType = "com.mybatis.po.Myuser">SELECT * FROM user</select>添加一个用户,#{uname}为com.mybatis.po.MyUser属性值<insert id ="addUser" parameterType = "com.mybatis.po.Myuser">INSERT INTO user (uname,usex) VALUES (#{uname},#{usex})</insert>修改一个用户<update id="updateUser" parameterType = "com.mybatis.po.Myuser">UPDATE user SET uname = #{unmae},usex = #{user} where uid = #{uid}</update>删除一个用户<delete id = "delectUser" parameterType = "Integer">DELECT from user WHERE uid = #{uid}</delete> --> </mapper >
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><mappers><!-- 映射文件--><mapper resource = "com/mybatis/dao/UserMapper.xml"/></mappers> </configuration>
- 创建数据访问接口;
package com.mybatis.dao;import java.util.List;import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository;import com.mybatis.po.Myuser;@Repository("userdao") @Mapper public interface UserDao {/** 接口的方法对应SQL语句映射文件UserMapper.xml中的id*/public Myuser selectUserById(Integer uid);/*public List<Myuser>selectAllUser();public int addUser(Myuser user);public int updateUser(Myuser user);public int deleteUser(Integer uid);*/}
- 创建日志文件
# Global logging configuration log4j.rootLogger=ERROR,stdout # MyBatis logging configuration... log4j.logger.org.mybatis.example.BlogMapper=DEBUG # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- 创建控制层;
package com.mybatis.controller;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller;import com.mybatis.dao.UserDao; import com.mybatis.po.Myuser; @Controller("UserController") public class UserController {@Autowired(required=false)private UserDao userdao;public void test() {Myuser mu = userdao.selectUserById(2);System.out.println(mu);} }
- 创建Spring配置文件;
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 指定需要扫描的包,使注解生效 --><context:component-scan base-package="com.mybatis.po"/><context:component-scan base-package="com.mybatis.dao"/><context:component-scan base-package="com.mybatis.Controller"/><!-- 配置数据源 --><bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value = "com.mysql.jdbc.Driver"/><property name="url" value ="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name="username" value = "root"/><property name="password" value ="mysql" /></bean> <!-- <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"><property name="driverClassName" value = "com.mysql.jdbc.Driver"/><property name="url" value ="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"/><property name="username" value = "root"/><property name="password" value ="mysql" />可同时连接的最大的连接数<property name="maxActive" value="60" />最大的空闲的连接数<property name="maxTotal" value="60" />最小的空闲的连接数,低于这个数量会被创建新的连接,默认为0 <property name="maxIdle" value="5" /> 连接池启动时创建的初始化连接数量,默认值为0 <property name="initialSize" value="5" /> </bean> --><!-- 添加事务支持 --><bean id = "txManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name = "dataSource" ref = "dataSource"/></bean><!-- 开启事务注解 --><tx:annotation-driven transaction-manager ="txManager"/><!-- 配置Mybatis工厂,同时指定数据源,并与MyBatista完美结合 --><bean id="sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref ="dataSource"/><!-- configLocation 的属性为Mybatis 的核心配置文件 --><property name = "configLocation" value = "classpath:mybatis-config.xml"></property></bean><!-- Mapper 代理开发,使用Spring自动扫描MyBatista的接口并装配 --><!-- Spring 将指定包中所有的被@Mapper注解标注的接口自动装配为MyBatatis的映射接口 --><bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- MyBatis-spring组件的扫描器 --><property name="basePackage" value = "com.mybatis.dao"/><property name="sqlSessionFactoryBeanName" value = "sqlSessionFactory"/> </bean></beans>
- 创建测试类;
package com.mybatis.test;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;import com.mybatis.controller.UserController;public class Test_Controller {public static void main(String[] args) {// TODO Auto-generated method stubApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext.xml");UserController coll = (UserController) app.getBean("UserController");coll.test();}}
使用MyBatis Generator插入自动性生成映射文件
使用Mybatis Genertor插入自动生MyBatis所需要的DAO接口,实体模型类,Mapping映射文件,将生成的代码赋值到项目工程中即可。有命令行,Eclipse插件和Maven插件三种常用方法自动生成相关代码。介绍命令行的方法.
- 准备相关的jar包:数据库去驱动包和Mybatis-generator……包;
- 创建文件目录;
- 创建配置文件;
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><!--数据库驱动包的位置--><classPathEntrylocation="E:\generator\mysql-connector-java-5.1.18-bin(1).jar"/><context id="mysqltables" targetRuntime="MyBatis3"><commentGenerator><property name="suppressDate" value="false"/><property name="suppressAllComments" value="true"/></commentGenerator><!--数据库链接地址账号密码--><jdbcConnection driverClass="com.mysql.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/Springtest?characterEncoding=utf8"userId="root"password="mysql"/><javaTypeResolver><property name = "froceBigDecimals" value = "false"/></javaTypeResolver><!--生成Model类存放位置--><javaModelGenerator targetPackage="com.po"targetProject="E:\generator\src"><property name="enableSubPackages" value="true"/><property name="trimStrings" value="true"/></javaModelGenerator><!--生成映射文件存放位置--><sqlMapGenerator targetPackage="mybatis"targetProject="E:\generator\src"><property name="enableSubPackages" value="true"/></sqlMapGenerator><!--生成Dao类存放位置--><javaClientGenerator targetPackage="com.dao"targetProject="E:\generator\src" type="XMLMAPPER"><property name="enableSubPackages" value="true"/></javaClientGenerator><table tableName="user"domainObjectName="Myuser"enableCountByExample="false"enableUpdateByExample="false"enableDeleteByExample="false"enableSelectByExample="false"selectByExampleQueryId="false"enableInsert="true"enableUpdateByPrimaryKey="false"enableDeleteByPrimaryKey="false"></table></context> </generatorConfiguration>
- 使用命令生成代码;
E:\generator>java -jar mybatis-generator-core-1.3.6.jar -configfile generator.xml -overwrite
第七章,映射器
Mybatis配置文件概述:
Mybatis 的核心配置文件影响很多MyBatis行为的信息,这些信息通常只会配置到一个文件中,不会轻易更改,Mybatis的核心配置文件模板,Mybatis的核心配置文件中的元素配合顺序不能颠倒,颠倒会在Mybatis启动阶段发生异常。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><properties></properties><!-- 属性 --><settings><setting name="" value=""/></settings><!-- 设置 --><typeAliases/><!-- 类型的命名 --><typeHandlers/><!-- 类型处理器 --><objectFactory type=""/><!-- 对象工厂 --><plugins><!-- 插件 --><plugin interceptor=""></plugin></plugins><environments default=""><!-- 配置环境 --><environment id=""><!-- 环境变量 --><transactionManager type=""/><!-- 事务管理器 --><dataSource type=""/><!-- 数据源 --></environment></environments><databaseIdProvider type=""/><!-- 数据库厂商标识 --><mappers><!-- 映射器,告诉Mybatis到哪里找映射文件 --><mapper/></mappers></configuration>
映射器的概述:
Mybatis映射器由一个接口加上xml(SQL映射文件组成),MyBatis映射器也可以使用注解完成,但在实际的应用中不广泛可读性差,面对复杂的SQL会显的无力),
SQL映射常用的配置元素:
1,< select>:用于映射SQL语句的查询语句:
<selelct id = "selectUserByid" parametrType = "Integer" resultType="com.po.MyUser">
select * from user where uid = #{uid}
</select>
id | 和Mapper的命名空间组合起来使用,是唯一的标识符,供Mybatis调用 |
parameterType | 表示传入的SQL语句的类型参数的全限定名或别名,它是一个可选属性,Mybatis可以推断出具体的传入语句的参数类型 |
resultType | SQL语句执行后的返回的类型(全限定名或者别名),如果为集合类型,返回集合元素的类型,返回时可以使用resultType或者resultMAp之一。 |
resultMap | 它是映射集的引用,与<resultMap>元素一起使用,返回时可以使用resultType或resultMap之一, |
flushCache | 用于设置在调用SQL语句后是否要求Mybatis清空之前查询的本地缓存和二级缓存,默认问false设置为true时任何时候SQL语句被掉用都将清空本地缓存和二级缓存 |
useCache | 启动二级缓存的开关,默认值为true,表示将查询结果存入二级缓存中 |
timeout | 用于设置超时参数,单位是秒(S),超时将抛出异常 |
fetchSize | 获取记录的总条数设定 |
statementType |
告诉MyBatis使用那个JDBC的statement工作,取值为STATEMENT,PREPARED,CALLABLE. |
resultSetType | 针对JDBC的ResultSet接口而言的,其值可以为FORWARD_ONLY(只允许向前访问),SCROLL_SENSTIVE(双向滚动,但不及时跟新),SCROLL_INSENSITIVE(双向滚动,及时更新) |
参数的传递:
使用Map接口传递多个参数:
当查询语句使用多参数时,< select >对于parameterType属性允许使用Map接口通过键值对传递多个参数。
public List<MyUser> selectAllUser(Map<String,Object>param);
<selelct id = "selectUserByid" parametrType = "map" resultType="com.po.MyUser">
select * from user where uname like concat('%',#{u_name},'%') and usex = #{u_sex}
</select>
@controller("userController")
public class UserController{@Autowriredprivate UserDao userDao;public void test(){//查询多个用户Map<Stirng,Object> map = new HashMap<>();map.put("u_name","liruikonbg");map.put("u_sex","nan");List<MyUser> list = userDao.selectAllUser(map);for(Myuser myUser :list){System.out.println(myUser);
}}
}
Map 不能限定其传递的数据类型,所以业务性不强,可读性较差。
使用Java Bean传递多个参数
将传递的参数整合为 一个POJO类,在< select >对于parameterType属性为POJO类。
// POJO 类
public class SelectUserParam{private String u_name;private String u_sex;//省略setter/getter方法
}
//Dao 层接口的方法
public List<MyUser>selectAllUser(SelectUserParam param);
<selelct id = "selectUserByid" parametrType = "pojo.SelectUserParam " resultType="com.po.MyUser">
select * from user where uname like concat('%',#{u_name},'%') and usex = #{u_sex}
</select>
SelectUserParam su = new SelectUserParam ();
su.setU_name("liruilpong");
su.setU_sex("nan");
List<MyUser> list = userDao.selectAllUser(su);
for(MyUser myUser:list){System.out.println(myUser);
}
在实际的开发,参数较少,选择Map,参数交多时选择java Bena;
2,<insert>元素:用于映射插入语句,MyBatis 执行完一条插入语句后将返回一个整数表示影响的行数,它的属性与<select>元素的属性大部分相同,特有属性:
- keyProperty:将插入或更新操作时的返回值赋值给PO类的某个属性,通常会设置为主键对应的属性,如果为联合主键,可以多个逗号隔开。
- keyColumn:设置第几列为主键,当主键列不是表中的第一列时需要设置,如果为联合主键,可以将多个值用逗号隔开。
- useGeneratedKeys:将使MyBatis使用JDBC的getGeneratedKeys()方法获取其有数据内部产生的主键,默认为false,
主键(自动增量)回填:
<insert id = addUser parameterType = "com.po.MyUser" keyProperty ="uid" useGeneratedKeys = "true">
insert into user (uname,usex) values(#{uname},#{usex})
</insert>
//添加一个用户
MyUser addmu = new MyUser();
addmu.setUname("liruilong");
addmu.setUsex("nan");
int add = userDao.addUser(addmu);
System.out.println(addmu);
System.out.println("添加了"+add+"条记录");
System.out.println("添加记录的主键是"+addmu.getUid());
自定义主键:
当实际工作中某些数据库不支持主键的自动增量(Oeacle),或者取消了主键自动递增的规则,可以使用MyBatis的<selectKey>元素来定义生成主键。
<insert id = "insertUser" parameterType = "com.po.MyUser">
<!--先使用selectKey元素定义主键,然后再定义SQL语句-->
<selectKey keyProperty = "uid" resultType ="Integer" order ="BEEFORE">select if(max(uid)) is null,1,max(uid)+1) as newUid from user
</selectKey><!--reder表示执行顺序,值可以为BEFORE(先执行)或者AFTER(后执行)-->
insert into user (uid ,uname,usex) values (#{uid},#{uname},#{usex})
</insert>
3,<update>与<delete>元素(属性与前面的类似):
<!--修改一个用户-->
<update id ="updateUser" parameterType ="com.po.MyUser">
update user set uname = #{uname},usex=#{usex} where uid = #{uid}
</update>
<!--删除一个用户-->
<delete id = "deleteUser" parameterType = "Integer">delete from user where uid = #{uid}
</delete>
4,<sql>元素:可以定义SQL语句的一部分,方便后面的SQL语句引用他,例如经常使用的部分。
<sql id = "comColumns">id,uname,usex</sql>
<select id = "selectUser" resultType = "com.po.MyUser">select <include refid = "comColumns"/> from user
</select>
5,<resultMap>元素:结果映射集,是MyBatis中最重要也是最强大的元素。它的主要作用是定义映射规则、级联的更新以及定义类型转化器等
<resultMap type="" id=""><!-- type表示需要的POJO,id是resultMap的唯一标识 --><constructor><!-- 类在实例化时注入结果到构造方法,当POJO未定义无参构造方法时使用 --><idArg/><!-- ID参数,结果为ID --><arg/><!-- 注入到构造方法的一个普通结果 --></constructor><id/><!-- 用于哪个键表示主键 --><result/><!-- 注入到字段或JavaBean属性的普通结果,即POJO和数据表普通列的映射关系 --><association property=""><!-- 用于一对一关联 --></association><collection property=""><!-- 用于一对多,多对多关联 --></collection><discriminator javaType=""><!--使用结果值来决定使用哪个结果映射 --><case value=""><!-- 基于某些值的映射 --></case></discriminator>
</resultMap>
SQl语句执行后的返回结果,可以使用Map存储,也可以使用POJO存储。
使用Map存储结果集:任何的SQl语句都可以使用Map存储结果,Map的key是Select语句查询的字段名,而Map是value是查询返回结果对应的值,一条记录映射到一个Mapped对象中。
<select id="selectAllUser_" resultType = "map" > SELECT * FROM user </select>
接口代码:
//查询所有学生的信息public List<Map<String,Object>> selectAllUser_();
调用接口的方法:
System.out.println("所有的学生信息:");List<Map<String, Object>> lMaps = userdao.selectAllUser_();for(Map<String, Object> map2:lMaps) {System.err.println(map2);}
使用POJO存储结果集:可以自动映射,使用resultType属性。有时候需要复杂 的的映射或级联,需要使用<select>元素的 resultMap属性配置映射集合。
创建POJO类
package com.mybatis.pojo;public class MapUser {private Integer muid;private String muname;private String msex;public Integer getMuid() {return muid;}public void setMuid(Integer muid) {this.muid = muid;}public String getMuname() {return muname;}public void setMuname(String muname) {this.muname = muname;}public String getMsex() {return msex;}public void setMsex(String msex) {this.msex = msex;}@Overridepublic String toString() {return "MapUser [muid=" + muid + ", muname=" + muname + ", msex=" + msex + "]";}}
配置<resultMap>元素,配置<select>元素
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "com.mybatis.dao.UserDao"><!-- 根据ID查询用户信息 --><select id="selectUserById" parameterType = "Integer" resultType = "com.mybatis.po.Myuser">SELECT * FROM user WHERE uid = #{uid}</select><select id="selectAllUser" resultType = "com.mybatis.po.Myuser" parameterType = "map">SELECT * FROM user WHERE uname LIKE CONCAT('%',#{uname},'%') and usex = #{usex}</select><select id="selectAllUser_" resultType = "map" > SELECT * FROM user </select><!--添加一个用户,#{uname}为com.mybatis.po.MyUser属性值--><insert id ="addUser" parameterType = "com.mybatis.po.Myuser" keyProperty ="uid" useGeneratedKeys = "true">INSERT INTO user (uname,usex) VALUES (#{uname},#{usex})</insert><!-- 修改一个用户<update id="updateUser" parameterType = "com.mybatis.po.Myuser">UPDATE user SET uname = #{unmae},usex = #{user} where uid = #{uid}</update>删除一个用户<delete id = "delectUser" parameterType = "Integer">DELECT from user WHERE uid = #{uid}</delete> --><select id="listResultMap" resultMap = "myResult">SELECT * FROM user;</select><resultMap type="com.mybatis.pojo.MapUser" id="myResult"><id property = "muid" column = "uid"/><result property = "muname" column = "uname"/><result property = "msex" column = "usex"/></resultMap>
</mapper >
添加接口方法
package com.mybatis.dao;import java.util.List;
import java.util.Map;import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;import com.mybatis.po.Myuser;
import com.mybatis.pojo.MapUser;@Repository("userdao")
@Mapper
public interface UserDao {/** 接口的方法对应SQL语句映射文件UserMapper.xml中的id*///查询单个信息public Myuser selectUserById(Integer uid);//查询多个用户public List<Myuser> selectAllUser(Map<String,Object> param);//添加信息public int addUser(Myuser user);//查询所有学生的信息public List<Map<String,Object>> selectAllUser_();public List<MapUser> listResultMap();/*public int updateUser(Myuser user);public int deleteUser(Integer uid);*/}
调用接口
package com.mybatis.controller;import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;import com.mybatis.dao.UserDao;
import com.mybatis.po.Myuser;
import com.mybatis.pojo.MapUser;@Controller("UserController")
public class UserController {@Autowired()private Myuser myuser;
@Autowired()private UserDao userdao;public void test() {Myuser mu = userdao.selectUserById(2);System.out.println("id为2的参数信息:"+mu);Map<String,Object> map = new HashMap<>();map.put("uname","陈");map.put("usex", "男");System.out.println("路有尽,人无期");List<Myuser> list = userdao.selectAllUser(map);System.out.println("所有陈姓男性信息:");for(Myuser myUser:list) {System.out.println(myUser);} myuser.setUname("李四");myuser.setUsex("男");int count = userdao.addUser(myuser);System.out.println("插入了"+count+"条记录");System.out.println("插入的Id为"+myuser.getUid()+"的录");System.out.println("所有的学生信息(map返回):");List<Map<String, Object>> lMaps = userdao.selectAllUser_();for(Map<String, Object> map2:lMaps) {System.err.println(map2);}System.out.println("所有学生信息pojo:");List<MapUser> list2 = userdao.listResultMap();for (MapUser mapUser : list2) {System.out.println(mapUser);} } }
级联查询
级联关系是一个数据库实体的概念,有三种级联关系,一对一级联,一对多级联,多对多级联,级联的优点是获取数据十分方便,但是级联过多会增加数据库系统的复杂度,同时降低系统的性能。
所谓级联查询:就是当A为字表,B为从表,当查询表A的数据时,通过表A的外键将表B的相关记录返回。
一对一级联查询:MyBatis中通过< resultMap>元素的子元素< association>处理一对一级联关系.
property | 指定映射到实体类的对相关属性的类型 |
column | 指定表中对应的字段(即查询返回的列名) |
JavaType | 指定映射到实体对象属性的类型 |
select | 指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询 |
创建持久化类
创建映射文件
创建POJO类
创建数据操作接口
调用接口方法及测试类
缓存
为数据库的查询进行缓存,是减少数据库压力的主要途径,Mybatis与hibernate类似,也支持缓存。并且也分为一级缓存和二级缓存
一级缓存:session级别缓存,作用于当前会话
二级缓存:SessionFactory级别缓存,作用于整个SessionFactory,多个会话之间可以共享缓存
一级缓存
特点:
mybatis的一级缓存默认就是开启的,并且无法关闭。
mybatis的一级缓存作用域是当前session,一次openSession()后,如果相同的statement和相同参数,则不进行查询而是从缓存命中并且返回,如果没有命中则查询数据库。
任何的增删改操作都会导致缓存被清空
缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回
二级缓存
基本使用
特点:
二级缓存需要手动开启,开启的方式是在Mapper.xml中添加标签:<cache/>
二级缓存的作用域是整个SessionFactory,并且是同一个Mapper中,如果namespace、statement和SQL参数一致,则缓存命中
第八章,动态的SQl语句:
Mybatis提供对SQL语句动态的组装功能,MyBatis的动态的SQl语句动态组装的功能,Mybatis的动态的SQL元素与JSTL元素或者XML文本处理器相似,常用的<if>,<choose><when><otherwise><trim><where><set>,<foreach>,<bind>等元素。
<if>元素:
<select id="selectAllUser" resultType = "com.mybatis.po.Myuser" parameterType = "map">SELECT * FROM user WHERE <if test="uname != null" >uname LIKE CONCAT('%',#{uname},'%') and usex = #{usex}</if></select>
<choose><when><otherwise>元素:从中择取一二。
<insert id ="addUser" parameterType = "com.mybatis.po.Myuser" keyProperty ="uid" useGeneratedKeys = "true">INSERT INTO user (uname,usex) <choose><when test="uname!=null">VALUES (#{uname},#{usex})</when></choose></insert><select id="selectUserById" parameterType = "Integer" resultType = "com.mybatis.po.Myuser">SELECT * FROM user WHERE <choose><when test="1==2"></when><otherwise>uid = #{uid} </otherwise></choose></select>
<trim>元素:在自己包含的内容前面加上某些前缀(prefix),也可以在其后加上某些后(suffix),可以把包含内容的某些首部内容覆盖掉(prefixOverried),也可以把尾部的某些内容覆盖掉(suffixOverrides),prefixOverrides="AND |OR"
代表去掉第一个and或者是or。
<select id="selectAllUser" resultType = "com.mybatis.po.Myuser" parameterType = "map">SELECT * FROM user <trim prefix = "WHERE" prefixOverrides = "AND|OR"><if test="unmae!=null">AND uname LIKE CONCAT('%',#{uname},'%') </if><if test="usex!=null">usex = #{usex}</if></trim></select><insert id="save" parameterType="NoticeEntity">INSERT INTO S_NOTICE <trim prefix="(" suffix=")" suffixOverrides=",">ID,<if test="title != null">TITLE,</if><if test="content != null">CONTENT,</if><if test="noticeStatus != null">NOTICE_STATUS,</if><if test="createdBy != null">CREATED_BY,</if>CREATED_TS,<if test="lastUpdBy != null">LAST_UPD_BY,</if>LAST_UPD_TS,</trim><trim prefix="values (" suffix=")" suffixOverrides=",">SYS_GUID(),<if test="title != null">#{title,jdbcType=VARCHAR},</if><if test="content != null">#{content,jdbcType=VARCHAR},</if><if test="noticeStatus != null">#{noticeStatus,jdbcType=VARCHAR},</if><if test="createdBy != null">#{createdBy,jdbcType=VARCHAR},</if>systimestamp,<if test="lastUpdBy != null">#{lastUpdBy,jdbcType=VARCHAR},</if>systimestamp,</trim></insert>
<where>,<set>元素:使用set元素可以动态的跟新列,
<update id="updateUser" parameterType = "com.mybatis.po.Myuser">UPDATE user <set> <if test="unume!=null">uname = #{uname},</if><if test = "usex!=null">usex = #{usex}</if> </set><where>and uid = #{uid}</where></update>
<foreach>元素:主要在构建in条件中,它可以在SQL语句迭代一个集合。<foreach> 元素的主要属性:
item | 每个元素迭代的别名 |
index | 指定一个别名,表示在迭代过程每次迭代到的位置 |
open | 表示语句 的开始符号 |
separator | 表示在每次迭代的分割符 |
close | 表示以书面符号结束 |
collection | 必选,单个参数时,可以为list或array,多参数时,需要封装为Map,Map的键为参数名。 |
<select id="selectforacet" resultType = "map" parameterType = "list">SELECT * FROM USER WHERE uid IN<foreach collection="list" item = "item" index = "index"open = "("close = ")"separator=",">#{item} </foreach>
<!--如果传入的参数是List,cllection的属性值为list-->
<!--如果传入的参数是array,cllection的属性值为array--></select>
<bind>元素:在进行模糊差查询时,如果使用“${}”拼接字符串,则无法防止SQL注入问题,且不同的数据库拼接函数不同<bind>元素解决这一问题。
<select id="selectAllUser" resultType = "com.mybatis.po.Myuser" parameterType = "map"><bind name="paran_name" value="'%'+uname+'%'"/>SELECT * FROM user <where > <if test="uname != null" >AND uname LIKE CONCAT('%',#{paran_name},'%')</if><if test="usex!=null">and usex = #{usex}</if></where></select>
第九章,SpringMVC入门
SpringMVC工作原理:
SpringMVC框架是高度可配的,包含多种视图技术,SpringMVC框架主要由DispatcherServlet,处理器映射,控制器,视图解析器,视图组成。
Spring工作流程:
- 客户端请求提交到DispatcherServlet;
- 由DispatcherServlet控制器寻找一个或多个HandlerMapping,找到请求的Controller;
- DispatcherServlet将请求提交到Controller;
- Controller调用业务逻辑处理后返回ModelAndView;
- DispatcherServlet寻找一个或多个ViewResolve视图解析器,找到ModelAndView指定的视图;
- 视图负责将结果显示到客户端。
在容器初始化时会建立所有的url和Controller的对应关系,保存到Map<url,Controller>中.Tomcat启动时会通知Spring初始化容器(加载Bean的定义信息和初始化所有的单例Bean),然后SpringMVC会遍历容器中的Bean,获取每一个Controller中的所有的方法访问的url,然后将url和Controller保存到一个Map中。
可以根据Request快速定位到Controller,因为最终处理Request的是Controller中的方法,Map中只保留了url和Controller中的对应关系,所以要根据Request的url进一步确定Controller中的Method,需要拼接Controller的url(Controller类上的@Requestmapping的值)和方法上的url(Method上的@Requestmapping的值), 与request的url进行匹配,找到执行业务逻辑的方法。
确定处理请求的Method之后,需要进行参数绑定,把Request中参数绑定到方法的形式参数上,springMVC提供两种Request参数与方法形参的绑定方法。
1,通过注解进行绑定,@RequestParma
2,通过参数名进行绑定,
springMVC源码分析:
一,ApplicationContext初始化时建立的所有的url和Controller类的对应关系(用Map保存):入口类为ApplicationObjectSupport的setApplicationContext方法,该方法的核心部分为初始化容器initApplicationContext(context),子类的AsbectDetectingUrlHandlerMapping实现了该方法。
二,根据请求的URL找到对应的Controller,并从Controller中找到处理请求的方法。由请求触发,入口为DispatcherServlet 的核心方法为doService(),该方法的核心逻辑由doDispatcher()实现。getHandler(processedRequest)方法实际上就是从HandlerMapping中找到url和Controller的对应关系,
三,request参数绑定到方法的形参上,执行方法处理请求,,并返回视图结果。
Spring MVC接口:DispatcherServlet,HandlerMapping,Controller和ViewResolver。
Spring MVC所有的请求都经过DispatcherServlet来统一分发,在DispatcherServlet将请求分发给Controller之前需要借助SpringMVC提供的HandlerMapping定位到具体的Controller。
HandlerMapping接口负责完成客户请求到Controller映射。
Controller接口将处理用户请求,类似于Servlet,一旦Controller处理完用户请求,将返回ModelAndView对象给DispatcherServlet前端控制器,ModeAndView中包含了模型和视图。
宏观上讲,DispatcherServlet是整个Web应用的控制器,微观上讲,Controller是单个Http请求处理过程中的控制器,而ModeAndView是Http请求过程中返回的模型和视图。
ViewResolver接口(视图解析器)在Web应用中负责查找View对象,从而将相应的结果渲染给客户。
视图解析器:
<!-- 配置视图解析器 -->
<bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver"id ="internalResourceViewResolver"><property name="prefix" value = "/WEB-INF/"></property> <property name="suffix" value = ".jsp"></property></bean>
第一个SpringMVC应用
- 创建并导入相关的Jar包
- 在web.xml文件中部署DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"><!-- 部署DispatcherServlet --> <servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup> </servlet> <servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern> </servlet-mapping> </web-app>
- 创建Web应用的首页
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>嘻,你好SpringMVC</title> </head> <body> 未登录<a href="${pageContext.request.contextPath }/login">登录</a><br/> 未注册<a href="${pageContext.request.contextPath }/register"> 注册</a></body> </html>
- 创建Controller类
package com.springmvc.controller;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession;import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;@Controller /** @Controller表示被注解的实例为一个控制器,* 相当于Controller("value =indexContext")*/ @RequestMapping("/") public class IndexContext {@RequestMapping("login")public String login(HttpSession Session , HttpServletRequest request) {Session.setAttribute("skey", "session范围的值");request.setAttribute("rkey", "request范围的取值");return "login";}@RequestMapping("register")public String register(Model model) {model.addAttribute("success","注册成功");return "register";} }
- 创建Spring MVC配置文件并配置Controller映射信息
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"> <!-- <bean name = "/login" class = "com.springmvc.controller.Logincontroller"/> <bean name = "/register" class = "com.springmvc.controller.RegisterController"/> --><context:component-scan base-package="com.springmvc.controller"/> <mvc:annotation-driven/><!-- annotation-driven用于简化开发的配置 ,注解DefaultAnnotationHanderMapping和AnnotationMethodHandlerAdapter--> <!-- 使用resources过滤掉不需要的dispatcherservlet的资源,如静态资源 --> <!-- 使用resources时必须使用annotation-driven,否则该元素会阻止任意控制器被调用 --> <!-- 允许css目录下的所有文件可见, --> <mvc:resources location="/css/" mapping="/css/**"></mvc:resources> <!-- 允html目录下的所有文件可见, --> <mvc:resources location="/html/" mapping="/html/**"></mvc:resources> <!-- 允许images目录下的所有文件可见, --> <mvc:resources location="/images/" mapping="/images/**"></mvc:resources> <!-- 配置视图解析器 --> <bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver"id ="internalResourceViewResolver"><!-- <property name="prefix" value = "/WEB-INF/"></property> --><property name="suffix" value = ".jsp"></property></bean> </beans>
第十章,SpringMVC的Controller
传统风格的控制器不仅需要在配置文件中部署映射,而且只能编写一个处理方法,不够灵活,使用注解的控制器具有的优点。:
1,在基于注解的控制器类可以编写多个处理方法,进而可以处理多个请求,允许将相关的操作编写在同一个控制器类中,从而减少控制器类的数量。方便维护。
2,基于注解的控制器不需要的配置文件中部署映射,仅需要使用RequestMapping注释类型注解一个方法进行请求处理。
Controller注解类型:
在springMVC中使用org.springframework.stereotype.Controller注解类型声明某类的实例是一个控制器。
RequestMapping注解类型:
在基于注解的控制器类中可以为每个请求编写对应的处理方法,将请求与注解方法相对应,需要使用org.springfarmwork.web.bind.annotation.RequestMapping注解类型将请求与处理方法一一对应。有两种级别的注解:
方法级别的注解:
类级别的注解:
编写请求处理的方法:
在控制类中每个请求处理的方法可以有多个不同类型的参数,以及一个多种类型的返回结果。
请求处理中常见的参数类型:ServletAPI,输入输出流,表单实体类,注解类型,和与Spring有关的其他类型。
Model类型:org.springframework.ui.Model类型,该类型是一个包含Map的Spring框架类型,在每次调用请求对象都会创建一个Model对象。
package com.springmvc.controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
/** @Controller表示被注解的实例为一个控制器,* 相当于Controller("value =indexContext")*/
@RequestMapping("/")
public class IndexContext {@RequestMapping("login")public String login(HttpSession Session , HttpServletRequest request) {Session.setAttribute("skey", "session范围的值");request.setAttribute("rkey", "request范围的取值");return "login";}@RequestMapping("register")public String register(Model model) {model.addAttribute("success","注册成功");return "register";}
}
1,通过实体Bean接收请求参数:通过实体Bean来接收请求参数,适用于get和post提交请求方式,需要注意的是Bean属性名称必须与请求参数名称相同。
/** 处理登录* 使用userForm对象(实体Bean)user接受注册页面提交的信息;*/@RequestMapping("/login")public String login(UserForm userForm,HttpSession session,Model model) {System.out.println(userForm.getUname());if("zhangsan".equals(userForm.getUname())&&"123".equals(userForm.getUpass())) {session.setAttribute("u",userForm );logger.info("成功");return "main";}else {logger.info("失败!");session.setAttribute("messageError","用户名或密码错误!!");model.addAttribute("messageError","用户名或密码错误!!");return "login";}}
2,通过处理方法的形参接受请求参数:由asm框架实现获取参数名。之间把表单的参数写在控制器类相应方法的形参中即形参名与请求参数名称完全相同,适用于get和post提交请求的方式。
/** 通过处理方法的形参接受请求的参数* */@RequestMapping("/register")public String register(String uname,String upass ,Model model) {if("1".equals(uname)&&"1".equals(upass)) {logger.info("成功");return "login";}else {logger.info("失败!");model.addAttribute("uname",uname);return "login";}}
3,通过HttpServletRequest,HttpSessiond等接受请求参数。
/** 通过HttpServletRequest接收请求参数*/@RequestMapping("/register")public String requester(HttpServletRequest request,Model model) {String uname = request.getParameter("uname");String upass = request.getParameter("upass");if("1".equals("uname")&&"1".equals("upass")) {logger.info("成功");return "login";}else {logger.info("失败!");model.addAttribute("uname",uname);return "login";}
4,通过@PathVariable接受URL中的请求参数;即通过路径占位符将URl中的模板变量{uname}和{upass}绑定到通过@PathVariable注解的同名参数上。
/** 通过@PathVariable获取URL中的参数,必须加method属性*/@RequestMapping(value = "/register/{uname}/{upass}",method = RequestMethod.GET)public String register(@PathVariable String uname,@PathVariable String upass,Model model) {if("1".equals(uname)&&"1".equals(upass)) {logger.info("成功");return "login";}else {logger.info("失败!");model.addAttribute("uname",uname);return "login";}}
4,通过@RequestParam接受请求参数:适用于get和post提交方式。与通过处理方法的形参接受请求参数的区别:当请求参数名不一致时,通过处理方法的形参接受请求参数不会报404错,而@RequestParam会报404错。
/** 通过@RequestParam接收请求参数*/@RequestMapping("/register")public String registers(@RequestParam("uname") String uname,@RequestParam String upass,Model model) {if("1".equals(uname)&&"1".equals(upass)) {logger.info("成功");return "login";}else {logger.info("失败!");model.addAttribute("uname",uname);return "login";}}
5,通过@ModelAttribute接受请求参数:当将@ModelAttribute注解放在处理方法的形参上时,用于将多个请求参数封装到一个实体对象,自动暴露为模型数据,在视图页面展示时使用,通过Bean接收的请求参数并不能暴露模型,需要使用model.addAtttribute才可以暴露。适用于get和post请求。
/** 通过@ModelAttribute接收请求参数*/@RequestMapping("/register")public String registerss(@ModelAttribute("user") UserForm user) {if("1".equals("uname")&&"1".equals("upass")) {logger.info("成功");return "login";}else {logger.info("失败!");//使用@ModelAttribute("user")与model.addAttribute("user",user)的功能相同//可以使用EL表达式表示。return "login";}}
第十一章,类型转换和格式化
第十二章,数据绑定和表单标签库
数据绑定是将用户参数输入绑定到领域模型的一种特性,在SpringMVC中Controller和View参数数据传递中所有的Http请求的类型都是字符串,,通过数据绑定,不许要手动类型转换,数据绑定的另一个 好处是当输入验证失败时会重新生成一个HTML表单,不需要重新填写输入字段 。
数据绑定的方式:绑定请求参数输入值到领域模型,模型数据到视图的绑定,模型数据到表单元素的绑定。
表单标签库:
JSP使用表单标签库时,必须在JSP页面开头处声明taglib指令。
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
表单标签库中,有form,input,password,hidden,textarea,checkboxs,checkbox,radiobutton,radiobuttons,select,option,options,errors。等标签。。
form标签
一般情况下,通过 GET 请求获取表单页面,而通过POST 请求提交表单页面,因此获取表单页面和提交表单页面的 URL 是相同的。只要满足该最佳条件的契约,form:form 标签就无需通过 action 属性指定表单提交的 URL。
可以通过 modelAttribute 属性指定绑定的模型属性,若没有指定该属性,则默认从 request 域对象中读取command 的表单 bean,如果该属性值也不存在,则会发生错误。
form标签的属性
modelAttribute | form绑定的模型属性名称,默认为command |
commandName | form绑定的模型属性名称,默认为command |
acceptCharset | 定义服务器接受的字符编码 |
cssClass | 定义要应用到被渲染的form元素CSS类 |
cssStyle | 定义要应用到被渲染的form元素CSS样式 |
htmlEscape | boolean值,表示被渲染的值是否应该进行HTML转 |
SpringMVC提供了多个表单组件标签,如form:input/、form:select/ 等,用以绑定表单字段的属性值,它们的共有属性:
path:表单字段,对应 html 元素的 name 属性,支持级联属性
htmlEscape:是否对表单值的 HTML 特殊字符进行转换,默认值为 true
cssClass:表单组件对应的 CSS样式类名
cssErrorClass:表单组件的数据存在错误时,采取的 CSS样
input标签
<!-- 如果不设置modelAttribute属性,会取属性名为command的对应属性值 --><form:form method="post" action="register" modelAttribute="user" >姓名:<form:input path="username"/><br/>性别:<form:input path="sex"/><br/>年龄:<form:input path="age"/><br/></form:form>
password标签
password标签的用法跟input标签相似,也能绑定表单数据,只是它生成的是一个密码框,并且多了一个showPassword属性
<form:password path="password"/>
.hidden标签
用法跟input相似,也能绑定表单数据,只是它生成的是一个隐藏域
<form:hidden path="id"/>
.textarea标签
是一个支持多行输入的HTML元素
<form:textarea path="remark" rows="5" cols="20"/>
checkbox标签
会被渲染为一个类型为checkbox的普通HTML input标签,当多选时会被绑定到个String数组中。
<!-- label为被渲染的复选框的值 --><form:form method="post" action="checkboxForm" modelAttribute="user"><!-- 当绑定的为数组、List、Set时,checkbox的value属性在绑定的列表数据中存在则为选中状态 --><form:checkbox path="courses" value="Spring" label="Spring"/> <form:checkbox path="courses" value="SpringMVC" label="SpringMVC"/> <form:checkbox path="courses" value="MyBatis" label="MyBatis"/> <!-- 当绑定的是一个boolean数据时,true为选中,false为不选中 --><form:checkbox path="reader" value="true"/>已经阅读相关协议</form:form>
checkboxes标签
items: 可以是一个 List、String[] 或 Map
itemLabel:指定 checkbox的 value 值,可以是集合中 bean 的一个属性值
itemValue:指定 checkbox 的 label值,可以是集合中 bean 的一个属性值
delimiter:定义两个input元素之间的分隔符,默认没有分隔符
<p><label>爱好</label><form:checkboxes path="hobby" items="${hobbys}" ></form:checkboxes></p>
radiobutton标签
会被渲染为一个类型为radio的普通HTML input标签,只允许单选。
<form:form action="radiobuttionForm" method="post" modelAttribute="student"><form:radiobutton path="sex" value="男"/>男<br/><form:radiobutton path="sex" value="女"/>女</form:form>
radiobuttons标签:是一个选项组,等价于path相同的radiobutton标签
<label>爱好</label><form:radiobuttons path="hobby" items="${hobbys}" >
</form:checkboxes>
select、option、options标签:
tems: 用于生成option列表元素的对象的Collection、Map或者Array
itemLabel: item属性中定义的对象属性,为每个option提供label
itemValue: item属性中定义的对象属性,为每个option提供value
<p><label>职业:</label><form:select path="carrer" ><option/>请选择职业<form:options items="${carrers}"/></form:select></p>
数据绑定的应用:
<%--Created by IntelliJ IDEA.User:LiruilongDate: 2019/5/25Time: 8:49To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head><title>用户信息录入页面</title>
</head>
<body>
<form:form modelAttribute="user" method="post" action="${pageContext.request.contextPath}/user/save.do"><fieldset><legend>添加一个用户</legend><p><label>用户名:</label><form:input path="username"/></p><p><label>爱好</label><form:checkboxes path="hobby" items="${hobbys}" ></form:checkboxes></p><p><label>朋友:</label><form:checkbox path="friends" value="张三" />张三<form:checkbox path="friends" value="张三"/>张三<form:checkbox path="friends" value="张三"/>张三<form:checkbox path="friends" value="张三"/>张三<form:checkbox path="friends" value="张三"/>张三<form:checkbox path="friends" value="张三"/>张三</p><p><label>职业:</label><form:select path="carrer" ><option/>请选择职业<form:options items="${carrers}"/></form:select></p><p><label>户籍:</label><form:select path="houseRegister"><option/>请选择户籍<form:options items="${houseRegisters}"/></form:select></p><p><form:textarea path="remark" rows="5"/></p><p><span style="font-weight: bold;"/><input type="reset" value="重置"><input type="submit" value="添加"></p></fieldset>
</form:form>
</body>
</html>
package com.qst.Controller;import com.qst.domain.User;
import com.qst.service.service.UserService;
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 javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.List;@Controller
@RequestMapping("user")
public class UserController {@AutowiredUserService userServiceimpl;/*由控制器到userAdd视图;*/@RequestMapping("input")public String userAdd(Model model,HttpSession session) {HashMap<String, String> hobbys = new HashMap<>();hobbys.put("篮球", "篮球");hobbys.put("2球", "篮球");hobbys.put("3篮球", "篮球");hobbys.put("4篮球", "篮球");hobbys.put("5篮球", "篮球");hobbys.put("6篮球", "篮球");model.addAttribute("user", new User());model.addAttribute("hobbys", hobbys);model.addAttribute("carrers", new String[]{"asd", "学生", "coding搬运工", "IT民工"});model.addAttribute("houseRegisters", new String[]{"北京", "上海", "广州", "其他"});return "userAdd";}@RequestMapping("save")public String userShow(@ModelAttribute User user, Model model , HttpSession session){if (userServiceimpl.userAdd(user)){session.setAttribute("message","成功");return "redirect:/user/list.do";}else {session.setAttribute("massage","提交错误");HashMap<String, String> hobbys = new HashMap<>();hobbys.put("篮球", "篮球");hobbys.put("2球", "篮球");hobbys.put("3篮球", "篮球");hobbys.put("4篮球", "篮球");hobbys.put("5篮球", "篮球");hobbys.put("6篮球", "篮球");model.addAttribute("hobbys", hobbys);model.addAttribute("carrers", new String[]{"asd", "学生", "coding搬运工", "IT民工"});model.addAttribute("houseRegisters", new String[]{"北京", "上海", "广州", "其他"});return "userAdd";}}@RequestMapping("list")public String userList(Model model,HttpSession session){List<User> users = userServiceimpl.userList();model.addAttribute("users",users);return "userList";}
}
JSON数据交换:
是一种轻量级的数据交换格式,有对象结构和数据结两方式,在使用注解开发需要使用两个重要的JSON格式转换注解,分别是@RequestBody和@ResponseBody。
@RequestBody,用于将请求中的数据绑定到方法的参数中,应用的的方法的形参上。
@ResponseBody用于直接返回return对象,默认返回为JSON格式,注解在应用方法上,
<html>
<head><title>JSON 学习</title><script type="text/javascript" src="${pageContext.request.contextPath}/WEB-INF/views/jquery-3.2.1.jar"></script><script type="text/javascript">function testJson() {//获取输入的ID值var pname = $("#pname").val();var password =$("#password").val();$.ajax({url:"${pageContext.request.contextPath}/testJson.do",type:"post",data:JSON.stringify({pname:pname,password:password,page:page}),contentType:"application/json;charset=utf-8",dataType:"json",success:function(data){if (data!=null){alert(data.pname+data.password+data.page);}}});}</script>
</head>
<body><form action=""><table><tr><td>用户名</td><td><input type="text" name="pname" id="pname"></td></tr><tr><td>密码</td><td><input type="password" name="password" id="password"></td></tr><tr><td><input type="button" value="测试" onclick="testJson()"></td></tr></table>
</form>
</body>
</html>
@Controller
public class TestController {@RequestMapping("testJson")@ResponseBodypublic Person testJson(@RequestBody Person person ){//打印接受到的JSON数据格式System.out.println(person.getPassword()+person.getPname());return person;}
第十三章 拦截器
Spring MVC的拦截器(Interceptor)与Java Servlet的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理。通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
在Spring MVC框架中,定义一个拦截器,需要对拦截器进行定义和配置。定义一个拦截器可以通过两种方式:
- 一种是通过实现HandlerInterceptor接口或继承HandlerInterceptor接口的实现类来定义;
- 另一种是通过实现WebRequestInterceptor接口或继承WebRequestInterceptor接口的实现类来定义。
- 实现HandlerInterceptor接口的定义方式为例,讲解自定义拦截器的使用方法。
public class TestInterceptor implements HandlerInterceptor{@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("preHandle方法在控制器的处理请求方法前执行");/**返回true表示继续向下执行,返回false表示中断后续操作*/return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");}
}
实现了HandlerInterceptor接口,并实现了接口中的三个方法。有关这三个方法的描述如下。
- preHandle()方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作。返回true表示继续向下执行,返回false表示中断后续操作。
- postHandle()方法:该方法在控制器的处理请求方法调用之后,解析视图之前执行。可以通过此方法对请求域中的模型和视图做进一步的修改。
- afterCompletion()方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。
在配置文件中,如果只定义了一个拦截器,程序首先将执行拦截器类中的preHandle()方法,如果该方法返回true,程序将继续执行控制器中处理请求的方法,否则中断执行。如果preHandle()方法返回false,并且控制器中处理请求的方法执行后返回视图前,将执行postHandle()方法。返回视图后,才执行afterCompletion()方法。
<!-- 配置拦截器 --><mvc:interceptors><!-- 配置一个全局拦截器,拦截所有请求 --><bean class="interceptor.TestInterceptor"/><mvc:interceptor><!-- 配置拦截器作用的路径 --><mvc:mapping path="/**"/><!-- 配置不需要拦截作用的路径 --><mvc:exclude-mapping path=""/><!-- 定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 --><bean class="interceptor.Interceptor1"/></mvc:interceptor><mvc:interceptor><!-- 配置拦截器作用的路径 --><mvc:mapping path="/gotoTest"/><!-- 定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 --><bean class="interceptor.Interceptor2"/></mvc:interceptor></mvc:interceptors>
实例,登录验证
package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor{@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {//获取请求的URLString url = request.getRequestURI();//login.jsp或登录请求放行,不拦截if(url.indexOf("/toLogin") >= 0 || url.indexOf("/login") >= 0) {return true;}//获取SessionHttpSession session = request.getSession();Object obj = session.getAttribute("user");if(obj != null)return true;//没有登录且不是登录页面,转发到登录页面,并给出提示错误信息request.setAttribute("msg", "还没登录,请先登录!");request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);return false;}
}
第十六章,统一的异常处理
springMvc在处理异常上采用了统一的异常处理,Mvc各层出现的异常,通过异常链传递异常,传递到DispatcherServlet由HandlerExceptionResolver策略统一处理。这里异常的传递用到了设计模式中的责任链模式,即将多个对象连成一条链,并沿着这条链传递异常,异常抛出不捕获,直到有对象处理为止。
SpringMVC在处理请求的时候,通过RequestMappingHandlerMapping得到HandlerExecutionChain,然后通过RequestMappingHandlerAdapter得到1个ModelAndView对象,这在之前发生的异常都会被catch到,然后得到这个异常并作为参数传入到processDispatchResult方法处理。然后调用processHandlerException方法使用HandlerExceptionResolver集合处理异常。
package exception;
public class MyException extends Exception {private static final long serialVersionUID = 1L;public MyException() {super();}public MyException(String message) {super(message);}
}
(1)简单异常处理器SimpleMappingExceptionResolver:
使用org.springframework.web.servlet.handler.SimpleMappingExceptionResolver类统一处理异常,继承自AbstractHandlerExceptionResolver抽象类,AbstractHandlerExceptionResolver实现了HandlerExceptionResolver和Ordered接口的抽象类。
需要在配置文件中,提前配置异常类和View的对应关系。配置文件springmvc-servlet.xml的具体代码如下:
<!-- 配置SimpleMappingExceptionResolver(异常类与View的对应关系)--><bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- 定义默认的异常处理页面,当该异常类型的注册时使用 --> <property name="defaultErrorView" value="error"></property> <!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception --> <property name="exceptionAttribute" value="ex"></property> <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页名作为值 --> <property name="exceptionMappings"> <props> <prop key="exception.MyException">my-error</prop> <prop key="java.sql.SQLException">sql-error</prop> <!-- 这里还可以继续扩展对不同异常类型的处理 --> </props> </property> </bean>
(2)实现HandlerExceptionResolver接口自定义异常:
org.springframework.web.servlet.HandlerExceptionResolver接口用于解析请求处理过程中所产生的异常。SpringMVC异常处理核心接口。该接口定义了1个解析异常的方法:
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
开发者可以开发该接口的实现类进行Spring MVC应用的异常统一处理。在exception包中,创建一个HandlerExceptionResolver接口的实现类MyExceptionHandler,
需要将实现类MyExceptionHandler在配置文件中托管给Spring MVC框架才能进行异常的统一处理。配置代码为:
<bean class="exception.MyExceptionHandler"/>。
具体代码如下:
public class MyExceptionHandler implements HandlerExceptionResolver {@Override
public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,Exception arg3) {Map<String, Object> model = new HashMap<String, Object>(); model.put("ex", arg3); // 根据不同错误转向不同页面(统一处理),即异常与view的对应关系if(arg3 instanceof MyException) { return new ModelAndView("my-error", model); }else if(arg3 instanceof SQLException) { return new ModelAndView("sql-error", model); } else { return new ModelAndView("error", model); } }
}
(3)使用@ExceptionHandler注解实现异常处理
创建BaseController类,并在类中使用@ExceptionHandler注解声明异常处理方法,
public abstract class BaseController {/** 基于@ExceptionHandler异常处理 */ @ExceptionHandlerpublic String exception(HttpServletRequest request, Exception ex) { request.setAttribute("ex", ex);// 根据不同错误转向不同页面,即异常与View的对应关系 if(ex instanceof SQLException) { return "sql-error";}else if(ex instanceof MyException) { return "my-error";} else { return "error";} }
}
将所有需要异常处理的Controller都继承BaseController类,使用@ExceptionHandler注解声明统一处理异常时,不需要配置任何信息
示例代码如下:
@Controller
public class TestExceptionController extends BaseController{……
}
第十七章,文件上传
Spring MVC框架的文件上传是基于commons-fileupload组件的文件上传,只不过Spring MVC框架在原有文件上传组件上做了进一步封装,简化了文件上传的代码实现,取消了不同上传组件上的编程差异。
- 基于表单的文件上传
- 基于表单的文件上传,不要忘记使用enctype属性,并将它的值设置为multipart/form-data。同时,表单的提交方式设置为post。为什么需要这样呢?下面从enctype属性说起。表单的enctype属性指定的是表单数据的编码方式,该属性有如下三个值:
application/x-www-form-urlencoded:这是默认的编码方式,它只处理表单域里的value属
multipart/form-data:该编码方式以二进制流的方式来处理表单数据,并将文件域指定文件的内容封装到请求参数里.()
text/plain:该编码方式当表单的action属性为mailto:URL的形式时才使用,主要适用于直接通过表单发送邮件的方式。
- 基于表单的文件上传,不要忘记使用enctype属性,并将它的值设置为multipart/form-data。同时,表单的提交方式设置为post。为什么需要这样呢?下面从enctype属性说起。表单的enctype属性指定的是表单数据的编码方式,该属性有如下三个值:
在Spring MVC框架中,上传文件时,将文件相关信息及操作封装到MultipartFile对象中。因此,开发者只需要使用MultipartFile类型声明模型类的一个属性,即可以对被上传文件进行操作。
byte[] getBytes():以字节数组的形式返回文件的内容。
String getContentType():返回文件的内容类型。
InputStream getInputStream():返回一个InputStream,从中读取文件的内容。
String getName():返回请求参数的名称。
String getOriginalFilename():返回客户端提交的原始文件名称。
long getSize():返回文件的大小,单位为字节。
boolean isEmpty():判断被上传文件是否为空。
void transferTo(File destination):将上传文件保存到目标目录下。
参考优秀博文:https://www.tianmaying.com/tutorial/spring-ioc
参考优秀博文:这里写链接内容
参考优秀博文:https://blog.csdn.net/weixin_43468667/article/details/88831441
参考优秀博文:https://blog.csdn.net/fy_java1995/article/details/82937976#4.hidden%E6%A0%87%E7%AD%BE
链接:https://pan.baidu.com/s/1iW8OleBqWR1-QXwgKn3lMA
提取码:7c7e
《JavaEE框架整合开发入门到实战——Spring+SpringMVC+MyBatis》读书笔记相关推荐
- 读书笔记 | 墨菲定律
1. 有些事,你现在不做,永远也不会去做. 2. 能轻易实现的梦想都不叫梦想. 3.所有的事都会比你预计的时间长.(做事要有耐心,要经得起前期的枯燥.) 4. 当我们的才华还撑不起梦想时,更要耐下心来 ...
- 读书笔记 | 墨菲定律(一)
1. 有些事,你现在不做,永远也不会去做. 2. 能轻易实现的梦想都不叫梦想. 3.所有的事都会比你预计的时间长.(做事要有耐心,要经得起前期的枯燥.) 4. 当我们的才华还撑不起梦想时,更要耐下心来 ...
- 洛克菲勒的38封信pdf下载_《洛克菲勒写给孩子的38封信》读书笔记
<洛克菲勒写给孩子的38封信>读书笔记 洛克菲勒写给孩子的38封信 第1封信:起点不决定终点 人人生而平等,但这种平等是权利与法律意义上的平等,与经济和文化优势无关 第2封信:运气靠策划 ...
- 股神大家了解多少?深度剖析股神巴菲特
股神巴菲特是金融界里的传奇,大家是否都对股神巴菲特感兴趣呢?大家对股神了解多少?小编最近在QR社区发现了<阿尔法狗与巴菲特>,里面记载了许多股神巴菲特的人生经历,今天小编简单说一说关于股神 ...
- 2014巴菲特股东大会及巴菲特创业分享
沃伦·巴菲特,这位传奇人物.在美国,巴菲特被称为"先知".在中国,他更多的被喻为"股神",巴菲特在11岁时第一次购买股票以来,白手起家缔造了一个千亿规模的 ...
- 《成为沃伦·巴菲特》笔记与感想
本文首发于微信公众帐号: 一界码农(The_hard_the_luckier) 无需授权即可转载: 甚至无需保留以上版权声明-- 沃伦·巴菲特传记的纪录片 http://www.bilibili.co ...
- 读书笔记002:托尼.巴赞之快速阅读
读书笔记002:托尼.巴赞之快速阅读 托尼.巴赞是放射性思维与思维导图的提倡者.读完他的<快速阅读>之后,我们就可以可以快速提高阅读速度,保持并改善理解嗯嗯管理,通过增进了解眼睛和大脑功能 ...
- 读书笔记001:托尼.巴赞之开动大脑
读书笔记001:托尼.巴赞之开动大脑 托尼.巴赞是放射性思维与思维导图的提倡者.读完他的<开动大脑>之后,我们就可以对我们的大脑有更多的了解:大脑可以进行比我们预期多得多的工作:我们可以最 ...
- 读书笔记003:托尼.巴赞之思维导图
读书笔记003:托尼.巴赞之思维导图 托尼.巴赞的<思维导图>一书,详细的介绍了思维发展的新概念--放射性思维:如何利用思维导图实施你的放射性思维,实现你的创造性思维,从而给出一种深刻的智 ...
- 产品读书《滚雪球:巴菲特和他的财富人生》
作者简介 艾丽斯.施罗德,曾经担任世界知名投行摩根士丹利的董事总经理,因为撰写研究报告与巴菲特相识.业务上的往来使得施罗德有更多的机会与巴菲特亲密接触,她不仅是巴菲特别的忘年交,她也是第一个向巴菲特建 ...
最新文章
- Rocksdb 的优秀代码(一) -- 工业级分桶算法实现分位数p50,p99,p9999
- 【原创】VSFTP: Login failure: 530 Login incorrect的解决办法
- SaltStack组件
- Core Foundation框架
- HDU 1043 Eight(八数码)
- SAP CRM One Order old design in index table
- 21天Jmeter打卡day15 配置元件之用户定义的变量
- Java常量、变量(标识符规则)、数据类型(转换和运算符)、数据优先级
- 常用的端口号(port number)
- 利用Docker 基于Uptime Kuma搭建服务器监控
- 百度2017春招度度熊买帽子问题Java代码
- 40几岁读研究生计算机,年近四十岁,还有必要去考研和继续考博吗?不建议考全日制研究生...
- 宏批量替换多个word指定文字
- Shell脚本编程基础 三 使用结构化命令
- 打开jpg显示没有注册类 打开txt显示找不到文件位置 画图板也打不开,总之微软默认的应用都打不开 解决方案如下:
- ODB++数据解析三
- 拼多多收php吗_php调用拼多多的接口
- 漏洞分析丨HEVD-11.DoubleFetch[win7x86]
- 语音助手——垂类永动机——自动化迭代框架
- 快手2020校招笔试题 2019.8.25
热门文章
- ctrl+shift+G中ppt中及搜狗输入法快捷键冲突
- 搜索引擎推广是什么意思?如何采用网络曝光的方式做好搜索引擎推广
- 解决StretchBlt()图像失真问题
- 2022年最新北京机动车签字授权人模拟试题及答案
- 理解Spark中RDD(Resilient Distributed Dataset)
- Word DocX 模板数据填充 .NET Word 报表
- 洛谷 P5149 会议座位(树状数组+Trie)
- win7计算机无法右键,win7鼠标右键失灵不能用怎么解决|win7鼠标右键失灵不能用具体解决方法...
- jvm-问题分析及优化利器-gceasy的使用
- 开启设计之门 设计基础教程之构图篇