spring中的拦截器和配置的演变

Interceptor类

SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。

(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。

(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行,这和Struts2 里面的Interceptor 的执行过程有点类型。Struts2 里面的Interceptor 的执行过程也是链式的,只是在Struts2 里面需要手动调用ActionInvocation 的invoke 方法来触发对下一个Interceptor 或者是Action 的调用,然后每一个Interceptor 中在invoke 方法调用之前的内容都是按照声明顺序执行的,而invoke 方法之后的内容就是反向的。

(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。

拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可 而 Filter 是被 Server(like Tomcat) 调用 Filter 不能够使用 Spring 容器资源,Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。Filter 定义在 web.xml 中,Filter在只在 Servlet 前后起作用。Filters 通常将 请求和响应(request/response) 当做黑盒子,Filter 通常不考虑servlet 的实现。

Interceptor使用

interceptor 的执行顺序大致为:

  1. 请求到达 DispatcherServlet
  2. DispatcherServlet 发送至 Interceptor ,执行 preHandle
  3. 请求达到 Controller
  4. 请求结束后,postHandle 执行

Spring 中主要通过 HandlerInterceptor 接口来实现请求的拦截,实现 HandlerInterceptor 接口需要实现下面三个方法:

  • preHandle() – 在handler执行之前,返回 boolean 值,true 表示继续执行,false 为停止执行并返回。

  • postHandle() – 在handler执行之后, 可以在返回之前对返回的结果进行修改

  • afterCompletion() – 在请求完全结束后调用,可以用来统计请求耗时等等

    //登陆拦截器

    public class LoginInterceptor implements HandlerInterceptor {

    /**

    • 在控制器执行之前完成业务逻辑操作

    • 方法的返回值决定逻辑是否继续执行, true,表示继续执行, false, 表示不再继续执行。
      */
      public boolean preHandle(HttpServletRequest request,
      HttpServletResponse response, Object handler) throws Exception {

      // 判断当前用户是否已经登陆
      HttpSession session = request.getSession();
      User loginUser = (User)session.getAttribute(“loginUser”);

      if ( loginUser == null ) {
      String path = session.getServletContext().getContextPath();
      response.sendRedirect(path + “/login”);
      return false;
      } else {
      return true;
      }
      }

    /**

    • 在Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用
      */
      public void postHandle(HttpServletRequest request,
      HttpServletResponse response, Object handler,
      ModelAndView modelAndView) throws Exception {
      // TODO Auto-generated method stub
      }

    /**

    • 在完成视图渲染之后,执行此方法。
      */
      public void afterCompletion(HttpServletRequest request,
      HttpServletResponse response, Object handler, Exception ex)
      throws Exception {
      }
      }

日志拦截器

/**
* 用于记录日志,在每次请求之前,打印请求的地址和参数,方便调试
 */

public class LogInterceptor implements HandlerInterceptor {

	/** 记录日志 */
	private static Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
	
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response, 
								Object arg2) throws Exception {
		StringBuilder sb = new StringBuilder();
		String uri = request.getRequestURI();
		sb.append("---------------> demo uri:").append(uri).append(" - ");

		Enumeration<String> enums2 = request.getParameterNames();
		while (enums2.hasMoreElements()) {
			String key = enums2.nextElement();
			sb.append("\"").append(key).append("\":").append(
					request.getParameter(key)).append(", ");
		}
		logger.info(sb.toString());
		return true;
	}	

	@Override
	public void postHandle(HttpServletRequest request,HttpServletResponse response, 
								Object arg2, ModelAndView arg3)throws Exception {
	
	}

	@Override
	public void afterCompletion(HttpServletRequest request,HttpServletResponse response, 
								Object arg2, Exception arg3) throws Exception {
	
	}
}

授权访问Url拦截器

	//此处采用继承抽象类的方式,不是实现Interceptor方式
public class AuthInterceptor extends HandlerInterceptorAdapter {
	@Autowired
	private PermissionService permissionService;
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
										Object handler) throws Exception {
		// 获取用户的请求地址
		String uri = request.getRequestURI();
		String path = request.getSession().getServletContext().getContextPath();
		
		// 判断当前路径是否需要进行权限验证。
		// 查询所有需要验证的路径集合
		List<Permission> permissions = permissionService.queryAll();
		Set<String> uriSet = new HashSet<String>();
		for ( Permission permission : permissions ) {
			if ( permission.getUrl() != null && !"".equals(permission.getUrl()) ) {
				uriSet.add(path + permission.getUrl());
			}
		}
		
		if ( uriSet.contains(uri) ) {
			// 权限验证
			// 判断当前用户是否拥有对应的权限
			Set<String> authUriSet = (Set<String>)request.getSession()
														.getAttribute("authUriSet");
			if ( authUriSet.contains(uri) ) {
				return true;
			} else {
				response.sendRedirect(path + "/error");
				return false;
			}
		} else {
			return true;
		}
	}
}
在spring中注册使用

需要把上面的这些拦截器给注册到spring中使用,在springMVC.xml中配置如下:

<mvc:interceptors>
<mvc:interceptor>
    <mvc:mapping path="/**" />
    <bean class="site.gaoyisheng.filter.LoginHandlerInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptor>
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <mvc:exclude-mapping path="/login" />
        <mvc:exclude-mapping path="/bootstrap/**" />
        <mvc:exclude-mapping path="/css/**" />
        <mvc:exclude-mapping path="/fonts/**" />
        <mvc:exclude-mapping path="/img/**" />
        <mvc:exclude-mapping path="/jquery/**" />
        <mvc:exclude-mapping path="/layer/**" />
        <mvc:exclude-mapping path="/script/**" />
        <mvc:exclude-mapping path="/ztree/**" />
        //该拦截器不对静态资源进行拦截
        <bean class="com.scorpios.atcrowdfunding.web.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>
在springboot中注册使用:
/**
 * 扩展SpringMVC
 * SpringBoot2使用的Spring5,因此将WebMvcConfigurerAdapter改为WebMvcConfigurer
 * 使用WebMvcConfigurer扩展SpringMVC好处既保留了SpringBoot的自动配置,又能用到我们自己的配置
 */
 //@EnableWebMvc //如果我们需要全面接管SpringBoot中的SpringMVC配置则开启此注解,
                 //开启后,SpringMVC的自动配置将会失效。
 @Configuration
 public class WebConfig implements WebMvcConfigurer {
     @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //设置对“/”的请求映射到index
         //如果没有数据返回到页面,没有必要用控制器方法对请求进行映射
        registry.addViewController("/").setViewName("index");
    }
   /**
     * 跨域支持
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .maxAge(3600 * 24);
    }

  @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
         // 默认访问"/"到"index.html"
         registry.addResourceHandler("/index.html")
                .addResourceLocations("/");
          // 资源处理 添加静态资源处理器 ,自定义静态资源映射目录
          registry.addResourceHandler("/**")
                  .addResourceLocations("classpath:/static/");
           // 配置映射资源,访问这个路径直接映射到文件 拦截器
    	registry.addResourceHandler("/image/**").addResourceLocations("file:E:/upload/image/");
      }
     //注册拦截器
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
        //SpringMVC下,拦截器的注册需要排除对静态资源的拦截(*.css,*.js)
        //SpringBoot已经做好了静态资源的映射
         registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
     }
 }

WebMvcConfigurer接口(spring 4.0时)

定义回调方法以自定义通过启用的Spring MVC的基于Java的配置

@EnableWebMvc-带注释的配置类可以实现此接口以使其被回调,并有机会自定义默认配置

主要方法:

void addFormatters(FormatterRegistry var1);

void configureMessageConverters(List<HttpMessageConverter<?>> var1);

void extendMessageConverters(List<HttpMessageConverter<?>> var1);

Validator getValidator();

void configureContentNegotiation(ContentNegotiationConfigurer var1);

void configureAsyncSupport(AsyncSupportConfigurer var1);

void configurePathMatch(PathMatchConfigurer var1);

void addArgumentResolvers(List<HandlerMethodArgumentResolver> var1);

void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> var1);

void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);

void addInterceptors(InterceptorRegistry var1);

MessageCodesResolver getMessageCodesResolver();

void addViewControllers(ViewControllerRegistry var1);

void configureViewResolvers(ViewResolverRegistry var1);

void addResourceHandlers(ResourceHandlerRegistry var1);

void configureDefaultServletHandling(DefaultServletHandlerConfigurer var1);

实现类:WebMvcConfigurerAdapter(5.0之后被弃用了,不推荐使用但是还可以用)

其实这个WebMvcConfigurerAdepter只是WebMvcConfigurer接口的一个实现类。是一个抽象类,里面是和父接口一样的方法。所以在spring5.0之后去除了WebMvcConfigurerAdapter这个类。当我们使用时直接实现WebMvcConfigurer接口即可,然后实现其中的方法。

Defines callback methods to customize the Java-based configuration for Spring MVC enabled via @EnableWebMvc.从5.0开始,WebMvcConfigurer具有默认方法(由Java 8基准实现),可以直接实现而无需此适配器

实现类 WebMvcConfigurerComposite (5.0后弃用)

主要方法如下:

 // 
   public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
          if (configurers != null) {
              this.delegates.addAll(configurers);
          }

      }
	// 注册自定义转化器  注册自定义的Formatter和Convert
    public void addFormatters(FormatterRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addFormatters(registry);
        }

    }
     拦截器配置,添加springmvc拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addInterceptors(registry);
        }

    }
	 添加自定义视图控制器 ,视图跳转控制 而无需书写controller
    public void addViewControllers(ViewControllerRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addViewControllers(registry);
        }

    }
	// 该方法的参数ViewResolverRegistry 是一个注册器,用来注册你想自定义的视图解析器等。
    public void configureViewResolvers(ViewResolverRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.configureViewResolvers(registry);
        }

    }
	// // 资源处理 添加静态资源处理器 ,自定义静态资源映射目录
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addResourceHandlers(registry);
        }

    }
使用WebMvcConfigurer:
/**
 * Spring 5 (或者Spring Boot 2.x)版本配置Web应用程序示例
 */
@Configuration
public class MvcConfigure implements WebMvcConfigurer{
 	@Override
    public void addFormatters(FormatterRegistry formatterRegistry) {
        // 注册自定义转化器  注册自定义的Formatter和Convert
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
		// 配置消息转换器。重载会覆盖默认注册的HttpMessageConverter
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
		// 配置消息转换器。仅添加一个自定义的HttpMessageConverter
    }

    @Override
    public Validator getValidator() {
    	// 
        return null;
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {
		//  内容协商配置
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
		// 
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {
    	// 配置路由请求规则
 		configurer.setUseSuffixPatternMatch(false);
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
		// 添加自定义方法参数处理器
    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
		// 添加自定义返回结果处理器
    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
		// 配置异常转换器
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
		// 拦截器配置,添加springmvc拦截器
      // 如果未配置了静态资源映射可以把排除拦截中的路径写静态资源路径用.excludePathPatterns("/static/**")排除静态资源路径,
		registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/js/**","/css/**","/images/**","/index.html","/","/user/login","/static/**");
		// addPathPatterns("/**")对所有请求都拦截,但是排除了/user/login和/login请求的拦截
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
    	// 
        return null;
    }
	/**
	 * 以前要访问一个页面需要先创建个Controller控制类,再写方法跳转到页面
     * 在这里配置后就不需要那么麻烦了,直接访问http://localhost:8080/toLogin就跳转到login.jsp页面了
     **/
    @Override
    public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
		// 添加自定义视图控制器 ,视图跳转控制 而无需书写controller
		viewControllerRegistry.addViewController("/toLogin").setViewName("login")
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
		// 从方法名称我们就能看出这个方法是用来配置视图解析器的,该方法的参数ViewResolverRegistry 是一个注册器,用来注册你想自定义的视图解析器等。
        	
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    	// 资源处理 添加静态资源处理器 ,自定义静态资源映射目录
      	// 自定义后在注册拦截器时不需要使用 排除路径的方法
		registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/public/")
                .addResourceLocations("classpath:/resources/");
         // 配置映射资源,访问这个路径直接映射到文件 拦截器
         registry.addResourceHandler("/image/**").addResourceLocations("file:E:/upload/image/");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
		// 默认静态资源处理器 默认seevlet处理
    }
  
}
使用 WebMvcConfigurationSupport

WebMvcConfigurationSupport是一个提供了以Java编程方式来配置Web应用程序的配置主类,

public class WebMvcConfigurationSupport implements ApplicationContextAware,ServletContextAware{}



1、springboot默认可以访问以下路径文件(见ResourceProperties):
    classpath:/static
    classpath:/public
    classpath:/resources
    classpath:/META-INF/resources
   当使用了@EnableWebMvc时,默认的静态资源访问无效了因为默认情况下mvc使用的配置是WebMvcAutoConfiguration,加入该配置变成了WebMvcConfigurationSupport
2、@EnableWebMvc、WebMvcConfigurationSupport、WebMvcConfigurationAdapter
    @EnableWebMvc=WebMvcConfigurationSupport,使用了@EnableWebMvc注解等于扩展了WebMvcConfigurationSupport但是没有重写任何方法
    @EnableWebMvc+extends WebMvcConfigurationAdapter,在扩展的类中重写父类的方法即可,这种方式会屏蔽springboot的WebMvcAutoConfiguration中的设置
    @EnableWebMvc+extends WebMvcConfigurationSupport 只会使用@EnableWebMvc
    extends WebMvcConfigurationSupport,在扩展的类中重写父类的方法即可,这种方式会屏蔽springboot的@WebMvcAutoConfiguration中的设置
    extends WebMvcConfigurationAdapter,在扩展的类中重写父类的方法即可,这种方式依旧使用springboot的WebMvcAutoConfiguration中的设置
    在springboot2.x中,WebMvcConfigurationAdapter已经过时,通过实现接口WebMvcConfigurer可以替代原有规则


public class MyWebMvcConfiguer extends WebMvcConfigurationSupport {
    public MyWebMvcConfiguer() {
        super();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        super.setApplicationContext(applicationContext);
        //  设置ApplicationContext
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        super.setServletContext(servletContext);
        // 设置域
    }
	/**
     * 拦截器配置
     *
     * @param registry 注册类
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
    	registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/js/**","/css/**","/images/**","/index.html","/","/user/login","/static/**");
		// addPathPatterns("/**")对所有请求都拦截,但是排除了/user/login和/login请求的拦截
        super.addInterceptors(registry);
        
    }
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        super.configurePathMatch(configurer);
        //配置路径匹配选项。
    }

    @Override
    protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        super.configureContentNegotiation(configurer);
        // 配置内容协商
    }

    @Override
    protected void addViewControllers(ViewControllerRegistry registry) {
        super.addViewControllers(registry);
        // 添加视图控制器
    }

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        // 添加用于处理静态资源的资源处理程序
    }

    @Override
    protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        super.configureDefaultServletHandling(configurer);
        // 配置“默认” Servlet处理
    }

    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        super.addArgumentResolvers(argumentResolvers);
        // 除了默认注册的方法外,还添加要使用的方法
    }

    @Override
    protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        super.addReturnValueHandlers(returnValueHandlers);
        // 除了默认注册的自定义内容外,还添加自定义内容
    }

    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        // 
    }

    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.extendMessageConverters(converters);
        // 以添加HttpMessageConverters 要与RequestMappingHandlerAdapter和 一起使用的自定义ExceptionHandlerExceptionResolver
    }

    @Override
    protected void addFormatters(FormatterRegistry registry) {
    	// 注册自定义转化器  注册自定义的Formatter和Convert
        super.addFormatters(registry);
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        super.configureAsyncSupport(configurer);
        // 配置异步请求处理选项
    }


    @Override
    protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        super.configureHandlerExceptionResolvers(exceptionResolvers);
        // 配置HandlerExceptionResolvers要使用的列表
    }

    @Override
    protected void configureViewResolvers(ViewResolverRegistry registry) {
        super.configureViewResolvers(registry);
        // 配置视图分辨率
    }
}

使用这些时仍需谨慎,按需使用。比如有些不必要的配置就不要去配置了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kay三石 [Alay Kay]

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值