spring

spring是一个轻量级控制反转和面向切面的容器框架。 https://repo.maven.apache.org/maven2/org/springframework/spring/ 查看最新版本的

简介

  • 2002年,首次退出Spring框架的雏形-interface21框架;

  • 2004年3月24日,发布了1.0正式版本;

  • Rod Johnson,spring Framework创始人,他是悉尼大学的博士,但他的专业是音乐学;

  • spring理念:使现有的技术更加容易使用,本身就是一个大杂烩,整合了现有的技术框架。

  • SSH:Spring + Struts2 + Hibernate

  • SSM:Spring +SpringMVC + Mybatis

官网:https://spring.io/projects/spring-framework#overview

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.9</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
</dependency>

优点

  • spring是一个开源的免费框架(容器)
  • spring是一个轻量级的、非入侵式的框架
  • 控制反转和面向切面
  • 支持事务的处理,对框架整合的支持

组成

alt

IOC推导

//最初由程序自己new对象,但随着需求越来越多,dao实例也越来越多,直接new的方式弊端显现,需要改动代码,代码量越大,成本越高
private UserDao userDao = new UserDaoImpl();
public void getUser() {
    userDao.getUser();
}
//使用set接口实现,利用set进行动态实现值的注入
private UserDao userDao;  
public void setUserDao(UserDao userDao){
    this.userDao=userDao;
}
public void getUser() {
    userDao.getUser();
}
  • 之前,程序是主动创建对象的!控制权在程序员手中
  • 使用了set注入后,程序不再具有主动性,而是变成被动的接受对象!

这种思想,从本质上解决问题,程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务实现上!这就是IOC的原型!

IOC本质

控制反转IoC(Inversion of Control),是一种思想,DI(依赖注入)实现IoC的一种方式。没有Ioc的程序中,我们使用的是面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建交给第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了。

控制反转是一种通过描述(XML或者注解)并通过第三方去生产或获取特定对象的方式。在spring中实现控制反转的是IoC容器,其实现方法就是依赖注入(Dependency Injection)。

springIoc案例

<?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">

    <bean id="userDao" class="com.kaka.dao.impl.UserDaoImpl"></bean>
    <bean id="userMysqlDao" class="com.kaka.dao.impl.UserMysqlDaoImpl"></bean>
    <bean id="userService" class="com.kaka.service.impl.UserServiceImpl">
        <property name="userDao" ref="userMysqlDao"></property>
    </bean>
</beans>
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserServiceImpl userService = (UserServiceImpl)context.getBean("userService");
        userService.getUser();
    }
}

如上,交给spring管理之后,甚至代码都不需要改动,将xml文件中的引用改动一下即可实现不同的操作。需要扩展新的dao,只需要新增dao类,注册到xml文件中,修改ref即可。

Ioc的创建对象方式

  1. 无参构造,默认的方式
<bean id="user" class="com.kaka.pojo.User">
    <property name="name" value="kaka" />
</bean>
  1. 有参构造
<!-- 下标赋值 -->
<bean id="user" class="com.kaka.pojo.User">
    <constructor-arg index="0" value="kaka" />
</bean>

<!-- 类型赋值 不推荐,多个参数相同类型的话不可以-->
<bean id="user" class="com.kaka.pojo.User">
    <constructor-arg type="java.lang.String" value="kaka" />
</bean>

<!-- 参数名赋值 -->
<bean id="user" class="com.kaka.pojo.User">
    <constructor-arg name="name" value="kaka" />
</bean>

spring 配置

<!-- ##别名 -->
<alias name="user" alias="kaka"/>

<!-- ##name也是别名,而且可以通过, 空格  分号取多个别名 -->
<bean id="user" class="com.kaka.pojo.User" name="kaka,jsj,hkjh">
    <property name="name" value="kaka" />
</bean>

<!-- 导入其他配置文件 -->
<import resource="beans.xml"/>

依赖注入

构造器注入

参见创建对象方式

set方式注入【*】

@Data
public class Student {
    private String name;
    private User friend;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private Properties info;
}
    <bean id="user" class="com.kaka.pojo.User">
        <constructor-arg index="0" value="kaka" />
    </bean>
    <bean id="student" class="com.kaka.pojo.Student">
        <!-- 普通方式注入  value -->
        <property name="name" value="卡卡罗特"/>
        <!-- Bean方式注入  ref -->
        <property name="friend" ref="user" />
		<!-- Array方式注入  value -->
        <property name="books">
            <array>
                <value>西游记</value>
                <value>东游记</value>
                <value>三国演义</value>
            </array>
        </property>
        <!-- List方式注入  value -->
        <property name="hobbys">
            <list>
                <value>唱</value>
                <value>跳</value>
                <value>rap</value>
            </list>
        </property>
		<!-- map方式注入  value -->
        <property name="card">
            <map>
                <entry key="身份证" value="3665568556556" />
            </map>
        </property>
        <!-- Set方式注入  value -->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CS</value>
            </set>
        </property>
        <!-- Properties方式注入  value -->
        <property name="info">
            <props>
                <prop key="学号" >41221214</prop>
                <prop key="姓名" >root</prop>
            </props>
        </property>
        <!--null注入  value -->
        <property name="wife">
            <null />
        </property>
    </bean>

拓展方式注入

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- p标签注入 须引入p约束 -->
    <bean id="user2" class="com.kaka.pojo.User" p:name="kaka" />
    <!-- c标签注入 须引入c约束 -->
    <bean id="user2" class="com.kaka.pojo.User" c:name="kaka" />
    <!-- 上述两种就相当于构造器注入和set注入的简写模式 -->
</beans>

bean的作用域scope

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
  1. 单例模式-spring默认的机制;
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
  1. 原型模式:每次从容器中get的时候,都会产生一个新的对象;
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

Bean的自动装配

  • 自动装配是Spring满足bean依赖一种方式
  • spring会在上下文中自动寻找,自动给bean装配属性

在spring中有两种方式装配:

  1. 在xml中显示配置
<bean id="student2" class="com.kaka.pojo.Student" autowire="byName"/>
##其中,有以下几个参数
<xsd:enumeration value="default"/>
<xsd:enumeration value="no"/>
<xsd:enumeration value="byName"/>	#名称要对应
<xsd:enumeration value="byType"/>   #类型全局唯一
<xsd:enumeration value="constructor"/>

byName:需要保证所有bean的id唯一,并且id和自动注入的属性的set方法的值一致;
byName:需要保证所有bean的class唯一,并且和自动注入的属性的类型值一致;
  1. 在java中显示配置(使用注解)
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 开启注解  导入aop的包-->
    <context:annotation-config/>
    <context:component-scan base-package="com.kaka" />
    
    
    <bean id="user" class="com.kaka.pojo.User" />
	<bean id="student" class="com.kaka.pojo.Student" />
</beans>
@Autowired
private User user;

@Autowired(required = false)  //后面加参数,false表示可以为null,不是必输项
//可以配合 @Qualifier("user1")使用
//可以直接在属性上和set方法上使用,属性上使用之后set方法也可以不要
    
//@Autowired和@Resource区别
1、@Autowired注解是Spring提供的,而@Resource注解是J2EE本身提供的
2、@Autowird注解默认通过byType方式注入,而@Resource注解默认通过byName方式注入
3、@Autowired注解注入的对象需要在IOC容器中存在,否则需要加上属性required=false,表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错

 	@Resource还有两个重要的属性:name和type,用来显式指定byName和byType方式注入

代理模式

为什么要代理模式?因为这就是SpringAOP的底层

静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理后会做一些附属操作
  • 客户:访问代理对象

好处:

  • 可以使真实角色的操作更加纯粹,不用关注一些公共的业务!
  • 公共业务交给代理角色!实现业务上的分工!
  • 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率变低!!!!

动态代理

  • 动态代理和静态代理角色是一样的
  • 动态代理的代理类是动态生成的,不是由我们直接写好的
  • 动态代理分为两类:
    • 基于接口的-------JDK动态代理(以此为例子)
    • 基于类的----------cglib
    • Java字节码实现--------javasist ===》jboss服务器
//接口
public interface RentO {
    void rent();
}    
//被代理类
public class Rent implements RentO{

    public void rent(){
        System.out.println("我有房出租!!!");
    }
}
//动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object obj;
    public void setObject(Object obj) {
        this.obj = obj;
    }
    //生成得到代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
    }
    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args)
        //动态代理的本质就是通过反射机制实现
        Object result = method.invoke(obj, args);
        return result;
    }
}

//调用
Rent rent = new Rent();
//代理处理类创建实例
ProxyInvocationHandler proxy = new ProxyInvocationHandler();
//设置被代理对象
proxy.setObject(rent);
//生成代理对象
RentO proxyRent = (RentO)proxy.getProxy();
//代理对象执行业务
proxyRent.rent();

AOP

AOP在srping中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关的,但是需要我们关注的部分就是横切关注点,譬如日志、安全、缓存、事务等;
  • 切面(ASPECT):横切关注点被模块化的特殊对象,也就是说它是一个类;
  • 通知(ADVICE):切面必须要完成的工作,类中的一个方法;
  • 目标:被通知对象;
  • 代理(Proxy):项目对象应用通知之后创建的对象;
  • 切入点(pointcut):切面通知 执行的地点定义;
  • 连接点(joinpoint):与切入点匹配的执行点;

使用spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

方式一:使用spring的API接口【springAIP的接口实现】

public class BeforeLog implements MethodBeforeAdvice {
    //method 要执行的目标对象的方法
    //args 参数
    //target 目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"开始执行["+method.getName()+"]方法!");
    }
}

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的["+method.getName()+"]方法执行完毕,返回值为:"+returnValue);
    }
}

public interface UserService {
    void add();
    void delete();
    void update();
    void list();
}

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("新增了一个用户");
    }
    public void delete() {
        System.out.println("删除了一个用户");
    }
    public void update() {
        System.out.println("更新了一个用户");
    }
    public void list() {
        System.out.println("查询了一个用户");
    }
}
 <!-- 注册bean-->
    <bean id="userService" class="com.kaka.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.kaka.aop.BeforeLog"/>
    <bean id="afterLog" class="com.kaka.aop.AfterLog"/>

    <!-- 配置aop,需导入aop约束-->
    <aop:config>
        <!-- 切入点  表达式:execution(要执行的位置!  * * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.kaka.service.UserServiceImpl.*(..))"/>
        <!-- 执行增强-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

测试结果

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理的是接口,注意点
UserService userService = context.getBean("userService", UserService.class);//这里只能用接口,,用实现类会报错
userService.add();

----------------------------------------------------------------------------
com.kaka.service.UserServiceImpl开始执行[add]方法!
新增了一个用户
com.kaka.service.UserServiceImpl的[add]方法执行完毕,返回值为:null
Process finished with exit code 0

方式二:自定义类实现【主要是切面的定义】

public class DiyPointcut {
    public void before(){
        System.out.println("===========方法执行前================");
    }
    public void after(){
        System.out.println("===========方法执行后================");
    }
}
public interface UserService {
    void add();
    void delete();
    void update();
    void list();
}

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("新增了一个用户");
    }
    public void delete() {
        System.out.println("删除了一个用户");
    }
    public void update() {
        System.out.println("更新了一个用户");
    }
    public void list() {
        System.out.println("查询了一个用户");
    }
}
<bean id="diyPointcut" class="com.kaka.zdy.DiyPointcut"/>
<aop:config>
    <aop:aspect ref="diyPointcut">
        <aop:pointcut id="point" expression="execution(* com.kaka.service.UserServiceImpl.*(..))"/>
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

测试

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理的是接口,注意点
UserService userService = context.getBean("userService", UserService.class);//这里只能用接口,,用实现类会报错
userService.add();
----------------------------------------------------------------------------
===========方法执行前================
新增了一个用户
===========方法执行后================

方式三:注解实现

<!---开启注解支持---->
<context:annotation-config/>
<context:component-scan base-package="com.kaka" />
<aop:aspectj-autoproxy/>
@Aspect
@Component
public class AnnotationPointcut {
    @Before("execution(* com.kaka..*.*(..))")
    public void before(){
        System.out.println("卧槽无情");
    }  
    @Around("execution(* com.kaka..*.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        System.out.println(jp.getSignature());//获得签名
        jp.proceed();
        System.out.println("环绕后");
    }
}

测试结果

com.kaka.service.UserServiceImpl开始执行[add]方法!
===========方法执行前================
环绕前
void com.kaka.service.UserService.add()
卧槽无情
新增了一个用户
环绕后
===========方法执行h后================
com.kaka.service.UserServiceImpl的[add]方法执行完毕,返回值为:null

注意:

<!-- 代理方式 :默认 JDK动态代理 proxy-target-class="false",cglib:proxy-target-class="true"--> 
<aop:aspectj-autoproxy proxy-target-class="false"/>

执行顺序可以参考上面的执行结果

#springApi > 自定义切面 > 注解
#before > 环绕

整合mybatis

原始的使用mybatis

导入依赖

  • junit
  • mybatis
  • mysql数据库
  • spring相关的
  • aop织入
  • mybatis-spring
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.22</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.9</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
</dependency>

配置文件

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.kaka.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!---  新版本驱动发生变化 com.mysql.jdbc.Driver已弃用 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>   
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
	<!---  注册mapper.xml -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kaka.mapper.UserMapper">
    <select id="getAll" resultType="com.kaka.pojo.User">
        select * from `user`
    </select>
</mapper>
public interface UserMapper {
    public List<User> getAll();
}

//测试
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession(true);//自动提交事务

UserMapper mapper = session.getMapper(UserMapper.class);
List<User> all = mapper.getAll();
System.out.println(all);

整合mybatis方式一

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!--  DataSource :使用spring的数据源替换mybatis的配置,C3p0  dbcp  druid -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!--  sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--  绑定配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>
    <!--  SqlSessionTemplate就是我们使用的sqlSession -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--  只能使用构造器注入,因为源码中没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    <bean id="userMapper" class="com.kaka.mapper.UserMapperTemplate">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>
public class UserMapperTemplate implements UserMapper{
    //SqlSessionTemplate完全可以替换SqlSession 而且是线程安全的
    private SqlSessionTemplate sqlSession;
    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }
    public List<User> getAll() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> all = mapper.getAll();
        return all;
    }
}
//测试
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapperTemplate userMapper = context.getBean("userMapper", UserMapperTemplate.class);
System.out.println(userMapper.getAll());

整合mybatis方式二

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    public List<User> getAll() {
        return getSqlSession().getMapper(UserMapper.class).getAll();
    }
}
<bean id="userMapper2" class="com.kaka.mapper.UserMapperImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

事务

  • 要么都成功,要么都失败
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性

事务的ACID原则

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源,防止数据损坏
  • 持久性
    • 事务一旦提交,无论系统发生了什么问题,结果都不会再被影响,被持久化的写到存储器中!

spring事务

  • 声明式事务:aop横切进去
  • 编程式事务:利用注解细粒度手动开启事务
<!-- 配置声明式事务 -->
<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="add" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="find" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* com.kaka..*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

事务的传播特性

1: PROPAGATION_REQUIRED
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,
ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚

2: PROPAGATION_SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行


3: PROPAGATION_MANDATORY
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

4: PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,
那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,
他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在
两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,
如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

5: PROPAGATION_NOT_SUPPORTED
当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

6: PROPAGATION_NEVER
不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
那么ServiceB.methodB就要抛出异常了。

7: PROPAGATION_NESTED
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。
*****************************************
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
try {
//savepoint
ServiceB.methodB(); //PROPAGATION_NESTED 级别
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}

}
********************************************
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如
ServiceC.methodC,继续执行,来尝试完成自己的事务。
但是这个事务并没有在EJB标准中定义。

Spring常用注解

@Autowired:自动装配,通过type   name
@Qualifier:和autowired联合使用,作用跟@Resource类似
@Nullable 字段标记这个注解表示这个字段可以为null

@Component:表明组件,下面是该注解的三个衍生注解,都表示将类注册到spring
	@Repository
	@Service
	@Controller
	
	----@Configuration  表明这是个配置类,相当于一个xml
		----@Bean 修饰方法,返回值交给spring管理
	----@ComponentScan("com.kaka")   包扫描
	----@Import(secondConfig.class) 引入其他配置类

@Scope("prototype")   作用域

以上内容如有错误之处,欢迎指正!!!

学习源为:https://www.bilibili.com/video/BV1WE411d7Dv?p=28&spm_id_from=333.880.my_history.page.click