首页 > 其他分享 >Spring

Spring

时间:2022-10-28 22:00:07浏览次数:64  
标签:Spring void kiang class com public

Spring

Spring简介

Spring是什么?

Spring是分层的Java SE/EE应用 full-stack 轻量级开源框架,以IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。

提供了展现层SpringMVC和持久层 Spring JDBCTemplate 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。

Spring发展历程

  • 1997年,IBM提出了EJB的思想
  • 1998年,SUN制定开发标准规范EJB1.01999年,EJB1.1发布
  • 2001年,EJB2.0发布2003年,EJB2.1发布2006年,EJB3.0发布

Rod Johnson(Spring 之父)

  • Expert One-to-One J2EE Design and Development(2002):阐述了J2EE使用EJB开发设计的优点及解决方案
  • Expert One-to-One J2EE Development without EJB(2004) 阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
  • 2017年9月份发布了Spring 的最新版本 Spring5.0 通用版(GA)

Spring的优势

  1. 方便解耦,简化开发

    通过Spring 提供的loC容器,可以将对象间的依赖关系交由Spring 进行控制,避免硬编码所造成的过度耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

  2. AOP编程的支持

    通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现。

  3. 声明式事务的支持

    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。

  4. 方便程序的测试

    可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

  5. 方便集成各种优秀框架

    Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。

  6. 降低JavaEE API的使用难度

    Spring对 JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。

  7. Java 源码是经典学习范例

    Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。

Spring体系结构

023

Spring快速入门

Spring程序开发步骤

  1. 导入Spring 开发的基本包坐标
  2. 编写Dao接口和实现类
  3. 创建 Spring 核心配置文件
  4. 在Spring 配置文件中配置 UserDaolmpl
  5. 使用Spring的API获得Bean实例

1.导入Spring 开发的基本包坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

2.编写Dao接口和实现类

package com.kiang.dao;

public interface UserDao {
    public void save();
}
package com.kiang.dao.impl;

import com.kiang.dao.UserDao;

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("save running......");
    }
}

3.创建 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.xsd">

</beans>

4.在Spring 配置文件中配置 UserDaolmpl

<bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>

5.使用Spring的API获得Bean实例

package com.kiang.demo;

import com.kiang.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserDaoDemo {
    public static void main(String[] args)
    {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao)app.getBean("userDao");
        userDao.save();
    }
}

Spring配置文件

Bean标签基本配置

用于配置对象交由Spring来创建。

默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。基本属性:

  • id:Bean实例在Spring容器中的唯一标识
  • class:Bean的全限定名称

Bean标签范围配置

scope:指对象的作用范围,取值如下:

  • singleton:默认值,单例的
    • Bean的实例化个数:1个
    • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
    • Bean的生命周期:
      • 对象创建:当应用加载,创建容器时,对象就被创建了
      • 对象运行:只要容器在,对象一直活着
      • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
  • prototype:多例的
    • Bean的实例化个数:多个
    • Bean的实例化时机:当调用getBean()方法时实例化Bean
      • 对象创建:当使用对象时,创建新的对象实例
      • 对象运行:只要对象在使用中,就一直活着
      • 对象销毁:当对象长时间不用时,被Java的垃圾回收器回收了
  • request:WEB 项目中,Spring 创建一个Bean的对象,将对象存入到 request 域中
  • session:WEB 项目中,Spring 创建一个Bean的对象,将对象存入到 session 域中
  • lobalSession:WEB 项目中,应用在Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于session

Bean生命周期配置

  • init-method:指定类中的初始化方法名称
  • destroy-method:指定类中销毁方法名称

Bean实例化三种方式

  • 无参构造方法实例化

    它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败

    <bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>
    
  • 工厂静态方法实例化

    静态方法无需实例化即可使用

    package com.kiang.factory;
    
    import com.kiang.dao.UserDao;
    import com.kiang.dao.impl.UserDaoImpl;
    
    public class StaticFactory {
        public static UserDao getUserDao() {
            return new UserDaoImpl();
        }
    }
    
    <bean id="userDao" class="com.kiang.factory.StaticFactory" factory-method="getUserDao"></bean>
    
  • 工厂实例方法实例化

    非静态方法需实例化再使用

    package com.kiang.factory;
    
    import com.kiang.dao.UserDao;
    import com.kiang.dao.impl.UserDaoImpl;
    
    public class DynamicFactory {
        public UserDao getUserDao() {
            return new UserDaoImpl();
        }
    }
    
    <bean id="factory" class="com.kiang.factory.DynamicFactory"></bean> <!--相当于实例化一个DynamicFactory对象-->
    <bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean>
    

Bean的依赖注入入门

  1. 创建UserService,UserService内部再调用UserDao的save()方法

    package com.kiang.service;
    
    public interface UserService {
        public void save() ;
    }
    
    package com.kiang.service.impL;
    
    import com.kiang.dao.UserDao;
    import com.kiang.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class UserServiceImpl implements UserService {
        @Override
        public void save() {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserDao userDao = (UserDao)app.getBean("userDao");
            userDao.save();
        }
    }
    
  2. 将UserServiceImpl的创建权交给Spring

    <bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>
    <bean id="userService" class="com.kiang.service.impL.UserServiceImpl"></bean>
    
  3. 从Spring容器中获得UserService进行操作

    package com.kiang.demo;
    
    import com.kiang.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class UserController {
        public static void main(String[] args)
        {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) app.getBean("userService");
            userService.save();
        }
    }
    

Bean的依赖注入分析

目前UserService实例和UserDao实例都存在与Spring容器中,当前的做法是在容器外部获得UserService 实例和UserDao实例,然后在程序中进行结合。

024

因为UserService和UserDao都在Spring容器中,而最终程序直接使用的是UserService,所以可以在Spring容器中,将UserDao设置到UserService内部。

025

Bean的依赖注入概念

依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。 IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

Bean的依赖注入方式

  • 构造方法
  • set方法

set方法注入

配置Spring容器调用set方法进行注入

<bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.kiang.service.impL.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>

P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下: 首先,需要引入P命名空间:

<?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:p="http://www.springframework.org/schema/p" <!-- 引入P命名空间-->
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>

    <bean id="userService" class="com.kiang.service.impL.UserServiceImpl" p:userDao-ref="userDao"></bean><!-- 修改注入方式 -->
</beans>

构造注入

创建有参构造

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserServiceImpl(UserDao userDao)
    {
        this.userDao = userDao;
    }
    public UserServiceImpl()
    {
    }

    public void save() {
        userDao.save();
    }
}

配置Spring容器调用有参构造时进行注入

<bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.kiang.service.impL.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

Bean的依赖注入的数据类型

上面的操作,都是注入的引用Bean,处了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。

注入数据的三种数据类型

  • 普通数据类型

    package com.kiang.dao.impl;
    
    import com.kiang.dao.UserDao;
    
    public class UserDaoImpl implements UserDao {
        private String username;
        private int age;
    
        public void setUsername(String username)
        {
            this.username = username;
        }
    
        public void setAge(int age)
        {
            this.age = age;
        }
    
        @Override
        public void save() {
            System.out.println(username + "======" + age);
            System.out.println("save running......");
        }
    }
    
    <bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl">
        <property name="username" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>
    
  • 引用数据类型

  • 集合数据类型

    package com.kiang.dao.impl;
    
    import com.kiang.dao.UserDao;
    import com.kiang.domain.User;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    
    public class UserDaoImpl implements UserDao {
        private List<String> strList;
        private Map<String, User> userMap;
        private Properties properties;
    
        public void setStrList(List<String> strList)
        {
            this.strList = strList;
        }
    
        public void setUserMap(Map<String, User> userMap)
        {
            this.userMap = userMap;
        }
    
        public void setProperties(Properties properties)
        {
            this.properties = properties;
        }
    
        @Override
        public void save() {
            System.out.println(strList);
            System.out.println(userMap);
            System.out.println(properties);
            System.out.println("save running......");
        }
    }
    
    
    <bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl">
        <property name="strList">
            <list>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>
        <property name="userMap">
            <map>
                <entry key="u1" value-ref="user1"></entry>
                <entry key="u2" value-ref="user2"></entry>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="p1">ppp1</prop>
                <prop key="p2">ppp2</prop>
                <prop key="p3">ppp3</prop>
            </props>
        </property>
    </bean>
    
    <bean id="user1" class="com.kiang.domain.User">
        <property name="name" value="tom"></property>
        <property name="addr" value="beijing"></property>
    </bean>
    <bean id="user2" class="com.kiang.domain.User">
        <property name="name" value="lucy"></property>
        <property name="addr" value="shanghai"></property>
    </bean>
    

引入其他配置文件(分模块开发)

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他 配置文件中,而在Spring主配置文件通过import标签进行加载

<import resource="applicationContext-xxx.xml"/>

总结

Spring的重点配置

  • <bean>标签
    • id属性:在容器中Bean实例的唯一标识,不允许重复
    • class属性:要实例化的Bean的全限定名
    • scope属性:Bean的作用范围,常用是singleton(默认)和prototype
    • <property>标签:属性注入
      • name属性:属性名称
      • value属性:注入的普通属性值
      • ref属性:注入的对象引用值
      • <list>标签
      • <map>标签
      • <properties>标签
    • <constructor-arg>标签
  • <import>标签:导入其他的Spring的分文件

Spring相关API

ApplicationContext的继承体系

applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象。

026

ApplicationContext的实现类

  • ClassPathXmlApplicationContext

    它是从类的根路径下加载配置文件,推荐使用这种

  • FileSystemXmlApplicationContext

    它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

  • AnnotationConfigApplicationContext

    当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

getBean()方法使用

public Object getBean(String name) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}

public <T> T getBean(Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType);
}
ApplicationContext app = new ClasspathXmlApplicationContext("xml文件");
app.getBean("id");
app.getBean(Class);

其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。 当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时, 则此方法会报错。

Spring配置数据源

数据源(连接池)的作用

  • 数据源(连接池)是提高程序性能如出现的
  • 事先实例化数据源,初始化部分连接资源
  • 使用连接资源时从数据源中获取
  • 使用完毕后将连接资源归还给数据源

常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等

数据源的开发步骤

  1. 导入数据源的坐标和数据库驱动坐标
  2. 创建数据源对象
  3. 设置数据源的基本连接数据
  4. 使用数据源获取连接资源和归还连接资源

数据源的手动创建

导入c3p0和druid的坐标

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

导入mysql数据库驱动坐标

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>

注意自己数据库的版本,别傻乎乎的跟着教程忘了改版本

创建C3P0连接池

@Test
public void test1() throws PropertyVetoException, SQLException
{
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setDriverClass("com.mysql.jdbc.Driver");
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC");
    dataSource.setUser("root");
    dataSource.setPassword("102576");
    
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

不加 ?serverTimezone=UTC 时间配置还跑不了,焯

创建Druid连接池

public void test2() throws SQLException
{
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC");
    dataSource.setUsername("root");
    dataSource.setPassword("102576");
    
    DruidPooledConnection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

提取jdbc.properties配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC
jdbc.username=root
jdbc.password=102576

读取jdbc.properties配置文件创建连接池

@Test
public void test3() throws PropertyVetoException, SQLException
{
    ResourceBundle rb = ResourceBundle.getBundle("jdbc");

    String driver = rb.getString("jdbc.driver");
    String url = rb.getString("jdbc.url");
    String username = rb.getString("jdbc.username");
    String password = rb.getString("jdbc.password");

    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setDriverClass(driver);
    dataSource.setJdbcUrl(url);
    dataSource.setUser(username);
    dataSource.setPassword(password);

    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

Spring配置数据源

可以将DataSource的创建权交由Spring容器去完成

  • DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的
  • DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入
<?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="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value="102576"></property>
    </bean>
</beans>

测试从容器当中获取数据源

@Test
public void test4() throws PropertyVetoException, SQLException {
    ApplicationContext app =new ClassPathXmlApplicationContext("applicationContext.xml");
    DataSource dataSource = app.getBean(DataSource.class);
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

抽取jdbc配置文件

applicationContext.xml加载jdbc.properties配置文件获得连接信息。applicationContext.xml加载jdbc.properties配置文件获得连接信息。

首先,需要引入context命名空间和约束路径:

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

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
</beans>

Spring注解开发

Spring原始注解

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置 文件可以简化配置,提高开发效率。

Spring原始注解主要是替代的配置:

  • @Component:使用在类上,用于实例化Bean

    //<bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>
    @Component("userDao")
    public class UserDaoImpl implements UserDao {
        @Override
        public void save() {
            System.out.println("save running......");
        }
    }
    
  • @Controller:使用在web层类上,用于实例化Bean

  • @Service:使用在service层类上,用于实例化Bean

    //<bean id="userService" class="com.kiang.service.impl.UserServiceImpl">
    //@Component("userService")
    @Service("userService")
    public class UserServiceImpl implements UserService {
        //<property name="userDao" ref="userDao"></property>
        @Autowired
        @Qualifier("userDao")
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void save() {
            userDao.save();
        }
    }
    
  • @Repository:使用在dao层类上,用于实例化Bean

    //<bean id="userDao" class="com.kiang.dao.impl.UserDaoImpl"></bean>
    //@Component("userDao")
    @Repository("userDao")
    public class UserDaoImpl implements UserDao {
        @Override
        public void save() {
            System.out.println("save running......");
        }
    }
    
  • @Autowired:使用在字段上,用于根据类型依赖注入

  • @Qualifier :结合@Autowired一起使用,用于根据名称进行依赖注入

    //<bean id="userService" class="com.kiang.service.impl.UserServiceImpl">
    @Component("userService")
    public class UserServiceImpl implements UserService {
        //<property name="userDao" ref="userDao"></property>
        @Autowired
        @Qualifier("userDao")//实际上@Qualifier也可以不要,@Autowired会根据数据类型匹配注入
        private UserDao userDao;
    /*  @Autowired@Qualifier默认使用构造器注入,可以不用set方法
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    */
        @Override
        public void save() {
            userDao.save();
        }
    }
    
  • @Resource:相当于@Autowired+@Qualifier,按照名称进行注入

    //<bean id="userService" class="com.kiang.service.impl.UserServiceImpl">
    //@Component("userService")
    @Service("userService")
    public class UserServiceImpl implements UserService {
        //<property name="userDao" ref="userDao"></property>
    //    @Autowired
    //    @Qualifier("userDao")
        @Resource(name = "userDao") // 相当于@Autowired+@Qualifier
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void save() {
            userDao.save();
        }
    }
    
    • tipes:使用@Resource需要导包

      <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
          </dependency>
      
  • @Value:注入普通属性

    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        //<property name="driverClass" value="${jdbc.driver}"></property>
        @Value("${jdbc.driver}")
        private String driver;
        @Autowired
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void save() {
            System.out.println(driver);
            userDao.save();
        }
    }
    
  • @Scope:标注Bean的作用范围

    @Service("userService")
    //<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" scope="prototype">
    @Scope("prototype")
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        @Override
        public void save() {
            System.out.println(driver);
            userDao.save();
        }
    }
    
  • @PostConstruct:使用在方法上,标注该方法是Bean的初始化方法

    @Service("userService")
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        @Override
        public void save() {
            System.out.println(driver);
            userDao.save();
        }
    
        //<bean id="userService" class="com.kiang.service.impl.UserServiceImpl" init-method="init()">
        @PostConstruct
        public void init() {
            System.out.println("Service对象初始化...");
        }
    }
    
  • @PreDestroy:使用在方法上,标注该方法是Bean的销毁方法

    @Service("userService")
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        @Override
        public void save() {
            System.out.println(driver);
            userDao.save();
        }
    
        //<bean id="userService" class="com.kiang.service.impl.UserServiceImpl" destroy-method="destory()">
        @PreDestroy
        public void destory() {
            System.out.println("Service对象销毁...");
        }
    }
    
    • 这俩跟@Resource一样需要导包

Tips:使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean 需要进行扫描以便识别使用注解配置的类、字段和方法。

<context:component-scan base-package="com.kiang"/>

Spring新注解

使用上面的注解还不能全部替代xml配置文件,需要以下新注解:

  • @Configuration:用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解

    package com.kiang.config;
    
    import org.springframework.beans.factory.annotation.Configurable;
    
    // 标志该类是Spring核心配置类
    @Configurable
    public class Springconfiguration {
    
    }
    
  • @ComponentScan:用于指定 Spring 在初始化容器时要扫描的包。

    @Configurable
    // <context:component-scan base-package="com.kiang"/>
    @ComponentScan("com.kiang")
    public class Springconfiguration {
    
    }
    
  • @PropertySource:用于加载.properties 文件中的配置

    @Configurable
    @ComponentScan("com.kiang")
    //<context:property-placeholder location="classpath:jdbc.properties"/>
    @PropertySource("classpath:jdbc.properties")
    public class Springconfiguration {
    
    }
    
  • @Bean:使用在方法上,标注将该方法的返回值存储到 Spring 容器中

    @Configurable
    @ComponentScan("com.kiang")
    @PropertySource("classpath:jdbc.properties")
    public class SpringConfiguration {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        /*
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
            <property name="driverClass" value="${jdbc.driver}"></property>
            <property name="jdbcUrl" value="${jdbc.url}"></property>
            <property name="user" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
         */
        @Bean("dataSource")
        public DataSource getDataSource() throws PropertyVetoException {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    
    
  • @Import:用于导入其他配置类

    把关于数据源的挪到另一个配置类:

    package com.kiang.config;
    
    @PropertySource("classpath:jdbc.properties")
    public class DataSourceConfiguration {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        @Bean("dataSource")
        public DataSource getDataSource() throws PropertyVetoException {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    
    

    主配置类这样式儿的就行:

    package com.kiang.config;
    
    @Configurable
    @ComponentScan("com.kiang")
    //<import resource=""/>
    @Import(DataSourceConfiguration.class)//可以以列表的形式引入多个{ , , }
    public class SpringConfiguration {
    
    }
    

    测试类也改下:

    package com.kiang.web;
    
    public class UserController {
        public static void main(String[] args) {
    //        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
            UserService userService = app.getBean(UserService.class);
            userService.save();
        }
    }
    

Spring整合Junit

原始Junit测试Spring的问题

在测试类中,每个测试方法都有以下两行代码:

ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);

这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

解决上述问题可以:

  • 让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它
  • 将需要进行测试Bean直接在测试类中进行注入

Spring集成Junit步骤

  1. 导入spring集成Junit的坐标
  2. 使用@Runwith注解替换原来的运行期
  3. 使用@ContextConfiguration指定配置文件或配置类
  4. 使用@Autowired注入需要测试的对象
  5. 创建测试方法进行测试

导入spring集成Junit的坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

使用@Runwith注解替换原来的运行期

@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunitTest {
    
}

使用@ContextConfiguration指定配置文件或配置类

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {

}

使用@Autowired注入需要测试的对象

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {   
    @Autowired
    private UserService userService;
}

创建测试方法进行测试

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
    @Autowired
    private UserService userService;

    @Test
    public void test1() {
        userService.save();
    }
}

Spring与Web环境的集成

ApplicationContext应用上下文获取方式

Dao和Service与以前一样,只不过之前Web是空的,这次写个实的:

package com.kiang.web;

public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }
}

web.xml配置:

<servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>com.kiang.web.UserServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/UserServlet</url-pattern>
</servlet-mapping>

应用上下文对象是通过 new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但是每次从 容器中获得Bean时都要编写 new ClasspathXmlApplicationContext(spring配置文件) ,这样的弊端是配置文件加载多次,应用上下文对象创建多次。

在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域 中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

整个监听器:

package com.kiang.listener;

public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        ServletContext servletContext = servletContextEvent.getServletContext();
        servletContext.setAttribute("app", app);
        System.out.println("Spring容器创建完毕......");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

在web.xml里给配置上:

<listener>
    <listener-class>com.kiang.listener.ContextLoaderListener</listener-class>
</listener>

就可以用了:

package com.kiang.web;

public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//        req.getServletContext();
        ServletContext servletContext = this.getServletContext();
        ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }
}

Spring提供获取应用上下文的工具

上面的分析不用手动实现,Spring提供了一个监听器 ContextLoaderListener 就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到 ServletContext 域中,提供了一个客户端工具 WebApplicationContextUtils 供使用者获得应用上下文对象。

所以我们需要做的只有两件事:

  • 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
  • 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext

通过工具获得应用上下文对象

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>

配置ContextLoaderListener监听器

<!--全局参数-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Spring监听器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

通过工具获得应用上下文对象

package com.kiang.web;

public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }
}

面向切面编程AOP

Spring AOP 简介

什么是 AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍 生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,并且便于维护

AOP 的底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

AOP 的动态代理技术

常用的动态代理技术

  • JDK 代理 : 基于接口的动态代理技术

    065
  • cglib 代理:基于父类的动态代理技术

    066
JDK 的动态代理
目标类接口
package com.kiang.proxy.jdk;

public interface TargetInterface {
    public void save();
}
目标类
package com.kiang.proxy.jdk;

public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("saving running.......");
    }
}
动态代理代码
package com.kiang.proxy.jdk;

public class ProxyTest {
    public static void main(String[] args) {
        //目标对象
        Target target = new Target();
        //增强对象
        Advice advice = new Advice();
        // 返回值是动态生成的代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标对象类加载器
                target.getClass().getInterfaces(), // 目标对象相同的接口字节码对象数组
                new InvocationHandler() { // 调用代理对象的任何方法实质执行的都是invoke方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        advice.before();//前置增强
                        Object invoke = method.invoke(target, args);// 执行目标方法
                        advice.after();// 后置增强
                        return invoke;
                    }
                }
        );

        // 调用代理对象的方法
        proxy.save();
    }
}
cglib 的动态代理
目标类
package com.kiang.proxy.cglib;

public class Target {
    public void save() {
        System.out.println("saving running.......");
    }
}
动态代理代码
package com.kiang.proxy.cglib;

public class ProxyTest {
    public static void main(String[] args) {
        //目标对象
        Target target = new Target();
        //增强对象
        Advice advice = new Advice();
        // 返回值是动态生成的代理对象
        Enhancer enhancer = new Enhancer(); //创建增强器
        enhancer.setSuperclass(Target.class); //设置父类
        enhancer.setCallback(new MethodInterceptor() { //设置回调
            public Object intercept(Object o, Method method, Object[] objects,
                                    MethodProxy methodProxy) throws Throwable {
                advice.before();
                Object invoke = method.invoke(target, objects);
                advice.after();
                return invoke;
            }
        });
        Target proxy = (Target) enhancer.create(); //创建代理对象

        proxy.save();
    }
}

AOP 相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编 写,并通过配置的方式完成指定目标的方法增强。

常用的术语如下:

  • Target(目标对象):代理的目标对象
  • Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方 法类型的连接点
  • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
  • Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
  • Aspect(切面):是切入点和通知(引介)的结合
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而 AspectJ采用编译期织入和类装载期织入

AOP 开发明确的事项

需要编写的内容
  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
AOP 技术实现的内容

Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的 代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

AOP 底层使用哪种代理方式

在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

基于 XML 的 AOP 开发

快速入门

  1. 导入 AOP 相关坐标
  2. 创建目标接口和目标类(内部有切点)
  3. 创建切面类(内部有增强方法
  4. 将目标类和切面类的对象创建权交给 spring
  5. 在 applicationContext.xml 中配置织入关系
  6. 测试代码
导入 AOP 相关坐标
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.4</version>
</dependency>
创建目标接口和目标类(内部有切点)
package com.kiang.aop;

public interface TargetInterface {
    public void save();
}
package com.kiang.aop;

public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("saving running.......");
    }
}
创建切面类(内部有增强方法)
package com.kiang.aop;

public class MyAspect {
    public void before() {
        System.out.println("前置增强......");
    }
}
将目标类和切面类的对象创建权交给 spring
<!--目标对象-->
<bean id="target" class="com.kiang.aop.Target"/>

<!--切面对象-->
<bean id="myAspect" class="com.kiang.aop.MyAspect"/>
在 applicationContext.xml 中配置织入关系

导入aop命名空间

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

配置切点表达式和前置增强的织入关系

<!--组织织入,告诉spring框架哪些方法(切点)需要进行哪些增强-->
<aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
        <!--切面,切点,通知-->
        <aop:before method="before" pointcut="execution(public void com.kiang.aop.Target.save())"/>
     </aop:aspect>
</aop:config>
测试代码
package com.kiang.test;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1() {
        target.save();
    }
}

XML 配置 AOP 详解

aop织入的配置
<aop:config>
    <aop:aspect ref=“切面类”>
        <aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>
    </aop:aspect>
</aop:config>
切点表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 访问修饰符可以省略

  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意

  • 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类

  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

  • 例如:

    execution(public void com.itheima.aop.Target.method())
    execution(void com.itheima.aop.Target.*(..))
    execution(* com.itheima.aop.*.*(..))
    execution(* com.itheima.aop..*.*(..))
    execution(* *..*.*(..))
    
通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
  • 前置通知 <aop:before>:用于配置前置通知。指定增强的方法在切入点方法之前执行
  • 后置通知 <aop:after-returning>:用于配置后置通知。指定增强的方法在切入点方法之后执行
  • 环绕通知 <aop:around>:用于配置环绕通知。指定增强的方法在切入点方法之前和之后都 执行
  • 异常抛出通知 <aop:throwing>:用于配置异常抛出通知。指定增强的方法在出现异常时执行
  • 最终通知 <aop:after>:用于配置最终通知。无论增强方式执行是否有异常都会执行
切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽 取后的切点表达式。

<aop:config>
    <!--引用myAspect的Bean为切面对象-->
    <aop:aspect ref="myAspect">
        <aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
    </aop:aspect>
</aop:config>

基于注解的 AOP 开发

快速入门

基于注解的aop开发步骤:

  1. 创建目标接口和目标类(内部有切点)
  2. 创建切面类(内部有增强方法)
  3. 将目标类和切面类的对象创建权交给 spring
  4. 在切面类中使用注解配置织入关系
  5. 在配置文件中开启组件扫描和 AOP 的自动代理
  6. 测试
创建目标接口和目标类(内部有切点)
package com.kiang.anno;

public interface TargetInterface {
    public void save();
}

package com.kiang.anno;

public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("saving running.......");
    }
}

创建切面类(内部有增强方法)
package com.kiang.anno;

public class MyAspect {
    public void before() {
        System.out.println("前置增强......");
    }
}
将目标类和切面类的对象创建权交给 spring
@Component("target")
public class Target implements TargetInterface {
    //同上
}

@Component("myAspect")
public class MyAspect {
    //同上
}
在切面类中使用注解配置织入关系
@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面
public class MyAspect {
    //同上
}
在配置文件中开启组件扫描和 AOP 的自动代理
<!--组件扫描-->
<context:component-scan base-package="com.kiang.anno"/>
<!--aop的自动代理-->
<aop:aspectj-autoproxy/>
测试代码
package com.kiang.test;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1() {
        target.save();
    }
}

注解配置 AOP 详解

注解通知的类型

通知的配置语法:

@通知注解(“切点表达式")
  • 前置通知 @Before :用于配置前置通知。指定增强的方法在切入点方法之前执行
  • 后置通知 @AfterReturning :用于配置后置通知。指定增强的方法在切入点方法之后执行
  • 环绕通知 @Around :用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
  • 异常抛出通知 @AfterThrowing :用于配置异常抛出通知。指定增强的方法在出现异常时执行
  • 最终通知 @After :用于配置最终通知。无论增强方式执行是否有异常都会执行
切点表达式的抽取

同 xml 配置 aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut 注解定义切点表达式,然后在在增强注解中进行引用。具体如下:

@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面
public class MyAspect {

    @Before("MyAspect.myPoint()") // 配置前置通知
    public void before() {
        System.out.println("前置增强......");
    }
    @Pointcut("execution(* com.itheima.aop.*.*(..))")
	public void myPoint(){}

}

声明式事务控制

编程式事务控制相关对象

PlatformTransactionManager

PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。

  • 获取事务的状态信息:TransactionStatus getTransaction(TransactionDefination defination)
  • 提交事务:void commit(TransactionStatus status)
  • 回滚事务:void rollback(TransactionStatus status)

TransactionDefinition

TransactionDefinition 是事务的定义信息对象,里面有如下方法:

  • 获得事务的隔离级别:int getIsolationLevel()
  • 获得事务的传播行为:int getPropogationBehavior()
  • 获得超时时间:int getTimeout()
  • 是否只读:boolean isReadOnly()
事务隔离级别

设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。

  • ISOLATION_DEFAULT
  • ISOLATION_READ_UNCOMMITTED
  • ISOLATION_READ_COMMITTED
  • ISOLATION_REPEATABLE_READ
  • ISOLATION_SERIALIZABLE
事务传播行为
  • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
  • REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
  • NEVER:以非事务方式运行,如果当前存在事务,抛出异常
  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
  • 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
  • 是否只读:建议查询时设置为只读

TransactionStatus

  • 是否存储回滚点:boolean hasSavepoint()
  • 事务是否完成:boolean hasSavepoint()
  • 是否是新事务:boolean isNewTransaction()
  • 事务是否回滚:boolean isRollbackOnly()

基于 XML 的声明式事务控制

什么是声明式事务控制

Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明 ,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

声明式事务处理的作

  • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如 此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话, 也只需要在定义文件中重新配置即可
  • 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译 ,这样维护起来极其方便

声明式事务控制的实现

引入tx命名空间
<?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"
       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 http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
配置事务增强
<!--配置平台事务管理器-->
<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="*"/>
    </tx:attributes>
</tx:advice>
配置事务 AOP 织入
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.kiang.service.impl.*.*(..))"/>
</aop:config>
测试事务控制转账业务代码
package com.kiang.service.impl;

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        int i = 1/0; // 故意放个错
        accountDao.in(inMan,money);
    }
}

切点方法的事务参数的配置

<!--事务增强配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

其中, <tx:method>代表切点方法的事务参数的配置,例如:

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
  • name:切点方法名称
  • isolation:事务的隔离级别
  • propogation:事务的传播行为
  • timeout:超时时间
  • read-only:是否只读

基于注解的声明式事务控制

使用注解配置声明式事务控制

编写 AccoutDao
package com.kiang.dao.impl;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
    }

    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}
编写 AccoutService
package com.kiang.service.impl;

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        int i = 1/0;
        accountDao.in(inMan,money);
    }
}
编写 applicationContext.xml 配置文件
<context:component-scan base-package="com.kiang"/>
<tx:annotation-driven transaction-manager="transactionManager"/>

注解配置声明式事务控制解析

  • 使用 @Transactional 在需要进行事务控制的类或是方法上修饰,注解可用的属性同 xml 配置方式,例如隔离 级别、传播行为等。
  • 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。
  • 使用在方法上,不同的方法可以采用不同的事务参数配置。
  • Xml配置文件中要开启事务的注解驱动

标签:Spring,void,kiang,class,com,public
From: https://www.cnblogs.com/AncilunKiang/p/16837657.html

相关文章

  • Spring 注解开发
    使用Spring注解前的配置<?xmlversion="1.0"encoding="UTF8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/20......
  • 16. Spring的基本使用
    一、引入Spring的依赖  通过maven的方式导入jar包,打包方式设置为jar包即可。<!--https://mvnrepository.com/artifact/org.springframework/spring-context-->......
  • springboot启动默认连接数据库问题
    这里是给新手介绍的springboot默认启动是连接数据库的经常很多人蒙蔽,//无数据源启动@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,DataSo......
  • springboot像springnvc那样访问视图
    1.SpringBoot访问静态资源的位置(优先级按以下顺序)classpath默认就是resources,所以classpath:/static/就是resources/static/classpath:/META‐INF/resources/cl......
  • Spring Boot 学习笔记
    SpringBoot简介为什么要使用SpringBoot因为Spring,SpringMVC需要使用的大量的配置文件(xml文件)还需要配置各种对象,把使用的对象放入到spring容器中才能使用对象......
  • 【SpringBoot】引入mybatis及连接Mysql数据库
    创建一个SpringBoot项目其他不赘叙了,引入MyBaties、MySql依赖编辑 创建mysql表CREATETABLEsp_users(`id`INTPRIMARYKEY,`username`VARCHAR(30),`age`INT);刚......
  • jwt+token,springsecurity认证方式总结
    基于redis的认证方式分析redis解决短信验证码时效性,以及使用token的方式判断是否登录的问题。(没用jwt)这里面使用两个拦截器的方式解决:1.给token有效期刷新2.判断用户......
  • SpringBoot 没有为 Service 创建代理类的一个原因
    最近一个SpringBoot 项目,最开始需要在一个Service 中使用AopContext.currentProxy(),谁知返回一个空,导致空指针异常再跟踪发现,原来spring 并没有为这个Service 创......
  • spring boot+dubbo+zookeeper
    111TRANSLATEwithxEnglishArabicHebrewPolishBulgarianHindiPortugueseCatalanHmongDawRomanianChineseSimplifiedHungarianRussian......
  • SpringBoot 为登录设置拦截器
    在学习一个项目的过程中有这样一个需求就是用户未登录是不可以访问其他主页的所以我们有两种做法:使用拦截器或者过滤器来实现这样的功能,这里就通过过滤器来记录下学习心......