Spring框架是一个容器,整合其他框架的框架,核心是IOC和AOP,由20多个模块组成
Spring为了解决企业级应用开发的复杂性创建的 简化开发
Spring的特点:
1.轻量级:由20多个模块构成,每个jar包都很小,核心包3M左右,对代码无污染
2.面向接口编程:灵活性,可扩展性,维护性都极高,使用时接口指向实现类,切换实现类即可切换整个功能
3.AOP:面向切面编程,就是将公共的通用的重复的代码单独开发,需要的时候给回去,底层的原理是动态代理
4.整合其他框架:整合后其他框架更好用
控制反转IOC(Inversion of Control)是一个概念,是一种思想。由spring容器进行对象的创建和依赖注入
正转:由程序员进行对象的创建和依赖注入称为正转,程序员说了算
反转:由spring创建对象和依赖注入,将控制权从程序员手中夺走,给spring容器,称为反转
基于xml的IOC
1.创建对象
<!--bean的id属性就是要创建的对象的名称,class属性是要创建的对象的类型,底层通过反射构建对象-->
<bean id="stu" class="com.lv.pojo.Student"></bean>
2.给创建的对象赋值
A.使用setter注入
注入分为简单类型注入和引用类型注入
简单类型使用value属性
引用类型使用ref属性
使用setter注入实体类里必须提供无参构造方法,必须提供set方法
<bean id="school" class="com.lv.pojo2.School">
<property name="name" value="清华大学"></property>
<property name="address" value="北京海淀区"></property>
</bean>
<bean id="stu" class="com.lv.pojo2.Student">
<property name="name" value="李四"></property> ---->简单类型注入
<property name="age" value="22"></property>
<property name="school" ref="school"></property> ---->引用类型注入
</bean>
B.使用构造方法注入
a.使用构造方法的参数名称进行注入值
<bean id="school" class="com.lv.pojo3.School">
<constructor-arg name="name" value="清华大学"></constructor-arg>
<constructor-arg name="address" value="海淀区"></constructor-arg>
</bean>
b.使用构造方法的参数下标注入值
<bean id="stu" class="com.lv.pojo3.Student">
<constructor-arg index="0" value="老三"></constructor-arg>
<constructor-arg index="1" value="22"></constructor-arg>
<constructor-arg index="2" ref="school"></constructor-arg>
</bean>
c.使用默认的构造方法的参数的顺序注入值
<bean id="stu1" class="com.lv.pojo3.Student">
<constructor-arg value="老四"></constructor-arg>
<constructor-arg value="22"></constructor-arg>
<constructor-arg ref="school"></constructor-arg>
</bean>
项目案例
使用三层架构进行用户的插入操作 (界面层,业务逻辑层,数据访问层)
Spring会接管三层架构中哪些对象的创建?
非Spring接管的三层项目构建:
实体类 Users
public class Users {
private int uid;
private String uname;
private int uage;
public Users() {
}
public Users(int uid, String uname, int uage) {
this.uid = uid;
this.uname = uname;
this.uage = uage;
}
@Override
public String toString() {
return "Users{" +
"uid=" + uid +
", uname='" + uname + '\'' +
", uage=" + uage +
'}';
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public int getUage() {
return uage;
}
public void setUage(int uage) {
this.uage = uage;
}
}
数据访问层 UsersMapper.java接口 UsersMapperImpl.java实现类
public interface UsersMapper {
int insert(Users u);
}
public class UsersMapperImpl implements UsersMapper{
@Override
public int insert(Users u) {
System.out.println(u.getUname()+"用户增加成功!");
return 1;
}
}
业务逻辑层 UsersService.java接口 UsersServiceImpl.java实现类
public interface UsersService {
int insert(Users users);
}
public class UsersServiceImpl implements UsersService {
//在所有的业务逻辑层中都必定有数据访问层的对象
private UsersMapper usersMapper = new UsersMapperImpl();
@Override
public int insert(Users users) {
return usersMapper.insert(users);
}
}
界面层 UsersController.java类
public class UsersController {
//访问业务逻辑层,就是创建对象(所有的界面层都会有业务逻辑层的对象) 接口指向实现类
public UsersService usersService = new UsersServiceImpl();
//对外提供访问的功能
public int insert(Users users){
return usersService.insert(users);
}
}
测试类
public class Mytest {
@Test
public void testA(){
UsersController usersController = new UsersController();
int num = usersController.insert(new Users(001,"张三",22));
System.out.println(num);
}
}
Spring用xml更改的三层项目构建:
applicationContext.xml文件
<!--创建数据访问层的对象-->
<bean id="umapper" class="com.lv.dao.UsersMapperImpl"></bean>
<!--创建业务逻辑层的对象-->
<bean id="uservice" class="com.lv.service.impl.UsersServiceImpl">
<property name="usersMapper" ref="umapper"></property>
</bean>
<!--创建界面层的对象-->
<bean id="ucontroller" class="com.lv.controller.UsersController">
<property name="usersService" ref="uservice"></property>
</bean>
业务逻辑层 UsersService.java接口 UsersServiceImpl.java实现类
public class UsersServiceImpl implements UsersService {
//在所有的业务逻辑层中都必定有数据访问层的对象
private UsersMapper usersMapper; //= new UsersMapperImpl();
//给spring创建对象提供set方法
public void setUsersMapper(UsersMapper usersMapper) {
this.usersMapper = usersMapper;
}
@Override
public int insert(Users users) {
return usersMapper.insert(users);
}
}
界面层 UsersController.java类
public class UsersController {
//访问业务逻辑层,就是创建对象(所有的界面层都会有业务逻辑层的对象) 接口指向实现类
public UsersService usersService ;//= new UsersServiceImpl();
//给spring创建对象提供set方法
public void setUsersService(UsersService usersService) {
this.usersService = usersService;
}
//对外提供访问的功能
public int insert(Users users){
return usersService.insert(users);
}
}
测试类
public class Mytest {
@Test
public void testA(){
//创建容器并启动
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//取出对象
UsersController usersController = (UsersController) ac.getBean("ucontroller");
//测试功能
int num = usersController.insert(new Users(001,"王五",24));
System.out.println(num);
}
}
基于注解的IOC
也称为DI(Dependency Injection),它是IOC的具体实现技术
基于注解的IOC,必须要在Spring核心配置文件中添加包扫描
<context:component-scan base-package="com.lv.s01"></context:component-scan>
1.创建对象的注解
@Component:可以创建任意对象 @Component(“stu”)指定名称为stu
@Controller:专门用来创建控制器的对象,可以接受用户请求,可以返回处理结果给客户端
@Service:专门用来创建业务逻辑层的对象,负责向下访问数据访问层,处理完毕后的结果返回给界面层
@Repository:专门用来创建数据访问层的对象,负责数据库中的增删改查
2.依赖注入的注解
简单类型的注入
@Value:用来给简单类型注入值 @Value(“张三”)
引用类型的注入
@Autowired:使用类型注入值,从整个Bean工厂中搜索同源类型的对象进行注入
同源类型也可注入
@Autowired
@Qualifier:使用名称注入值,从整个Bean工厂中搜索相同名称的对象进行注入 @Qualifier(“school”)
为应用指定多个Spring配置文件
1.按层拆
2.按功能拆
批量导入配置文件
<import resource="applicationContext_*.xml"></import>
AspectJ框架,面向切面的框架,扩展了Java语言,提供强大的切面实现
AspectJ常用的通知
1.前置通知@Before
@Aspect //交给AspectJ的框架去识别切面类
@Component //创建对象
public class MyAspect {
/* //所有切面的功能都是由切面方法实现
//前置通知的切面方法规范
1.访问权限是public
2.方法的返回值是void
3.方法名称自定义
4.方法无参,有的话只能是JoinPoint类型
5.必须使用@Before注解声音切入时机和切入点
参数value:指定切入点表达式
*/
@Before(value = "execution(* com.lv.s01.*.*(..))")
public void MyBefore(){
System.out.println("切面方法的前置通知功能实现");
}
}
2.后置通知@AfterReturning
@Aspect
@Component
public class MyAspect {
//后置通知的方法规范
// 1.访问权限是public
// 2.方法没有返回值void
// 3.名称自定义
// 4.方法有参数
//value属性指定切入点表达式 returning指定目标方法的返回值的名称,名称必须与切面方法的参数名称一致
@AfterReturning(value ="execution(* com.lv.s02.*.*(..))" ,returning = "obj")
public void myAfterReturning(Object obj){
System.out.println("后置通知功能实现");
if(obj != null){
if(obj instanceof String){
//把业务代码的返回值小写转大写
obj = obj.toString().toUpperCase();
System.out.println("在切面方法中目标方法的返回值:"+obj);
}
if (obj instanceof Student){
Student stu = (Student) obj;
stu.setName("李四");
System.out.println("在切面方法中目标方法的返回值:"+stu);
}
}
}
}
3.环绕通知@Around
通过拦截目标方法的方式,在目标方法前后增强功能的通知
@Aspect
@Component
public class MyAspect {
//环绕通知方法的规范
// 访问权限是public
// 有返回值,就是目标方法的返回值
// 名称自定义
// 方法有参数,此参数就是目标方法,回避异常
//value:指定切入点表达式
//ProceedingJoinPoint 底层就是调用目标方法
@Around(value = "execution(* com.lv.s03.*.*(..))")
public Object myAround(ProceedingJoinPoint pjt) throws Throwable {
//前切功能实现
System.out.println("环绕通知的前切功能实现");
//目标方法调用
Object obj = pjt.proceed(pjt.getArgs());
//后切功能实现
System.out.println("环绕通知的后切功能实现");
return obj.toString().toUpperCase();//改变了目标方法的返回值
}
}
4.最终通知@After
无论目标方法是否正常执行,最终通知都会被执行
@Aspect
@Component
public class MyAspect {
//最终通知方法的规范
// 访问权限是public
// 方法没有返回值
// 名称自定义
// 方法没有参数,如果有只能是JoinPoint
//value:指定切入点表达式
@After(value = "execution(* com.lv.s04.*.*(..))")
public void myAfter(){
//最终通知功能实现
System.out.println("最终通知的功能实现");
}
}
5.定义切入点名称@Pointcut
@Pointcut(value = "execution(* com.lv.s04.*.*(..))")
public void myPointcut(){}
AspectJ切入点表达式(重点):
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
* 代表任意字符
.. 如果出现在方法的参数中代表任意参数,如果出现在路径中代表本路径及其所有的子路径
execution(public * *(..)) 任意公共方法
execution(* set*(..)) 任意一个set开始的方法
execution(* com.xyz.service.impl.*.*(..)) 任意方法返回值,在com.xyz.service.impl的任意类里面的任意方法的任意参数
execution(* com.xyz.service..*.*(..)) 任意方法返回值,在com.xyz.service包下任意路径的任意类里面的任意方法的任意参数
execution(* *..service.*.*(..)) 任意方法返回值,任意包下的任意路径的service包里的任意类里面的任意方法的任意参数
execution(* *.service.*.*(..)) 任意方法返回值,任意 一级包下的service包里的任意类里面的任意方法的任意参数
AspectJ框架切换JDK动态代理和CGLib动态代理:
JDK动态代理:<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 只能用接口
CGlib:<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 可以用接口和实现类
用接口来接不会出错
SM整合项目重点
applicationContext_mapper.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"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--读取jdbc属性文件-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--创建数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置SqlSessionFactoryBean类-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--配置Mybatis的核心配置文件-->
<property name="configLocation" value="SqlMapConfig.xml"></property>
<!--注册实体类的别名-->
<property name="typeAliasesPackage" value="com.lv.pojo"></property>
</bean>
<!--注册mapper.xml文件MapperScannerConfigurer-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lv.mapper"></property>
</bean>
</beans>
applicationContext_service.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"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--导入applicationContext_mapper.xml-->
<import resource="applicationContext_mapper.xml"></import>
<!--SM基于注解的开发,添加包扫描-->
<context:component-scan base-package="com.lv.service.impl"></context:component-scan>
<!--事务处理-->
<!--添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--事务必须关联数据库处理,需要配置数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--添加事务的注解驱动 后缀是tx-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
@Transactional注解参数详解
@Transactional(propagation = Propagation.REQUIRED,noRollbackForClassName = "ArithmeticException",timeout = -1,readOnly = false,isolation = Isolation.DEFAULT)
//propagation:事务的传播特性
//noRollbackForClassName:指定发生异常的名称不回滚,
//rollbackForClassName:指定发生什么异常必须回滚
//timeout:连接超时设置,默认-1表示永不超时
//readOnly:默认是false,如果是查询操作必须为true
//isolation = Isolation.DEFAULT:使用当前数据库的隔离级别
Spring的两种事务处理方式
1.注解式的事务
2.声明式事务(重点)
要求项目中的方法命名有规范
1.增加操作需要包含add,save,insert,set
2.更新操作需要包含update,change,modify
3.删除操作需要包含delete,drop,remove,clear
4.查询操作需要包含select,find,search,get
applicationContext_trans.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"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--此配置文件与applicationContext_service.xml功能一样,事务配置不同-->
<!--导入applicationContext_mapper.xml-->
<import resource="applicationContext_mapper.xml"></import>
<!--添加包扫描-->
<context:component-scan base-package="com.lv.service.impl"></context:component-scan>
<!--添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务切面 后缀tx-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*select*" read-only="true"/>
<tx:method name="*find*" read-only="true"/>
<tx:method name="*search*" read-only="true"/>
<tx:method name="*get*" read-only="true"/>
<tx:method name="*insert*" propagation="REQUIRED"/>
<tx:method name="*add*" propagation="REQUIRED"/>
<tx:method name="*save*" propagation="REQUIRED"/>
<tx:method name="*update*" propagation="REQUIRED"/>
<tx:method name="*change*" propagation="REQUIRED"/>
<tx:method name="*delete*" propagation="REQUIRED"/>
<tx:method name="*remove*" propagation="REQUIRED"/>
<tx:method name="*drop*" propagation="REQUIRED"/>
<tx:method name="*clear*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--绑定切面和切入点-->
<aop:config>
<aop:pointcut id="mycut" expression="execution(* com.lv.service.impl.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="myadvice" pointcut-ref="mycut"></aop:advisor>
</aop:config>
</beans>
Spring中事务的五大隔离级别
1.未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
2.提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
3.可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
4.串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
5.isolation = Isolation.DEFAULT:使用当前数据库的隔离级别
(重点MySQL的隔离级别是可重复读)
默认系统事务隔离级别是READ COMMITTED,也就是读已提交
MySQL:mysql默认的事务处理级别是’REPEATABLE-READ’,也就是可重复读
Oracle:oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。
事务管理器,项目所有事务必须添加到业务逻辑层!
如果使用Mybatis框架,必须使用DataSourceTransactionManager类完成处理
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--事务必须关联数据库处理,需要配置数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
Spring事务的传播特性
多个事务之间的合并,互斥等都可以通过设置事务的传播特性来解决
常用
PROPAGATION_REQUIRED:必被包含事务(增删改必用)
PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
不常用
PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
PROPAGATION_NESTED:嵌套事务