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");
}
}