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时)
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:一般用于持久层 用于注入数据的:<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:用于指定值 用于改变作用范围的:<bean id="" class="" ==scope=""==> </bean>
@Scope
指定bean的作用范围value:指定范围的值(==singleton==、==prototype==、request、session、globalsession) 和生命周期相关的:<bean id="" class="" ==init-method=""== ==destory-method=""== />
@PostConstruct
用于指定初始化方法 @PreDestroy
用于指定销毁方法 用于纯注解配置的注解
@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[]:用于指定其他配置类的字节码 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> ``` 基于注解的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:指定表达式的内容 针对AOP的四种状态对应的注释
@Before
作用:将当前方法看作前置通知value:用于指定切入点表达式,还可以指定切入点表达式的引用 @After-Returning
作用:将当前方法看作后置通知value:用于指定切入点表达式,还可以指定切入点表达式的引用 @AfterThrowing
作用:将当前方法看作异常通知value:同上 @After
作用:将当前方法看作最终通知value:同上 @Around
作用:将当前方法看作环绕通知value:同上 @Around("pt1()") //引入切入点时括号要加上 使用纯注解配置时
在配置类上使用注解:@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());
}
}