Spring
1. Spring概述
Spring是应用full-stack轻量级框架,以IoC(Inverse Of Control : 控制反转)和AOP(Aspect Oriented Programming : 面向切面编程)为内核,提供了表现层SpringMVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界中众多的第三方框架和类库。
2. Spring的优势
- 方便解耦,简化开发
- 通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,编码硬编码所造成的过度程序耦合。
- AOP编程支持
- 通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传荣OOP实现的功能可以通过AOP轻松实现。
- 声明式事务的支持
- 可以简化在事务管理方面的代码,通过声明的方式进行灵活的事务管理,提高开发效率和质量
- 方便程序的测试
- 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试的难度被降低了。
- 方便集成各种优秀框架
- Spring提供了对各种邮箱框架的直接支持。
- 降低JavaEE API的使用难度
- Spring对JavaEE API中的代码(JDBC、JavaMail、远程调用等)进行了封装,降低了使用API的难度。
3. Spring的IoC应用
1. 在Maven中导入Spring的坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
2. Spring的主配置文件: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">
</beans>
- 简单的Spring配置文件
<!-- 配置service
id : 唯一标识
class : 指定类的全类名
-->
<bean id="accountService" class="com.hsh.study.service.impl.AccountServiceImpl"></bean>
<!-- 配置dao -->
<bean id="accountDao" class="com.hsh.study.dao.impl.AccountDaoImpl"></bean>
- 常见属性标签
- bean标签:用于配置对象让Spring来创建。默认情况下Spring调用类中的无参构造函数,没有无参构造函数则不能创建成功
- 属性:
- id:给对象在容器总提供一个唯一标识。用于获取对象。
- class:指定类的全限定类名。用于反射创建对象,默认情况下调用无参构造函数
- scope:指定对象的作用范围。
- singleton:默认值,单例对象
- prototype:多例的
- request:在WEB中,Spring创建一个Bean对象,将对象存入request域中。
- session:在WEB中,Spring创建一个Bean对象,将对象存入session域中。
- global session:在WEB中,应用在Porlet环境,如果没有Porlet环境那么globalSession相当于Session。
- init-method:指定类中的初始化方法名称。
- destory-method:指定类中销毁方法的名称
- factory-method:指定生产对象的静态方法(使用静态工厂初始化Bean时)
- factory-bean:用于指定实例工厂bean的id(使用实例工厂初始化Bean时)
- factory-method:用于指定实例工厂中创建对象的方法(使用实例工厂初始化Bean时)
- 属性:
- bean标签:用于配置对象让Spring来创建。默认情况下Spring调用类中的无参构造函数,没有无参构造函数则不能创建成功
3. Spring的执行方法
- 使用ApplicaionContext接口获取Spring的容器:
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
- 根据配置文件获取对象:
AccountService accountService = ac.getBean("accountService",AccountService.class);
- 使用对象:
accountService.saveAccount();
ApplicationContext和BeanFactory的区别:
- BeanFactory是Spring容器中的顶层接口。
- ApplicationContext是BeanFactory的子接口。
- BeanFactory和ApplicationContext创建对象的时间点不同;
- BeanFactory:什么时候使用什么时候创建对象
- ApplicationContext:只要读取完配置文件,默认情况下就会创建对象。
4. Spring的依赖注入
- 依赖注入概念:在编写程序时,通过IoC(控制反转)将对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IoC解耦只是降低了代码间的依赖关系,但不会完全消除。
- 构造函数的注入(通过类中的构造函数给成员变量完成赋值的操作)
- 编写需要进行赋值的实体类
public class User { private String username; private Integer age; private Date birthday; public User(String username, Integer age, Date birthday) { this.username = username; this.age = age; this.birthday = birthday; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", birthday=" + birthday + '}'; } }
- 用配置文件通过构造函数对属性进行赋值
<!-- 通过构造器进行属性注入 --> <bean id="user" class="com.hsh.study.domain.User"> <!-- 通过<constructor-arg>标签对构造函数中的属性进行赋值 index:指定参数在构造函数参数列表的索引位置 type:指定参数在构造函数中的数据类型 name:指定参数在构造函数中的名称 ==--- 上面用于指定给谁赋值,下面指定赋什么值 ---== value:能赋值的类型为基本数据类型和String类型 ref:能赋值的类型是其他bean类型,必须通过配置文件配置bean --> <constructor-arg name="username" value="张三"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="birthday" ref="now"></constructor-arg> </bean> <!-- 引入日期 --> <bean id="now" class="java.util.Date"></bean>
- set方法的注入(常用)
- 编写需要进行赋值的实体类
public class User { private String username; private Integer age; private Date birthday; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", birthday=" + birthday + '}'; } }
- 用配置文件通过构造函数对属性进行赋值
<!-- 通过set方法注入 --> <bean id="user" class="com.hsh.study.domain.User"> <!-- 通过<property>标签对属性进行赋值 name:找类中set方法后面的部分 ref:给属性赋值是其他bean类型 value:给属性赋值的是基本数据类型或者string类型 --> <property name="username" value="李四"></property> <property name="age" value="16"></property> <property name="birthday" ref="now"></property> </bean> <!-- 引入日期 --> <bean id="now" class="java.util.Date"></bean>
- 使用p名称空间注入(本质还是调用set方法)
- 编写需要进行赋值的实体类
public class User { private String username; private Integer age; private Date birthday; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", birthday=" + birthday + '}'; } }
- 用配置文件通过构造函数对属性进行赋值
<!-- 导入p标签的约束文件 --> xmlns:p="http://www.springframework.org/schema/p" <!-- 通过p标签进行注入 --> <bean id="user" class="com.hsh.study.domain.User" p:username="王五" p:age="18" p:birthday-ref="now" ></bean> <!-- 引入日期 --> <bean id="now" class="java.util.Date" ></bean>
- 注入集合属性
- 编写需要进行赋值的实体类
public class Collection { private String[] myStrs; private List<String> myList; private Set<String> mySet; private Map<String,String> myMap; private Properties myProps; public void setMyStrs(String[] myStrs) { this.myStrs = myStrs; } public void setMyList(List<String> myList) { this.myList = myList; } public void setMySet(Set<String> mySet) { this.mySet = mySet; } public void setMyMap(Map<String, String> myMap) { this.myMap = myMap; } public void setMyProps(Properties myProps) { this.myProps = myProps; } public String[] getMyStrs() { return myStrs; } public List<String> getMyList() { return myList; } public Set<String> getMySet() { return mySet; } public Map<String, String> getMyMap() { return myMap; } public Properties getMyProps() { return myProps; } }
- 用配置文件通过构造函数对属性进行赋值
<!-- 集合的注入 --> <bean id="collection" class="com.hsh.study.domain.Collection"> <!-- 注入集合数据 List结构:array、List、Set Map结构:map、properties、entry --> <!-- 数组注入 --> <property name="myStrs"> <list> <value>AAA</value> <value>BBB</value> <value>CCC</value> </list> </property> <!-- list集合注入 --> <property name="myList"> <array> <value>AAA</value> <value>CCC</value> <value>BBB</value> </array> </property> <!-- set集合注入 --> <property name="mySet"> <set> <value>BBB</value> <value>AAA</value> <value>CCC</value> </set> </property> <!-- map集合注入 --> <property name="myMap"> <map> <entry key="111" value="AAA"></entry> <entry key="222" value="BBB"></entry> <entry key="333" value="CCC"></entry> </map> </property> <!-- Props注入 --> <property name="myProps"> <props> <prop key="333">AAA</prop> <prop key="222">CCC</prop> <prop key="111">BBB</prop> </props> </property> </bean>
5. Spring基于注解的使用
- 在Maven中导入Spring的坐标
- 配置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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 告知spring创建容器时要扫描的包 --> <context:component-scan base-package="com.hsh.study"></context:component-scan> </beans>
- Spring的常用注解
- 创建对象的:<bean id="" class="" ></bean>
- @Component()
- 将资源交由Spring进行管理,相当于在xml中配置<bean>标签
- 默认属性为value,value即是hean的id,如果不指定value属性则默认id为当前类名,首字母小写
- @Controller @Service @Repository
- 这三个注释的作用与@Component的作用一致,只不过提供了更加明确的语义化
- @Controller:一般用于表现层
- @Service:一般用于业务层
- @Repository:一般用于持久层
- 这三个注释的作用与@Component的作用一致,只不过提供了更加明确的语义化
- @Component()
- 用于注入数据的:<property name="" ref="" /> <property name="" value="" />
- @Autowired
- 自动按照类型注入。当使用注解注入属性时,可以不用声明set方法。只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象变量名称作为bean的id。
- @Qualifier
- 在自动按照类型的基础上,再按照Bean的id注入。在给字段注入时不能独立使用,必须和@Autowired一起使用;但是给方法参数注入时可以独立使用。
- value:指定bean的id
- @Resource
- 直接按照Bean的id进行注入。也是只能注入其他Bean类型
- name:指定bean的id
- @Value
- 注入基本数据类型和String类型的数据
- value:用于指定值
- @Autowired
- 用于改变作用范围的:<bean id="" class="" ==scope=""==> </bean>
- @Scope
- 指定bean的作用范围
- value:指定范围的值(==singleton==、==prototype==、request、session、globalsession)
- @Scope
- 和生命周期相关的:<bean id="" class="" ==init-method=""== ==destory-method=""== />
- @PostConstruct
- 用于指定初始化方法
- @PreDestroy
- 用于指定销毁方法
- @PostConstruct
- 用于纯注解配置的注解
- @Configuration
- 作用:用于指定当前类是一个spring的配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration注解的类.class)
- value:用于指定配置类的字节码
- 注:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
- @ComponentScan
- 作用:用于指定Spring再初始化容器时要扫描的包。作用和在spring的xml配置文件中的<context:component-scan base-package="com.hsh.study" />的作用相同
- basePackages:用于指定要扫描的包,和注解中的value属性作用一致。
- @Bean
- 作用:该注解只能写在方法上,表明使用此方法创建一个对象并放入Spring的容器中
- name:给当前的@Bean注解方法创建的对象指定一个名称
- @PropertySource
- 作用:用于加载.properties文件中的配置。例如当配置数据源时,可以把连接数据库的信息写道properties配置文件中,使用注解指定配置文件的位置。
- value[]:用于指定properties文件位置。如果是在类路径下,需要写上==classpath:==
- @Import
- 作用:用于导入其他配置类,在引入其他配置时可以不用再写@Configuration注解。
- value[]:用于指定其他配置类的字节码
- @Configuration
- 创建对象的:<bean id="" class="" ></bean>
- Spring使用XML和使用注解配置的优劣
- 使用注解的优势:配置简单,维护方便,适用于Bean的是实现类由用户自己开发
- 使用XML的优势:修改时不需要改源码,不涉及重新编译和部署。适用于Bean来自第三方,使用其他的Bean。
6. Spring中使用纯注解配置
- 创建一个主配置类
//@Configuration
@ComponentScan("com.hsh.study")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
- 创建JDBC配置类
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建QueryRunner对象
* @param dataSource
* @return
*/
@Bean(name = "runner")
@Scope("singleton")
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 创建数据库连接对象
* @return
*/
@Bean("ds1")
public DataSource createDataSource(){
try{
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
- 使用注释完成创建对象和注入
//1. 业务层
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
//2. 持久层
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner runner;
@Override
public List<Account> findAll() {
try {
return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
- 测试
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
// 基于注解创建applicationContext
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
List<Account> accounts = accountService.findAll();
for (Account account1 : accounts){
System.out.println(account1);
}
4. Spring整合Junit
1. 导入整合junit的jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2. 使用注解@RunWith代替原有运行器
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTest2 {
@Test
public void test() {
}
}
3. 使用@ContextConfiguration指定spring配置文件位置
- @ContextConfiguration属性:
- locations属性:用于指定配置文件的位置。位于类路径下需使用classpath:表示。
- calsses属性:用于指定注解的类。当不使用xml时,需要用此属性指定注解类的位置。
- 应用
@RunWith(SpringJUnit4ClassRunner.class)
// 使用XML配置文件时
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
// 使用注解时
@ContextConfiguration(classes = {config.SpringConfiguration.class})
public class JdbcTest2 {
@Test
public void test() {
}
}
4. 使用@Autowired给变量注入数据
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {config.SpringConfiguration.class})
public class JdbcTest2 {
@Autowired
private AccountService accountService;
@Test
public void test() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
accountService = applicationContext.getBean("accountService", AccountService.class);
List<Account> accounts = accountService.findAll();
for (Account account1 : accounts){
System.out.println(account1);
}
}
}
5. Spring的AOP
1. 概念:
AOP(Aspect Oriented Programming)面向切面编程。利用AOP对业务逻辑的各个部分进行隔离,从而降低业务逻辑各部分之间的耦合度
2. AOP的作用及优势
- 作用:在程序运行期间,在不修改源码的基础上对已有方法进行增强。
- 优势:①减少重复代码②提高开发效率③维护方便
3. AOP中相关的术语
- JoinPoint(连接点):连接点指的是哪些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
- Pointcut(切入点):切入点指的是对哪些连接点进行拦截的定义。
- Advice(通知/增强):通知是指拦截到JoinPoint后所做的事情便是通知。
- 通知的类型:前置通知,后置通知,异常通知,最终通知和环绕通知。
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期间为类动态的添加一些方法或者Field。
- Target(目标对象):代理的目标对象。
- Weaving(织入):指把增强应用到目标对象来创建新的代理对象的过程。
- Proxy(代理):一个类被AOP织入增强后,就产生了一个结果代理类。
- Aspect(切面):是切入点和通知(引介)的结合。
3. AOP的使用
- 在Maven中引入Spring的AOP坐标
<!-- AOP坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
- 在spring的配置文件中导入约束
- spring-AOP的约束头信息
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
- spring-AOP的配置
- execution表达式
- 在AOP中使用execution表达式首先要导入表达式的坐标
<!-- AOP使用execution表达式坐标 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
- execution表达式的使用方法
- 普通的表达式:权限修饰符 返回值类型 包名.包名...类名.方法名(参数列表)
- 标准表达式写法:
public void com.hsh.study.service.impl.AccountServiceImpl.findAll()
- 权限修饰符可以省略,返回值类型使用通配符代替:
* com.hsh.study.service.impl.AccountServiceImpl.findAll()
- 包名使用通配符代替,几个类几个通配符:
* *.*.*.*.*.AccountServiceImpl.findAll()
- 包名可以使用..表示当前包及其子包:
* *..AccountServiceImpl.findAll()
- 方法名和类名都可以使用通配符:
* *..*.*()
- 参数列表:
- 基本数据类型直接写名称:int
- 引用数据类型写包名.类名的方式 java.lang.String
- 使用..表示有无参数均可,有参数可以为任意类型
- 全通配写法:
execution(* *..*.*(..))
- AOP的具体配置
- 基本配置
<!-- 首先导入对应的bean --> <bean id="accountService" class="..."></bean> <!-- 导入配置方法的bean --> <bean id="logger" class="..." ></bean> <!-- 配置aop --> <aop:config> <!-- 配置切入点表达式<aop:pointcut /> id:指定表达式唯一标识 expression:指定表达式内容 此标签写在<aop:aspect>内部表示当前切面可用 此标签写在<aop:aspect>外部表示所有切面可用 --> <aop:pointcut id="pt1" expression="execution(* *..*.*(..))"/> <!-- 配置切面 --> <aop:aspect id="logAdvice" ref="transactionManager"> <!-- 配置前置通知" --> <aop:before method="beforePringLog" pointcut-ref="pt1" /> <!-- 配置后置通知 --> <aop:after-returning method="afterReturningPringLog" pointcut-ref="pt1" /> <!-- 配置异常通知 --> <aop:after-throwing method="afterThrowingPringLog" pointcut-ref="pt1" /> <!-- 配置最终通知 --> <aop:after method="afterPringLog" pointcut-ref="pt1" /> <!-- 配置环绕通知 --> <!-- <aop:around method="aroundPrintLog" pointcut-ref="pt1" />--> </aop:aspect> </aop:config>
- 环绕通知配置
- 类中
public Object aroundPrintLog(ProceedingJoinPoint pjp){ Object resultValue = null; try{ // 得到方法执行所需的参数 Object[] args = pjp.getArgs(); // 执行前置通知 System.out.println("前置通知"); // 执行切入点方法 resultValue = pjp.proceed(args); // 执行后置通知 System.out.println("后置通知"); return resultValue; }catch (Throwable e){ System.out.println("异常通知"); throw new RuntimeException(e); }finally { System.out.println("最终通知"); }
* 配置文件中
<bean id="accountService" class="..."></bean>
<bean id="logger" class="..." ></bean>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* ...*(..))"/>
<aop:aspect id="logAdvice" ref="transactionManager">
</aop:aspect>
</aop:config>
```
- execution表达式
- 基于注解的Spring-AOP配置
- 在配置文件中导入AOP相关的约束头信息
<?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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </bean>
- 指定Spring注解所需扫描的包,并开启对注解AOP的支持
<!-- 需要扫描的包 --> <context:component-scan base-package="com.hsh.study"></context:component-scan> <!-- 开启对注解AOP的支持 --> <aop:aspectj-autoproxy/>
- 针对各实体类进行注解配置
- 在通知类中使用注解进行通知
- 在通知类上使用@Aspect注释表明这是一个切面类
@Component("logger") @Aspect public class Logger { }
- 切入点表达式注解
- @Aspect
- 作用:指定切入点表达式
- value:指定表达式的内容
- @Aspect
- 针对AOP的四种状态对应的注释
- @Before
- 作用:将当前方法看作前置通知
- value:用于指定切入点表达式,还可以指定切入点表达式的引用
- @After-Returning
- 作用:将当前方法看作后置通知
- value:用于指定切入点表达式,还可以指定切入点表达式的引用
- @AfterThrowing
- 作用:将当前方法看作异常通知
- value:同上
- @After
- 作用:将当前方法看作最终通知
- value:同上
- @Around
- 作用:将当前方法看作环绕通知
- value:同上
@Around("pt1()") //引入切入点时括号要加上
- @Before
- 使用纯注解配置时
- 在配置类上使用注解:@EnableAspectJAutoProxy即可
6. Spring中的JdbcTemplate
1. 概念
JdbcTemplate是Spring框架中提供的一个对象,是对原始JDBC API的简单封装。其中提供了许多的操作模板类。
- 操作关系数据库:JdbcTemplate、HibernateTemplate
- 操作nosql数据:RedisTemplate
- 操作消息队列:JmsTemplate
2. 基于Spring的JdbcTemplate所必须的jar包或坐标
<!-- JdbcTemplate坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 事务控制坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
3. Spring-JdbcTemplate的使用
1. 编写Spring的配置文件
- 导入spring-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">
</beans>
- 配置相应的数据源
<!-- 使用c3p0数据库连接池时 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false" />
<property name="user" value="root" />
<property name="password" value="123" />
</bean>
<!-- 使用Spring内置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 使用外部配置文件的Spring内置数据源时 -->
<!-- 引入外部配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"></property>
</bean>
<!-- 使用注解引入外部文件时 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
2. 使用JdbcTemplate实现增删改查操作
- 在配置文件中配置JdbcTemplate
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
- 可以直接使用JdbcTemplate实现增删改查操作
3. 在Dao中使用JdbcTemplate
- 不通过继承JdbcDaoSupport来简化获取JdbcTemplate的操作(用于xml配置和注解配置中)
- 在Dao实现类中添加jdbcTemplate属性
private JdbcTemplate jdbcTemplate; // 添加注入 public void setJdbcTemplate(JdbcTemplate jdbcTemplate){ this.jdbcTemplate = jdbcTemplate; }
- 在配置文件中添加Dao实现类的bean
<bean id="accountDao" class="...AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean>
- 使用jdbcTemplate属性执行数据库操作
- 通过继承JdbcDaoSupport来简化获取JdbcTemplate的操作(仅限于xml配置中)
- 在Dao实体类中继承JdbcDaoSupport,不用添加jdbcTemplate属性
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { }
- 在配置文件中添加Dao实体类的Bean
<bean id="accountDao" class="..AccountDaoImpl"> <!-- 可以配置JdbcTemplate也可以配置DataSource 配置一个即可--> <!-- 配置JdbcTempldate --> <property name="jdbcTemplate" ref="jdbcTemplate" /> <!-- 配置DataSource --> <property name="dataSource" ref="dataSource" /> </bean>
- 调用父类的方法super.getJdbcTemplate()来执行相应的数据库操作
7. Spring中的事务控制
-
Spring中关于事务控制的API
1. PlatformTransactionManager(Spring中的事务管理器)
PlatformTransactionManager接口提供了事务操作的方法,其中包含3各具体的操作:
- 获取事务状态信息
TransactionStatus getTransaction(TransactionDefinition definition)
- 提交事务
void commit(TransactionStatus status)
- 回滚事务
void rollback(TransactionStatus status)
真正使用的是PlatformTransactionManager接口的实体类: - org.springframework.jdbc.datasource.==DataSourceTransactionManager== 使用Spring JDBC或iBatis进行持久化数据时使用
- org.springframework.orm.hibernate5.==HibernateTransactionManager== 使用Hibernate版本进行持久化数据时使用
2. TransactionDefinition
TransactionDefinition是事务的信息定义对象,方法如下:
- 获取事务对象名称:
String getName()
- 获取事务隔离级别:
int getIsolationLevel()
- 默认级别,归属以下某一种:ISOLATION_DEFAULT
- 可以读取未提交数据:ISOLATION_READ_UNCOMMITTED
- 只能读取已提交数据,解决脏读问题(Oracle默认级别):ISOLATION_READ_COMMITTED
- 是否读取其他事务提交修改后的数据,解决不可重复读问题(MySql默认级别):ISOLATION_REPEATABLE_READ
- 是否读取其他事务提交添加后的数据,解决幻影读的问题:ISOLATION_SERIALIZABLE
- 获取事务传播途径:
int getPropagationBehavior()
- REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到该事务中。(默认值)
- SUPPORTS:支持当前事务,如果当前没有事务,就通过非事务方式执行
- MANDATORY:使用当前的事务,如果当前没有事务则抛异常
- REQUERS_NEW:新建事务,如果当前在事务中,则将当前事务挂起
- NOT_SUPPORTED:以非事务的方式执行操作,如果当前存在事务,则将当前事务挂起
- NEVER:以非事务的方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作
- 获取事务超时时间:
int getTimeout()
- 默认值为-1,没有超时限制。如果有则以秒为单位进行设置
- 获取事务是否只读:
boolean isReadOnly()
- 只读事务:查询
- 读写型事务:增删改
3. TransactionStatus
TransactionStatus接口提供某个时间点上事务对象的状态信息,其中有6个具体的操作
- 刷新事务:
void flush()
- 获取是否存在存储点:
boolean hasSavepoint()
- 获取事务是否完成:
boolean isCompleted()
- 获取事务是否为新的事务:
boolean isNewTransaction()
- 获取事务是否回滚:
boolean isRollbackOnly()
- 设置事务回滚:
void setRollbackOnly()
-
Spring中事务控制的具体操作
1. 导入Spring事务控制的相关坐标
<!-- JDBC相关坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 事务控制相关坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2. 在配置文件中导入aop和tx约束
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
</beans>
3. 配置Bean、数据源、事务控制、AOP
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- 配置service -->
<bean id="accountService" class="com.hsh.study.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.hsh.study.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.classDriver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置数据库配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 导入数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务的配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<!-- 配置事务的属性 -->
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!-- AOP的配置 -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut id="pt1" expression="execution(* com.hsh.study.service.impl.*.*(..))"/>
<!-- 配置AOP -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>
-
Spring中事务控制的具体操作(使用注解开发)
- 主注解类的配置
// 声明式配置类
@Configuration
// 告诉Spring要扫描的包
@ComponentScan("com.hsh.study")
// 导入的另一个配置类
@Import(JdbcConfig.class)
// 配置文件的声明
@PropertySource("classpath:jdbc.properties")
// 开启注解AOP支持
@EnableAspectJAutoProxy
// 开启注解事务支持
@EnableTransactionManagement
public class SpringConfig {
}
- Jdbc注解类的配置
public class JdbcConfig {
// @Value:表示引入配置文件中的值
@Value("${jdbc.classDriver}")
private String classDriver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// 声明jdbcTemplate
@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
/**
* 创建事务管理器
* Bean注解不配值时,默认类名首字母小写如DataSourceTransactionManager变成dataSourceTransactionManager
*/
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
// 声明数据源
@Bean("dataSource")
public DataSource createDataSource(){
try {
DriverManagerDataSource manager = new DriverManagerDataSource();
manager.setDriverClassName(classDriver);
manager.setUrl(url);
manager.setUsername(username);
manager.setPassword(password);
return manager;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
- 业务层实现
@Service("accountService")
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void transfer(String sourceName, String targetName, Double money) {
// 获取两个用户
Account source = accountDao.findByName(sourceName);
Account target = accountDao.findByName(targetName);
// 执行转账操作
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
// 执行更新操作
accountDao.update(source);
accountDao.update(target);
}
- 持久层实现
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findById(Integer accountId) {
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",
new BeanPropertyRowMapper<Account>(Account.class), accountId);
return accounts.isEmpty()?null:accounts.get(0);
}
@Override
public Account findByName(String accountName) {
List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",
new BeanPropertyRowMapper<Account>(Account.class), accountName);
if (accounts.isEmpty()){
return null;
}
if (accounts.size() > 1){
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}
@Override
public void update(Account account) {
jdbcTemplate.update("update account set money = ? where id = ?",
account.getMoney(),account.getId());
}
}