Spring中的AOP的使用(xml与方法路径注解方式)

Spring AOP(面向切面) 注解使用方法 开启自动织入支持 在XML中开启 添aop标签 <aop:aspectj-autoproxy /> 开启注解支持,同时强制指定代理机制为cglib <aop:aspectj-autoproxy proxy-target-class="true" /> 通过注解开启 @Configuration // 开启注解支持,同时强制指定代理机制为cglib @EnableA... 阅读详情

原文地址:https://blog.youkuaiyun.com/weixin_40055163/article/details/108511997?biz_id=102&utm_term=spring%E4%B8%AD%E7%9A%84aop%E4%BD%BF%E7%94%A8&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-108511997&spm=1018.2118.3001.4449

Spring中的AOP的使用

 

环绕增强是在调用业务方法之前和调用业务方法之后都可以执行响应的增强语法,也就是说:一个前置增强和一个后置增强相当于是组成了一个环绕增强;不同的是,在前置增强和后置增强中,在AOP中前置增强和后置增强只能拿到JoinPoint类,而在环绕增强中,可以拿到ProceedingJoinPoint类;

 

ProceedingJoinPoint类继承了JoinPoint类,也继承了JoinPoint所有的非私有的方法;比如获取连接点相关信息、获取参数信息、获取方法等等,并且ProceedingJoinPoint类扩展了JoinPoint类的方法,ProceedingJoinPoint可以调用业务方法执行业务逻辑,而JoinPoint则不可以;

 

也就是在环绕增强中,可以执行业务方法,而在前置增强和后置增强中则不可以;这里可以通过环绕增强实现数据库事务的实现,也可以通过环绕增强实现程序运行时间的记录;

 

1.1 什么是AOP
AOP:全称是Aspect Oriented Programming即:面向切面编程。在这里插入图片描述
简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
1.2 AOP的作用及优势
作用: 在程序运行期间,不修改源码对已有方法进行增强。
优势: 减少重复代码 提高开发效率 维护方便。
1.3 AOP相关术语
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点): 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强): 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
通知的类型如图所示:在这里插入图片描述
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
Target(目标对象): 代理的目标对象。
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。
spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面): 是切入点和通知(引介)的结合。
1.4 spring中的AOP要明确的事
a、开发阶段(我们做的) 编写核心业务代码(开发主线):
大部分程序员来做,要求熟悉业务需求。
把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP编程人员来做。
在配置文件中,声明切入点与通知间的关系,即切面。:AOP编程人员来做。
b、运行阶段(Spring框架完成的):
Spring框架监控切入点方法的执行。
一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
1.5 spring基于XML的AOP编写必要的代码
1.打开IDEA的界面如图所示,并点击Create New Project。在这里插入图片描述
2.选择Maven工程和JDK的版本,并点击Next。如图所示:在这里插入图片描述
3.填写项目名称和保存的地址,点击Finish。如图所示:在这里插入图片描述
4.导入相应的依赖jar包的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.txw</groupId>
    <artifactId>day03__03springAOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--打包的方式-->
    <packaging>jar</packaging>
    <dependencies>
        <!--导入spring的依赖jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <!--导入切入点的表达式的依赖jar包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>
</project>
  • 户的业务层接口的代码如下:
package com.txw.service;

/**
 * 账户的业务层接口
 * @author Adair
 */
@SuppressWarnings("all")          // 注解警告信息
public interface IAccountService {
    /**
     * 模拟保存账户
     */
   void saveAccount();
    /**
     * 模拟更新账户
     * @param i
     */
   void updateAccount(int i);
    /**
     * 删除账户
     * @return
     */
   int  deleteAccount();
}
  1. 账户的业务层实现类的代码如下:
package com.txw.service.impl;

import com.txw.service.IAccountService;
/**
 * 账户的业务层实现类
 * @author Adair
 */
@SuppressWarnings("all")          // 注解警告信息
public class AccountServiceImpl implements IAccountService{
    /**
     * 模拟保存账户
     */
    public void saveAccount() {
        System.out.println("执行了保存");
    }
    /**
     * 模拟更新账户
     * @param i
     */
    public void updateAccount(int i) {
        System.out.println("执行了更新"+i);

    }
    /**
     * 删除账户
     * @return
     */
    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}

7.创建用于记录日志的工具类的代码如下:

package com.txw.utils;

/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 * @author Adair
 */
@SuppressWarnings("all")          // 注解警告信息
public class Logger {
    /**
     * 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
     */
    public  void printLog(){
        System.out.println("Logger类中的pringLog方法开始记录日志了。。。");
    }
}

8.在resource目录下创建bean.xml的代码如下:

<?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: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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 配置srping的Ioc,把service对象配置进来-->
    <bean id="accountService" class="com.txw.service.impl.AccountServiceImpl"></bean>
    <!--spring中基于XML的AOP配置步骤
        1、把通知Bean也交给spring来管理
        2、使用aop:config标签表明开始AOP的配置
        3、使用aop:aspect标签表明配置切面
                id属性:是给切面提供一个唯一标识
                ref属性:是指定通知类bean的Id。
        4、在aop:aspect标签的内部使用对应标签来配置通知的类型
               我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
               aop:before:表示配置前置通知
                    method属性:用于指定Logger类中哪个方法是前置通知
                    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
            切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
                标准的表达式写法:
                    public void com.txw.service.impl.AccountServiceImpl.saveAccount()
                访问修饰符可以省略
                    void com.txw.service.impl.AccountServiceImpl.saveAccount()
                返回值可以使用通配符,表示任意返回值
                    * com.txw.service.impl.AccountServiceImpl.saveAccount()
                包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
                    * *.*.*.*.AccountServiceImpl.saveAccount())
                包名可以使用..表示当前包及其子包
                    * *..AccountServiceImpl.saveAccount()
                类名和方法名都可以使用*来实现通配
                    * *..*.*()
                参数列表:
                    可以直接写数据类型:
                        基本类型直接写名称           int
                        引用类型写包名.类名的方式   java.lang.String
                    可以使用通配符表示任意类型,但是必须有参数
                    可以使用..表示有无参数均可,有参数可以是任意类型
                全通配写法:
                    * *..*.*(..)
                实际开发中切入点表达式的通常写法:
                    切到业务层实现类下的所有方法
                        * com.txw.service.impl.*.*(..)
    -->
    <!-- 配置Logger类 -->
    <bean id="logger" class="com.txw.utils.Logger"></bean>
    <!--配置AOP-->
    <aop:config>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
            <aop:before method="printLog" pointcut="execution(* com.txw.service.impl.*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>

9.测试类的代码如下:

package com.txw.test;

import com.txw.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试AOP的配置
 * @author Adair
 */
@SuppressWarnings("all")          // 注解警告信息
public class AOPTest {
    public static void main(String[] args) {
        // 1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        // 3.执行方法
        as.saveAccount();
        as.updateAccount(1);
        as.deleteAccount();
    }
}

运行结果如图所示:在这里插入图片描述
1.6 四种常用通知类型
1.打开IDEA的界面如图所示,并点击Create New Project。在这里插入图片描述
2.选择Maven工程和JDK的版本,并点击Next。如图所示:在这里插入图片描述
3.填写项目名称和保存的地址,点击Finish。如图所示:在这里插入图片描述
4.导入相应的依赖jar包的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.txw</groupId>
    <artifactId>day03_04adviceType</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--打包的方式-->
    <packaging>jar</packaging>
    <dependencies>
        <!--导入spring的依赖jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <!--导入切入点的表达式的依赖jar包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>
</project>
  1. 账户的业务层接口的代码如下:
package com.txw.service.impl;

import com.txw.service.IAccountService;
/**
 * 账户的业务层实现类
 * @author Adair
 */
@SuppressWarnings("all")     // 注解警告信息
public class AccountServiceImpl implements IAccountService{
    /**
     * 模拟保存账户
     */
    public void saveAccount() {
        System.out.println("执行了保存");
//        int i=1/0;
    }
    /**
     * 模拟修改账户
     * @param i
     */
    public void updateAccount(int i) {
        System.out.println("执行了更新"+i);

    }
    /**
     * 删除账户
     * @return
     */
    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}

6.创建用于记录日志的工具类的代码如下:

package com.txw.utils;

import org.aspectj.lang.ProceedingJoinPoint;
/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 * @author Adair
 */
@SuppressWarnings("all")     // 注解警告信息
public class Logger {
    /**
     * 前置通知
     */
    public  void beforePrintLog(){
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
    }
    /**
     * 后置通知
     */
    public  void afterReturningPrintLog(){
        System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
    }
    /**
     * 异常通知
     */
    public  void afterThrowingPrintLog(){
        System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
    }
    /**
     * 最终通知
     */
    public  void afterPrintLog(){
        System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
    }
    /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
     * 解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
     */
    public Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args = pjp.getArgs();    // 得到方法执行所需的参数
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
            rtValue = pjp.proceed(args);    // 明确调用业务层方法(切入点方法)
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
        }
    }
}

7.在resource创建bean.xml的代码如下:

<?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: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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 配置srping的Ioc,把service对象配置进来-->
    <bean id="accountService" class="com.txw.service.impl.AccountServiceImpl"></bean>
    <!-- 配置Logger类 -->
    <bean id="logger" class="com.txw.utils.Logger"></bean>
    <!--配置AOP-->
    <aop:config>
        <!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
              此标签写在aop:aspect标签内部只能当前切面使用。
              它还可以写在aop:aspect外面,此时就变成了所有切面可用
          -->
        <aop:pointcut id="pt1" expression="execution(* com.txw.service.impl.*.*(..))"></aop:pointcut>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置前置通知:在切入点方法执行之前执行
            <aop:before method="beforePrintLog" pointcut-ref="pt1" ></aop:before>-->
            <!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知永远只能执行一个
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>-->
            <!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>-->
            <!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行
            <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
            <!-- 配置环绕通知 详细的注释请看Logger类中-->
            <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

8.测试类代码如下:

package com.txw.test;

import com.txw.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试AOP的配置
 * @author Adair
 */
@SuppressWarnings("all")     // 注解警告信息
public class AOPTest {
    public static void main(String[] args) {
        // 1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        // 3.执行方法
        as.saveAccount();
    }
}

运行结果如图所示:
在这里插入图片描述
1.7 spring基于注解的AOP配置
1.打开IDEA的界面如图所示,并点击Create New Project。在这里插入图片描述
2.选择Maven工程和JDK的版本,并点击Next。如图所示:在这里插入图片描述
3.填写项目名称和保存的地址,点击Finish。如图所示:在这里插入图片描述
4.导入相应的依赖jar包的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.txw</groupId>
    <artifactId>day03_05annotationAOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--打包的方式-->
    <packaging>jar</packaging>
    <dependencies>
        <!--导入spring的依赖jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <!--导入切入点的表达式的依赖jar包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>
</project>
  1. 账户的业务层接口的代码如下:
package com.txw.service;

/**
 * 账户的业务层接口
 */
@SuppressWarnings("all")     // 注解警告信息
public interface IAccountService {
    /**
     * 模拟保存账户
     */
   void saveAccount();
    /**
     * 模拟更新账户
     * @param i
     */
   void updateAccount(int i);
    /**
     * 删除账户
     * @return
     */
   int  deleteAccount();
}
  1. 账户的业务层实现类的代码如下:
package com.txw.service.impl;

import com.txw.service.IAccountService;
/**
 * 账户的业务层实现类
 * @author Adair
 */
@SuppressWarnings("all")     // 注解警告信息
@Service("accountService")
public class AccountServiceImpl implements IAccountService{
    /**
     * 模拟保存账户
     */
    public void saveAccount() {
        System.out.println("执行了保存");
//        int i=1/0;
    }
    /**
     * 模拟修改账户
     * @param i
     */
    public void updateAccount(int i) {
        System.out.println("执行了更新"+i);

    }
    /**
     * 删除账户
     * @return
     */
    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}
  •  

7.在resource目录下创建bean.xml的代码如下:

<?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:aop="http://www.springframework.org/schema/aop"
       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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.txw"></context:component-scan>
    <!-- 配置spring开启注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

8.创建用于记录日志的工具类的代码如下:

package com.txw.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 * @author Adair
 */
@SuppressWarnings("all")     // 注解警告信息
@Aspect   // 表示当前类是一个切面类
@Component("logger")
public class Logger {
    /**
     * 切入点表达式
     */
    @Pointcut("execution(* com.txw.service.impl.*.*(..))")
    private void pt1(){}
    /**
     * 前置通知
     */
//    @Before("pt1()")
    public  void beforePrintLog(){
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
    }
    /**
     * 后置通知
     */
//    @AfterReturning("pt1()")
    public  void afterReturningPrintLog(){
        System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
    }
    /**
     * 异常通知
     */
//    @AfterThrowing("pt1()")
    public  void afterThrowingPrintLog(){
        System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
    }
    /**
     * 最终通知
     */
//    @After("pt1()")
    public  void afterPrintLog(){
        System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
    }
    /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
     * 解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
     */
    @Around("pt1()")
    public Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args = pjp.getArgs();    // 得到方法执行所需的参数
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
            rtValue = pjp.proceed(args);    // 明确调用业务层方法(切入点方法)
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
        }
    }
}

9.测试类的代码如下:

package com.txw.test;

import com.txw.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试AOP的配置
 * @author Adair
 */
@SuppressWarnings("all")     // 注解警告信息
public class AOPTest {
    public static void main(String[] args) {
        // 1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        // 3.执行方法
        as.saveAccount();
    }
}
  • 图所示:
    在这里插入图片描述
一文吃透 Spring 中的 AOP 编程 基于 Aspectj 实现 AOP 操作: 第一版:基于xml(aop:config)配置文件; 第二版:基于xml(aop:aspect)配置文件; 第三版:基于注解实现通知。 阅读详情

相关推荐

Spring基础(十一):AOP注解和XML方式实现

AspectJ本身并不是spring框架中的组成部分, 是一个独立的AOP框架,一般把AspectJ和Spring框架的AOP依赖一起使用,所以要导入一个独立的依赖。execution([权限修饰符][返回值类型][类的全路径名][方法名](参数 列表) )通过一个表达式来确定AOP要增强的是哪个或者那些方法。开启注解扫描和AOP切面编程自动生成代理对象配置。数字越小,其代理位置越靠近注入位置。2、基于XML配置方式 (了解)1、基于注解方式实现 (熟练)数字越小,优先级越高。

Lansonli(蓝深李)的博客 923

Spring_动态代理_AOP

Spring_动态代理_AOP

底层码农 6594

Spring AOP配置实战:XML与注解方式详解

Spring AOP(面向切面编程)是一种编程范式,旨在提高代码的模块化,通过将横切关注点(如日志记录、权限控制、事务管理)从业务逻辑中分离出来。其核心概念包括:切面(Aspect):一个模块化的组件,封装了横切逻辑,例如日志切面。连接点(Join Point):程序执行过程中的某个点,如方法调用或异常抛出。切入点(Pointcut):定义哪些连接点将被切面处理,通常通过表达式匹配方法。通知(Advice):切面在特定连接点上执行的动作,如前置通知、后置通知等。

weixin_42611177的博客 853

aop执行跳过某个方法_[Spring 系列] 尽量通俗讲一下 AOP 源码解析

前言前言的仪式感还是要有的。这次回来讲 Spring AOP 的主要原因之前写过一篇文章是关于 AOP 的一个概念性文章,然后作死地在文末讲“下一篇会进行源码”解析。但是后面却跑去写别的知识了。额...其实造成我这么偏执写 Spring AOP 的原因是,毕竟是框架重点,里面的抽象代码以及思想不能丢;况且,咱们淳朴的程序猿,如果不能实打实地看到代码实现,怎么可能善罢甘休!这篇文章主要还是...

weixin_39652810的博客 1218

Ubuntu22.04下3分钟搞定GitHub加速:SwitchHosts+小鱼工具保姆级配置指南

本文提供Ubuntu22.04下快速解决GitHub下载超时问题的保姆级教程,通过SwitchHosts图形化工具和小鱼一键换源工具实现高效加速。详细步骤包括环境准备、工具安装配置及验证方法,帮助开发者轻松优化GitHub访问速度,提升工作效率。

weixin_29258297的博客 564

量子磁场测量技术在生物磁场成像领域的应用现状与产业趋势

首先对量子磁场测量技术的发展进行了简述,然后介绍了量子磁场测量技术在生物磁场成像领域的应用,最后分析了量子磁场测量技术的两大重要应用方向(心磁图仪和脑磁图仪)及其产业现状和未来发展趋势。美国普林斯顿大学在《自然》上公布了目前人类磁场测量灵敏度最高的基于量子磁场测量技术的零场原子磁力计。拥有巨大的发展潜力及庞大的市场空间。因此其数千万元的高昂售价及每年数百万元的超导维护成本限制了超导式心磁图仪的发展和临床应用。而基于超导式脑磁图仪的癫痫、阿尔兹海默症、抑郁症等脑疾病患者的临床研究也取得丰富的研究成果。

罗伯特技术屋 1528

基于@Aspect实现AOP的两种方式

第一种: package com.anno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(R...

coderchem的博客 2190

Altium Designer铺铜陷阱:为什么你的死铜总是清除不干净?

本文深入探讨Altium Designer中铺铜操作导致死铜无法清除的常见问题,分析死铜形成的五大诱因,并提供高级参数优化和网络连接性验证技巧。通过调整边界计算精度和泪滴算法设置,有效解决死铜残留问题,提升PCB设计质量与制造良率。

weixin_29169505的博客 359

aop执行跳过某个方法_【干货分享】SpringBoot—AOP详解及开发模板

AOP介绍AOP概述  AOP是Aspect-Oriented Programming,即为面向(切面)方面编程。在维基百科中的解释:Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点。从关注点中分离出横切关注点是面向切面的程序设计核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑代码不需要再包含针对特定领域问题代码的调用,比如一些公用...

weixin_39566387的博客 1199

Excel地理探测器实战:5分钟搞定空间分异性分析(附完整数据集)

本文详细介绍了如何使用Excel地理探测器插件进行空间分异性分析。通过5分钟实战教程,手把手指导数据格式准备、软件操作及四大探测器(分异及因子、交互作用、风险区、生态探测器)的结果解读,帮助规划师、研究者快速验证地理因素对现象分布的影响,大幅降低GIS分析门槛。

peace的博客 607

Jupyter Notebook 命名出现“error”且运行后没反应

Jupyter Notebook 命名出现“error”且运行后没反应提前声明错误截图(方便大家看看是不是自己的错误)解决方法 提前声明 我的问题是在看到答主 @开始King的回答后才解决的,因为答主大大的文章后面有些地方不是很详细,我在这里再在King大大的基础上完善一下子 错误截图(方便大家看看是不是自己的错误) 1.命名失败,上面出现一个“error”字样 2.运行失败,无论是点击上方的运行按钮还是按快捷键shift+enter 都只会向下再增加一个代码块,而不会运行程序。 (也不是很多人出现的[*

qq_52770743的博客 1万+

aop执行跳过某个方法_简谈前端开发中的AOP(一) -- 前端AOP的实现思路

前言本意不想用太长的篇幅,来阐述这个话题。但是有些概念和设计思路有必要讲清楚,以便于搞清楚其深层次的内在逻辑。这是我一直遵从的“知其然,知其所以然”的原则。首先,本文将简单的阐述一下概念;进而,举例一起探讨一下前端AOP实现方式,以及随着前端语言ES5、ES6、Typescript的发展,实现方式的演变;最后,一起了解一下实际项目中AOP的应用场景、相关框架以及开发模式。什么是AOP?AOP为As...

weixin_39640008的博客 476

逆向小记 bd-ticket-guard-client-cert | bd-ticket-guard-client-data 通过现象看本质

依然没什么花哨的直接解析出来,既然这是个X509证书,那么要么是服务器下发,要么是自己生成喽,但是如果是本地直接生成,那服务器还校验个鬼哦!既然是服务器下发的,那服务器又是怎么校验这个证书是自己下发的呢,这就要讲到CA验证了,首先根据上图我们发现证书的Issuer"(颁发者)是。怎么来的呢,因为涉及到逆向咱就不讲了,其实也是涉及到非对称加密的,就是客户端用私钥去签名数据,服务器用上传过去的证书公钥验签的过程!当服务器收到这个证书的时候,会做几件事,首先用自己的公钥解开签名获得解密后的证书hash。

2503_90751789的博客 1205

快速了解spring中的AOP

Java中增强代码的功能一般是通过接口和继承来实现代码的重写增强功能,而在spring中是通过AOP来增强代码的功能,且原代码不变。 1.创建一个接口和一个实现接口的类 (如图),其中代码功能随意,该代码主要是用来增强的 代码如下 package com.lin.spring.aopexample; /** * @author liuqunliang * @date 2020/11/24 14:43 */ public interface UserDao { public int add(

qq_41761210的博客 226

深入了解BPM及BPM业务流程管理系统

BPM 即业务流程管理,是一种以规范化的构造端到端的卓越业务流程为中心,以持续地提高组织业务绩效为目的的系统化方法。它涵盖了业务流程的整个生命周期,包括流程的设计、执行、监控、分析和优化。BPM 作为一种先进的管理方法,对于企业提高运营效率、降低成本、提升客户满意度和增强竞争力具有重要意义。BPM 流程管理系统为企业实现 BPM 提供了强大的工具和支持,而 F2BPM 流程管理平台以其先进的技术架构、丰富的功能模块、简单易用的操作界面和强大的定制能力,成为企业实现业务流程管理的理想选择。

Stone3625的博客 2950

【思考模型框架】MECE原则,确保分析时,考虑到的所有因素,既不重叠又无遗漏

MECE原则,是Mutually Exclusive and Collectively Exhaustive的缩写,中文译为“相互独立、完全穷尽”。MECE原则,这是一种逻辑思维和组织信息的方法,旨在确保分析时既没有遗漏也没有重复。MECE原则,相互独立、完全穷尽(Mutually Exclusive,Collectively Exhaustive)的原则MECE原则,确保分析时考虑到的所有因素既不重叠又无遗漏。

奶爸的编程之路,也就一周冷个三天 2752

SSM框架-Spring3

本文系统介绍了Spring框架中AOP(面向切面编程)的核心内容。首先阐述了AOP的基本概念和Spring对AspectJ的实现方式,详细解释了通知、连接点、切入点等关键术语。然后通过XML配置方式演示了AOP的实际应用,包括六种通知类型(前置/后置/环绕/异常/最终通知)的具体实现和切入点表达式语法。最后讲解了基于注解的AOP开发方式,通过@Aspect等注解简化配置。全文通过完整代码示例展示了如何将切面逻辑(如权限检查、日志记录)与业务代码解耦,体现了AOP在实现横切关注点方面的优势

m0_53499553的博客 365

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spring、SpringMVC

spring5 Spring框架是一个轻量级的框架,只需导入相关jar包即可使用。按笔者当前的学习来讲,对于Spring框架的认识在IOC容器,AOP编程,以及Spring对于JDBC的封装-JDBCTemplate,Spring框架中的事务管理等。 IOC IOC(Inversion of Control)称为控制反转,可以通过xml文件配置或注解的方法,让IOC容器来创建所需类的实例再返回给我们使用。 IOC的底层原理:xml解析,工厂模式,反射 让该类在IOC容器中进行注册。 通过创建Applicat

qq_43232973的博客 271

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

idea创建Spring项目实现AOP(XML方式与注解方式)

文章目录Spring AOP一. 概述1. Aop简介2. OOP和AOP3. AOP主要的特点二 在idea里创建Spring项目1. 新建spring项目(熟悉的可以直接跳过)2.到官网下载aspect相关的jar包三 简单AOP实现1. xml式2. 注解式 Spring AOP 一. 概述 1. Aop简介 AOP为Aspect Oriented Programming 即面向切面编程,可...

李小立 7229

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Spring注解方式实现AOP

*** 定义切面类*/@Component // 切面对象的创建对象的权限也交给了spring@Aspect // aspectj 框架的注解 表示当前类是一个切面类System.out.println("前置通知:在目标方法之前被调用的通知");System.out.println("后置通知:在目标方法之后被调用,返回值result=" + result);System.out.println("方法执行之前执行-----");

qq_64468032的博客 1467

Spring中的AOP

目录 零、码仙励志 一、Spring AOP简介 1.什么是AOP 2.AOP术语 二、动态代理(了解) 1.JDK动态代理 2.CGLIB代理 三、基于代理类的AOP实现 1.Spring的通知类型 2.ProxyFactoryBean 四、AspectJ开发 1.基于 XML 的声明或 AspectJ 2.基于注解的声明式AspectJ 零、码仙励志 能力配不上野...

秦哥的博客 7438

【Spring框架】Spring的AOP使用

Spring的AOP使用 我们在Spring框架中实现AOP功能主要是通过AspectJ实现的,AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。需要注意的是,AspectJ并不是Spring框架的一部分,它只是用于和Spring结合在一起共同完成有关AOP的操作而已。 AOP结合AspectJ的使用主要有两种方式: 基于XML配置的方式实现; 基于注解的方式实现; 基于XML配置的方式实现 1

Sampson_S的博客 5043

【Spring入门】17.Spring使用AspectJ进行AOP开发(基于注解)

Spring使用AspectJ进行AOP开发(基于注解)

CoderX(远离Bug)的博客 2261

Spring框架的AOP使用方法

本文介绍了AOP(面向切面编程)的概念、原理及实现方式。AOP通过动态代理在不修改源代码的情况下增强业务逻辑,降低耦合度。底层实现分为JDK动态代理(针对有接口的情况)和CGLIB动态代理(无接口情况)。文章详细讲解了AOP术语(连接点、切入点、通知、切面)和五种通知类型(@Before、@After、@AfterReturning、@Around、@AfterThrowing)。重点阐述了基于AspectJ的两种AOP实现方式:注解方式(推荐)和XML配置方式,包括切入点表达式语法和增强类优先级设置。

MyNAMS的博客 1015

掌握Spring AOP编程:tech-pdai-spring-demos中的XML与注解式切面实现教程

Spring AOP(面向切面编程)是Spring框架的核心功能之一,它通过分离横切关注点(如日志记录、性能监控、事务管理等)与业务逻辑,极大提升了代码的模块化和可维护性。本文将以**tech-pdai-spring-demos**项目为基础,详细介绍XML配置与注解式两种切面实现方式,帮助开发者快速掌握Spring AOP的实战应用。 ## 🌟 Spring AOP核心概念快速入门 在深入

gitblog_00607的博客 462

spring框架:简述AOP的使用(xml方式和注解方式)

本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。 AOP概述 AOP的概念: AOP,全称Apect Oriented Programming,译为面向切面编程,简单的说它可以帮我们把程序中重复的代码抽取出去,在需要执行的时候,使用动态代理技术,在不修改源码的基础上,对我们已有的方法进行增强。 AOP的作用及优势: 作用:在程序运行期间,不修改源码对已有的方法进行增强。 优势:减...

qq_43138206的博客 824
上一篇: 反射及动态代理
下一篇: Spring中的5种Aop常见应用方式(扫描注解方式)
ArtisticProgramming
博客等级 码龄8年 1粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值