ssm项目开发过程中常用的注解记录

@GetMapping @PostMapping

@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。
@PostMapping是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。

@NoArgsConstructor @AllArgsConstructor @AllArgsConstructor @Data

@NoArgsConstructor:注解在类上;为类提供一个无参的构造方法
@AllArgsConstructor:注解在类上;为类提供一个全参的构造方法
@Data :注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法

@ApiModelProperty()

用于方法,字段; 表示对model属性的说明或者数据操作更改
value–字段说明
name–重写属性名字
dataType–重写属性类型
required–是否必填
example–举例说明
hidden–隐藏

@Transactional 的写法

Spring框架的事务基础架构代码将默认地 只 在抛出运行时异常和unchecked exceptions时才标识事务回滚。 也就是说,当抛出个RuntimeException 或其子类例的实例时。(Errors 也一样 - 默认地 - 标识事务回滚。)从事务方法中抛出的Checked exceptions将 不 被标识进行事务回滚。

1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)

2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

注意: 如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。

注意:@Transactional不能应用在接口方法上
当@Transactional应用在接口方法上
@Transactional注解是基于AOP实现的,进一步说明是基于动态代理实现的,spring会帮我们自动选择是接口代理还是cglib代理,基于接口的代理实现时需要传入被代理类的接口数组,而此时接口数组为空,cglib代理实现的原理是:cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,此时在前置通知执行完成后调用invoke方法时,如果有多个实现类,invoke方法就会报错了。因此执行不管哪种代理,在接口上加@Transactional注解是不可取的

@CrossOrigin

其中@CrossOrigin中的2个参数:
origins :表示允许跨域访问自己的请求发送时所在的域
maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。

@RestController

@RestController注解相当于@ResponseBody + @Controller合在一起的作用

@GeneratedValue注解

JPA(JPA是Java Persistence API的简称,中文名Java持久层API)之@GeneratedValue注解
JPA的@GeneratedValue注解,在JPA中,@GeneratedValue注解存在的意义主要就是为一个实体生成一个唯一标识的主键(JPA要求每一个实体Entity,必须有且只有一个主键),@GeneratedValue提供了主键的生成策略。@GeneratedValue注解有两个属性,分别是strategy和generator,其中generator属性的值是一个字符串,默认为"",其声明了主键生成器的名称(对应于同名的主键生成器@SequenceGenerator和@TableGenerator)。
JPA为开发人员提供了四种主键生成策略,其被定义在枚举类GenerationType中,包括GenerationType.TABLE,GenerationType.SEQUENCE,GenerationType.IDENTITY和GenerationType.AUTO。下面分别介绍这四种主键生成策略。

1.GenerationType.TABLE
使用一个特定的数据库表格来保存主键,持久化引擎通过关系数据库的一张特定的表格来生成主键,这种策略的好处就是不依赖于外部环境和数据库的具体实现,在不同数据库间可以很容易的进行移植,但由于其不能充分利用数据库的特性,所以不会优先使用。该策略一般与另外一个注解一起使用@TableGenerator,@TableGenerator注解指定了生成主键的表(可以在实体类上指定也可以在主键字段或属性上指定),然后JPA将会根据注解内容自动生成一张表作为序列表(或使用现有的序列表)。如果不指定序列表,则会生成一张默认的序列表,表中的列名也是自动生成,数据库上会生成一张名为sequence的表(SEQ_NAME,SEQ_COUNT)。序列表一般只包含两个字段:第一个字段是该生成策略的名称,第二个字段是该关系表的最大序号,它会随着数据的插入逐渐累加。类似于

  1.  @Id
    
  2.  @GeneratedValue(strategy = GenerationType.TABLE, generator = "roleSeq")
    
  3.  @TableGenerator(name = "roleSeq", allocationSize = 1, table = "seq_table", pkColumnName = "seq_id", valueColumnName = "seq_count")
    
  4.  private Integer id;
    

在以上例子中,roleSeq唯一的标识了该生成器,在@GeneratedValue注解中的generator属性可以根据此标识来声明主键生成器。
2.GenerationType.SEQUENCE
在某些数据库中,不支持主键自增长,比如Oracle,其提供了一种叫做"序列(sequence)"的机制生成主键。此时,GenerationType.SEQUENCE就可以作为主键生成策略。该策略的不足之处正好与TABLE相反,由于只有部分数据库(Oracle,PostgreSQL,DB2)支持序列对象,所以该策略一般不应用于其他数据库。类似的,该策略一般与另外一个注解一起使用@SequenceGenerator,@SequenceGenerator注解指定了生成主键的序列.然后JPA会根据注解内容创建一个序列(或使用一个现有的序列)。如果不指定序列,则会自动生成一个序列SEQ_GEN_SEQUENCE。类似于

  1.  @Id
    
  2.  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "menuSeq")
    
  3.  @SequenceGenerator(name = "menuSeq", initialValue = 1, allocationSize = 1, sequenceName = "MENU_SEQUENCE")
    
  4.  private Integer id;
    

同样,在以上例子中,menuSeq唯一的标识了该生成器,@SequenceGenerator可以理解为将数据库中存在的序列进行了一个映射,在@GeneratedValue注解中的generator属性可以根据此标识来声明主键生成器。
3.GenerationType.IDENTITY
此种主键生成策略就是通常所说的主键自增长,数据库在插入数据时,会自动给主键赋值,比如MYSQL可以在创建表时声明"auto_increment" 来指定主键自增长。该策略在大部分数据库中都提供了支持(指定方法或关键字可能不同),但还是有少数数据库不支持,所以可移植性略差。使用自增长主键生成策略是只需要声明strategy = GenerationType.IDENTITY即可。类似于

  1.  @Id
    
  2.  @GeneratedValue(strategy = GenerationType.IDENTITY)
    
  3.  private Integer id;
    

需要注意的是,同一张表中自增列最多只能有一列。

4.GenerationType.AUTO
把主键生成策略交给持久化引擎(persistence engine),持久化引擎会根据数据库在以上三种主键生成策略中选择其中一种。此种主键生成策略比较常用,由于JPA默认的生成策略就是GenerationType.AUTO,所以使用此种策略时.可以显式的指定@GeneratedValue(strategy = GenerationType.AUTO)也可以直接@GeneratedValue
类似于

  1.  @GeneratedValue(strategy = GenerationType.AUTO)
    
  2.  private Integer id;
    

  1.  @GeneratedValue
    
  2.  private Integer id;
    

@RequestParam、@RequestBody和@ModelAttribute区别

一、@RequestParam
GET和POST请求传的参数会自动转换赋值到@RequestParam 所注解的变量上
@RequestParam用于将指定的请求参数赋值给方法中的形参。
一般用来解决前端请求参数与后端接收参数名称不一致情况

@RequestParam
用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容。提交方式为get或post。(Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型)
RequestParam实质是将Request.getParameter() 中的Key-Value参数Map,利用Spring的转化机制——ConversionService配置,转化成参数接收对象或字段。
get方式中query String的值,和post方式中body data的值都会被Servlet接受到并转化到Request.getParameter()参数集中,所以@RequestParam可以获取的到。
二、@RequestBody
@RequestBody注解可以接收json格式的数据,并将其转换成对应的数据类型。

  1. @RequestBody接收一个对象

  2. @RequestBody接收Map

  3. 处理HttpEntity传递过来的数据,一般用来处理非Content-Type: application/x-www-form-urlencoded编码格式的数据。
    GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。
    POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。

三、@ModelAttribute
@ModelAttribute使用详解
1. @ModelAttribute注释方法
例子(1),(2),(3)类似,被@ModelAttribute注释的方法会在此controller每个方法执行前被执行,因此对于一个controller映射多个URL的用法来说,要谨慎使用。

(1)@ModelAttribute注释void返回值的方法

    @Controller
    public class HelloWorldController {
            @ModelAttribute
            public void populateModel(@RequestParam String abc, Model model) {
               model.addAttribute("attributeName", abc);
            }
            @RequestMapping(value = "/helloWorld")
            public String helloWorld() {
               return "helloWorld";
            }
        }

这个例子,在获得请求/helloWorld 后,populateModel方法在helloWorld方法之前先被调用,它把请求参数(/helloWorld?abc=text)加入到一个名为attributeName的model属性中,在它执行后helloWorld被调用,返回视图名helloWorld和model已由@ModelAttribute方法生产好了。
这个例子中model属性名称和model属性对象有model.addAttribute()实现,不过前提是要在方法中加入一个Model类型的参数。
当URL或者post中不包含次参数时,会报错,其实不需要这个方法,完全可以把请求的方法写成,这样缺少此参数也不会出错

	       @RequestMapping(value = "/helloWorld")
	        public String helloWorld(String abc) {
	           return "helloWorld";
	        }

(2)@ModelAttribute注释返回具体类的方法

	@ModelAttribute
	    public Account addAccount(@RequestParam String number) {
	       return accountManager.findAccount(number);
	    }

这种情况,model属性的名称没有指定,它由返回类型隐含表示,如这个方法返回Account类型,那么这个model属性的名称是account。这个例子中model属性名称有返回对象类型隐含表示,model属性对象就是方法的返回值。它无须要特定的参数。

(3)@ModelAttribute(value="")注释返回具体类的方法

1.	@Controller
2.	    public class HelloWorldController {
3.	 
4.	        @ModelAttribute("attributeName")
5.	        public String addAccount(@RequestParam String abc) {
6.	           return abc;
7.	        }
8.	 
9.	        @RequestMapping(value = "/helloWorld")
10.	        public String helloWorld() {
11.	           return "helloWorld";
12.	        }
13.	    }

这个例子中使用@ModelAttribute注释的value属性,来指定model属性的名称。model属性对象就是方法的返回值。它无须要特定的参数。

(4)@ModelAttribute和@RequestMapping同时注释一个方法

1.	@Controller
2.	    public class HelloWorldController {
3.	 
4.	        @RequestMapping(value = "/helloWorld.do")
5.	        @ModelAttribute("attributeName")
6.	        public String helloWorld() {
7.	           return "hi";
8.	        }
9.	    }

这时这个方法的返回值并不是表示一个视图名称,而是model属性的值,视图名称由RequestToViewNameTranslator根据请求"/helloWorld.do"转换为逻辑视图helloWorld。
Model属性名称有@ModelAttribute(value=””)指定,相当于在request中封装了key=attributeName,value=hi。

2.@ModelAttribute注释一个方法的参数
(1)从model中获取

1.	@Controller
2.	    public class HelloWorldController {
3.	 
4.	        @ModelAttribute("user")
5.	        public User addAccount() {
6.	           return new User("jz","123");
7.	        }
8.	 
9.	        @RequestMapping(value = "/helloWorld")
10.	        public String helloWorld(@ModelAttribute("user") User user) {
11.	           user.setUserName("jizhou");
12.	           return "helloWorld";
13.	        }
14.	    }

在这个例子里,@ModelAttribute(“user”) User user注释方法参数,参数user的值来源于addAccount()方法中的model属性。此时如果方法体没有标注@SessionAttributes(“user”),那么scope为request,如果标注了,那么scope为session

(2)从Form表单或URL参数中获取(实际上,不做此注释也能拿到user对象)

1.	@Controller
2.	    public class HelloWorldController {
3.	 
4.	        @RequestMapping(value = "/helloWorld")
5.	        public String helloWorld(@ModelAttribute User user) {
6.	           return "helloWorld";
7.	        }
8.	    }
注意这时候这个User类一定要有没有参数的构造函数。

@Resource

@Service(“UserService”)
public Class UserServiceImpl implements UserService{};

现在你想在UserController 里面使用这个UserServiceImpl
public Class UserController {
@AutoWired //当使用这个注入的时候上面的 UserServiceImpl 只需要这样写 @Service,这样就会自动找到UserService这个类型以及他的子类型。UserServiceImpl 实现了UserService,所以能够找到它。不过这样有一个缺点,就是当UserService实现类有两个以上的时候,这个时候会找哪一个呢,这就造成了冲突,所以要用@AutoWire注入的时候要确保UserService只有一个实现类。
@Resource 默认情况下是按照名称进行匹配,名字默认是userServiceImpl,可以指定如:@Resource(“name”)或者在这里插入图片描述 ,也会根据小写变量名对应的类去实例化bean,如果没有找到相同名称的Bean,则会按照类型进行匹配,根据这个注解的匹配效果可以看出,它进行了两次匹配
这个注解适合一个接口有多个实现类的时候

@Order

@Order(Ordered.HIGHEST_PRECEDENCE)代表这个过滤器在众多过滤器中级别最高,也就是过滤的时候最先执行
而@Order(Ordered.LOWEST_PRECEDENCE)恰恰相反,表示级别最低,最后执行过滤操作。可以与@ControllerAdvice一起用

@annotation

@annotation @within @args用法类似一般用在切面类的切点上
@annotation(com.choice.cloud.smp.basic.lock.Lock)表示被Lock这个注解标注的所有方法都会被切入

@Pointcut("@annotation(com.choice.cloud.smp.basic.lock.Lock)")
public void lockPointcut() {
}

spring的@Value注解使用

1.@Value注解作用
该注解的作用是将我们配置文件的属性读出来,有@Value(“${}”)和@Value(“#{}”)两种方式,区别之后介绍

2.@Value注解作用的两种方式
第一种方式@Value(“${}”):
我使用的是spring boot搭建的项目,配置文件application.propertites已经被加载到了项目中,application.propertites配置属性如下:
在这里插入图片描述
我们读取他的 server.port 属性,springMVC的controller结构如下:
在这里插入图片描述
运行程序 看到:
在这里插入图片描述
属性读取成功

第二种方式 @Value(“#{}”):
修改controller,如图:
在这里插入图片描述
启动程序发现报错:
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field ‘server’ cannot be found on object of type ‘org.springframework.beans.factory.config.BeanExpressionContext’ - maybe not public?
解决之前 说一下${}和#{}区别.
@Value的值有两类:
① ${ property : default_value }
② #{ obj.property? :default_value }
第一个注入的是外部配置文件对应的property,第二个则是SpEL表达式对应的内容。 那个
default_value,就是前面的值为空时的默认值。注意二者的不同,#{}里面那个obj代表对象。
好了,知道了#{}的用法 我们改进一下,如图 准备一个实体类,并且注册到sping中:

在这里插入图片描述

修改controller,userBean为UserBean实体类在spring容器注册的默认id,详情自行阅读spring ioc。修改后如下图
在这里插入图片描述运行结果:
在这里插入图片描述
运行成功。
3.注意事项
将配置文件交给sping加载,最好不要交给springMVC加载 避免出现错误,因为web.xml配置时spring的监听先启动,springMVC的Dispatcherservlet接收到请求时初始化springMVC的配置文件。

@Transient

一般来说用@Transient是希望该属性不会在数据表中产生字段,但又可以在程序中使用它
@Transient的作用是指定该属性或字段不是永久的。 它用于注释实体类,映射超类或可嵌入类的属性或字段。

当实体类有@Data注解的时候没有set get方法,@Transient可以写在属性上面,当有get方法的时候就必须写在属性的get方法上。
我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。

参与开发的一个项目中重写了RunTimeException类,在重写方法的时候有以下代码:
在这里插入图片描述
对于这个的解释如下:
在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。

@PostConstruct

1、从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct和@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。写法有如下两种方式:
@PostConstruct
public void someMethod(){}
或者
public @PostConstruct void someMethod(){}
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。PreDestroy()方法在destroy()方法知性之后执行
在这里插入图片描述
另外,spring中Constructor、@Autowired、@PostConstruct的顺序
其实从依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象a和对象p,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowried注解,那么@Autowired注入是发生在A的构造方法执行完之后的。
如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么久无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
Constructor >> @Autowired >> @PostConstruct
举个栗子:

public Class AAA {
    @Autowired
    private BBB b;

    public AAA() {
        System.out.println("此时b还未被注入: b = " + b);
    }
    @PostConstruct
    private void init() {
        System.out.println("@PostConstruct将在依赖注入完成后被自动调用: b = " + b);
    }
}

@ConfigurationProperties

有时候有这样子的情景,我们想把配置文件的信息,读取并自动封装成实体类,这样子,我们在代码里面使用就轻松方便多了,这时候,我们就可以使用@ConfigurationProperties(SpringBoot中的注解),它可以把同类的配置信息自动封装成实体类
首先在配置文件里面,这些信息是这样子滴
connection.username=admin
connection.password=kyjufskifas2jsfs
connection.remoteAddress=192.168.1.1
这时候我们可以定义一个实体类在装载配置文件信息

@Component
@ConfigurationProperties(prefix="connection")  //此处的前缀指的是配置文件里键值对中键的前缀
public class ConnectionSettings {

   private String username;
   private String remoteAddress;
   private String password ;

   public String getUsername() {
       return username;
   }
   public void setUsername(String username) {
       this.username = username;
   }
   public String getRemoteAddress() {
       return remoteAddress;
   }
   public void setRemoteAddress(String remoteAddress) {
       this.remoteAddress = remoteAddress;
   }
   public String getPassword() {
       return password;
   }
   public void setPassword(String password) {
       this.password = password;
   }

}

我们还可以把@ConfigurationProperties直接定义在@Bean注解上,这时bean实体类(ConnectionSettings)就不用@Component和@ConfigurationProperties了

@SpringBootApplication
public class DemoApplication{

   //...

   @Bean
   @ConfigurationProperties(prefix = "connection")
   public ConnectionSettings connectionSettings(){
       return new ConnectionSettings();
   }

   public static void main(String[] args) {
       SpringApplication.run(DemoApplication.class, args);
   }
}

然后我们需要使用的时候就直接这样子注入

@RestController
@RequestMapping("/task")
public class TaskController {

@Autowired 
ConnectionSettings conn;

@RequestMapping(value = {"/",""})
public String hellTask(){
   String userName = conn.getUsername();     
   return "hello task !!";
}

}

如果发现@ConfigurationPropertie不生效,有可能是项目的目录结构问题,
你可以通过@EnableConfigurationProperties(ConnectionSettings.class)来明确指定需要用哪个实体类来装载配置信息

@Qualifier用法(Spring的注解)

在Controller中需要注入service那么我的这个server有两个实现类如何区分开这两个impl呢?
根据注入资源的注解不同实现的方式有一点小小的区别
在这里插入图片描述

在Controller中使用 @Autowired注入时
在这里插入图片描述

Qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,添加@Qualifier注解,需要注意的是@Qualifier的参数名称为我们之前定义@Service注解的名称之一。

使用@Resource注入时
在这里插入图片描述
使用@resource注入时比较简单了,注解自带了“name”的val就是@Service注解的名称。

@EqualsAndHashCode(callSuper=true)

@Data相当于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode这5个注解的合集。
通过官方文档,可以得知,当使用@Data注解时,则有了@EqualsAndHashCode注解,那么就会在此类中存在equals(Object other) 和 hashCode()方法,且不会使用父类的属性,这就导致了可能的问题。
比如,有多个类有相同的部分属性,把它们定义到父类中,恰好id(数据库主键)也在父类中,那么就会存在部分对象在比较时,它们并不相等,却因为lombok自动生成的equals(Object other) 和 hashCode()方法判定为相等,从而导致出错。

修复此问题的方法很简单:

  1. 使用@Getter @Setter @ToString代替@Data并且自定义equals(Object other) 和 hashCode()方法,比如有些类只需要判断主键id是否相等即足矣。
  2. 或者使用在使用@Data时同时加上@EqualsAndHashCode(callSuper=true)注解。

@JsonProperty

此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name,@JsonProperty(“name”)。

@ConditionalOnExpression

在开发中会遇到一些需求:在配置文件中设置一个enable,当这个配置为true的时候,才进行相关的配置类的初始化。

示例:

需要实例化的bean,请不要加@Component注解

public class TestBean {
  
  public TestBean(){
    
  }
  
  public doSomeThing(){
    
  }
}

配置类:

@Configuration
@ConditionalOnExpression("${test.enabled:true}")
public class TestConfiguration {
    @Bean
    public TestBean testBean() {
        return new TestBean();
    }
}

配置文件:
test.enabled: true
这个bean只有在test.enabled: true的时候才会进行初始化。

@ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
    //Spring Boot的自动配置机制依靠@ConditionalOnMissingBean注解判断是否执行初始化代码,
    // 即如果用户已经创建了bean,则相关的初始化代码不再执行。

@Primary

当你一个接口的实现类有多个的时候,你通过@Component来注册你的实现类有多个,但是在注入的时候使用@Autowired,但是Spring就不知道你注入哪个,那现在就可以通过下面两个办法解决:
@Primary 优先考虑,优先考虑被注解的对象注入
@Qualifier 名字声明,声明后对名字进行使用

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值