SPring介绍
IOC介绍(控制反转)
对象的创建交给外部容器来完成,这个就是控制反转,容器存在的必要性就是管理对象,spring中使用控制反转来实现对象在不同程序中使用。
容器的好处: 提供容器,来管理所有的对象,所有对象的创建,销毁也好都交给容器,谁适用对象,在容器中获取该对象的使用权,哪一个对象需要,就将使用的对象注入到该对象中,对于使用方而言,只需要使用即可,对象的生命周期的管理交给IOC容器进行管理。IOC容器负责实例化所有的组件,需要进行以来的对象都交给容器管理,IOC最简单的方式就是通过XML方式来管理。
<beans>
数据源交给容器管理
<bean id="datasource" class="DataSourceService" >
<bean id="userservice" class="UserService">
<property name="datasource" ref="datasource"/>
</bean>
...
</beans>
IOC思想
对象的创建交给外部容器完成,这个就是控制反转,spring中使用控制反转来实现对象在不同程序中的使用,控制反转解决对象处理的问题,将对象交给容器进行创建。
依赖注入(DI):dendendency injection;对象与对象之间的依赖问题即依赖注入 AOP->DI;Spring使用依赖注入来实现对象之间的依赖关系;在对象创建之后,对象的关系处理就是依赖注入
无论对象的创建,处理对象之间的依赖关系,对象创建的时间还有对象创建的数量,都可以在spring的IOC容器上配置对象的信息就好了
Spring管理对象
集成依赖
<!--spring的核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
spring配置文件
配置容器管理的对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
</beans>
创建实体类
创建需要容器管理的类
public class User {
private Integer id;
private String name;
}
将user对象交给容器管理
<!--使用bean标签类管理对象 id:属性标识对象,不可重复 class属性代表要管理的对象的全路径-->
<bean id="user" class="com.tulun.bean.User"/>
通过容器来获取User对象
//获取IOC容器,通过读取classpath路径下的spring的配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationcontext.xml");
//在IOC容器获取需要的对象实例
User user = (User) applicationContext.getBean("user");
总结:
- 创建一个spring的配置文件
- 对xml配置文件进行解析
- BeanFactory共厂类
- 在工厂类中通过反射创建Bean对象
Spring的IOC容器介绍
Spring IOC容器中接口的继承关系,ApplicationContext是BeanFactory子接口实现之一,BeanFactory是IOC容器定义的最底层的接口。ApplicationContext是高级实现之一,对BeanFactory的接口做了很多的扩展,大部分情况ApplicationContext使用
ApplicationContext接口常见的实现类:
ClassPathXmlApplicationContext:读取classpath中的资源
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationcontext.xml");
FileSystemXmlApplicationContext:读取系统指定的资源文件
FileSystemXmlApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("d://test.xml");
XmlWebApplicationContext:读取web环境下的资源文件
XmlWebApplicationContext applicationcontext = new XmlWebApplicationContext();
SPring IOC容器获取Bean对象的过程
Spring对Bean实例化方式
基于配置形式
bean的实例化基于配置的方式:
- 通过无参构造函数实例化
- 通过静态工厂方式实例化
- 通过普通工厂方式实例化
通过无参构造函数实例化
<!--通过无参构造来实例化Bean-->
<bean id="user" class="com.tulun.bean.User" scope="singleton"/>
spring容器对上面的类的实例化采用的是无参构造函数来实例化bean
注: 如果不指定无参构造函数,会生成一个默认的无参构造函数,如果指定了有参构造函数,(就不会生成默认的无参构造函数),就必须显性的写明一个无参构造函数
通过静态工厂方式
首先创建静态工厂类来获取User对象
public class StaticFactory {
public static User getUser(){
return new User();
}
}
spring容器管理配置如下:
<!--通过静态工厂类获取User对象-->
<bean id="user1" class="com.tulun.factory.StaticFactory" factory-method="getUser"/>
静态工厂方式获取对象是 class属性工厂类的全路径,factory-method属性指在工厂类中指定的方法来获取需要的对象
通过普通工厂方式实例化Bean
创建一个普通工厂来管理user
/**
* 普通工厂
*/
public class CommonFactory {
public User getUser(){
return new User();
}
}
spring容器配置如下:
<!--通过普通工厂类获取User对象-->
<!--先获取工厂实例-->
<bean id="factory" class="com.tulun.factory.CommonFactory"/>
<!--指定工厂类的方法-->
<bean id="user2" factory-bean="factory" factory-method="getUser"/>
首先需要获取工厂实例,通过普通工厂实例获取user对象时,factory-bean属性指的是容器管理的工厂对象,factory-method属性指的是工厂中获取user对象的方法
基于注解形式基于
注解形式比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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--开启扫描注解:指定的包路径下的类,会扫描的到类的方法,属性上-->
<context:component-scan base-package="com.tulun.bean"/>
<!--只扫描类中属性的注解(不建议使用)-->
<!--<context:annotation-config></context:annotation-config>-->
</beans>
在对应类上添加注解:
@Component("user")
//注解中user=XML配置形式中bean id属性
public class User {
private Integer id;
private String name;
}
注解:
- @Component:通用注解
- @Repository:对DAO层实现类进行标注
- @Service:对Service层业务逻辑层进行标注
- @Controller:对Controller的实现类进行标注
四个注解可以通用,功能一样,可以互换,使用不同注解主要是区分被注解的类处在不同的业务层,使逻辑更加清晰
SPring中依赖注入方式
bean的实例化说明的是IOC的问题,创建对象之后对象中的属性如何赋值问题,即依赖注入问题(DI)
- xml配置方式进行依赖注入
- 注解形式进行依赖注入
基于xml配置实现依赖注入
通过有参构造、通过setter方式
有参构造函数
user类必须提供有参构造函数
public class User {
private Integer id;
private String name;
/**
* 有参构造函数
*/
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
容器的配置如下:
<!--依赖注入:有参构造函数-->
<bean id="user3" class="com.tulun.bean.User">
<!--
注入属性
name属性:给定类的属性名称
value属性:对指定属性名称赋值 ,直接获取的是String类型的值
ref属性:对指定属性进行赋值 指定的是一个容器管理的对象
-->
<constructor-arg name="id" value="1" />
<constructor-arg name="name" value="cc"/>
<constructor-arg name="book" ref="book"/>
</bean>
通过有参构造函数完成依赖注入时,使用constructor-arg标签进行属性赋值
setter方式
在User类的属性具有setter的方法
public class User {
private Integer id;
private String name;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
容器中的配置信息如下:
<!--依赖注入:setter方式-->
<bean id="user4" class="com.tulun.bean.User">
<!--属性注入-->
<property name="id" value="2"/>
<property name="name" value="cc"/>
</bean>
setter方式的依赖注入:使用property标签进行赋值
包括自定义类型
自定义类型无论是set方法还是有参构造均可,注意,对自定义类型赋值时使用ref
<bean id="user3" class="com.tulun.bean.User">
<constructor-arg name="book" ref="book"/>
</bean>
注入集合类型、:数组、Set、List、Map的个形式集合都可以注入
处理集合存在以下标签:
以map类型为例,进行介绍
public class User {
private Integer id;
private String name;
private Map<String ,String> map; //map 类型属性
容器中的配置
<!--map集合类型-->
<bean id="user5" class="com.tulun.bean.User">
<!--属性注入-->
<property name="id" value="3"/>
<property name="name" value="cc"/>
<property name="map" >
<
<map >
<entry key="1" value="11"></entry>
<entry key="2" value="22"></entry>
<entry key="3" value="33"></entry>
</map>
</property>
</bean>
基于注解形式注入依赖
@Value(“3”)//注入普通类型属性
@Resource //注入对象类型
@Autowired //注入对象类型,默认是按照类型注解
依赖的注入都是在类的属性上
@Resource 和@Autowired的区别?
- @Resource是Java自己的注解,@Resource有两个属性是比较重要的,分是name和type;Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
- @Autowired是spring的注解,是spring2.5版本引入的,Autowired只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰。
SPring AOP介绍
本质上是针对方法的编程
https://www.yuque.com/docs/share/f69a585c-81d0-4ba6-922f-79f9ec54c5c7?#
密码:nggv
SPring事务
事务:逻辑上的一组操作,要么全部成功,要么全部失败
SPring中的事务管理
1、spring针对不同的dao层框架,提供接口不同的实现类
Spring为不同的持久层框架提供不同的PlatformTransactionManager接口实现
事物的隔离级别就是用来解决以上问题:和Mysql中的隔离级别相同
Mysql数据库用的是REPEATABLE_READ隔离级别,定义事物传播行为:
Spring中的事务管理
编程式的事务管理
使用xml配置声明式事务
- 基于xml配置文件实现
- 基于注解形式实现
基于xml事务管理
<!--第一步:配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步:配置事务增强-->
<td:advice id = "txAdvice" transaction-manager="transactionManager">
<!--事务操作-->
<td:attributes>
<!--
设置事务操作方法匹配规则
add*:指定方法名
propagation;事务传播行为
-->
<td:method name="add*" propagation="REQUIRED"/>
</td:attributes>
</td:advice>
<!--第三步:配置切面-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointrcout" expression="execution(* com.tulun.bean.Student.*(..))"/>
<!--切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointrcout"/>
</aop:config>
基于注解事务管理
<!--第一步:配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步:开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
代码上添加注解:
@Transactional
public class Student {
@Transactional
public void addStudent(){
//插入数据
//修改数据
//查询操作
System.out.println("Student.addStudent");
}
}
Spring事务传播行为Spring
在 TransactionDefinition 接口中规定了 7 种类型的事务传播行为。事务传播行为是 Spring 框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是 Spring 为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。但是人们对他的误解也颇多,你一定也听过“service 方法事务最好不要嵌套”的传言。要想正确的使用工具首先需要了解工具。这里对七种事务传播行为做详细介绍,内容主要代码示例的方式呈现。
事务传播行为介绍
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
伪代码:
public void methodA(){
methodB();
//doSomething
}
@Transaction(Propagation=XXX)
public void methodB(){
//doSomething
}
代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transaction(Propagation=XXX)设置决定。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。
Spring中7种事务传播行为
代码验证
文中代码以传统三层结构中两层呈现,即 Service 和 Dao 层,由 Spring 负责依赖注入和注解式事务管理,DAO 层由 Mybatis 实现,你也可以使用任何喜欢的方式,例如,Hibernate,JPA,JDBCTemplate 等。数据库使用的是 MySQL 数据库,你也可以使用任何支持事务的数据库,并不会影响验证结果。
首先我们在数据库中创建两张表:
PROPAGATION_REQUIRED
我们为 User1Service 和 User2Service 相应方法加上Propagation.REQUIRED属性
@Service
public class User1ServiceImpl implements User1Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequired(User1 user){
user1Mapper.insert(user);
}
}
@Service
public class User2ServiceImpl implements User2Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequired(User2 user){
user2Mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequiredException(User2 user){
user2Mapper.insert(user);
throw new RuntimeException();
}
}
场景1:外围方法没有开启事务
验证1:
@Override
public void notransaction_exception_required_required(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequired(user2);
throw new RuntimeException();
}
验证2:
@Override
public void notransaction_required_required_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiredException(user2);
}
执行结果:
结论:通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
场景2:外围方法开启事务(使用率高)
验证1:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_required(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequired(user2);
throw new RuntimeException();
}
验证2:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_required_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiredException(user2);
}
验证3:
@Transactional
@Override
public void transaction_required_required_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
try {
user2Service.addRequiredException(user2);
} catch (Exception e) {
System.out.println("方法回滚");
}
}
执行结果:
结论:在外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
REQUIRED:
如果当前存在事务,则使用当前的事务,如果不存在事务,则新建一个事务
1、如果外部方法没有开启事务,Propagation.REQUIRED修饰的内部的方法会开启自己的事务,且开启的事务是相互独立的,互不影响
2、如果外部方法开始事务并且是Propagation.REQUIRED修饰,Propagation.REQUIRED修饰的内部方法和外部方法属于同一个事务,只要一个方法回滚,整个事务都回滚
扩展
https://www.yuque.com/docs/share/af71facb-1c6d-4035-823b-7b2ab4adda34?# 密码:fgyq