四,Spring注解开发

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Spring day04

1 Spring基于注解的开发

XML方式配置bean存在的问题开发效率低下。Spring2.x提供了开发效率更高的注解式配置。注解开发替换XML配置的好处简化编程提高开发效率。

  • XML方式配置繁琐但功能强大维护性好可配置第3方类型非自己编码的类型
  • 注解方式配置简单开发效率高。一般用于自定义类型的配置

1.1 注解开发的思路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQL1p8z8-1673747421009)(Spring day04.assets/image-20210617223657666.png)]

1.2 注解开发的步骤

  1. 查找注解配置查找注解的起始包名

    applicationContext.xml

     <!-- 扫描指定包下以及子包下所有使用注解的类自动配置为Bean-->
    <context:component-scan base-package="com.bz"/>
    
  2. 为类和属性添加注解

    package com.bz.service.impl;
    
    import com.bz.entity.User;
    import com.bz.mapper.UserMapper;
    import com.bz.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component("userService")
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserMapper userMapper;
    
        public void setUserMapper(UserMapper userMapper) {
            this.userMapper = userMapper;
        }
    
        @Override
        public boolean login(String name, String password) {
            User user = userMapper.selectUserByName(name);
    
            return user!= null && user.getPassword().equals(password);
        }
    
    
    }
    

1.3 核心注解

1.3.1 bean配置相关注解

@Component替换bean标签创建对象。

@Controller 用在action层
@Service 用在service
@Repository 用在dao层

@Service
//@Component("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
	...
}

注意

  1. Controller Service Repository和Component作用一样但更有识别性。

  2. MyBatis框架下Repository注解没有必需的使用场景

  3. 4个注解使用时都可以不用设置id属性id默认值类名首字母小写

    例如UserServiceImpl ==> userServiceImpl

@Scope 决定对象是否单例

​ 默认对象是单例的可以通过@Scope(“prototype”)设置为多例

1.3.2 属性注入的相关注解

@Autowired 用来完成属性注入。

@Service
public class UserServiceImpl implements UserService {
    @Autowired(required = false)
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    ...
}

注意

  1. Autowired根据属性的类型从Spring容器中获取对象完成属性赋值
  2. Autowired用在属性上通过反射直接操作属性为属性赋值不会使用set方法
  3. Autowired还可以用在set方法上此时通过调用属性的set方法赋值
  4. Autowired修饰的属性默认是必须的如果注入失败就会抛出异常可以设置@Autowired(required=false)允许注入失败

@Qualifier(“bean的id”)

与@Autowired配合使用当有多个满足属性类型要求的Bean时可以用@Qualifier(“id”)来指定要注入的依赖。

class Address{
    private String city;
    private String address;
    ...
}
<bean id="addr1" class="com.bz.entity.Address">
	<property name="city" value="郑州"/>
    <property name="street" value="文化路"/>
</bean>
<bean id="addr2" class="com.bz.entity.Address">
	<property name="city" value="郑州"/>
    <property name="street" value="白庙路"/>
</bean>
class Person{
    private String personName;
    
    @Autowired
    @Qualifier("addr1")
    private Address addr;
    ...
}

@Value用于为简单属性赋值还可以读取properties文件中的参数

jdbc.properties

url=jdbc:mysql://localhost:3306/spring?useUnicde=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=Asia/Shanghai
driverClassName=com.mysql.cj.jdbc.Driver
user=root
password=123456

JdbcProeprties.java

@Component
public class JdbcProperties{
    //@Value("root")
    @Value("${user}")
    private String username;
    @Value("${password}")
    private String password;
    @Value("${url}")
    private String url;
    @Value("${driverClassName}")
    private String driverClassName;
    
    ...
}

行业标准

  1. 自定义类型Service、Dao使用注解开发
  2. 第3方类型数据库连接池、事务管理器…使用xml配置

2 基于注解的事务控制

使用Transactional注解描述类或者方法添加事务。

开发步骤

  1. 创建原始对象

  2. 定义通知类功能增强事务控制

    Spring内置的DataSourceTransactionManager

  3. 配置通知类

    <!-- 配置事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"/>
    </bean>
    
    <tx:annotation-driven transaction-manager="txManager"/>
    
  4. 使用Transactional注解描述类或方法

    @Service
    @Transactional
    public class UserServiceImpl implements UserService {
    	@Autowired
        private UserDao userDao;
    
        @Transactional(readOnly=true)
         @Override
        public boolean login(String name, String password) {
            User user = userDao.selectUserByName(name);
    
            return user!= null && user.getPassword().equals(password);
        }
        
        public void register(String username,String password){
            userDao.insertUser(new User(null,username,password));
        }
    }
    

注意

传统的Spring项目中注解的事务控制使用较少更多的还是使用xml配置的事务控制。但是在SpringBoot中大量的使用注解方式的事务配置。

3 Spring集成JUnit

直接使用JUnit测试Spring项目需要手动编码获取context工厂需要手动从工厂中获取对象测试繁琐。
解决方案使用Spring-test简化了Spring项目的测试。

准备工作导入JUnit、spring-test依赖

<!-- junit版本必须是4.12及以上-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

测试

//开启Spring对Junit的支持
@RunWith(SpringRunner.class)
//设置配置文件的路径
@ContextConfiguration("classpath:applicationContext.xml")
public class ApplicationContextTest {
    //自动从工厂中获取对象为属性赋值
    @Autowired
    private ApplicationContext context;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserService userService;
    
    ...
}

小技巧如果有多个测试类多个测试类上方也有重复的配置可以定义父类抽取共性

//开启Spring对Junit的支持
@RunWith(SpringRunner.class)
//设置配置文件的路径
@ContextConfiguration("classpath:applicationContext.xml")
public abstract class AbstractApplicationContextTest {
}

public class ApplicationContextTest extends AbstractApplicationContextTest{
    @Autowired
    private UserService userService;
    ...
}

4 再谈web.xml

4.1 web.xml标签的加载顺序

目前为止学习的标签servlet filter listener context-param

加载顺序

  1. context-param
  2. listener
  3. filter
  4. servlet

4.2 servlet的url-pattern配置形式以及优先级

url-pattern配置形式种类

  1. 精确匹配

    /student/showPageStudents

  2. 路径匹配

    /* /student/*

  3. 扩展名匹配

    *.jsp *.do *.action

  4. 缺省匹配

    / 在前3种都无法匹配时做默认匹配

优先级:精确匹配>路径匹配>扩展名匹配>缺省匹配

注意

  1. 都是路径匹配时最长路径优先

    http://localhost:8989/spring-day05/student/b

    /student/* 比/* 优先匹配到。

  2. 路径匹配和扩展名匹配不能混用

    不能出现如下配置

    /student/*.do

    / * / *.jsp

另外注意

  1. Filter一定早于Servlet执行
  2. 多个Filter执行的优先级只跟配置顺序有关跟url-pattern的优先级没有关系

5 事务详解概念重点

5.1 read-only

read-only="true"表示只读事务提高查询检索效率。read-only的默认值是false。
注意只能应用到查询操作不能应用到增删操操作上。

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- 所有以show开头的方法执行只读的事务-->
        <tx:method name="show*" read-only="true"/>
        <!-- 除了show开头的其它所有方法执行读写的事务(false也是默认值)-->
        <tx:method name="*" read-only="false"/>
    </tx:attributes>
</tx:advice>

5.2 timeout

timeout超时机制单位秒事务执行时间超过指定的时长后自动失败回滚。
timeout默认值-1表示跟数据库的默认超时设置有关。

//通过sleep方法模拟业务方法执行超时
public void removeUser(Integer id) {
    try {
        Thread.sleep(5000);//当前线程睡眠5s
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    userDao.deleteUserById(id);
}
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="show*" read-only="true"/>
        <!-- 超时时间时间设置为3s-->
        <tx:method name="*" timeout="3"/>
    </tx:attributes>
</tx:advice>
@Test
//测试超时
public void testTimeout(){
     ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = (UserService)ctx.getBean("userServiceImpl");
    userService.removeUser(1);
}

5.3 rollback-for和no-rollback-for

rollback-for当发生rollback-for配置的异常类型的异常时执行回滚。
no-rollback-for:当发生no-rollback-for配置的异常类型的异常时不执行回滚。

Spring中默认

RuntimeException及其子类运行时异常的异常执行回滚

Exception及其子类编译时异常的异常不会执行回滚

public void addUser(User u) {
    userDao.insertUser(u);
    // throw new RuntimeException("自定义未检查异常");//默认回滚
    throw new Exception("自定义已检查异常");//默认不回滚
}

rollback-for和no-roollback-for的生效规则

  • 2个的属性本身没有优先级区别
  • 程序产生的异常与哪个属性中配置的异常类型继承关系更近就是用哪个配置
  • 如果产生的异常和配置的异常类型没有继承关系则使用默认规则

5.4 propagation传播机制

企业开发中会出现业务层方法调用业务层方法的情况。propagation定义了一个业务方法被另外一个业务方法调用时的事务传播方式。

常用的事务传播机制

传播机制内部方法对事务的要求特点
REQUIRED必须有如果外部有事务则加入外部没有事务则新建
REQUIRES_NEW必须有无论外部是否有事务内部都会新建
SUPPORTS可有可无如果外部有事务则加入如果没有则以无事务状态运行
NOT_SUPPORTED必须没有如果外部有事务则挂起外部事务
MANDATORY必须有如果外部没有事务直接抛出异常
NEVER必须没有如果外部有事务直接抛出异常
NESTED必须有如果外部没有事务开启事务如果外部有事务记录当前的保存点一旦出错不会全部回滚只回滚到保存点

5.5 isolation(隔离级别)

解决事务并发执行的问题微观上2个执行时间相近的事务相互影响的问题。

5.5.1 4种标准的隔离级别

隔离性在数据库的并发访问时得以体现随着数据库并发事务处理能力的大大增强数据库资源的利用率也会大大提高从而提高了数据库系统的事务吞吐量可以支持更多的用户并发访问。但并发事务处理也会带来一些问题如脏读、不可重复读、幻读。这些问题是和隔离性相伴相生的。下面一一解释其含义。

  • 脏读

    B事务正在对一条记录做修改在这个事务提交前这条记录的数据就处于不一致状态这时另一个事务A也来读取同一条记录如果不加控制第二个事务读取了这些未提交的“脏”数据并据此做进一步的处理就会产生未提交的数据依赖关系。这种现象被形象的叫作"脏读"Dirty Reads。

  • 不可重复读

    一个事务中多次执行相同的查询SQL不同时刻读取的数据不同。要么发现其读出的数据已经发生了改变、或某些记录已经被删除了这种现象就叫作“ 不可重复读”Non-Repeatable Reads。

  • 幻读

    一个事务按相同的查询条件重新读取以前检索过的数据却发现其他事务插入了满足其查询条件的新数据这种现象就称为“幻读”Phantom Reads。

隔离级别特点存在的问题
READ-UNCOMMITTED读未提交脏读、不可重复读、幻读
READ-COMMITTED读已提交只能读到另外一个事务提交后的数据。不可重复读、幻读
REPEATABLE-READ可重复读同一个事务下前后两次结果一致不同数据库不同有的数据库会有幻读
SERIALIZABLE序列化读串行性能太差

Oracle只支持READ-COMMITTED和SERIALIZABLE 2种隔离级别。

MySQL支持4种。

实战时使用读已提交即可。

5.5.2 MySQL下演示隔离级别

MySQL支持多种存储引擎InnoDB支持事务、MyISAM、MEMORY

存储引擎决定了数据库如何的保存管理数据。

  1. 确认MySQL使用的存储引擎
show engines;//查询数据库支持的存储引擎
show variables like 'default_storage_engine';//查询默认引擎

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NbDFE9k-1673747421010)(Spring day04.assets/image-20200406163015651.png)]

  1. 查询当前使用的隔离级别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mk7KSqyh-1673747421011)(Spring day04.assets/image-20200406163149179.png)]

  2. 修改当前使用的隔离级别[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZGwkePr-1673747421011)(Spring day04.assets/image-20200406163503131.png)]

  3. MySQL中开启事务和结束事务

    >start transaction;//开启事务
    >commit;//提交
    >rollback;//回滚
    

隔离级别

准备工作

create table t_account(
	id int primary key,
    money double
);
start transaction;
insert into t_account values(1,100);
insert into t_account values(2,200);
commit;
  1. 读未提交隔离级别[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRqsFGUG-1673747421012)(Spring day04.assets/image-20200406170439133.png)]

    脏读B事务未提交A事务可以读取到B事务插入的数据

  2. 读提交隔离级别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9SrrMkJY-1673747421012)(Spring day04.assets/image-20210508221628304-1624412671148.png)]

    B事务不提交A事务读取不到。B事务提交后A事务就可以读取到。

    仍存在的问题不可重复读和幻读

  3. 可重复读隔离级别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pxcOqJoQ-1673747421013)(Spring day04.assets/image-20210509185633571.png)]

  4. 序列化读

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RjhwJf7f-1673747421013)(Spring day04.assets/image-20200406171658872-1624412785930.png)]

    文章对应源码地址:https://download.csdn.net/download/qq_36827283/87386038

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring