前言:SSM是针对企业开发的框架一共分为三部分分别为 Spring+Spring MVC+MyBatis为什么要学习这个框架呢是因为这个框架相对于传统的开发它有高度的维护性和可扩展性,同时也大大降低了开发和维护的成本和提高了开发的效率
一.Spring的基础应用
- Spring的概述:spring它是以IOC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)为内核,使用基本的JavaBane来完成以前只可能由EJB(Enterprise JavaBans,Java 企业 Bean)完成工作,取代了EJB的臃肿和低效开发模式。
- Spring 框架的优点:Spring具有简单,可测试和松耦合等特点,所以Spring不仅可以用于服务器端的开发,也可以应用于任何Java应用的开发中。
- 非侵入式开发:Spring是一种非侵入式(non-invasive)框架,它可以使应用程序代码对框架的依赖最小化。
- 方便解耦,简化开发:Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护工作都交给Spring容器管理,大大降低了组件之间的耦合性。
- 支持AOP:Spring提供了对AOP的支持,它允许将一些通用任务,如安全,事务,日志等进行集中式处理,从而提高程序的复用性。
- 支持声明式事务处理:只需要通过配置就可以完成对事物的管理,而无需手动编程。
- 方便集成各种优秀框架:spring不排除各种优秀的开源框架,其内部提供了对各种优秀框架(如Struts ,Hibernate,MyBatis ,Ouartz等)的直接支持。
- 降低JavaEE APl的使用难度:Spring对Java EE 开发中非常难用的一些APl(如JDBC,JavaMail等),都提供了封装,使用这些APl应用难度大大降低了。
一.(需要导入的JAR包)
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Book</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Book Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<!--导入依赖 junit,数据库驱动,连接池,servlet,jsp,mybatis,mybatis-spring,spring-->
<dependencies>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--数据库连接池:c3p0,dbcp-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Servlet JSP-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--偷懒 lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<finalName>SSMDemo</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
1.Spring的入门程序
1.项目结构
2.UserDao
package com.itheima.ioc;
public interface UserDao {
public void say();
}
3.UserDaoImpl
package com.itheima.ioc;
public class UserDaoImpl implements UserDao{
@Override
public void say() {
System.out.println("userDao say hello World !");
}
}
4.applicationContext.xml
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
java中的写法:
类型 变量名 = new 类型();
Hello hello= new Hello();
Spring中的写法呢:
<bean id="hello" class="com.cloud.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
id = 就相当于变量名
class= 就相当于new的那个对象
property 相当于给对象中的属性设置一个值
下面这个就相当于把"Spring"这个值赋给str这个变量
-->
<bean id="userDao" class="com.itheima.ioc.UserDaoImpl">
</bean>
</beans>
5.MyText
package com.itheime.ioc;
import com.itheima.ioc.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// 1.处始化 spring 容器,加载配置文件
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过容器获取 userDao 实例
UserDao userDao =(UserDao) applicationContext.getBean("userDao");
//3.调用实例中的say()方法
userDao.say();
}
@Test
private void Text1(){
System.out.println("第一个Spring程序");
}
}
总结:第一步先创建一个项目并且补齐一些类包并在java文件夹先创建包com.itheima.ioc并在该包下创建UserDao接口并定义say()方法并创建 UserDaoImpl类来实现UserDao接口中的方法,并且在resources文件夹下创建配置文件applicationContext.xml进行配置最后呢创建测试类MyTest进行测试
2.依赖的注入(DI)
1 .项目结构
2 .UserService
package com.itheima.ioc;
public interface UserService {
public void say();
}
3 .UserServiceImpl
package com.itheima.ioc;
public class UserServiceImpl implements UserService {
//声明UserDao属性
private UserDao userDao;
//添加UserDao属性的setter方法,用于实现依赖的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
//实现接口中的方法
public void say() {
//调用UserDao中的say()方法,并执行输出语句
this.userDao.say();
System.out.println("userService say hello World!");
}
}
4 .applicationContext.xml
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
java中的写法:
类型 变量名 = new 类型();
Hello hello= new Hello();
Spring中的写法呢:
<bean id="hello" class="com.cloud.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
id = 就相当于变量名
class= 就相当于new的那个对象
property 相当于给对象中的属性设置一个值
下面这个就相当于把"Spring"这个值赋给str这个变量
-->
<bean id="userDao" class="com.itheima.ioc.UserDaoImpl">
</bean>
<bean id="userService" class="com.itheima.ioc.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
5 .MyTest
package com.itheime.ioc;
import com.itheima.ioc.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// 1.处始化 spring 容器,加载配置文件
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过容器获取 userDao 实例
UserServiceImpl userService =(UserServiceImpl) applicationContext.getBean("userService");
//3.调用实例中的say()方法
userService.say();
}
@Test
private void Text1(){
System.out.println("第一个Spring程序");
}
}
二.Spring中的Bean
1.Bean的配置
- Spring可以被看作是一个大工厂,这个大工厂的作用就是生产和管Spring容器中的Bean。如果想要在项目中使用这个工厂就需要对开发者对Spring的配置文件进行配置。
- Spring配置文件支持XML和Properties两种格式的配置文件,最常用的还是XML文件来注册和管理Bean之间的依赖关系
- 在Spring中XML的根本元素就是,中包含了多个子元素,在每一个子元素定义了一个Bean,并描述了该Bean如何被装配到Spring容器中。
- 表 元素的常用属性及其子元素
属性或子元素名称 | 描述 |
---|---|
id | 是一个Bean的唯一标识符,Spring容器对Bean的配置,管理都通过该属性来完成 |
name | Spring容器同样可以通过此属性对容器中的Bean进行配置和管理,name属性中可以为Bean指定多个名称,每个名称之间可以用逗号和分号来隔开。 |
class | 该属性指定了Bean的具体实现类,它必须是一个完整的类名,使用类的全限定名 |
scope | 用来设定Bean实例的作用域,其属性值有:singleton(单例),prototype(原型),request,session,globalSession,application和websocket。其默认值为singleton |
constructor-arg | bean元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的index属性指定构造参数的序号(从0开始),type属性指定构造参数的类型,参数值可以通过ref属性或value属性直接指定,也可以通过ref或value子元素指定 |
property | bean元素的的子元素,可以调用Bean实例中的setter方法完成属性赋值,从而完成依赖的注入。该元素的name属性指定Bean实例中的相应属性名,ref属性或者value属性用于指定参数值 |
ref | property constructor-arg 等元素的属性或者子元素,可以用于指定对Bean工厂中某个Bean实例的引用 |
value | property,constructor-arg等元素的属性或者子元素,可以用于指定一个常量值 |
list | 用于封装List或者数组类型的依赖注入 |
set | 用于封装Sett或者属性类型的依赖注入 |
map | 用于封装Map或者数组类型的依赖注入 |
entry | map元素的子元素,用于设置一个键值对。其key属性指定字符串的键值,ref或者value子元素指定其值,也可以通过value-ref或者value属性指定其值 |
- 在配置文件中,通常一个普通的Bean只需要定义id(或者name)和class这两个属性就可以了
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
java中的写法:
类型 变量名 = new 类型();
Hello hello= new Hello();
Spring中的写法呢:
<bean id="hello" class="com.cloud.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
id = 就相当于变量名
class= 就相当于new的那个对象
property 相当于给对象中的属性设置一个值
下面这个就相当于把"Spring"这个值赋给str这个变量
-->
//使用ID属性定义bean1,其应对的实现类为com.itheima.ioc.UserServiceImpl
<bean id="bean1" class="com.itheima.ioc.UserServiceImpl">
<property name="" value=""/>
</bean>
//使用NMAE属性定义bean2,其应对的实现类为com.itheima.ioc.UserDaoImpl
<bean name="bean2," class="com.itheima.ioc.UserDaoImpl" scope="singleton">
<property name="" ref="" />
</bean>
<bean id="bean3" class="com.itheima.ioc.UserDaoImpl">
<constructor-arg ref="" value=""/>
</bean>
</beans>
如果在Bean中未指定id和name,则Spring会将class值当作id使用
2.Bean的实例化
- 在面向对象的程序中,要想使用某个对象,就需要先实例化这个对象。同样在Spring中,想要使用容器中的Bean,也需要实例化Bean。分别有构造器实例化,静态工厂方式和实例工厂方式实例化(其中最常用的就是构造器实例化)
-
构造器实例化
- 项目结构
- Bean1
package com.itheima.instance.constructor;
public class Bean1 {
}
- Bean1.xml
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
java中的写法:
类型 变量名 = new 类型();
Hello hello= new Hello();
Spring中的写法呢:
<bean id="hello" class="com.cloud.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
id = 就相当于变量名
class= 就相当于new的那个对象
property 相当于给对象中的属性设置一个值
下面这个就相当于把"Spring"这个值赋给str这个变量
-->
<bean id="bean1" class="com.itheima.instance.constructor.Bean1">
</bean>
</beans>
- MyTest
import com.itheima.instance.constructor.Bean1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//定义了配置文件路径
String xmlPath= "Bean1.xml";
//ApplicationContext 在加载配置文件时,对Bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Bean1 bean1 = (Bean1) applicationContext.getBean("bean1");
System.out.println(bean1);
}
}
构造器实例化
package com.itheima.instance.static_factory;
public class Bean2 {
}
- MyBean2Factoy
package com.itheima.instance.static_factory;
public class MyBean2Factcory {
//使用自己的工厂创建Bean2实例
public static Bean2 createBean(){
return new Bean2();
}
}
- bean2.xml
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
java中的写法:
类型 变量名 = new 类型();
Hello hello= new Hello();
Spring中的写法呢:
<bean id="hello" class="com.cloud.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
id = 就相当于变量名
class= 就相当于new的那个对象
property 相当于给对象中的属性设置一个值
下面这个就相当于把"Spring"这个值赋给str这个变量
-->
<bean id="bean2" class="com.itheima.instance.static_factory.MyBean2Factcory" factory-method="createBean">
</bean>
</beans>
- MyTest
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//定义了配置文件路径
String xmlPath= "Bean2.xml";
//ApplicationContext 在加载配置文件时,对Bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
System.out.println(applicationContext.getBean("bean2"));
}
}
package com.itheima.instance.factory;
public class Bean3 {
}
- MyBean3Factory
package com.itheima.instance.factory;
public class MyBean3Factory {
public MyBean3Factory(){
System.out.println("bean3的工厂实例化");
}
//创建Bean3实例方法
public Bean3 creatBean(){
return new Bean3();
}
}
- Bean.xml
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
java中的写法:
类型 变量名 = new 类型();
Hello hello= new Hello();
Spring中的写法呢:
<bean id="hello" class="com.cloud.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
id = 就相当于变量名
class= 就相当于new的那个对象
property 相当于给对象中的属性设置一个值
下面这个就相当于把"Spring"这个值赋给str这个变量
-->
<!--配置工厂-->
<bean id="myBean3Factory" class="com.itheima.instance.factory.MyBean3Factory"/>
<!--使用factory-bean属性指向配置的实例工厂-->
<!--使用factory-method属性确定使用工厂中的那个方法 -->
<bean id="bean3" factory-bean="myBean3Factory" factory-method="creatBean"/>
</beans>
- MyTest
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//定义了配置文件路径
String xmlPath= "Bean3.xml";
//ApplicationContext 在加载配置文件时,对Bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
System.out.println(applicationContext.getBean("bean3"));
}
}
3.Bean的作用域
- 通过Spring容器创建一个Bean的实例时,不仅可以完成Bean的实例化,还可以为Bean指定特定的作用域
- 作用域的种类:Spring4.3中为Bean的实例定义了7中作用于
作用域的名称 | 说明 |
---|---|
singleton( 单例) | 使用singleton定义的Bean在Spring容器中将只有一个实例,也就是说,无论有多少个Bean引用它,始终将指向同一个对象。这也是Spring容器默认的作用域 |
prototype(原型) | 每次通过Spring容器获取的protoype定义的Bean时,容器都将创建一个新的Bean实例 |
三.Spring AOP
1.Spring AOP简介
- AOP的全称是Aspect-OrientedProgramming,即面向切面编程(也叫面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。
- 在传统的业务处理代码时,通常会进行事务处理,日志记录等操作。虽然使用OOP可以通过组合或者是继承来实现代码的复用,但是如果要实现某个功能比如说日志记录,同样的代码就会分散到各个方法中,这样如果想要修改或者关闭某个功能,就需要对所有相关方法进行修改。这样就大大增加了程序员的工作量,并且还会使代码的出错率大大提升。
- 为了解决上述问题AOP的思想就此产生了。AOP是采用横向抽取机制,将分散在各个方法中重复的代码提取出来,然后再程序运行或许编译的时候,在把代码应用到需要执行的地方。这种采用横向抽取机制的方式,传统的OOP思想是无法办到的,因为OOP只能实现父子关系的纵向使用,虽然AOP是一种新的编程思想,但是它却不是OOP的代替品而是OOP的延伸和补充。
- AOP的使用让程序员在编写业务逻辑是可以更加专心与核心业务,而不用过多的去关注其它业务逻辑的实现,这样不仅提高了开发效率而且业增强了代码的可维护性
- 目前最流行的AOP框架有两个分别为Spring AOP和AspectJ。SpringAOP使用纯Java代码实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。
- AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,SpringAOP引入对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码织入。
- AOP术语:AOP的专业术语包括切面(Aspect),连接点(Joinpoint),切入点(Pointcut),通知和增强处理(Advice),目标对象(Target Object),代理(Proxy),织入(Weaving)
补充
2.动态代理
- 项目结构
- UserDao
package com.itheima.jdk;
public interface UserDao {
//添加数据
public void addUser();
//删除数据
public void deleteUser();
}
- UserDaoImpl
package com.itheima.jdk;
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("添加用户");
}
@Override
public void deleteUser() {
System.out.println("删除用户");
}
}
- JdkProxy
package com.itheima.jdk;
import com.itheime.aspect.MyAspect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK代理类
*/
public class JdkProxy implements InvocationHandler {
//声明目标类接口
private UserDao userDao;
//创建代理方法
public Object createProxy(UserDao userDao){
this.userDao=userDao;
//1.类加载器
ClassLoader classLoader=JdkProxy.class.getClassLoader();
//2.被代理对象实现的所有接口
Class[] clazz=userDao.getClass().getInterfaces();
return Proxy.newProxyInstance(classLoader,clazz,this);
}
/**
*所有动态代理类的方法调用,都会交由invoke()方法处理
* Proxy被代理后的对象
* method将要被执行的方法信息(反射)
* args 执行方法时需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//声明切面
MyAspect myAspect=new MyAspect();
//前增强
myAspect.check_Permissions();
//在目标类上调用方法,并传入参数
Object ojb = method.invoke(userDao, args);
//后增强
myAspect.log();
return ojb;
}
}
- MyAspect
package com.itheime.aspect;
//切面类:可以存在多个通知Advice(增强方法)
public class MyAspect {
public void check_Permissions(){
System.out.println("模拟检查权限...");
}
public void log(){
System.out.println("模拟记录日志...");
}
}
- JdkTest
import com.itheima.jdk.JdkProxy;
import com.itheima.jdk.UserDao;
import com.itheima.jdk.UserDaoImpl;
public class JdkTest {
public static void main(String[] args) {
//创建代理对象
JdkProxy jdkProxy = new JdkProxy();
//创建目标对象
UserDao userDao = new UserDaoImpl();
//从代理对象中获取增强后的目标对象
UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
//执行方法
userDao1.addUser();
userDao1.deleteUser();
}
}
2.CGLIB代理
- JDK动态代理有一定的局限性——使用动态代理的对象必须实现一个或者多个接口。如果要对没有实现接口的类进行代理,就可以使用CGLIB代理来完成。
- CGLIB是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring的核心包中已经集成了CGLIB需要的包,所以在开发中不需要另外在导入JRA包。
- 项目结构
- UserDao
package com.itheima.cglib;
public class UserDao {
public void addUser(){
System.out.println("添加数据");
}
public void deleteUser(){
System.out.println("删除数据");
}
}
- CglibProxy
package com.itheima.cglib;
import com.itheime.aspect.MyAspect;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//代理类
public class CglibProxy implements MethodInterceptor {
//代理方法
public Object createProxy(Object target){
//创建一个动态类对象
Enhancer enhancer = new Enhancer();
//确定需要增强的类设置其父类
enhancer.setSuperclass(target.getClass());
//添加回调函数
enhancer.setCallback(this);
return enhancer.create();
}
/**
*proxy CGlib根据指定父类生成的代理对象
* method 拦截方法
* args 拦截方法的参数数组
* methodProxy 方法的代理对象,用于执行父亲的方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//创建切面对象
MyAspect myAspect=new MyAspect();
//前增强
myAspect.check_Permissions();
Object obj=methodProxy.invokeSuper(proxy,args);
//后增强
myAspect.log();
return obj;
}
}
- MyAspect
package com.itheime.aspect;
//切面类:可以存在多个通知Advice(增强方法)
public class MyAspect {
public void check_Permissions(){
System.out.println("模拟检查权限...");
}
public void log(){
System.out.println("模拟记录日志...");
}
}
- jdkTest
import com.itheima.cglib.CglibProxy;
import com.itheima.cglib.UserDao;
public class JdkTest {
public static void main(String[] args) {
//创建代理对象
CglibProxy cglibProxy=new CglibProxy();
//创建目标对象
UserDao userDao=new UserDao();
//获取增强后的目标对象
UserDao userDao1= (UserDao) cglibProxy.createProxy(userDao);
//执行方法
userDao1.addUser();
userDao1.deleteUser();
}
}
补充
3.基于代理类的AOP实现
- Spring中的AOP代理默认就是使用JDK动态代理的方式实现的。在Spring中使用ProxyFactoryBean是创建AOP代理的最基本方式。
- Spring的通知类型:在写练习之前我们先了解一下Spring的通知类型。Spring中的通知按照在目标类方法的连接点位置,可以分为5种类型。
- org.aopalliance.intercept.MethodInterceptor(环绕通知)
在目标方法执行前后实施增强,可以用于日志,事务管理等功能。 - org.springframework.aop.MethodBeforeAdvice(前置通知)
在目标方法执行前实施增强,可以应用于权限管理等功能。 - org.springframework.aop.AfterReturningAdvice(后置通知)
在目标方法执行后实施增强,可以用于关闭流,上传文件,删除临时文件等功能。 - org.spring.framework.aop.ThrowsAdvice(异常通知)
在方法抛出异常后实施增强,可以用于处理异常记录日志等功能。 - org.springframework.aop.IntroductionInterceptor(引介通知)
在目标类中添加一些新的方法和属性,可以应用于修改老版本程序(增强类) - ProxyFactoryBean:ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactory负责为其它Bean创建代理实例。在Spring中使用ProxyFactoryBean是创建AOP代理的基本方式。
- ProxyFactoryBean类中的常用配置属性表
属性名称 | 描述 |
---|---|
target | 代理的目标对象 |
proxyInterfaces | 代理要实现的接口,如果是多个接口,可以使用以下格式赋值<list> <value></value></list> |
proxyTargetClass | 是否对类代理而不是接口,设置为true时,使用CGLIB代理 |
interceptorNames | 需要织入的Advice |
singleton | 返回的代理是否为单实例,默认为true(即返回单实例) |
optimize | 设置为true时,强制使用CGLIB |
-
项目结构
-
MyAspect
package com.itheima.factorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
check_Permissions();
//执行目标方法
Object obj=mi.proceed();
log();
return obj;
}
public void check_Permissions(){
System.out.println("模拟检查权限");
}
public void log(){
System.out.println("模拟日志记录");
}
}
- applicationContext.xml
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
java中的写法:
类型 变量名 = new 类型();
Hello hello= new Hello();
Spring中的写法呢:
<bean id="hello" class="com.cloud.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
id = 就相当于变量名
class= 就相当于new的那个对象
property 相当于给对象中的属性设置一个值
下面这个就相当于把"Spring"这个值赋给str这个变量
-->
<!--1.目标类-->
<bean id="userDao" class="com.itheima.jdk.UserDaoImpl"/>
<!--2.切面类-->
<bean id="myAspect" class="com.itheima.factorybean.MyAspect"/>
<!--3.使用Spring代理工厂定义一个名称为userDaoProxy的代理对象-->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--3.1指定代理实现的接口-->
<property name="proxyInterfaces" value="com.itheima.jdk.UserDao"/>
<!--3.2指定目标对象-->
<property name="target" ref="userDao"/>
<!--3.3指定切面,植入环绕通知-->
<property name="interceptorNames" value="myAspect"/>
<!--3.4指定代理方式,true: 使用cglib,false(默认):使用jdk动态代理-->
<property name="proxyTargetClass" value="true"/>
</bean>
</beans>
- ProxypryBeanTest
import com.itheima.jdk.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//测试类
public class ProxyFactoryBeanTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//从spring容器获得内容
UserDao userDao= (UserDao) applicationContext.getBean("userDaoProxy");
//执行方法
userDao.addUser();
userDao.deleteUser();
}
}
补充
4. AspectJ开发
四.Spring的数据库开发
1.SpringJDBC
- Spring的JDBC模块负责数据库资源管理和错误处理,简化了开发人员对数据库的操作
1.Spring JdbcTemplate
- Spring JdbcTemplate的解析:针对数据库操作Spring框架提JdbcTemplate类,该类是Spring框架数据抽象层的基础,其他更高层次的抽象类确是构建
- 于JdbcTemplate类之上可以说JdbcTemplate类是SpringJDBC的核心类。
- jdbcTemplate类继承关系十分简单。它继承自抽象类JdbcAccessor,同时实现类JdbcOperations接口。
- JdbcTemplate类的直接父类是JdbcAccessor,该类为子类提供了一些访问数据库时使用的公共属性:DataSource:其主要功能是获取数据库的连接,具体实现时还可以引入对数据库连接的缓冲池和分布式事务的支持,它可以作为访问数据的支持,它可以作为访问数据库资源的标准接口。
- SQLExceptionTranslatororg.springframework.jdbc.support.SQLExceptionTranslator接口负责对SQLException进行转译工作。通过必要的设置或者获取SQLExceptionTranslator中的方法,可以使JdbcTemplate在需要处理SQLException时,委托SQLExceptionTranslator的实现类来完成相关的转译工作
- JdbcOperations接口定义在JdbcTemplate类中可以使用的操作集合,包括添加,修改,查询和删除等操作。
2.Spring JDBC的配置
- Spring JDBC模块主要由4个包组成,分别是core(核心包),dataSource(数据源包),object(对象包)和support(支持包)。
包名 | 说明 |
---|---|
core | 包含了JDBC的核心功能,包括JbdcTemplate类,SimpleJdbcInsert类,SimpleJdbcCall类以及NamedParameterJdbcTemplate类 |
dataSource | 访问数据源的实用工具类,它有多种数据源的实现,可以在JavaEE容器外部测试JDBC代码 |
object | 以面向对象的方式访问数据库,它允许执行查询并将返回结果作为业务对象,可以在数据表的列和业务对象的属性之间映射查询结果 |
support | 包含了core和object包的支持类,例如,提供异常转换功能的SQLException类 |
Spring对数据库的操作都封装在了上面4个包中,而想要使用SpringJDBC,就需要对其进行配置。在Spring中,JDBC的配置是在配置文件applicationContext.xml中完成的。
<?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/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
<!--1.配置数据源-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--数据库驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url-->
<property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
<!--连接数据库的用户名-->
<property name="username" value="root"/>
<!--连接数据库的密码-->
<property name="password" value="password"/>
</bean>
<!--2.配置JDBC的模板 -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<!--默认必须使用数据源-->
<property name="dataSource" ref="dataSource" />
</bean>
<!--3.配置注入类-->
<bean id="xxx" class="Xxx">
<property name="jdbcTemplate" ref="jdbcTemplate">
</bean>
...
</beans>
在上述代码分别定义了3个Bean,分别是dataSource,jdbcTemplate和需要注入类的Bean。其中dataSource对应的org.springframework.jdbc.datasource.DriverManagerDataSource类用于对数据源进行配置,jdbcTemplate对应的org.springframework.jdbc.core.JdbcTemplate类中定义了JdbcTemplate的相关配置。代码中dataSource的配置就是JDBC连接数据库时所需要的4个属性,表中的4个属性,需要根据数据库类型或者机器配置的不同设置相应的属性值,例如数据库类型不同,需要更改驱动名称;如果数据库不在本地,则需要将地址中的localhost替换成相应的主机IP;如果修改过MySQL数据库的端口号(默认为3306),则需要加上修改后的端口号,如果未修改,则端口号可以省略;同时连接数据库的用户名和密码需要与数据库创建时设置的用户名和密码保持一致。
属性名 | 含义 |
---|---|
driverClassName | 所使用的驱动名称,对应驱动JAR包中的Driver类 |
url | 数据源所在地址 |
username | 访问数据库的用户名 |
password | 访问数据库的密码 |
定义JdbcTemplate时,需要将dataSource注入到JdbcTemplate中,而其他需要使用JdbcTemplate的Bean,也需要将JdbcTemplate注入到该Bean中(通常注入到Dao类中,在Dao类中进行与数据库的相关操作)。
3.Spring JdbcTemplate的常用方法
- 在JdbcTemplate类中,提供了大量的更新和查询数据库的方法,我们就是使用这些方法来操作数据库的。
1. execute()
-
execute(String sql)方法能够完成执行SQL语句的功能。
-
在MySQL中,创建一个名为spring的数据库
先使用SQL语句创建了数据库spring,然后选择使用spring,为了验证数据库数据表是通过execute(Spring sql)方法执行创建的,这里使用了show tables语句查看数据库中的表,其结果显示为空。 -
项目结构
-
applicationContext.xml
<?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/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
<!--1.配置数据源-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--数据库驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url-->
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<!--连接数据库的用户名-->
<property name="username" value="root"/>
<!--连接数据库的密码-->
<property name="password" value="123456"/>
</bean>
<!--2.配置JDBC的模板 -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<!--默认必须使用数据源-->
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
- JdbcTemplateTest
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcTemplateTest {
/**
* 使用execute()方法建表
*/
public static void main(String[] args) {
//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取JdbcTemplate实例
JdbcTemplate jdbcTemplate= (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
//使用execute()方法执行SQL语句,创建用户账号管理表account
jdbcTemplate.execute("create table account(" +
"id int primary key auto_increment," +
"username varchar(50)," +
"balance double)");
System.out.println("账户表account创建成功");
}
}
- 运行成功后,在打开cmd去查看数据库中是否创建了account表
2.update()
- update()方法可以完成插入,更新和删除数据的操作。在JdbcTemplate类中,提供了一系列的update()方法。
方法 | 说明 |
---|---|
int update(String sql) | 该方法是最简单的update方法重载形式,它直接执行传入的SQL语句,并返回受影响的行数 |
int update(Prepared Statement Creator psc) | 该方法执行从Prepared Statement Creator返回的语句,然后返回受影响的行数 |
int update(String sql,PreparedStatementSetter pss) | 该方法通过PreparedStatementSetter设置SQL语句中的参数,并返回受影响的行数 |
int update(String sql,Object … args) | 该方法使用Object …设置SQL语句中的参数,要求参数不能NULL,并返回受影响的行数 |
- 项目结构
- Account
package com.jdbc;
public class Account {
private Integer id; //账户id
private String username; //用户名
private Double balance; //账户余额
public Account() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", username='" + username + '\'' +
", balance=" + balance +
'}';
}
}
- AccountDao
package com.jdbc;
public interface AccountDao {
//添加
public int addAccount(Account account);
//更新
public int updateAccount(Account account);
//删除
public int deleteAccount(int id);
}
- AccountDaoImpl
package com.jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
public class AccountDaoImpl implements AccountDao{
//声明JdbcTemplate属性及其setter方法
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//添加账户
public int addAccount(Account account) {
//定义SQL
String sql="insert into account(username,balance) values(?,?)";
//定义数组来存储SQL语句中的参数
Object[] objects=new Object[]{
account.getUsername(),
account.getBalance()
};
//执行添加操作,返回的是受SQL语句影响的记录条数
int num=this.jdbcTemplate.update(sql,objects);
return num;
}
//更新账户
public int updateAccount(Account account) {
//定义SQL
String sql="update account set username=?,balance=? where id=?";
//定于数组来储存SQL语句中的参数
Object[] params=new Object[]{
account.getBalance(),
account.getUsername(),
account.getId()
};
//执行更新操作,返回的是受SQL语句影响的记录条数
int num=this.jdbcTemplate.update(sql,params);
return num;
}
//删除账户
public int deleteAccount(int id) {
//定义SQL
String sql="delete from account where id=?";
//执行删除操作,返回的是SQL语句受影响的记录条数
int num=this.jdbcTemplate.update(sql,id);
return num;
}
}
- applicationContext.xml
<?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/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
<!--1.配置数据源-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--数据库驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url-->
<property name="url" value="jdbc:mysql://localhost:3306/spring?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<!--连接数据库的用户名-->
<property name="username" value="root"/>
<!--连接数据库的密码-->
<property name="password" value="123456"/>
</bean>
<!--2.配置JDBC的模板 -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<!--默认必须使用数据源-->
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="accountDao" class="com.jdbc.AccountDaoImpl">
<!--将jdbcTemplate注入到accountDao实例中-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
- JdbcTemplateTest
import com.jdbc.Account;
import com.jdbc.AccountDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcTemplateTest {
/**
* 使用execute()方法建表
*/
public static void main(String[] args) {
//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取JdbcTemplate实例
JdbcTemplate jdbcTemplate= (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
//使用execute()方法执行SQL语句,创建用户账号管理表account
jdbcTemplate.execute("create table account(" +
"id int primary key auto_increment," +
"username varchar(50)," +
"balance double)");
System.out.println("账户表account创建成功");
}
//添加数据
@Test
public void addAccountTest(){
//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取AccountDao实例
AccountDao accountDao= (AccountDao) applicationContext.getBean("accountDao");
//创建Account对象,并向Account对象中添加数据
Account account=new Account();
account.setUsername("tom");
account.setBalance(1000.00);
//执行addAccount()方法,并获取返回结果
int num=accountDao.addAccount(account);
if (num>0){
System.out.println("成功插入了"+num+"条数据");
}else {
System.out.println("插入操作执行失败");
}
}
//修改数据
@Test
public void updateAccountTest(){
//加载配置文件
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
//获取AccountDao实例
AccountDao accountDao= (AccountDao) applicationContext.getBean("accountDao");
//创建Account对象,并向Account对象中添加数据
Account account = new Account();
account.setId(2);
account.setBalance(2000.00);
account.setUsername("tom");
accountDao.updateAccount(account);
}
//删除数据
@Test
public void deleteAccount(){
//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
AccountDao accountDao= (AccountDao) applicationContext.getBean("accountDao");
int num=accountDao.deleteAccount(1);
if (num>0){
System.out.println("成功删除"+num+"条数据");
}else {
System.out.println("删除操作执行失败");
}
}
}
补充
3.query()
JdbcTemplate类中还提供了大量的query()方法来处理各种对数据库表的查询操作。其中最常用的几个query()方法如下:
方法 | 声明 |
---|---|
List query(String sql,RowMapper rowMapper) | 执行String类型参数提供的SQL语句,并通过RowMapper返回一个List类型结果 |
List query(String sql,PreparedStatementSetter pss,RowMapper) | 根据String类型参数提供的SQL语句创建PreparedStatement对象,通过RowMapper将结果返回到List中 |
List query(String sql,Object[] args, RowMapperrowMapper) | 使用Object[]的值来设置SQL语句中的参数值,采用RowMapper回调方法可以直接返回List类型的数据 |
queryForObject(String sql, Row Mapper rowMapper,Object…args) | 将args参数绑定到SQL语句中,并通过RowMapper返回一个Object类型的单行记录 |
queryForObject(String sql,Object[] args, classelementType) | 该方法可以返回多行数据的结果,但必须是返回列表,elementType参数返回的是List元素类型 |
五.Spring的事务管理
- 事务管理的核心接口:在Spring的所有JAR包中,包含一个名为spring-tx-4.3.6.RELEASE的JAR包,该包就是Spring提供用于事务管理的依赖包。在该JAR包的org.springframework.transaction包中,我们可以找到3个接口文件PIatformTransactionManager,TransactionDefinition和TransactionStatus
1. PIatformTransactionManager
PIatformTransactionManager:接口是Spring提供的平台事务管理器,主要用于管理事务。该接口中提供了3个事务操作的方法:TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息。void commit(TransactionStatus status):用于提交事务。void rollback (TransactionStatus status):用于回滚事务。
在上面的三个方法中,getTransaction(Transaction Definition definition)方法会根据 Transaction Definition 参数返回一个TransactionStatus对象,TransactionStatus对象就表示一个事务,它被关联在当前执行的线程上。
PlatformTransactionManager接口只是代表事务管理的接口,它并不知道底层如何管理事务的,它只需要事务管理提供上面的3个方法,但具体如何管理事务则由它的实现类来完成。
PlatfromTransactionManager接口有许多不同的实现类,常见的实现类有
- org.springframework.jdbc.datasource.DataSourceTransactionManager:用于配置JDBC数据源的事务管理器。
- org.springframework.orm.hibernate4.HibernateTransactionManager:用于配置Hibernate的事务管理器。
- org.springframework.transaction.jta.JtaTransactionManager:用于配置全局的事务管理器。
当底层采用不同的持久层技术时,系统只需要使用不同的PlatformTransactionManager实现类即可
2.TransactionDefinition
TransactionDefinition接口是事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法,具体如下。
- String getName():获取事务对象名称。
- int getIsolationLevel():获取事务的隔离级别。
- int getPropagationBehavior():获取事务的传播行为。
- boolean isReadOnly():获取事务是否只读。
上述方法中,事务的传播行为是指在同一个方法中,不同操作前后所使用的事务。传播行为有很多种。
属性名称 | 值 | 描述 |
---|---|---|
PROPAGATION_REQUIRED | REQUIRED | 表示当前方法必须运行在一个事务环境当中,如果当前方法已经处在一个事务环境中,则可以直接使用该方法;否则会开启一个新事务后执行该方法 |
PROPAGATION_SUPPORTS | SUPPORTS | 如果当前方法处于事务环境中,则使用当前事务,否则不使用事务 |
PROPAGATION_MANDATORY | MANDATORY | 表示调用该方法的现线程必须处于当前事务环境中,否则将抛出异常 |
PROPAGATION_REQUIRES_NEW | REQUIRES_NEW | 要求方法在新的事务环境中执行。如果当前方法已经在事务环境中,则先暂停当前事务,在启动新的事务后执行该方法;如果当前方法不在事务环境中,则会启动一个新的事务后执行该方法 |
PROPAGATION_NOT_SUPPORTED | NOT_SUPPORTED | 不支持当前事务,总是以非事务的状态去执行,如果调用该方法的线程处于事务环境中,则先暂停当前事务,然后执行该方法 |
PROPAGATION_NEVER | NEVER | 不支持当前事务。如果调用该方法的线程处于事务环境中,将抛出异常 |
PROPAGATION_NESTED | NESTED | 即使当前执行的方法处于事务环境中,依旧会启动一个新的事务,并且方法在嵌套的事务里执行;即使当前执行的方法不在事务环境中,也会启动一个新的事务,然后执行该方法 |
- 在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务,通常情况下,数据的查询不会影响原数据的改变,所以不需要进行事务管理,而对于数据的插入,更新和删除操作,必须进行事务管理。如果没有指定事务的传播行为,Spring默认传播行为是REQUIRED。
3.TransactionStatus
- TransactionStatus接口是事务的状态,他描述了某一时间点上事务的状态信息。该接口中包含6个方法,
- void flush():刷新事务
- boolean hasSavepoint():获取是否存在保持点。
- boolean isCompleted():获取事务是否完成。
- boolean iNewTransaxtion():获取是否是新事务。
- boolean isRollbackOnly(): 获取是否回滚。
- void setRollbackOnly(): 设置事务回滚。
2.事务管理的方式
- Spring中的事务管理分为两种方式:一种是传统的编程式事务管理,另一种是声明式事务管理。
- 编程式事务管理:是通过编写代码实现事务管理,包括定义事务的开始,正常执行后的事务提交和异常时的事务回滚。
- 声明式事务管理:是通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个”切面“代码单独编写,然后通过AOP技术将事务管理的”切面“代码织入到业务目标类中。
- 声明式事务管理最大的优点在于开发者无需通过编程的方式来管理事务,只需要在配置文件中进行相关的事务规则声明,就可以将事务规则应用到业务逻辑中。
六.声明式事务管理
- Spring的声明式事务管理可以通过两种方式来实现,一种是基于XML的方式,另一种是基于Annotation的方式。
1.基于XML方式的声明事务
- 基于XML方式的声明式事务管理是通过在配置文件中配置事务规则的相关声明来实现的。Spring2.0以后,提供了tx命名空间来配置事务,tx命名空间下提供了tx:advice元素来配置事务通知(增强处理)。当使用tx:advice元素配置了事务的增强处理后,就可以通过编写的AOP配置,让Spring自动对目标生产代理。
- 配置tx:advice元素时,通常需要指定id和transaction-manager属性,其中id属性是配置文件中的唯一标识,transaction-manager属性用于指定事务管理器。除此之外,还需要配置一个tx:attributes子元素,该子元素可通过配置多个tx:method子元素来配置执行事务的细节。
配置tx:advice元素的重点配置tx:method子元素,标点的几个属性是tx:method元素中常用的属性。
属性名称 | 描述 |
---|---|
name | 该属性为必选属性,它指定了于事务属性相关的方法名。其属性值支持所有通配符, |
propagation | 用于指定事务的传播行为,它的默认值为REQUIRED |
isolation | 该属性用于指定事务的隔离级别,其属性值可以为DEFAULT,READ_UNCOMMITTED,REPEATABLE_READ和SERIALIZABLE,其默认值为DEFAULT |
read-only | 该属性用于指定事务是否只读,其默认值为false |
timeout | 该属性用于指定事务超时的时间,其默认值为-1,即永不超时 |
rollback-for | 该属性用于指定触发事务回滚的异常类,在指定多个异常类时,异常类之间以英文逗号分隔 |
no-rollback-for | 该属性用于指定不触发事务回滚的异常类,在指定多个异常类时,异常类之间以英文逗号分隔 |
Spring的基本内容就全部学习完成了,接下来就开始学习MyBatis
一.初识MyBatis
Mybatis是当前主流的Java持久层框架之一,它和Hibernate一样,也是一种ORM框架。因其性能优异,具有高度的灵活性,可优化性和易于维护等特点,所以应用非常广泛。
1.什么是Mybatis
- Mybatis(前身是一个支持普通SQL查询),储存过程以及高级影视的持久层框架,它消除了几乎所以的JDBC代码和参数的手动设置以及对结果集的检索,并使用简单的XML或注解进行配置和原始影视,用以将接口和Java的POJO(Plain Old Java Object,普通Java对象)映射成数据库中的记录,使得Java开发人员可以使用面向对象的编程思维来操作数据库。
- MyBatis框架也被称为ORM(Object/Relational Mapping,即对象关系映射)框架。所谓的ORM就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述Java对象与数据表之间的映射关系,自动将Java应用程序中的对象持久层关系映射到数据库表中。ORM框架的工作原理
- 从图中可以看出,使用ORM框架后,应用程序不再直接访问底层数据库,而是以面向对向方式来操作持久化对象(Persisent Object,PO),而ORM框架则会通过映射关系将这些面向对象的操作转换成底层的SQL操作。
- ORM框架有Hibernate和MyBatis两个框架
- Hibernate:是一个全表映射的框架,通常开发者只需要定义好持久层对象到数据库表的映射关系,就可以通过Hibernate提供的方法完成持久层操作。开发者并不需要熟练的掌握SQL语句编写,Hibernate会根据制定的储存逻辑,自动的生成对于的SQL,并调用JDBC接口来执行所以其的开发效率是高于MyBatis。然而Hibernate自身也存在着一些缺点,例如它在多表关联时,对SQL查询的支持较差;更新数据时,需要发送所有字段;不支持储存过程;不能通过优化SQL来优化性能等。这些问题导致其只适合在场景不太复杂且对性能要求不高的项目中使用。
- Mybatis:是一个半自动映射框架。这里所谓的“半自动”是相对于Hibernate全表映射而言,Mybatis需要手动匹配提供POJO,SQL和映射关系,而Hibernate只需要提供POJO和映射关系即可。与Hibernate相比,虽然MyBtais手动编写SQL要比使用Hibernate的工作量大,但MyBatis可以配置动态SQL并优化SQL,可以通过配置决定SQL的映射规则,它还支持储存过程等。对于一些复杂的和需要优化性能的项目来说,显然使用MyBatis更加合适。
2.MyBatis工作原理
- MyBatis框架操作数据库一般分为8步骤
1.读取MyBatis配置文件mybatis-config.xml。mybatis-config.xml作为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息,其主要内容是获取数据库的连接。
2.加载映射文件Mapper.xml。Mapper.xml文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要mybatis-config.xml中加载才能执行。mybatis-config.xml可以加载多个配置文件,每个配置文件对应数据库中的一张表。
3.构建会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。
4.创建SqlSession对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL的所有方法。
5.MyBatis底层定义了一个Executor接口来操作数据库,它会根据SqlSession传递的参数动态的生成需要执行的SQL语句,同时负责查询缓存的维护。
6.在Executor接口的执行方法中,包含一个MappedStatement类型的参数,该参数是对映射信息的封装,用于储存要映射的SQL语句的id,参数等。Mapper.xml中一个SQL对应一个MappedStatement对象,SQL的id即是MappedStatement的id。
7.输入参数映射。在执行方法时,MappedStatement对象会对用户执行SQL语句的输入参数进行定义(可以定义为Map和List类型,基本类型,POJO类型),Exector执行器会通过MappedStatement对象在执行SQL语句前,将输入的JAVA对象映射到SQL语句中。这种对输入参数的映射过程就类似于JDBC编程中对preparedStatement对象设置参数的过程。
8.输出结果映射。在数据库中执行完SQL语句后,MappedStatement对象会对SQL执行输出的结果进行定义(可以定义为Map和List类型,基本类型,POJO类型),Executor执行器会通过MappedStatement对象在执行SQL语句后,将输出结果映射至java对象中。这种将输出结果映射到Java对象中的过程就类似于JDBC编程中对结果的解析处理过程。