04.spring查询整张表、事务、注解
一、spring查询整张表
案例:
model层 UsersInfo.java
import java.util.Date;
public class UsersInfo {
private Integer userId;
private String userName;
private String userPwd;
private Integer userBalance;
private Date userTime;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
public Integer getUserBalance() {
return userBalance;
}
public void setUserBalance(Integer userBalance) {
this.userBalance = userBalance;
}
public Date getUserTime() {
return userTime;
}
public void setUserTime(Date userTime) {
this.userTime = userTime;
}
public UsersInfo() {
}
public UsersInfo( String userName, String userPwd, Integer userBalance, Date userTime) {
this.userName = userName;
this.userPwd = userPwd;
this.userBalance = userBalance;
this.userTime = userTime;
}
@Override
public String toString() {
return "UsersInfo{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userPwd='" + userPwd + '\'' +
", userBalance=" + userBalance +
", userTime=" + userTime +
'}';
}
}
dao层 UserInfoDao.java
import org.springframework.jdbc.core.JdbcTemplate;
public class UserInfoDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 查询所有的信息
* @return 用户信息集合
*/
public List<UsersInfo> queryUserAll(){
String sql = "select * from user_info";
List<UsersInfo> query = jdbcTemplate.query(sql, new MyRowMapper());
return query;
}
}
// 实现查询所有方法的
class MyRowMapper implements RowMapper<UsersInfo>{
@Override
public UsersInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
UsersInfo ui = new UsersInfo();
ui.setUserId(rs.getInt(1));
ui.setUserName(rs.getString(2));
ui.setUserPwd(rs.getString(3));
ui.setUserBalance(rs.getInt(4));
ui.setUserTime(rs.getDate(5));
return ui;
}
}
service层 UserInfoService.java
public class UserInfoService {
private UserInfoDao userInfoDao;
public void setUserInfoDao(UserInfoDao userInfoDao) {
this.userInfoDao = userInfoDao;
}
/**
* 查询所有信息
* @return 返回用户集合对象
*/
public List<UsersInfo> queryAllUserInfo(){
return userInfoDao.queryUserAll();
}
}
测试
// 测试集合类型的查询
@Test
public void test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoService uis = ac.getBean(UserInfoService.class);
List<UsersInfo> list = uis.queryAllUserInfo();
for (UsersInfo ui:list){
System.out.println(ui);
}
}
配置文件
db.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.pwd=123456
jdbc.url=jdbc:mysql:///user_db?characterEncoding=UTF-8
applicationContext.xml
<!--引入属性文件-->
<context:property-placeholder location="db.properties"/>
<!--创建数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
<!--创建SpringJDBCTemplate对数据库进行访问-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean name="userInfoDao" class="com.yl.dao.UserInfoDao" autowire="byType"/>
<bean name="userInfoService" class="com.yl.service.UserInfoService" autowire="byType"/>
二、事务
Spring 的事务属性分别为传播行为、隔离级别、只读和超时属性,这些属性提供了事务应用的方法和描述策略
事务管理的三个核心接口:PlatformTransactionManager、TransactionDefinition 和 TransactionStatus
四大特性:
- **原子性:**事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
- **一致性:**这表示数据库的引用完整性的一致性,表中唯一的主键等。
- **隔离性:**可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
- **持久性:**一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。
三个核心接口的作用及其提供的方法
1.PlatformTransactionManager
PlatformTransactionManager 接口是 Spring 提供的平台事务管理器,用于管理事务。该接口中提供了三个事务操作方法。
- TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息。
- void commit(TransactionStatus status):用于提交事务。
- void rollback(TransactionStatus status):用于回滚事务。
2.TransactionDefinition
TransactionDefinition 接口是事务定义(描述)的对象,它提供了事务相关信息获取的方法,其中包括五个操作。
- String getName():获取事务对象名称。
- int getIsolationLevel():获取事务的隔离级别。
- int getPropagationBehavior():获取事务的传播行为。
- int getTimeout():获取事务的超时时间。
- boolean isReadOnly():获取事务是否只读。
传播行为的种类
属性名称 | 值 | 描 述 |
---|---|---|
PROPAGATION_REQUIRED | required | 支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将创建新事务 |
PROPAGATION_SUPPORTS | supports | 支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将以非事务状态执行 |
PROPAGATION_MANDATORY | mandatory | 支持当前事务。如果 A 方法没有事务,则抛出异常 |
PROPAGATION_REQUIRES_NEW | requires_new | 将创建新的事务,如果 A 方法已经在事务中,则将 A 事务挂起 |
PROPAGATION_NOT_SUPPORTED | not_supported | 不支持当前事务,总是以非事务状态执行。如果 A 方法已经在事务中,则将其挂起 |
PROPAGATION_NEVER | never | 不支持当前事务,如果 A 方法在事务中,则抛出异常 |
PROPAGATION.NESTED | nested | 嵌套事务,底层将使用 Savepoint 形成嵌套事务 |
3. TransactionStatus
TransactionStatus 接口是事务的状态,它描述了某一时间点上事务的状态信息。其中包含六个操作
事务的操作
名称 | 说明 |
---|---|
void flush() | 刷新事务 |
boolean hasSavepoint() | 获取是否存在保存点 |
boolean isCompleted() | 获取事务是否完成 |
boolean isNewTransaction() | 获取是否是新事务 |
boolean isRollbackOnly() | 获取是否回滚 |
void setRollbackOnly() | 设置事务回滚 |
案例:
dao层
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class UserInfoDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 修改用户余额方法
* @param userName 用户名
* @param money 钱
* @return 返回受影响行数
*/
public int updateUserBalanceByUserName(String userName,Integer money){
return jdbcTemplate.update(" UPDATE user_info SET user_balance = user_balance + ? WHERE user_name = ? ",money,userName);
}
/**
* 根据用户名查询用户余额
* @param userName 用户名
* @return 返回金额
*/
public Integer selectUserInfoBalanceByUserName(String userName){
return jdbcTemplate.queryForObject(" SELECT user_balance FROM user_info WHERE user_name = ? ",Integer.class,userName);
}
}
service 层
import java.util.List;
public class UserInfoService {
private UserInfoDao userInfoDao;
public void setUserInfoDao(UserInfoDao userInfoDao) {
this.userInfoDao = userInfoDao;
}
//事务需要具有一致性,要么都成功,要么都失败!
/**
* 转账功能
* @param un1 转入的用户
* @param un2 转出的用户
* @param money 金额
* @return 成功就返回 true
*/
public boolean zhuanZhang(String un1,String un2,Integer money){
//un1加钱
userInfoDao.updateUserBalanceByUserName(un1,money);
//判断余额
if(getUserBalanceByUserName(un2)<money){
//当余额不足,则抛出余额不足异常
throw new BalanceBuZu();
}else{
//un2减钱
userInfoDao.updateUserBalanceByUserName(un2,-money);
}
return true;
}
/**
* 根据用户账号查询余额
* @param userName 用户名
* @return 返回该用户的金额
*/
public Integer getUserBalanceByUserName(String userName){
return userInfoDao.selectUserInfoBalanceByUserName(userName);
}
}
/**
* 自定义异常 RuntimeExceptionL:只有这个异常会回滚
*/
class BalanceBuZu extends RuntimeException{
public BalanceBuZu() {
super("余额不足");
}
}
测试
// 测试 事务
@Test
public void test2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoService uis = ac.getBean(UserInfoService.class);
//至少两段数据库操作,一个账户减钱,一个账户加钱
boolean bl = uis.zhuanZhang("admin1","admin2",500);
System.out.println(bl?"转账成功":"转账失败");
}
配置
<?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:tx="http://www.springframework.org/schema/tx"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--引入属性文件-->
<context:property-placeholder location="db.properties"/>
<!--创建数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
<!--创建SpringJDBCTemplate对数据库进行访问-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean name="userInfoDao" class="com.yl.dao.UserInfoDao" autowire="byType"/>
<bean name="userInfoService" class="com.yl.service.UserInfoService" autowire="byType"/>
<!-- 事务 -->
<!--
Spring基于AOP的声明式事务
Spring事务管理器
dataSource:指定该事务管理器管理的数据源对象
-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务的通知 -->
<tx:advice id="myAdvice01" transaction-manager="transactionManager">
<!-- 设置SpringAOP事务的相关属性,暂时就这样写 -->
<tx:attributes>
<!--
该策略应用到哪些方法上
name:方法名称
*所有方法
aaaXXX
propagation:指定事务的传播行为
REQUIRED_NEW:匹配该name的所有方法都自己开启一个新事务
REQUIRED:匹配该name的所有方法都是用之前的事务
isolation:指定事务的隔离级别
READ_UNCOMMITED:读未提交
READ_COMMITTED:读已提交
REPEATABLE_READ:可重复读
SERIALIZABLE:序列化
rollback-for:哪些异常回滚,RuntimeException
no-rollback-for:哪些异常不回滚
read-only:针对于查询方法,里面所有的数据库操作只能有读取(select),Spring不会为这些方法开启事务,以提成程序的性能
timeout:设置事务的超时时间,超过该时间之后,事务直接回滚
-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 织入事务通知到目标方法上 -->
<aop:config>
<aop:advisor advice-ref="myAdvice01" pointcut="execution(* com.yl.service.UserInfoService.*(..))"/>
</aop:config>
</beans>
官方案例:
1. 实现 DAO
1)创建 AccountDao 接口
public interface AccountDao {
// 汇款
public void out(String outUser, int money);
// 收款
public void in(String inUser, int money);
}
2)创建DAO层接口实现类
import org.springframework.jdbc.core.JdbcTemplate;
import com.mengma.dao.AccountDao;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 汇款的实现方法
public void out(String outUser, int money) {
this.jdbcTemplate.update("update account set money =money-?"
+ "where username =?", money, outUser);
}
// 收款的实现方法
public void in(String inUser, int money) {
this.jdbcTemplate.update("update account set money =money+?"
+ "where username =?", money, inUser);
}
}
2. 实现 Service
1)创建 Service 层接口
public interface AccountService {
// 转账
public void transfer(String outUser, String inUser, int money);
}
2)创建 Service 层接口实现类
public class AccountServiceImpl {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outUser, String inUser, int money) {
this.accountDao.out(outUser, money);
this.accountDao.in(inUser, money);
}
}
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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- 加载properties文件 -->
<context:property-placeholder location="classpath:c3p0-db.properties" />
<!-- 配置数据源,读取properties文件信息 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}" />
<property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置dao -->
<bean id="accountDao" class="com.mengma.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!-- 配置service -->
<bean id="accountService" class="com.mengma.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!-- 事务管理器,依赖于数据源 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 编写通知:对事务进行增强(通知),需要编写切入点和具体执行事务的细节 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 给切入点方法添加事务详情,name表示方法名称,*表示任意方法名称,propagation用于设置传播行为,read-only表示隔离级别,是否只读 -->
<tx:method name="find*" propagation="SUPPORTS"
rollback-for="Exception" />
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"
read-only="false" />
</tx:attributes>
</tx:advice>
<!-- aop编写,让Spring自动对目标生成代理,需要使用AspectJ的表达式 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* com.mengma.service.*.*(..))"
id="txPointCut" />
<!-- 切面:将切入点与通知整合 -->
<aop:advisor pointcut-ref="txPointCut" advice-ref="txAdvice" />
</aop:config>
</beans>
测试
public void test() {
// 获得Spring容器,并操作
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
xmlPath);
AccountService accountService = (AccountService) applicationContext
.getBean("accountService");
accountService.transfer("zhangsan", "lisi", 100);
}
三、注解
在spring4之后,想要使用注解形式,必须得要引入aop的包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
在配置文件当中,还得要引入一个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">
</beans>
Bean的实现
我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!
1、配置扫描哪些包下的注解
<!--指定注解扫描包-->
<context:component-scan base-package="com.kuang.pojo"/>
2、在指定包下编写类,增加注解
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
public String name = "小果果";
}
3、测试
@Test
public void test(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("beans.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
属性注入
使用注解注入属性
1、可以不用提供set方法,直接在直接名上添加@value(“值”)
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Value("小果果")
// 相当于配置文件中 <property name="name" value="小果果"/>
public String name;
}
2、如果提供了set方法,在set方法上添加@value(“值”);
@Component("user")
public class User {
public String name;
@Value("小果果")
public void setName(String name) {
this.name = name;
}
}
衍生注解
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
- @Controller:web层
- @Service:service层
- @Repository:dao层
写上这些注解,就相当于将这个类交给Spring管理装配了!
自动装配注解
在Bean的自动装配已经讲过了,可以回顾!
让Spring管理某些类
1、在需要被SpringIOC容器管理的类上打上相应的注解
@Component:任意组件
@Controller:控制层组件注解
@Service:服务层组件注解
@Repository:持久层组件注解
value:如果不指定该属性,则被注解的类在SpringIOC容器默认的注册名称为类名首字母小写,如果指定之后则不使用默认名称注册
2、在XML文件中开启注解驱动
context:component-scan:配置扫描器扫描的包,扫描器会自动扫描包下被打上了组件注解的类,并加入到SpringIOC容器中
设置该类在被SpringIOC容器创建时的初始值
@Value:设置该属性的初始值,如果注入引用值,则需要使用SpEL
懒加载
@Lazy:打上该注解之后,默认值为true,进行懒加载,只有在使用的时候才回去初始化,如果没打该注解,则会在SpringIOC容器初始化时一并初始化
作用域(默认是单例的)
@Scope:作用域,指定该类的对象创建出来是单例的还是非单例的的singleton,prototype
自动注入属性
@Autowired:自动注入属性,前提是被注入的内容也需要被SpringIOC容器管理
根据名称注入
@Qualifier:设置引用的Bean的name
vlaue:指定name
1、什么是注解?
(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化xml配置
2、Spring针对Bean管理创建对象提供注解
(1)@Component:任意组件
(2)@Service:服务层组件注解
(3)@Controller:控制层组件注解
(4)@Repository:持久层组件注解
*上面四个注解功能是一样的,都可以用来创建bean实例
3、基于注解方式实现对象创建
<!-- 开启组件扫描
1、如果要扫描多个包,多个包使用逗号隔开
2、扫描上层目录
-->
<context:component-scan base-package="com.yl"/>
4、开启组件扫描细节配置
<!--
示列1
use-default-filters="false" 表示现在不使用默认filter,自己配置filter
context:include-filter 扫描哪些内容
-->
<context:component-scan base-package="com.yl" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
<!--
示列2
下面配置扫描包所有内容
context:exclude-filter:设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.yl">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
作用域
@scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user")
@Scope("prototype")
public class User {
@Value("小果果")
public String name;
}
小结
5、XML与注解比较
- XML可以适用任何场景 ,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :推荐最佳实践
- xml管理Bean
- 注解完成属性注入
- 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>
作用:
- 进行注解驱动注册,从而使注解生效
- 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
- 如果不扫描包,就需要手动配置bean
- 如果不加注解驱动,则注入的值为null!
基于Java类进行配置
JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。
测试:
1、编写一个实体类,Dog
@Component //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
public String name = "dog";
}
2、新建一个config配置包,编写一个MyConfig配置类
@Configuration //代表这是一个配置类
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public Dog dog(){
return new Dog();
}
}
3、测试
@Test
public void test2(){
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) applicationContext.getBean("dog");
System.out.println(dog.name);
}
4、成功输出结果!
导入其他配置如何做呢?
1、我们再编写一个配置类!
@Configuration //代表这是一个配置类
public class MyConfig2 {
}
2、在之前的配置类中我们来选择导入这个配置类
@Configuration
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
@Bean
public Dog dog(){
return new Dog();
}
}
关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!