Spring(续)

93 阅读10分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

8、使用注解开发

使用注解需要导入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
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:annotation-config/>
 </beans>
  1. bean

     @Component
     public class User {
         //相当于<property name = "name" value = "rop"/>
         @Value("rop")
         public String name;
     }
    
  2. 属性如何注入

     public class User {
         //相当于<property name = "name" value = "rop"/>
         @Value("rop")
         public String name;
     }
    
  3. 衍生的注解

    @Component的几个衍生注解,,在web开发中会按照MVC三层架构分层

    • dao【@Repository】

    • service【@Service】

    • controller【@Controller】

      这四个注解功能都是一样的,都代表将某个类注册到Spring中,装配Bean

  4. 自动装配

     @Autowired:默认通过byType的方式,当遇到多个同类型时,使用@Qualifier配合byName进行装配
     @Resource:默认通过byName方式,如果找不到名字,则通过byType实现。如果两个都找不到,就报错
    
  5. 作用域

     @Scope("")
    
  6. 小结

    xml与注解:

    • xml更加万能,适用于任何场合!维护简单方便
    • 注解 不是自己的类使用不了,维护相对复杂

    xml与注解最佳实践:

    • 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
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <!--指定要扫描的包,这个包下的注解就会生效-->
     <context:component-scan base-package="com.rop"/>
     <context:annotation-config/>
 ​
 </beans>

9、使用JavaConfig实现配置

完全不使用Spring的xml配置,全权交给Java。

JavaConfig是Spring的一个子项目。

 //这里这个注解的意思,就是这个类被Spring接管了,注册到了容器中
 @Component
 public class User {
 ​
     private String name;
 ​
     public String getName() {
         return name;
     }
     @Value("rop") //属性注入值
     public void setName(String name) {
         this.name = name;
     }
 ​
     @Override
     public String toString() {
         return "User{" +
                 "name='" + name + ''' +
                 '}';
     }
 }
 //这个也会被Spring托管,注册到容器中,因为这个本质就是一个@Component
 //@Configuration就是一个配置类,跟之前的beans.xml是一样的
 @Configuration
 @ComponentScan("com.rop.pojo")
 @Import(RopConfig2.class)
 public class RopConfig {
 ​
     //注册一个bean,就相当于我们之前在beans.xml中的bean标签
     //方法的名字 == bean标签的id属性
     //方法的返回值 == bean标签中的class属性
     @Bean
     public User user(){
         return new User(); //返回要注入到bean的对象
     }
 }
 public class SpringTest07 {
     @Test
     public void test(){
         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(RopConfig.class);
         User user = context.getBean("user", User.class);
         System.out.println(user.getName());
     }
 }

10、代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层【面试必问:SpringAOP与SpringMVC】

代理模式分类:

  • 静态代理
  • 动态代理

10.1、静态代理

角色分析:

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

代码步骤:

1.接口

 // 租房的接口
 public interface Rent {
     void rent();
 }

2.真实角色

 //房东
 public class Host implements Rent {
     public void rent() {
         System.out.println("房东要出租房子");
     }
 }

3.代理角色

 //中介
 public class Proxy implements Rent {
     private Host host;
 ​
     public Proxy() {
     }
 ​
     public Proxy(Host host) {
         this.host = host;
     }
 ​
     public void rent() {
         seeHouse();
         host.rent();
         heTong();
         fee();
     }
 ​
     //看房
     public void seeHouse(){
         System.out.println("中介带你看房");
     }
 ​
     //签合同
     public void heTong(){
         System.out.println("中介带你签合同");
     }
 ​
     //收中介费
     public void fee(){
         System.out.println("中介收费");
     }
 }

4.客户端访问代理角色

 public class Client {
     public static void main(String[] args) {
         //房东要租房子
         Host host = new Host();
         //代理,中介帮房东租房子,但中介会有一些附属操作
         Proxy proxy = new Proxy(host);
 ​
         //你不用面对房东,直接找中介租房即可
         proxy.rent();
     }
 }

代理模式的好处:

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

缺点:

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

10.2、加深理解

image-20220420151233442

 public interface UserService {
     void add();
     void delete();
     void update();
     void query();
 }
 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 query() {
         System.out.println("查询了一个用户");
     }
 }
 public class UserServiceProxy implements UserService {
     private UserServiceImpl userService;
 ​
     // 注入
     public void setUserService(UserServiceImpl userService) {
         this.userService = userService;
     }
 ​
     public void add() {
         log("add");
         userService.add();
     }
 ​
     public void delete() {
         log("delete");
         userService.delete();
     }
 ​
     public void update() {
         log("update");
         userService.update();
     }
 ​
     public void query() {
         log("query");
         userService.query();
     }
 ​
     //日志方法
     public void log(String msg){
         System.out.println("[Debug]:"+ "使用了" + msg + "方法");
     }
 }
 public class Client {
     public static void main(String[] args) {
         UserServiceImpl userService = new UserServiceImpl();
 ​
         UserServiceProxy userServiceProxy = new UserServiceProxy();
         userServiceProxy.setUserService(userService);
 ​
         userServiceProxy.add();
     }
 }

10.3、动态代理

  • 动态代理和静态代理角色一样

  • 动态代理的代理类是动态生成的,不是直接写好的

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口---JDK的动态代理
    • 基于类---cglib
    • java字节码实现:javasist

需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序

InvocationHandler

1.真实角色(扮演真实角色的 新来的演员)

2.代理角色(携带技能的代理角色的 导演)

3.代理对象(将代理角色赋予真实角色 彩排)

4.动态生成代理类(经过训练的演员)

动态代理的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注公共业务
  • 公共业务交给代理角色。实现了业务的分工
  • 公共业务发生扩展时,方便集中管理
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
 //租房的接口
 public interface Rent {
     void rent();
 }
 //房东
 public class Host implements Rent {
     public void rent() {
         System.out.println("房东要出租房子");
     }
 }
 //实现动态代理
 public class ProxyInvocationHandler implements InvocationHandler {
 ​
     //要被代理的接口
     private Rent rent;
 ​
     public void setRent(Rent rent) {
         this.rent = rent;
     }
 ​
     //生成得到代理类
     public Object getProxy(){
         return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
     }
 ​
     //处理代理实例并返回结果
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         //动态代理的本质:使用反射机制实现
         seeHouse();
         Object result = method.invoke(rent, args);
         fee();
         return result;
     }
 ​
     public void seeHouse(){
         System.out.println("中介带你看房子");
     }
 ​
     public void fee(){
         System.out.println("中介收费");
     }
 }
 //模拟场景
 public class Client {
     public static void main(String[] args) {
         //真实角色
         Host host = new Host();
         //代理角色:现在没有
         ProxyInvocationHandler pih = new ProxyInvocationHandler();
         //通过调用程序处理角色来处理我们要调用的接口对象
         pih.setRent(host);
         //这里的proxy就是动态生成的,
         Rent proxy = (Rent) pih.getProxy();
         proxy.rent();
     }
 }

10.4、优化InvocationHandler配置

 public class ProxyInvocationHandler implements InvocationHandler {
 ​
     //要被代理的接口
     private Object target;
 ​
     public void setTarget(Object target) {
         this.target = target;
     }
 ​
     //生成得到代理类
     public Object getProxy(){
         return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
     }
 ​
     //处理代理实例并返回结果
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         //动态代理的本质:使用反射机制实现
         log(method.getName());
         Object result = method.invoke(target, args);
         return result;
     }
     public void log(String msg){
         System.out.println("执行了" + msg + "方法");
     }
 }

将ProxyInvocationHandler变成万能的动态代理,任何真实角色都可以被代理

 public class Client {
     public static void main(String[] args) {
         //真实对象
         UserServiceImpl userService = new UserServiceImpl();
         //代理角色,不存在
         ProxyInvocationHandler pih = new ProxyInvocationHandler();
         //设置需要代理的角色
         pih.setTarget(userService);
         //动态生成代理类
         UserService proxy = (UserService) pih.getProxy();
 ​
         proxy.add();
     }
 }

11、AOP

11.1、什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高了开发的效率。

image-20220420210706500

11.2、AOP在Spring中的作用

提供声明式服务:允许用户自定义切面

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

image-20220420211522420

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

image-20220420211814980

即AOP在不改变原有代码的情况下,去新增功能。

11.3、使用Spring实现AOP

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

 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
 <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
 </dependency>

方式一:使用Spring的API接口

UserService接口

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

UserServiceImpl

 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 query() {
         System.out.println("查询了一个用户");
     }
 }

BeforeLog

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

LogAfter

 public class LogAfter implements AfterReturningAdvice {
     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
         System.out.println("执行了" + method.getName() + "返回结果为:" + returnValue);
     }
 }

配置applicationContext.xml

 <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"
        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">
 ​
     <!--注册bean-->
     <bean id="userService" class="com.rop.service.UserServiceImpl"/>
     <bean id="beforeLog" class="com.rop.log.BeforeLog"/>
     <bean id="logAfter" class="com.rop.log.LogAfter"/>
     <!--配置AOP:需要导入AOP的约束-->
     <aop:config>
         <!--切入点:expression:表达式 execution(要执行的位置 * * * * *)(死的)-->
         <aop:pointcut id="pointCut" expression="execution(* com.rop.service.UserServiceImpl.*(..))"/>
         <!--执行环绕-->
         <aop:advisor advice-ref="beforeLog" pointcut-ref="pointCut"/>
         <aop:advisor advice-ref="logAfter" pointcut-ref="pointCut"/>
     </aop:config>
 </beans>

测试

 public class SpringTest09 {
     @Test
     public void test(){
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         //注意点:动态代理的是接口,不是实现类
         UserService userService = context.getBean("userService", UserService.class);
         userService.add();
     }
 }

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

 public class DiyPointCut {
     public void before(){
         System.out.println("==============执行方法前==============");
     }
 ​
     public void after(){
         System.out.println("==============执行方法后==============");
     }
 }
 <!--方式二:自定义类-->
 <bean id="diy" class="com.rop.diy.DiyPointCut"/>
 ​
 <aop:config>
     <!--自定义切面,ref:要引用的类-->
     <aop:aspect ref="diy">
         <!--切入点-->
         <aop:pointcut id="point" expression="execution(* com.rop.service.UserServiceImpl.*(..))"/>
         <!--通知-->
         <aop:before method="before" pointcut-ref="point"/>
         <aop:after method="after" pointcut-ref="point"/>
     </aop:aspect>
 </aop:config>

方式三:注解实现AOP

 @Aspect //标注这个类是一个切面
 public class AnnotationPointCut {
 ​
     @Before("execution(* com.rop.service.UserServiceImpl.*(..))")
     public void before(){
         System.out.println("==============执行方法前==============");
     }
 ​
     @After("execution(* com.rop.service.UserServiceImpl.*(..))")
     public void after(){
         System.out.println("==============执行方法后==============");
     }
 ​
     @Around("execution(* com.rop.service.UserServiceImpl.*(..))")
     public void around(ProceedingJoinPoint pjp) throws Throwable {
         System.out.println("==============环绕前==============");
         Object proceed = pjp.proceed();
         System.out.println("==============环绕后==============");
     }
 }
 <!--方式三:注解实现-->
 <bean id="anno" class="com.rop.diy.AnnotationPointCut"/>
 <!--开启注解支持-->
 <aop:aspectj-autoproxy/>

12、整合MyBatis

步骤:

  1. 导入相关jar包

    • junit
    • mybatis
    • mysql数据库
    • spring相关的
    • aop织入
    • mybatis-spring【新知识点】
     <dependencies>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.12</version>
         </dependency>
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.47</version>
         </dependency>
         <dependency>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis</artifactId>
             <version>3.5.2</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-webmvc</artifactId>
             <version>5.1.19.RELEASE</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-jdbc</artifactId>
             <version>5.1.19.RELEASE</version>
         </dependency>
         <dependency>
             <groupId>org.aspectj</groupId>
             <artifactId>aspectjweaver</artifactId>
             <version>1.8.13</version>
         </dependency>
         <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
         <dependency>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis-spring</artifactId>
             <version>2.0.7</version>
         </dependency>
     </dependencies>
    
  2. 编写配置文件

  3. 测试

12.1、回忆MyBatis

1.编写实体类

 @Data
 public class User {
     private int id;
     private String name;
     private String pwd;
 }

2.编写核心配置文件

 <?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.rop.pojo"/>
     </typeAliases>
 ​
     <!--configuration核心配置文件-->
     <environments default="development">
         <environment id="development">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
                 <property name="driver" value="com.mysql.jdbc.Driver"/>
                 <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                 <property name="username" value="root"/>
                 <property name="password" value="123456"/>
             </dataSource>
         </environment>
     </environments>
 ​
     <mappers>
         <mapper class="com.rop.dao.UserMapper"/>
     </mappers>
 ​
 </configuration>

3.编写接口

 public interface UserMapper {
     List<User> getUser();
 }

4.编写mapper.xml

 <?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.rop.dao.UserMapper">
     <select id="getUser" resultType="user">
         select * from mybatis.user;
     </select>
 </mapper>

5.测试

 public class SpringTest10 {
     @Test
     public void getUser(){
         SqlSession sqlSession = MyBatisUtils.getSqlSession();
         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
         List<User> user = mapper.getUser();
         for (User u : user) {
             System.out.println(u);
         }
         sqlSession.close();
     }
 }

12.2、Mybatis-Spring

导包

 <dependencies>
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
     </dependency>
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.47</version>
     </dependency>
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.2</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>5.1.19.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-jdbc</artifactId>
         <version>5.1.19.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.8.13</version>
     </dependency>
     <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis-spring</artifactId>
         <version>2.0.7</version>
     </dependency>
     <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.16.10</version>
     </dependency>
 </dependencies>
 <build>
     <resources>
         <resource>
             <directory>src/main/resources</directory>
             <includes>
                 <include>**/*.properties</include>
                 <include>**/*.xml</include>
             </includes>
             <filtering>true</filtering>
         </resource>
         <resource>
             <directory>src/main/java</directory>
             <includes>
                 <include>**/*.properties</include>
                 <include>**/*.xml</include>
             </includes>
             <filtering>true</filtering>
         </resource>
     </resources>
 </build>

1.编写数据源配置

spring-dao.xml

 <!--DataSource:使用Spring的数据源替换MyBatis的配置
 使用Spring提供的jdbc:org.springframework.jdbc.datasource.DriverManagerDataSource
 -->
 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
     <property name="username" value="root"/>
     <property name="password" value="123456"/>
 </bean>

2.sqlSessionFactory

spring-dao.xml

 <!--sqlSessionFactory
    这里配置相当于在之前的mybatis-config.xml里面的配置,数据源从上面的dataSource获取
 -->
 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
     <property name="dataSource" ref="dataSource"/>
     <!--绑定MyBatis配置文件-->
     <property name="configLocation" value="classpath:mybatis-config.xml"/>
     <property name="mapperLocations" value="classpath:com/rop/dao/UserMapper.xml"/>
     <!--<property name="mapperLocations" value="classpath*:com/rop/dao/*.xml"/>-
     <!--<property name="typeAliases" value="com.rop.pojo.User"/>-->
 </bean>

3.sqlSessionTemplate

spring-dao.xml

 <!--SqlSessionTemplate就是我们要使用的sqlSession
     这一步相当于一个sqlSession的工厂,就是以前的MyBatisUtils中的sqlSession,将要用的sqlSession传递给接口实现类进行注入
 -->
 <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
     <!--只能只用构造器注入sqlSessionFactory,因为它没有set方法-->
     <constructor-arg index="0" ref="sqlSessionFactory"/>
 </bean>

4.需要给接口加实现类

UserMapperImpl

 public class UserMapperImpl implements UserMapper {
     //所有的操作都是由sqlSession来完成,现在用SqlSessionTemplate新建sqlSession
     private SqlSessionTemplate sqlSession;
 ​
     //这一步的注入很关键Spring的特点
     public void setSqlSession(SqlSessionTemplate sqlSession) {
         this.sqlSession = sqlSession;
     }
 ​
     public List<User> getUser() {
         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
         return mapper.getUser();
     }
 }

5.将自己写的实现类注入到spring中

applicationContext.xml

 <import resource="spring-dao.xml"/>
 <bean id="userMapperImpl" class="com.rop.dao.UserMapperImpl">
     <property name="sqlSession" ref="sqlSession"/>
 </bean>

6.测试

SpringTest10

 public class SpringTest10 {
     @Test
     public void getUser(){
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         UserMapper sqlSession = context.getBean("userMapperImpl", UserMapper.class);
         for (User user : sqlSession.getUser()) {
             System.out.println(user);
         }
     }
 }

12.3、12.2的新方法(SQLSessionDaoSupport)

UserMapperImplT

 public class UserMapperImplT extends SqlSessionDaoSupport implements UserMapper {
 ​
     public List<User> getUser() {
         return getSqlSession().getMapper(UserMapper.class).getUser();
     }
 }

applicationContext.xml

这里可以省去一个步骤,直接去factory里面拿取sqlSession

 <bean id="userMapperImplT" class="com.rop.dao.UserMapperImplT">
     <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
 </bean>

test

 public class SpringTest10 {
     @Test
     public void test(){
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         UserMapper userMapperImpl = context.getBean("userMapperImplT", UserMapper.class);
         List<User> user = userMapperImpl.getUser();
         for (User u : user) {
             System.out.println(u);
         }
     }
 }

13、声明式事务

13.1、回顾事务

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

事务的ACID原则:

  • 原子性

  • 一致性

  • 隔离性

    • 多个业务可能操作同一个资源,防止数据损坏
  • 持久性

    • 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中

13.2、spring中的事务管理

  • 声明式事务:AOP
  • 编程式事务:需要在代码中,进行事务的管理

UserMapperImpl

 public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
     public List<User> getUser() {
 ​
         User user = new User(5, "Mr.Rop", "123123");
         UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
         mapper.addUser(user);
         mapper.deleteUser(5);
         return mapper.getUser();
     }
 ​
     public int addUser(User user) {
         return getSqlSession().getMapper(UserMapper.class).addUser(user);
     }
 ​
     public int deleteUser(int id) {
         return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
     }
 }

applicationContext.xml

 <!--配置声明事务-->
 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource"/>
 </bean>
 ​
 <!--结合AOP实现事务的织入-->
 <!--配置事务通知-->
 <tx:advice id="txAdvice" transaction-manager="transactionManager">
     <tx:attributes>
         <!--给哪些方法配置事务-->
         <!--配置事务的传播特性:propagation-->
         <tx:method name="add" propagation="REQUIRED"/>
         <tx:method name="delete" propagation="REQUIRED"/>
         <tx:method name="update" propagation="REQUIRED"/>
         <tx:method name="query" propagation="REQUIRED"/>
         <tx:method name="*" propagation="REQUIRED"/>
     </tx:attributes>
 </tx:advice>
 ​
 <!--配置事务切入-->
 <aop:config>
     <aop:pointcut id="txPointCut" expression="execution(* com.rop.mapper.*.*(..))"/>
     <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
 </aop:config>

思考:

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致
  • 如果不在spring中配置声明式事务,就需要在代码中手动配置事务
  • 事务在项目的开发中,十分重要,涉及到数据的一致性和完整性