Spring02-Spring注解的使用、基于注解的IOC、纯注解配置、整合Junit、AOP入门、基于配置文件的AOP、切入点表达式、基于配置的文件环绕通知

这篇具有很好参考价值的文章主要介绍了Spring02-Spring注解的使用、基于注解的IOC、纯注解配置、整合Junit、AOP入门、基于配置文件的AOP、切入点表达式、基于配置的文件环绕通知。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Spring注解的使用

一、项目注解 和 XML 的选择问题

  • 学习基于注解的 IOC 配置,即注解配置 和 XML 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
  • 关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯 , 所以这两种配置方式我们都需要掌握。
  • 把 Spring 的 xml 配置内容改为使用注解逐步实现

二、用于创建对象的注解

1、注解介绍

  • @Component
  • @Repository
  • @Service
  • @Controller
注解 说明
@Component 使用在类上用于实例化Bean
@Controller 使用在web层类上用于实例化Bean
@Service 使用在service层类上用于实例化Bean
@Repository 使用在dao层类上用于实例化Bean

2、注意问题

  • 使用注解开发,需要在application.xml中配置组件扫描。
  • 主键扫描的作用:指定那个包及其子包的Bean需要扫描以便识别使用注解配置的类、字段和方法。
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <context:component-scan base-package="com.etime"></context:component-scan>
</beans>

3、编写示例代码

(1)使用@Component或@Repository标识UserDaoImpl需要Spring进行实例化。
  • UserDao接口
package com.etime.dao;
public interface UserDao {
    void show();
}
  • UserDaoImpl实现类

package com.etime.dao.impl;
package com.etime.dao.impl;

import com.etime.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

//@Component("ud")
@Repository("ud")
public class UserDaoImpl implements UserDao {
    @Override
    public void show() {
        System.out.println("这是userDao的方法");
    }
}
(2)使用@Component或@Service标识UserServiceImpl需要Spring进行实例化
  • UserService接口
package com.etime.service;

public interface UserService {
    void show();
}
  • UserServiceImpl
package com.etime.service.impl;

import com.etime.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {
    @Override
    public void show() {
        System.out.println("这是userService的方法");
    }
}
(3)测试
    @Test
    public void t07() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserDao userDao = (UserDao) context.getBean("ud");
        userDao.show();

        UserService userService = (UserService) context.getBean("us");
        userService.show();
    }

三、基于注入数据的注解

(1)注解介绍

  • @Value
  • @Resource
  • @Autowrited
  • Qualifier
注解 说明
@Value 注入普通属性
@Autowired 自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。 如果IOC容器当中有多个接口得实现类, 首先根据类型自动装配, 然后再根据名称自动装配。
@Qualifier 结合@Autowired一起使用用于根据名称进行依赖注入
@Resource 相当于@Autowired+@Qualifier,按照名称进行注入,是java提供的,不是框架提供的

(2)使用@Value进行字符串的注入

@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Value("字符串")
    private String str;
    public void addUser() {
        System.out.println(str);
        System.out.println("开始新增用户了");
    }
}

(3)使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入

第一种:
package com.etime.service.impl;

import com.etime.dao.UserDao;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public void show() {
        userDao.show();
    }
}

第二种:
package com.etime.service.impl;

import com.etime.dao.UserDao;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {

    @Autowired
    @Qualifier("ud")
    private UserDao userDao;

    @Override
    public void show() {
        userDao.show();
    }
}

第三种:
package com.etime.service.impl;

import com.etime.dao.UserDao;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

//@Component("us")
@Service("us")
public class UserServiceImpl implements UserService {

    @Resource(name = "ud")
    private UserDao userDao;

    @Override
    public void show() {
        userDao.show();
    }
}

(4)测试

   @Test
    public void t07() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        /*UserDao userDao = (UserDao) context.getBean("ud");
        userDao.show();*/
        UserService userService = (UserService) context.getBean("us");
        userService.show();
    }

四、和生命周期相关的注解

(1)注解介绍

  • @PostConstruct
  • @PreDestroy
注解 说明
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法

(2) 使用注解初始化方法、注销方法

  • 这两个注解是java提供的
package com.etime.dao.impl;

import com.etime.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//@Component("ud")
@Repository("ud")
public class UserDaoImpl implements UserDao {

    @PostConstruct
    public void init(){
        System.out.println("userDao被初始化了");
    }

    @Override
    public void show() {
        System.out.println("这是userDao的方法");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("userDao被销毁了");
    }
}

(3)测试

@Test
    public void t08() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserDao userDao = (UserDao) context.getBean("ud");
        userDao.show();
        context.close();
    }

五、用户改变作用范围的注解:@Scope

1、注解介绍

  • 使用@Scope标注Bean的范围
注解 说明
@Scope 标注Bean的作用范围,scope取值singleton prototype request session globalsession

Spring基于注解的IOC

1、 构建maven工程,添加框架技术依赖

<dependencies>
    <!--导入spring的context坐标-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!--导入Jdbc模块依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!--导入Mysql 驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <!--导入C3P0连接池-->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.2</version>
    </dependency>
    <!--导入junit单元测试-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2、创建数据库及数据表,并创建实体类

  • 实体类
package com.etime.entity;

public class User {

    private int id;
    private String name;
    private String gender;

    public User() {
    }

    public User(int id, String name, String gender) {
        this.id = id;
        this.name = name;
        this.gender = gender;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}

3、注解配置管理的资源

(1)Dao层

  • UseDaor接口
package com.etime.dao;
import com.etime.entity.User;
import java.util.List;
public interface UserDao {
    List<User> getAllUser();
}
  • USerDaoImpl实现类
package com.etime.dao.impl;
import com.etime.dao.UserDao;
import com.etime.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("ud")
public class UserDaoImpl implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<User> getAllUser() {
        List<User> list = null;
        String sql = "select * from user";
        list = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(User.class));
        return list;
    }
}

(2)Service层

  • UserService接口
package com.etime.service;
import com.etime.entity.User;
import java.util.List;
public interface UserService {
    List<User> getAllUser();
}

  • UseServiceImpl实现类
package com.etime.service.impl;
import com.etime.dao.UserDao;
import com.etime.entity.User;
import com.etime.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("us")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public List<User> getAllUser() {
        return userDao.getAllUser();
    }
}

4、配置文件并开启对注解的支持并配置

<?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:component-scan base-package="com.etime"/>

    <!--读取properties配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--JdbcTemplate对象的创建-->
    <bean id="jt" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="ds"></property>
    </bean>
</beans>

5、编写测试代码

    @Test
    public void t09() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = (UserService) context.getBean("us");
        List<User> list = userService.getAllUser();
        System.out.println(list);
    }

Spring纯注解配置

1、注解IOC案例待改造的问题

(1)问题引入

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

(2)注解替代的配置

  • 非自定义的Bean的配置:@Bean
  • 加载properties文件的配置:context:property-placeholder
  • 组件扫描的配置:context:component-scan
  • 引入其他文件:@import
  • 替代Spring核心配置文件:@Configuration,@ComponentScan,@Import

2、新注解说明

注解 说明
@Configuration 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解,作用等价于applicationContext.xml 配置文件。
@ComponentScan 用于指定 Spring在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package=“com.offcn”/>一样
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring容器中。 id的值默认是方法的名称, 可以自定义id的值
@PropertySource 用于加载xxx.properties 文件中的配置 结合@Value(“${}”) 取配置文件的值。
@Import 用于导入其他配置类

2、编写示例代码

(1)创建db.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=111

(2)@PropertySource,@value,@Bean的使用:

package com.etime.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

/**
 * 做数据库相关配置
 * 1、读取properties文件内容
 * 2、根据读取的内容生成连接池对象
 * 3、根据连接池对象获取JdbcTemplate对象
 */

@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
    @Value("${jdbc.driver}")
    private String driverClass;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    public DataSource getDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(url);
        ds.setUser(username);
        ds.setPassword(password);
        return ds;
    }

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate getJdbcTemplate() throws PropertyVetoException {
        return new JdbcTemplate(getDataSource());
    }
}

(3)@Configuration,@ComponentScan,@Import的使用

package com.etime.util;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(DataSourceConfig.class)
@ComponentScan("com.etime")
public class SpringConfig {
}

3、通过注解获取容器

    @Test
    public void t10() {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = (UserService) context.getBean("us");
        List<User> list = userService.getAllUser();
        System.out.println(list);
    }

Spring整合Junit

1、Spring整合Junit介绍

  • Spring整合Junit单元测试在学习阶段、开发阶段是经常使用的。
  • 如果开发过程当中完全不使用该技术点,不影响开发效果,可能会影响开发进度,为提高开发效率,避免重复性代码,Spring整合Junit单元测试务必要掌握,在使用Spring框架开发时,我们也推荐使用整合Junit后进行测试

2、IOC测试类中的问题和解决思路

(1)问题引出

  • 在测试类中,每个测试方法都有以下两行代码:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

(2)解决思路

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

3、注解介绍

注解 说明
@RunWith 替换原有运行器
@ContextConfiguration 指定 Spring 配置文件的位置
@Autowired 给测试类中的变量注入数据

4、 整合Junit配置步骤

(1) 添加技术依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
</dependency>

(2)使用注解完成容器的加载

@RunWith :替换原有运行器

@ContextConfiguration:指定 Spring 配置文件的位置

@Autowired:给测试类中的变量注入数据

package com.etime.test;

import com.etime.entity.Account;
import com.etime.dao.UserDao;
import com.etime.entity.User;
import com.etime.service.AccountService;
import com.etime.service.UserService;
import com.etime.util.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
//加载Spring的配置类,生成容器对象
@ContextConfiguration(classes = {SpringConfig.class})
public class SpringTest {

    @Autowired
    private ApplicationContext context;

    @Test
    public void t11() {
        UserService userService = (UserService) context.getBean("us");
        List<User> list = userService.getAllUser();
        System.out.println(list);
    }
}

SpringAOP入门

1、什么是 AOP

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

2、AOP编程思想

  • 面向切面编程(AOP)是一种编程的思想,是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是 函数式编程的一种衍生泛型。
  • 利用 AOP 可以对业务逻辑的各个部分进行隔离,,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • AOP 使用场景
    • 事务管理
    • 权限校验
    • 日志记录
    • 性能监控

3、SPring中的常用术语

(1)Joinpoint

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。
    • 在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

(2)Pointcut

  • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
  • 真正被增强的方法

(3)Advice

  • Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。

4、面试问题

(1)通知的类型

  • 前置通知,正常返回通知,异常通知,最终通知,环绕通知。

(2)Introduction

  • Introduction(引介,了解): 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。

(3)Target

  • Target(目标对象):代理的目标对象

(4)Proxy

  • Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类

(5)Weaving

  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。
  • spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

(5)Aspect

  • Aspect(切面):是切入点和通知(引介)的结合,描述了 增强具体应用的位置。

5、AOP 的作用及优势

(1)作用

  • 在程序运行期间,在不修改源码的情况下对方法进行功能增强。体现了java语言的动态性【反射】

(2)优势

  • 减少重复代码,提高开发效率,并且便于维护

Spring实现转账案例

一、实现步骤

1、搭建一个转账的业务环境,准备所需资源,数据库表以及实体类
2、持久层具体实现使用DBUtils 方便操作连接对象,管理事务
3、搭建业务层,完成对持久层的调用
4、演示转账测试用例,在没有遇到异常情况下成功转账,在转账过程当中遇到意外情况,损失了数据的一致性,从而引出事务。

二、编写代码示例

1、准备Account表以及对应的实体bean

public class Account implements Serializable {
    private Integer id;
    private String name;
    private double money;

    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;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

2、创建AccountDao接口以及对应的实现类

  • AccountDao接口
package com.etime.dao;

import com.etime.entity.Account;

public interface AccountDao {
    public Account getByName(String name);
    public int update(Account account);
}
  • AccountDaoImpl实现类
package com.etime.dao.impl;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.etime.dao.AccountDao;
import com.etime.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;

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

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Account getByName(String name) {
        String sql ="select *from account where name =? ";
        return jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Account>(Account.class),name);
    }

    @Override
    public int update(Account account) {
        String sql ="update account set money =? where name =? ";
        return jdbcTemplate.update(sql,account.getMoney(),account.getName());
    }
}

3、创建AccountService接口以及对应的实现类

package com.etime.service;

public interface AccountService {
    public void transfer(String sourceAccountName,String targetAccountName,double money);
}
package com.etime.service.impl;

import com.etime.dao.AccountDao;
import com.etime.dao.impl.AccountDaoImpl;
import com.etime.entity.Account;
import com.etime.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

    @Autowired
    private AccountDao accountDao;

    @Override
    public void transfer(String sourceAccountName, String targetAccountName, double money) {
        //收钱的
        Account tAccount = accountDao.getByName(targetAccountName);
        tAccount.setMoney(tAccount.getMoney()+money);
        int rows2 = accountDao.update(tAccount);
        System.out.println("收钱的:"+rows2);

        //给钱的
        Account sAccount = accountDao.getByName(sourceAccountName);
        sAccount.setMoney(sAccount.getMoney()-money);
        int rows1 = accountDao.update(sAccount);
        System.out.println("给钱的:"+rows1);
    }
}

4、测试转账业务

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfiguration.class})
public class SpringTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void t01(){
        AccountService us = (AccountService)applicationContext.getBean("accountService");
        us.transfer("aaa","bbb",500);
    }

5、测试没有事务正常情况和异常情况转账结果

  • 没有遇到异常情况,转账结果正常。aaa账户减少500元,bbb账户增加500元

  • 模拟异常情况转账结果aaa向bbb转账1000

6、编写ConnectionUtils工具类管理数据库连接

  • 转账过程,由于遇到异常情况,转账损失了数据的一致性,加入事务控制。

  • 事务要么同时成功,事务提交;要么都同时失败,事务回滚;要保证咋同一个事务当中进行操作,那么必须保证同一个连接对象。

  • 如何保证持久层和业务层使用同一个连接对象呢?

    • 引入ThreadLocal对象,该对象的特点,对象提供的存,取,移除的常用方法。
  • ConnectionUtils 工具类提供的管理连接的方法,就能保证持久层以及业务层获得相同的连接对象,保证在同一个事务当中进行操作文章来源地址https://www.toymoban.com/news/detail-437879.html

package com.etime.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;

@Component
public class ConnectionUtil {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 获取当前线程上的连接
     * @return
     */
    public Connection getThreadConnection() {
        try{
            //1.先从ThreadLocal上获取
            Connection connection = tl.get();
            //2.判断当前线程上是否有连接
            if (connection == null) {
                //3.从jdbcTemplate中获取一个连接,并且存入ThreadLocal中
                connection = jdbcTemplate.getDataSource().getConnection();
                tl.set(connection);
            }
            //4.返回当前线程上的连接
            return connection;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
    /**
     * 把连接和线程解绑
     */
    public void removeConnection(){
        tl.remove();
    }
}

7、编写事务管理工具类TransactionManager

  • TransactionManager 是管理事务的工具类,它主要定义了 和事务相关的方法。
    • 例如:例如:事务的开启,事务的提交,事务的回滚操作等
  • 该工具类目前需要手动创建和管理。
  • 使用Spring进行事务管理后,该工具类由Spring提供,不需要手动编写和管理。
  • 该工具类重在理解,为Spring进行事务管理做好铺垫
package com.etime.util;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class TransactionManager {

    @Autowired
    private ConnectionUtil connectionUtil;

    //开启事务
    public  void beginTransaction(){
        try {
            connectionUtil.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //提交事务
    public  void commit(){
        try {
            connectionUtil.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //回滚事务
    public  void rollback(){
        try {
            connectionUtil.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //释放资源
    public  void release(){
        try {
            connectionUtil.removeConnection();//当前线程解除绑定
            connectionUtil.getThreadConnection().close();//还回连接池中
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

8、编写业务层及数据访问层事务控制代码并配置Spring的IOC

(1)Dao层
package com.etime.dao.impl;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.etime.dao.AccountDao;
import com.etime.entity.Account;
import com.etime.util.ConnectionUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

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

    @Autowired
    private ConnectionUtil connectionUtil;

    @Autowired
    private QueryRunner queryRunner;

    @Override
    public Account getByName(String name) throws SQLException {
        Connection conn = connectionUtil.getThreadConnection();
        String sql ="select *from account where name =? ";
        return queryRunner.query(conn,sql,new BeanHandler<>(Account.class),name);
    }

    @Override
    public int update(Account account) throws SQLException {
        Connection conn = connectionUtil.getThreadConnection();
        String sql ="update account set money =? where name =? ";
        return queryRunner.update(conn,sql,account.getMoney(),account.getName());
    }
}
  • 数据访问层用到了QueryRunner,相关配置
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
  • SpringConfiguration.java 配置类将QueryRunner对象交给Spring进行管理
 @Bean
    public QueryRunner getQueryRunner(){
        return new QueryRunner();
    }
(2)Service层
package com.etime.service.impl;

import com.etime.dao.AccountDao;
import com.etime.entity.Account;
import com.etime.service.AccountService;
import com.etime.util.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private TransactionManager transactionManager;

    @Override
    public void transfer(String sourceAccountName, String targetAccountName, double money) {
        try{
            //开启事务
            transactionManager.beginTransaction();

            //收钱的
            Account tAccount = accountDao.getByName(targetAccountName);
            tAccount.setMoney(tAccount.getMoney()+money);
            int rows2 = accountDao.update(tAccount);
            System.out.println("收钱的:"+rows2);

            //给钱的
            Account sAccount = accountDao.getByName(sourceAccountName);
            sAccount.setMoney(sAccount.getMoney()-money);
            int rows1 = accountDao.update(sAccount);
            System.out.println("给钱的:"+rows1);

            //提交事务
            transactionManager.commit();
        }catch (Exception exception){
            //回滚事务
            transactionManager.rollback();
            exception.printStackTrace();
        }finally {
            //释放资源
            transactionManager.release();
        }
    }
}
(3)转账测试
package com.etime.test;

import com.etime.entity.User;
import com.etime.service.AccountService;
import com.etime.service.UserService;
import com.etime.service.impl.AccountServiceImpl;
import com.etime.util.DataSourceConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfiguration.class})
public class SpringTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void t01(){
        AccountService us = (AccountService)applicationContext.getBean("accountService");
        us.transfer("aaa","bbb",2000);
    }

}
  • 结果:正常转账成功,遇到异常情况, 事务进行回滚,保证了数据的一致性。

Spring基于配置文件的AOP

1、环境搭建

(1)构建maven工程添加依赖

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

(2)代码编写

  • 实体类、持久层、业务层使用Spring实现转账案例的代码

(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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath*:db.properties</value>
            </list>
        </property>
    </bean>
    <!--数据源对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--配置JdbcTemplate模板对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <context:component-scan base-package="com.etime"></context:component-scan>
    <bean id="transactionManager" class="com.etime.util.TransactionManager"></bean>
    
</beans>

2、AOP 配置步骤

(1)使用 aop:confing 声明 AOP 配置

  • 作用: 开始声明aop配置
<aop:config>
      <!-- 配置的代码都写在此处 -->
</aop:config>

(2) 使用 aop:aspect 配置切面

  • 作用: 用于配置切面
  • 属性
    • id :给切面提供一个唯一标识。
    • ref:引用配置好的通知类 bean 的 id。
   <aop:aspect id="tm" ref="transactionManager">
            ...
   </aop:aspect>

(3) 使用 aop:pointcut 配置切入点表达式

  • 作用: 用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。
  • 属性
    • expression:用于定义切入点表达式。
    • id:用于给切入点表达式提供一个唯一标识
<aop:pointcut id="point1" expression="execution(public void com.etime.service.impl.AccountServiceImpl.transfer(..))"/

(4) 使用 aop:xxx 配置对应的通知类型

a.前置通知
  • aop:before
  • 作用:用于配置前置通知。指定增强的方法在切入点方法之前执行
  • 属性
    • method:用于指定通知类中的增强方法名称
    • ponitcut-ref:用于指定切入点的表达式的引用
    • poinitcut:用于指定切入点表达式
  • 执行时间
    • 切入点方法执行之前执行
<aop:before method="beginTransaction" pointcut-ref="point1"></aop:before>
b.后置通知
  • aop:after-returning
  • 作用: 用于配置后置通知
  • 属性
    • method:指定通知中方法的名称。
    • pointct:定义切入点表达式
    • pointcut-ref:指定切入点表达式的引用
  • 执行时间
    • 切入点方法正常执行之后。它和异常通知只能有一个执行
<aop:after-returning method="commit" pointcut-ref="point1"></aop:after-returning> 
c.异常通知
  • aop:after-throwing
  • 作用:用于配置异常通知
  • 属性
    • method:指定通知中方法的名称。
    • pointct:定义切入点表达式
    • pointcut-ref:指定切入点表达式的引用
  • 执行时间
    • 切入点方法执行产生异常后执行。它和后置通知只能执行一个
<aop:after-throwing method="rollback" pointcut-ref="point1"></aop:after-throwing>
d.最终通知
  • aop:after
  • 作用:用于配置最终通知
  • 属性
    • method:指定通知中方法的名称。
    • pointct:定义切入点表达式
    • pointcut-ref:指定切入点表达式的引用
  • 执行时间
    • 无论切入点方法执行时是否有异常,它都会在其后面执行。
<aop:after method="release" pointcut-ref="point1"></aop:after>

3、切入点表达式说明

(1)切点表达式的语法

execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意
  • 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
  • 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
a.全匹配方式
public void 
com.etime.service.impl.AccountServiceImpl.saveAccount(com.etime.domain.Account)
b.访问修饰符可以省略
void com.etime.service.impl.AccountServiceImpl.saveAccount(com.etime.domain.Account)
c.返回值可以使用*号,表示任意值
* com.etime.service.impl.AccountServiceImpl.saveAccount(com.etime.domain.Account)
d.包名可以使用 * 号,表示任意包 (但是有几级包,需要写几个 *)
* *.*.*.*.AccountServiceImpl.saveAccount(com.etime.domain.Account)
e.使用…来表示当前包,及其子包
* com..AccountServiceImpl.saveAccount(com.etime.domain.Account)
f.类名可以使用*号,表示任意类
* com..*.saveAccount(com.etime.domain.Account)
g.方法名可以使用*号,表示任意方法
* com..*.*( com.etime.domain.Account)
h.参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数
* com..*.*(*)
i.参数列表可以使用…表示有无参数均可,有参数可以是任意类型
* com..*.*(..)
j.全通配方式:
* *..*.*(..)
k.注意:通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
execution(* com.etime.service.impl.*.*(..))

4、环绕通知配置事务管理

(1)在TransactionManager类当中添加方法

/**
     * 环绕通知:
     * spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。
     * 在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。
     * @param pjp
     * @return
     */
public Object transactionAround(ProceedingJoinPoint pjp) {
    //定义返回值
    Object returnValue = null;
    try {
        //获取方法执行所需的参数
        Object[] args = pjp.getArgs();
        //前置通知:开启事务
        beginTransaction();
        //执行方法
        returnValue = pjp.proceed(args);
        //后置通知:提交事务
        commit();
    }catch(Throwable e) {
        //异常通知:回滚事务
        rollback();
        e.printStackTrace();
    }finally {
        //最终通知:释放资源
        release();
    }
    return returnValue;
}

(2)环绕通知

  • aop:around
  • 作用:用于配置环绕通知
  • 属性
    • method:指定通知中方法的名称。
    • pointct:定义切入点表达式
    • pointcut-ref:指定切入点表达式的引用
  • 说明:它是 spring 框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
  • 注意:通常情况下,环绕通知都是独立使用的
    <aop:config>
        <aop:aspect id="tm" ref="transactionManager">
            <aop:pointcut id="point1"
                          expression="execution(public void com.etime.service.impl.AccountServiceImpl.transfer(..))"/>
            <aop:around method="transactionAround" pointcut-ref="point1"></aop:around>
        </aop:aspect>
    </aop:config>

到了这里,关于Spring02-Spring注解的使用、基于注解的IOC、纯注解配置、整合Junit、AOP入门、基于配置文件的AOP、切入点表达式、基于配置的文件环绕通知的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring——基于注解的AOP配置

    1.1.pom.xml 1.2.dao 1.3.service 1.4.applicationContext.xml 1.5.测试 2.1.applicationContext.xml 2.2.AOP配置 常用注解 @Aspect:把当前类声明为切面类 @Before:前置通知,可以指定切入点表达式 @AfterReturning:后置【try】通知,可以指定切入点表达式 @AfterThrowing:异常【catch】通知,可以指定切入点表达

    2024年01月22日
    浏览(37)
  • 【Spring进阶系列丨第八篇】Spring整合junit & 面向切面编程(AOP)详解

    ​ @ContextConfiguration注解需要注意的细节是: classes:指定的是主配置类的字节码 locations:指定xml文件的位置 ​ 首先来看一个问题,假如我现在有一个UserServiceImpl用户业务类,其中呢,有一个保存用户的方法,即: ​ 现在的需求是:我要在保存用户之前新增事务的功能,你

    2024年04月13日
    浏览(58)
  • 浅谈 Spring IOC 和 DI 以及 Spring 整合 Mybatis 的十四 个注解

    控制 : 指对对象的创建权 反转: 对象的创建由程序员在类中主动创建变为由Spring自动创建 Spring方式:所有的对象都是由Spring框架提前创建出来,存储到一个容器中,servlet需要Service了,就向Spring要一个 技术的作用 : 通过底层反射原理来 解开对象之间的耦合 注解 @Compone

    2024年02月16日
    浏览(37)
  • MyBatis与Spring集成&常用注解以及AOP和PageHelper分页插件整合

    目录 前言 一、MyBatis与Spring整合的好处以及两者之间的关系 1.好处 2.关系  二、MyBatis和Spring集成 1.导入pom.xml 2.编写配置文件  3.利用mybatis逆向工程生成模型层代码 三、常用注解  四、AOP整合pageHelper分页插件 创建一个切面 测试 MyBatis是一个开源的持久层框架,而Spring是一个

    2024年02月07日
    浏览(60)
  • 注解实现(基于Spring AOP)

    切入点表达式 Spring AOP 支持的切入点主要有以下几种: execution:用于匹配方法执行的连接点。这是最常用的切入点指示器。你可以指定具体的方法,或者类来匹配。 例如: execution(* com.example.service.*.*(..)) ,这个表达式表示匹配 com.example.service 包下的所有类的所有方法。 wit

    2024年02月16日
    浏览(42)
  • Spring AOP官方文档学习笔记(二)之基于注解的Spring AOP

    1.@Aspect注解 (1) @Aspect注解用于声明一个切面类,我们可在该类中来自定义切面,早在Spring之前,AspectJ框架中就已经存在了这么一个注解,而Spring为了提供统一的注解风格,因此采用了和AspectJ框架相同的注解方式,这便是@Aspect注解的由来,换句话说,在Spring想做AOP框架之前,

    2023年04月17日
    浏览(46)
  • 11、Spring之基于注解的AOP

    创建名为spring_aop_annotation的新module,过程参考9.1节 注意:AOP需要在IOC的基础上实现,因此需要导入IOC的依赖 11.2.1.1、配置前置方法 11.2.1.2、测试使用效果 由控制台日志可知,切面类的前置通知(方法),通过切入点表达式,作用到了目标方法的连接点上 11.2.2.1、改进前置方

    2024年02月13日
    浏览(41)
  • Spring IOC基于XML和注解管理Bean(一)

    Spring IOC基于XML和注解管理Bean(二) IoC 是 Inversion of Control 的简写,译为“ 控制反转 ”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出 松耦合 、更优良的程序。 Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控

    2024年02月08日
    浏览(77)
  • Spring IOC基于XML和注解管理Bean(二)

    Spring IOC基于XML和注解管理Bean(一) 2.9、实验八:p命名空间 引入p命名空间 引入p命名空间后,可以通过以下方式为bean的各个属性赋值 2.10、实验九:引入外部属性文件 ①加入依赖 ②创建外部属性文件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    2024年02月09日
    浏览(45)
  • Spring Boot学习随笔- 集成JSP模板(配置视图解析器)、整合Mybatis(@MapperScan注解的使用)

    学习视频:【编程不良人】2021年SpringBoot最新最全教程 在main创建webapp,然后创建index.jsp进行测试,在访问之前需要进行一个设置,否则springboot是找不到jsp页面的 修改jsp无需重启应用 数据库访问框架:hibernate、jpa、mybatis【主流】 SpringBoot(微框架) = Spring(工厂) + SpringMV

    2024年02月05日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包