Spring AOP的概念及简单使用

Spring AOP的概念及简单使用

一、AOP的概念

AOP(Aspect-Oriented Programming,面向切面编程或面向方面编程)是一种技术,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系。

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。在AOP中,将具有公共逻辑的、与其他模块的核心逻辑纠缠在一起的公共行为称为横切关注点(Crosscutting Concern)”,因为它跨越了给定编程模型中的典型职责界限。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似,比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

例如一个复杂的系统,它由许多关注点组合实现,如业务逻辑、性能,数据存储、日志和调度信息、授权、安全、线程、错误检查等,还有开发过程中的关注点,如易懂、易维护、易追查、易扩展等。通过对系统需求和实现的识别,可以将模块中的这些关注点分为:核心关注点和横切关注点。对于核心关注点而言,通常来说,实现这些关注点的模块是相互独立的,他们分别完成了系统需要的商业逻辑,这些逻辑与具体的业务需求有关。而对于日志、安全、持久化等关注点而言,他们却是商业逻辑模块所共同需要的,这些逻辑分布于核心关注点的各处。在AOP中,诸如这些模块,都称为横切关注点。应用AOP的横切技术,关键就是要实现对关注点的识别。

AOP的目的就是要将诸如Logging(日志)、Securtity(安全)之类的横切关注点从BusinessLogic(业务逻辑)类中分离出来。利用AOP技术,可以对相关的横切关注点封装,形成单独的“aspect”。这就保证了横切关注点的复用。由于BusinessLogic类中不再包含横切关注点的逻辑代码,为达到调用横切关注点的目的,可以利用横切技术(如动态代理),截取BusinessLogic类中的相关方法,然后将这些“aspect”织入到该方法中。

通过利用AOP技术,改变了整个系统的设计方式。在分析系统需求之初,利用AOP的思想,分离出核心关注点和横切关注点。在实现了诸如日志、事务管理、权限控制等横切关注点的通用逻辑后,开发人员就可以专注于核心关注点,将精力投入到解决企业的商业逻辑上来。同时,这些封装好了的横切关注点提供的功能,可以最大限度地复用于商业逻辑的各个部分,既不需要开发人员作特殊的编码,也不会因为修改横切关注点的功能而影响具体的业务功能。

Spring的AOP是基于JDK动态代理或CGLIB(动态字节码增强技术)动态代理技术实现的 。

AOP技术的优势使得需要编写的代码量大大缩减,节省了时间,控制了开发成本。同时也使得开发人员可以集中关注于系统的核心商业逻辑。此外,它更利于创建松散耦合、可复用与可扩展的大型软件系统。

AOP相关的概念

1. Aspect :切面,切入系统的一个切面。比如日志记录是一个切面,权限管理也是一个切面;

2. Join point :连接点,也就是可以进行横向切入的位置;

3. Advice :通知,切面在某个连接点执行的操作(分为: Before advice , After returning advice , After throwing advice , After (finally) advice , Around advice );

4. Point cut :切点,符合切点表达式的连接点,也就是真正被切入的地方。

Advice的类型

before advice:在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行join point 中的代码)

after return advice:在一个join point正常返回后执行的advice

after throwing advice:当一个join point抛出异常后执行的advice

after(final) advice:无论一个join point是正常退出还是发生了异常, 都会被执行的advice.

around advice:在join point前和joint point退出后都执行的advice,这个是最常用的advice.

Introduction:introduction可以为原有的对象增加新的属性和方法。

execution表达式

execution用来匹配执行方法的切点,即被通知的方法。

execution表达式的基本语法格式为:

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?),除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。修饰符模式是指public、protected、private和默认修饰符,可有可无。

如:execution(* com.example.spring.service..*.*(..))

上式中,第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示方法的任意参数个数。..*表示本包及其子包下的所有类,.*表示本包下的所有类,*(..)表示任意参数个数的所有方法。如execution(* com.example.service.*.*(..))表示匹配com.example.service包中所有类的所有方法,不包括子包中的方法。

Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成、管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

在AOP中主要包括三部分:业务类、切面类和xml配置文件,在xml配置文件中主要是管理和生成业务类、切面类的bean对象。业务类中主要是与业务逻辑有关的一些方法,并不知道也无需知道切面类的存在,切面类主要是为了增加业务类中非业务功能服务的。在切面类中编写一些advice(其实就是方法),如before advice,around advice和after advice等。

实现AOP的方式主要有两种:基于注解和基于XML配置文件。下面对这两种方式分别进行简单的描述。

1、基于注解的方式实现AOP

业务类:

package com.example.service;
@Service
public class UserService {
    public void add(){
        System.out.println("UserService add()");
    }
    public boolean delete(){
        System.out.println("UserService delete()");
        return true;
    } 
}

切面类:

package com.example.aspect;
@Component
@Aspect
public class Operator {
    //定义切点,此处是com.example.service包及子包中所有的方法都作为切点
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void pointCut(){}
    //前置通知,在切点执行之前先执行该方法
    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("AOP Before Advice...");
    }
    //在切点执行完之后执行该方法,无论切点是正常退出还是抛出异常,都会执行该方法
    @After("pointCut()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println("AOP After Advice...");
    }
    //在切点正常执行完之后执行该方法
    @AfterReturning(pointcut="pointCut()",returning="returnVal")
    public void afterReturn(JoinPoint joinPoint,Object returnVal){
        System.out.println("AOP AfterReturning Advice:" + returnVal);
    }
    //在切点抛出异常后执行该方法,主要用来处理程序中未处理的异常
    @AfterThrowing(pointcut="pointCut()",throwing="error")
    public void afterThrowing(JoinPoint joinPoint,Throwable error){
        System.out.println("AOP AfterThrowing Advice..." + error);
        System.out.println("AfterThrowing...");
    }
    //环绕通知,在切点执行前后执行该方法
    @Around("pointCut()")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("AOP Aronud before...");
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("AOP Aronud after...");
    }
}

环绕通知是最重要的通知类型,像事务、日志等都是环绕通知。环绕通知中的核心是ProceedingJoinPoint,必须调用ProceedingJoinPoint的proceed()方法才会执行目标方法(即切点)。

由上面切面类的代码可以看出,基于注解方式实现的AOP,就是在切面类上使用@Aspect注解,在切面类中编写所需类型的通知方法,并在通知上使用相应类型的注解,同时标明需要被通知的切点。

既可以在启动类或带有@Configuration注解的类上使用@EnableAspectJAutoProxy注解开启AOP操作,也可以在spring配置文件中通过<aop:aspectj-autoproxy />标签开启AOP操作。

下面就是在spring配置文件applicationContext.xml中通过<aop:aspectj-autoproxy />标签开启AOP操作,并通过spring管理和注入bean对象。

此处在beans中省略了一些约束,包括与aop有关的约束。

<beans>
<-- 开启aop注解操作 ->
<aop:aspectj-autoproxy />
<-- 通过spring容器管理并注入bean对象 ->
<bean id="userService" class="com.example.service.UserService"/>
     <bean id="operator" class="com.example.aspect.Operator">
</beans>

测试类:

public class Test {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = (UserService) ctx.getBean("userService");
        userService.add();
    }
}

通知执行的优先级:

进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入AfterReturning,再才织入After,最后织入Around。

2、基于配置文件的方式实现AOP

在基于注解方式实现的AOP代码的基础上稍作修改,即可实现基于配置文件方式实现的AOP。业务类无需修改,切面类只需将类上的@Aspect注解和类中的@Before等注解去掉,如果有@Pointcut注解,还需将该注解及对应的方法去掉。基于配置文件方式实现的AOP重点在于spring配置文件。

切面类:

package com.example.aspect;
@Component
public class Operator {
    public void doBefore(JoinPoint joinPoint){
        System.out.println("AOP Before Advice...");
    }
    public void doAfter(JoinPoint joinPoint){
        System.out.println("AOP After Advice...");
    }
    public void afterReturn(JoinPoint joinPoint,Object returnVal){
        System.out.println("AOP AfterReturning Advice:" + returnVal);
    }
    public void afterThrowing(JoinPoint joinPoint,Throwable error){
        System.out.println("AOP AfterThrowing Advice..." + error);
        System.out.println("AfterThrowing...");
    }
    public void around(ProceedingJoinPoint pjp){
        System.out.println("AOP Aronud before...");
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("AOP Aronud after...");
    }
}

Spring配置文件:

此处在beans中省略了一些约束,包括与aop有关的约束

<beans>
    <-- 通过spring容器管理并注入bean对象 ->
    <bean id="userService" class="com.example.service.UserService"/>
    <bean id="operator" class="com.example.aspect.Operator">

    <-- 配置aop操作 ->
    <aop:config>
        <-- 配置切点 ->
        <aop:pointcut id="point" expression="execution(* com.example.service..*.*(..))" />

        <-- 配置切面 ->
        <aop:aspect ref="operator" >
            <-- 前置通知 ->
            <aop:before method="doBefore" pointcut-ref="point" />
            <-- 后置通知 ->
            <aop:after-returning method="doAfter" pointcut-ref="point" />
            <-- 环绕通知 ->
            <aop:around method="around" pointcut-ref="point" />
        </aop:aspect>
    </aop:config>
</beans>

由此可以看出,基于配置文件方式实现的AOP就是在spring配置文件中通过<aop:config>

标签配置切点和切面,来实现对切点的通知。

二、AOP的简单使用

spring和springboot1.x都是默认使用jdk动态代理创建代理对象,springboot2.x默认使用cglib动态代理创建代理对象。在application.properties配置文件中可以通过下面这个属性修改。

spring.aop.proxy-target-class=false

在启动类上加上@EnableAspectJAutoProxy(proxyTargetClass = false)注解不起作用,依然使用cglib动态代理。

1.pom.xml文件

<?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">

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>dynamic-proxy</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

</project>

2.application.yml配置文件,是为了使用jdk动态代理

spring:
  aop:
    proxy-target-class: false

3.接口及其实现类

package com.dynamic.proxy.jdkProxy.service;

/**
 * @Author: 倚天照海
 */
public interface UserService {

    void buy();

}


package com.dynamic.proxy.jdkProxy.service;

import org.springframework.stereotype.Service;

/**
 * @Author: 倚天照海
 */
@Service
public class UserServiceImpl implements UserService {

    @Override
    public void buy() {
        System.out.println("用户买东西.........");
    }

    public void search(){
        System.out.println("用户搜索商品.......");
    }

}

4.切面类

package com.dynamic.proxy.jdkProxy.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @Author: 倚天照海
 */
@Component
@Aspect
public class UserAspect {
    //此例中只对buy方法增强
    @Pointcut(value = "execution(* com.dynamic.proxy.jdkProxy.service.UserService.buy(..))")
    public void pointCut(){}

    @Before(value = "pointCut()")
    public void beforeAdvice(){
        System.out.println("前置通知-用户浏览商品........");
    }

    @After(value = "pointCut()")
    public void afterAdvice(){
        System.out.println("后置通知-用户付款...........");
    }

    @Around(value = "pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知的前置通知...............");
        pjp.proceed();
        System.out.println("环绕通知的后置通知...............");
        return null;
    }

    @AfterReturning(value = "pointCut()")
    public void afterReturn(){
        System.out.println("正常返回后置通知.................");
    }

}
 
 

由上图debug可知,拦截器链(是个list)中的顺序是AspectJAroundAdvice(对应@Around)MethodBeforeAdviceInterceptor(对应@Before)AspectJAfterAdvice(对应@After)AfterReturningAdviceInterceptor(对应@AfterReturning),但是得到的结果与此顺序却不同。在调用目标方法之前是按照此顺序排列的,即先调用@Around注解的方法(@Around的前置通知),再调用@Before注解的方法,然后调用目标方法。在调用目标方法之后是逆序排列的,即先调用@AfterReturning注解的方法,接着调用@After注解的方法,最后再调用@Around注解的方法(@Around的后置通知)。

执行顺序:@Around前置→@Before→目标方法→@AfterRunning(若目标方法抛出异常,则执行@AfterThrowing) →@After→@Around后置(如果目标方法抛出了异常,@Around的后置通知不会执行)

下面模拟抛出异常时增强方法执行的顺序。

5.自定义ApplicationContextAware,为了从spring容器中获取bean

package com.dynamic.proxy.jdkProxy.config;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @Author: 倚天照海
 */
@Component
public class MyAppContext implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        MyAppContext.context = applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }

    public static <E> E getBean(Class<E> cls){
        return context.getBean(cls);
    }

    public static <E> E getBean(String name, Class<E> cls){
        return context.getBean(name,cls);
    }

    public static <E> String[] getBeanNames(Class<E> cls){
        return context.getBeanNamesForType(cls);
    }

}

6.启动类

package com.dynamic.proxy.jdkProxy;

import com.dynamic.proxy.jdkProxy.config.MyAppContext;
import com.dynamic.proxy.jdkProxy.geneProxy.GeneProxySourceCode;
import com.dynamic.proxy.jdkProxy.service.UserService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @Author: 倚天照海
 */
@SpringBootApplication
//@EnableAspectJAutoProxy(proxyTargetClass = false)
public class JdkProxyApplication {

    public static void main(String[] args) {
        SpringApplication.run(JdkProxyApplication.class,args);
        UserService userService = MyAppContext.getBean(UserService.class);
        // 在代理对象调用重写的代理方法时,会调用JdkDynamicAopProxy的invoke方法(通过jdk动态代理生成的代理对象),
        // 或者调用DynamicAdvisedInterceptor的intercept方法(通过cglib生成的代理对象)
        userService.buy();
        //生成jdk代理类的class文件
        GeneProxySourceCode.geneProxyCode();
    }

}

在springboot环境下,不加@EnableAspectJAutoProxy注解spring AOP仍然生效。这是因为在springboot环境下,由于存在spring-boot-autoconfigure依赖,默认会注入AopAutoConfiguration配置类,该类的作用等同于@EnableAspectJAutoProxy注解,所以在这种情况下可以不加@EnableAspectJAutoProxy注解。

AopAutoConfiguration可以通过spring.aop.auto属性控制,该属性的默认值是true,即默认AOP是自动生效的,如果通过配置文件将该属性改成false,则AOP不会自动生效,需要在启动类上加上@EnableAspectJAutoProxy注解。

三、生成代理类的class文件

1、生成jdk代理类的class文件

查看代理类的源码,可以验证代理类中没有持有目标类

package com.dynamic.proxy.jdkProxy.geneProxy;

import com.dynamic.proxy.jdkProxy.service.UserService;
import sun.misc.ProxyGenerator;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @Author: 倚天照海
 */
public class GeneProxySourceCode {

    public static void geneProxyCode(){
        //指定生成的代理类名称
        String proxyClassName = "$proxy0";
	    //指定被代理的接口
        Class<UserService> interfaceClass = UserService.class;
        //指定保存代理类的路径
        String path = "D:\\workspace\\java-study\\dynamic-proxy\\src\\main\\java\\com\\dynamic\\proxy\\jdkProxy\\test\\";
        path = path.concat(proxyClassName).concat(".class");
        writeClassToFile(proxyClassName,interfaceClass,path);
    }

    private static <T> void writeClassToFile(String proxyClassName, Class<T> interfaceClass, String path){
        byte[] proxyClass = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{interfaceClass});
        OutputStream os = null;
        try {
            os = new FileOutputStream(new File(path));
            os.write(proxyClass);
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null){
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

生成的代理类的代码如下所示,代理类$proxy0继承了Proxy,实现了目标接口。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.dynamic.proxy.jdkProxy.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $proxy0 extends Proxy implements UserService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buy() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.dynamic.proxy.jdkProxy.service.UserService").getMethod("buy");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

在代理类中只有四个Method类型的静态变量,并没有目标类的实例变量,所以代理类并不持有对目标类的引用。既然代理类不持有对目标类的引用,在对目标方法进行增强时,是如何调用到目标对象的目标方法呢?其实根据之前对AOP原理的分析可知,最终是通过反射调用目标对象的目标方法。既然通过反射调用目标方法,肯定要有目标对象,目标对象是保存在spring容器中的吗?目标对象不是保存在spring容器中的,spring容器中保存的是该目标对象的代理对象。在创建代理对象时,会将目标对象封装成SingletonTargetSource保存在代理工厂中,proxyFactory.setTargetSource(targetSource)。代理工厂ProxyFactory继承AdvisedSupport,实际是保存在AdvisedSupport的targetSource属性中。当代理对象调用被重写后的目标方法时(如此处$proxy0类中的buy方法),会调用JdkDynamicAopProxy(实现InvocationHandler接口)的invoke方法(通过jdk动态代理创建的代理对象),或者调用DynamicAdvisedInterceptor的intercept方法(通过cglib生成的代理对象),在invoke或intercept方法中会获取AdvisedSupport的targetSource属性,进而从targetSource中获取到目标对象target,在需要调用目标方法时,就通过目标对象target反射调用目标方法。

2、生成cglib代理类的class文件

1、启动项目,在Terminal窗口输入jps,查看当前运行项目的pid。

2、打开cmd窗口,进入到JDK安装目录下的lib目录,输入如下命令后回车,弹出HSDB工具窗口。

cmd运行命令:Java -classpath "%JAVA_HOME%/lib/sa-jdi.jar" sun.jvm.hotspot.HSDB

3、点击File下的第一个选项Attach to HotSpot process…,在小弹框中输入当前项目的pid,点击OK。

 

4、点击Tools下的第一个选项:Class Browser,在搜索框中输入目标类所在的包,搜索到之后,可以点击Create .class for all classes(为所有类创建.class文件),也可以单独点击代理类之后,再点击Create .class File。然后关掉HSDB工具,否则代理类.class文件一直卡住无法生成。

5、在HSDB工具运行的lib目录下就会生成对应的class文件,文件所在目录结构与目标类的包结构相同。会生成与代理类相关的三个文件,这三个文件都是以目标类+$$开头,有一个是代理类的.class文件,另外两个带有FastClass的分别是目标类与代理类的FastClass文件。

生成的代理类如下所示,去掉了很多其他的方法。

public class UserServiceImpl$$EnhancerBySpringCGLIB$$698f9bcb extends UserServiceImpl implements SpringProxy, Advised, Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private MethodInterceptor CGLIB$CALLBACK_1;
    private NoOp CGLIB$CALLBACK_2;
    private Dispatcher CGLIB$CALLBACK_3;
    private Dispatcher CGLIB$CALLBACK_4;
    private MethodInterceptor CGLIB$CALLBACK_5;
    private MethodInterceptor CGLIB$CALLBACK_6;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$search$0$Method;
    private static final MethodProxy CGLIB$search$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$buy$1$Method;
    private static final MethodProxy CGLIB$buy$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    public UserServiceImpl$$EnhancerBySpringCGLIB$$698f9bcb() {
        CGLIB$BIND_CALLBACKS(this);
    }

    static {
        CGLIB$STATICHOOK3();
    }
    //此例中只对buy方法进行增强,在代理类中会对父类中所有的方法进行重写,如果某个方法无需增强,则直接调用父类的方法即可。
    public final void search() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$search$0$Method, CGLIB$emptyArgs, CGLIB$search$0$Proxy);
        } else {
            super.search();
        }
    }

    public final void buy() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$buy$1$Method, CGLIB$emptyArgs, CGLIB$buy$1$Proxy);
        } else {
            super.buy();
        }
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        UserServiceImpl$$EnhancerBySpringCGLIB$$698f9bcb var1 = (UserServiceImpl$$EnhancerBySpringCGLIB$$698f9bcb)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            Callback[] var10001 = (Callback[])var10000;
            var1.CGLIB$CALLBACK_6 = (MethodInterceptor)((Callback[])var10000)[6];
            var1.CGLIB$CALLBACK_5 = (MethodInterceptor)var10001[5];
            var1.CGLIB$CALLBACK_4 = (Dispatcher)var10001[4];
            var1.CGLIB$CALLBACK_3 = (Dispatcher)var10001[3];
            var1.CGLIB$CALLBACK_2 = (NoOp)var10001[2];
            var1.CGLIB$CALLBACK_1 = (MethodInterceptor)var10001[1];
            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0];
        }

    }

    static void CGLIB$STATICHOOK3() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.dynamic.proxy.jdkProxy.service.UserServiceImpl$$EnhancerBySpringCGLIB$$698f9bcb");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$2$Method = var10000[0];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[1];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[2];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[3];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        var10000 = ReflectUtils.findMethods(new String[]{"search", "()V", "buy", "()V"}, (var1 = Class.forName("com.dynamic.proxy.jdkProxy.service.UserServiceImpl")).getDeclaredMethods());
        CGLIB$search$0$Method = var10000[0];
        CGLIB$search$0$Proxy = MethodProxy.create(var1, var0, "()V", "search", "CGLIB$search$0");
        CGLIB$buy$1$Method = var10000[1];
        CGLIB$buy$1$Proxy = MethodProxy.create(var1, var0, "()V", "buy", "CGLIB$buy$1");
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值