SpringAOP-底层实现源码解析

news2025/1/21 12:15:35

目录

1. Spring AOP原理流程图

2. 动态代理

3. ProxyFactory

4. Advice的分类

5. Advisor的理解

6. 创建代理对象的方式

ProxyFactoryBean

BeanNameAutoProxyCreator

DefaultAdvisorAutoProxyCreator

7. 对Spring AOP的理解

8. AOP中的概念

9. Advice在Spring AOP中对应API

10. TargetSource的使用

Introduction

LoadTimeWeaver

11. ProxyFactory选择cglib或jdk动态代理原理

代理对象创建过程

JdkDynamicAopProxy

ObjenesisCglibAopProxy

12. 代理对象执行过程

13. 各注解对应的MethodInterceptor

14. AbstractAdvisorAutoProxyCreator

15. @EnableAspectJAutoProxy


1. Spring AOP原理流程图

2. 动态代理

代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。

比如,现在存在一个UserService类:

public class UserService  {
 	public void test() {
        System.out.println("test...");
    }
}

此时,我们new一个UserService对象,然后执行test()方法,结果是显而易见的。

如果我们现在想在不修改UserService类的源码前提下,给test()增加额外逻辑,那么就可以使用动态代理机制来创建UserService对象了,比如:

UserService target = new UserService();

// 通过cglib技术
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);

// 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
 @Override
 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  System.out.println("before...");
  Object result = methodProxy.invoke(target, objects);
  System.out.println("after...");
  return result;
 }
}});

// 动态代理所创建出来的UserService对象
UserService userService = (UserService) enhancer.create();

// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
userService.test();//test

// 运行结果
before...
test
after...

得到的都是UserService对象,但是执行test()方法时的效果却不一样了,这就是代理所带来的效果。

上面是通过cglib来实现的代理对象的创建,是基于父子类的,被代理类(UserService)是父类,代理类是子类,代理对象就是代理类的实例对象,代理类是由cglib创建的,对于程序员来说不用关心。

除开cglib技术,jdk本身也提供了一种创建代理对象的动态代理机制,但是它只能代理接口,也就是UserService得先有一个接口才能利用jdk动态代理机制来生成一个代理对象,比如:

public interface UserInterface {
     public void test();
}

public class UserService implements UserInterface {
    public void test() {
      System.out.println("test...");
    }
}

利用JDK动态代理来生成一个代理对象:


UserService target = new UserService();

// UserInterface接口的代理对象
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  System.out.println("before...");
  Object result = method.invoke(target, args);
  System.out.println("after...");
  return result;
 }
});

UserInterface userService = (UserInterface) proxy;
userService.test();

// 运行结果
before...
test...
after...

如果把new Class[]{UserInterface.class},替换成new Class[]{UserService.class},允许代码会直接报错:

Exception in thread "main" java.lang.IllegalArgumentException : com.bubble.service.UserService is not an interface
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java: 590)
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
	at java.lang.reflect.WeakCache$Factory.get(Weakcache.java: 230)
	at java.lang.reflect.WeakCache.get(WeakCache.java:127)
	at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
	at com.bubble.Test.main(Test.java:50)

表示一定要是个接口。

由于这个限制,所以产生的代理对象的类型是UserInterface,而不是UserService,这是需要注意的。

3. ProxyFactory

上面介绍了两种动态代理技术,那么在Spring中进行了封装,封装出来的类叫做ProxyFactory,表示是创建代理对象的一个工厂,使用起来会比上面的更加方便,比如:

UserService target = new UserService();

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(new MethodInterceptor() {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("before...");
        Object result = invocation.proceed();
        System.out.println("after...");
        return result;
    }
});

UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();

// 运行结果
before...
test...
after...

通过ProxyFactory,我们可以不再关心到底是用cglib还是jdk动态代理了,ProxyFactory会帮我们去判断,如果UserService实现了接口,那么ProxyFactory底层就会用jdk动态代理,如果没有实现接口,就会用cglib技术,上面的代码,就是由于UserService实现了UserInterface接口,所以最后产生的代理对象是UserInterface类型。

4. Advice的分类

  1. Before Advice:方法之前执行
  2. After returning advice:方法return后执行
  3. After throwing advice:方法抛异常后执行
  4. After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后
  5. Around advice:这是功能最强大的Advice,可以自定义执行顺序
public class BubbleBeforeAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("方法执行前执行");
	}
}
public class BubbleAfterReturningAdvice implements AfterReturningAdvice {
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("方法return后执行");
	}
}
public class BubbleAroundAdvice implements MethodInterceptor {
	@Nullable
	@Override
	public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
		System.out.println("方法执行Around前");
		Object proceed = invocation.proceed();
		System.out.println("方法执行Around后");
		return proceed;
	}
}
public class BubbleThrowsAdvice implements ThrowsAdvice {
	public void afterThrowing(Method method, Object[] args, Object target, NullPointerException ex) {
		System.out.println("方法抛出异常后执行");
	}
}
UserService target = new UserService();

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(new BubbleBeforeAdvice()); //MethodInteceptor.invoke(invocation) -->before--->invocation.proceed()

UserService userService = (UserService) proxyFactory.getProxy();
userService.test(); // invocation.proceed()

5. Advisor的理解

跟Advice类似的还有一个Advisor的概念,一个Advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑,比如一个UserService类中有两个方法,按上面的例子,这两个方法都会被代理,被增强,那么我们现在可以通过Advisor,来控制到具体代理哪一个方法,比如:

UserService target = new UserService();

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Pointcut getPointcut() {
        return new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return method.getName().equals("test"); //执行test方法才增强
            }
        };
    }

    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before...");
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        };
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
});

UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();

// 运行结果
before...
test...
after...

上面代码表示,产生的代理对象,只有在执行test这个方法时才会被增强,会执行额外的逻辑,而在执行其他方法时是不会增强的。

6. 创建代理对象的方式

上面介绍了Spring中所提供了ProxyFactory、Advisor、Advice、PointCut等技术来实现代理对象的创建,但是我们在使用Spring时,并不会直接这么去使用ProxyFactory,比如,我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到UserSerivce的代理对象,而这些,Spring都是支持的,只不过,作为开发者肯定得告诉Spring,那些类需要被代理,代理逻辑是什么。

ProxyFactoryBean

@Bean
public ProxyFactoryBean userServiceProxy(){
    UserService userService = new UserService();

    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    proxyFactoryBean.setTarget(userService);
    proxyFactoryBean.addAdvice(new MethodInterceptor() {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before...");
            Object result = invocation.proceed();
            System.out.println("after...");
            return result;
        }
    });
    return proxyFactoryBean;
}

通过这种方法来定义一个UserService的Bean,并且是经过了AOP的。但是这种方式只能针对某一个Bean。它是一个FactoryBean,所以利用的就是FactoryBean技术,间接的将UserService的代理对象作为了Bean。

ProxyFactoryBean还有额外的功能,比如可以把某个Advise或Advisor定义成为Bean,然后在ProxyFactoryBean中进行设置

@Bean
public MethodInterceptor bubbleAroundAdvise(){
    return new MethodInterceptor() {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before...");
            Object result = invocation.proceed();
            System.out.println("after...");
            return result;
        }
    };
}

@Bean
public ProxyFactoryBean userService(){
    UserService userService = new UserService();

    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    proxyFactoryBean.setTarget(userService);
    proxyFactoryBean.setInterceptorNames("bubbleAroundAdvise");
    return proxyFactoryBean;
}
@Bean
public ProxyFactoryBean userService(){
    UserService userService = new UserService();
    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    proxyFactoryBean.addAdvice(new BubbleBeforeAdvice());
    proxyFactoryBean.setTarget(userService);
    return proxyFactoryBean;
}

BeanNameAutoProxyCreator

ProxyFactoryBean得自己指定被代理的对象,那么我们可以通过BeanNameAutoProxyCreator来通过指定某个bean的名字,来对该bean进行代理

@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
     BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
     beanNameAutoProxyCreator.setBeanNames("userSe*");
     beanNameAutoProxyCreator.setInterceptorNames("bubbleAroundAdvise");
     beanNameAutoProxyCreator.setProxyTargetClass(true);

    return beanNameAutoProxyCreator;
}

通过BeanNameAutoProxyCreator可以对批量的Bean进行AOP,并且指定了代理逻辑,指定了一个InterceptorName,也就是一个Advise,前提条件是这个Advise也得是一个Bean,这样Spring才能找到的,但是BeanNameAutoProxyCreator的缺点很明显,它只能根据beanName来指定想要代理的Bean。

DefaultAdvisorAutoProxyCreator

/*
* AppConfig.java
*/
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
    NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    pointcut.addMethodName("test");

    DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
    defaultPointcutAdvisor.setPointcut(pointcut);
    defaultPointcutAdvisor.setAdvice(new BubbleAfterReturningAdvice());

    return defaultPointcutAdvisor;
}

@Bean //AppConfig类上加注解@Import(DefaultAdvisorAutoProxyCreator.class)代替 或 @EnableAspectJAutoProxy代替
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {

    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();

    return defaultAdvisorAutoProxyCreator;
}

通过DefaultAdvisorAutoProxyCreator会直接去找所有Advisor类型的Bean,根据Advisor中的PointCut和Advice信息,确定要代理的Bean以及代理逻辑。

但是,我们发现,通过这种方式,我们得依靠某一个类来实现定义我们的Advisor,或者Advise,或者Pointcut,那么这个步骤能不能更加简化一点呢?(能,通过注解!)

我们能只定义一个类,然后通过在类中的方法上通过某些注解,来定义PointCut以及Advice,比如:

@Aspect
@Component
public class BubbleAspect {

 @Before("execution(public void com.bubble.service.UserService.test())")
 public void bubbleBefore(JoinPoint joinPoint) {
  System.out.println("bubbleBefore");
 }

}

通过上面这个类,我们就直接定义好了所要代理的方法(通过一个表达式),以及代理逻辑(被@Before修饰的方法),简单明了,这样对于Spring来说,它要做的就是来解析这些注解了,解析之后得到对应的Pointcut对象、Advice对象,生成Advisor对象,扔进ProxyFactory中,进而产生对应的代理对象,具体怎么解析这些注解就是@EnableAspectJAutoProxy注解所要做的事情了。

7. 对Spring AOP的理解

OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想,而我们上面所描述的就是Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持,换句话说,就是Spring提供了一套机制,可以让我们更加容易的来进行AOP,所以这套机制我们也可以称之为Spring AOP。

但是值得注意的是,上面所提供的注解的方式来定义Pointcut和Advice,Spring并不是首创,首创是AspectJ,而且也不仅仅只有Spring提供了一套机制来支持AOP,还有比如 JBoss 4.0、aspectwerkz等技术都提供了对于AOP的支持。而刚刚说的注解的方式,Spring是依赖了AspectJ的,或者说,Spring是直接把AspectJ中所定义的那些注解直接拿过来用,自己没有再重复定义了,不过也仅仅只是把注解的定义赋值过来了,每个注解具体底层是怎么解析的,还是Spring自己做的,所以我们在用Spring时,如果你想用@Before、@Around等注解,是需要单独引入aspecj相关jar包的,比如:

compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.5'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'

值得注意的是:AspectJ是在编译时对字节码进行了修改,是直接在UserService类对应的字节码中进行增强的,也就是可以理解为是在编译时就会去解析@Before这些注解,然后得到代理逻辑,加入到被代理的类中的字节码中去的,所以如果想用AspectJ技术来生成代理对象 ,是需要用单独的AspectJ编译器的。我们在项目中很少这么用,我们仅仅只是用了@Before这些注解,而我们在启动Spring的过程中,Spring会去解析这些注解,然后利用动态代理机制生成代理对象的。

8. AOP中的概念

上面我们已经提到Advisor、Advice、PointCut等概念了,还有一些其他的概念,首先关于AOP中的概念本身是比较难理解的,Spring官网上是这么说的:

Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific. Unfortunately, AOP terminology is not particularly intuitive. However, it would be even more confusing if Spring used its own terminology

意思是,AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是,如果Spring重新定义自己的那可能会导致更加混乱

  1. Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等
  2. Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
  3. Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链
  4. Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
  5. Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
  6. Target object:目标对象,被代理对象
  7. AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
  8. Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP

9. Advice在Spring AOP中对应API

上面说到的Aspject中的注解,其中有五个是用来定义Advice的,表示代理逻辑,以及执行时机:

  1. @Before
  2. @AfterReturning
  3. @AfterThrowing
  4. @After
  5. @Around

我们前面也提到过,Spring自己也提供了类似的执行实际的实现类:

  1. 接口MethodBeforeAdvice,继承了接口BeforeAdvice
  2. 接口AfterReturningAdvice
  3. 接口ThrowsAdvice
  4. 接口AfterAdvice
  5. 接口MethodInterceptor

Spring会把五个注解解析为对应的Advice类:

  1. @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
  2. @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
  3. @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
  4. @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
  5. @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor

10. TargetSource的使用

在日常的AOP中,被代理对象就是Bean对象,是由BeanFactory给我们创建出来的,但是Spring AOP中提供了TargetSource机制,可以让我们用来自定义逻辑来创建被代理对象

比如@Lazy注解,当加在属性上时,会产生一个代理对象赋值给这个属性,产生代理对象的代码为:

/**
 * 构建一个延迟解析的代理对象,用于实现对依赖的懒加载解析。
 * @param descriptor 依赖描述符,描述需要解析的依赖的信息
 * @param beanName 如果适用,表示当前 Bean 的名称
 * @return 延迟解析的代理对象
 */
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
    BeanFactory beanFactory = getBeanFactory();// 获取当前 BeanFactory
    Assert.state(beanFactory instanceof DefaultListableBeanFactory,
            "BeanFactory needs to be a DefaultListableBeanFactory");
    final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;

    TargetSource ts = new TargetSource() {// 创建一个 TargetSource 实例,用于延迟解析依赖
        @Override
        public Class<?> getTargetClass() {
            return descriptor.getDependencyType();
        }
        @Override
        public boolean isStatic() {
            return false;
        }
        @Override
        public Object getTarget() {
            Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
            // 使用 BeanFactory 解析依赖对象
            Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
            if (target == null) {
                // 如果依赖对象为空,根据类型返回一个空的集合或映射
                Class<?> type = getTargetClass();
                if (Map.class == type) {
                    return Collections.emptyMap();
                }
                else if (List.class == type) {
                    return Collections.emptyList();
                }
                else if (Set.class == type || Collection.class == type) {
                    return Collections.emptySet();
                }// 抛出异常,表示延迟注入点的可选依赖不存在
                throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
                        "Optional dependency not present for lazy injection point");
            }// 如果存在依赖对象,并且有关联的 BeanName,将这些 BeanName 注册为当前 Bean 的依赖
            if (autowiredBeanNames != null) {
                for (String autowiredBeanName : autowiredBeanNames) {
                    if (dlbf.containsBean(autowiredBeanName)) {
                        dlbf.registerDependentBean(autowiredBeanName, beanName);
                    }
                }
            }
            return target;
        }
        @Override
        public void releaseTarget(Object target) {// 这里无需实现,因为是懒加载的代理对象
        }
    };

    ProxyFactory pf = new ProxyFactory();// 使用 ProxyFactory 创建代理对象并返回
    pf.setTargetSource(ts);
    Class<?> dependencyType = descriptor.getDependencyType();
    if (dependencyType.isInterface()) {
        pf.addInterface(dependencyType);
    }
    return pf.getProxy(dlbf.getBeanClassLoader());
}

这段代码就利用了ProxyFactory来生成代理对象,以及使用了TargetSource,以达到代理对象在执行某个方法时,调用TargetSource的getTarget()方法实时得到一个被代理对象

Introduction

Spring AOP之Introduction(@DeclareParents)简介 - 门罗的魔术师 - 博客园 (cnblogs.com)

package com.bubble.aspect;
import com.bubble.service.UserImplement;
import com.bubble.service.UserInterface;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BubbleAspect {

	@DeclareParents(value = "com.bubble.service.UserService", defaultImpl = UserImplement.class)
	private UserInterface userInterface;
}
package com.bubble.service;

public interface UserInterface {
	public void testXxx();
}
package com.bubble.service;

public class UserImplement implements UserInterface{
	@Override
	public void testXxx() {
		System.out.println("testXxx");
	}
}
import com.bubble.service.*;
import org.springframework.context.annotation.*;
@ComponentScan("com.bubble")
@EnableAspectJAutoProxy
public class AppConfig {
}
package com.bubble;
import com.bubble.service.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {

	public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		UserInterface userService = (UserInterface) applicationContext.getBean("userService");
		userService.testXxx();//运行结果-> testXxx
    }
}

LoadTimeWeaver

Spring之LoadTimeWeaver——一个需求引发的思考---转 - 一天不进步,就是退步 - 博客园 (cnblogs.com)

11. ProxyFactory选择cglib或jdk动态代理原理

ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术:

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快
    // 或者isProxyTargetClass为true,
    // 或者被代理对象没有实现接口,
    // 或者只实现了SpringProxy这个接口
    // 那么则利用Cglib进行动态代理,但如果被代理类是接口,或者被代理类已经是进行过JDK动态代理而生成的代理类了则只能进行JDK动态代理

    // 其他情况都会进行JDK动态代理,比如被代理类实现了除SpringProxy接口之外的其他接口

    // 是不是在GraalVM虚拟机上运行
    if (!NativeDetector.inNativeImage() &&
            // config就是ProxyFactory对象
            // optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface
            (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {

        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

代理对象创建过程

JdkDynamicAopProxy

  1. 在构造JdkDynamicAopProxy对象时,会先拿到被代理对象自己所实现的接口,并且额外的增加SpringProxy、Advised、DecoratingProxy三个接口,组合成一个Class[],并赋值给proxiedInterfaces属性
  2. 并且检查这些接口中是否定义了equals()、hashcode()方法
  3. 执行Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this),得到代理对象,JdkDynamicAopProxy作为InvocationHandler,代理对象在执行某个方法时,会进入到JdkDynamicAopProxy的invoke()方法中
/**
 * 创建一个 JDK 动态代理对象,并返回。
 * @param classLoader 用于加载代理类的类加载器
 * @return 创建的 JDK 动态代理对象
 */
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    // 使用 Proxy.newProxyInstance() 创建 JDK 动态代理对象,实现的接口为 this.proxiedInterfaces,
    // 代理对象的方法调用会由 this(实现了 InvocationHandler 接口)处理
    // this实现了InvocationHandler
    return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
/**
 * Implementation of {@code InvocationHandler.invoke}.
 * <p>Callers will see exactly the exception thrown by the target,
 * unless a hook method throws an exception.
 * 实现了 {@code InvocationHandler.invoke} 方法的逻辑。
 * <p>调用者将会看到目标抛出的异常,除非 hook 方法抛出异常。
 */
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;

    // 拿到被代理对象
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        // 如果接口中没有定义equals()方法,那么则直接调用,不走代理
        // equals 方法不被代理,因为 Proxy 的 equals 逻辑默认是比较代理对象是否是同一个对象
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            // 目标本身不实现 equals(Object) 方法。
            return equals(args[0]);
        }
        // 如果接口中没有定义hashCode()方法,直接调用,不走代理
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        // 如果方法来自 DecoratingProxy 接口,获取被代理的实际类
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            // 得到代理对象的类型,而不是所实现的接口
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        // 如果方法来自 Advised 接口,直接调用代理对象的相关方法,不走代理逻辑
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            // 也是直接调用Advised接口中的方法,不走代理逻辑
            // 其实就是利用代理对象获取ProxyFactory中的信息
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        // 如果ProxyFactory的exposeProxy为true,则将代理对象设置到currentProxy这个ThreadLocal中去
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.// 如有必要,使调用可用。
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        // 尽可能晚地到达,以尽量减少我们“拥有”目标的时间,// 如果它来自池。
        // 被代理对象和代理类
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // Get the interception chain for this method.// 获取该方法的拦截器链
        // 代理对象在执行某个方法时,根据方法筛选出匹配的Advisor,并适配成Interceptor
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        // 检查是否有拦截器。如果没有,直接调用目标对象的方法,避免创建 MethodInvocation。
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            // 我们可以跳过创建方法调用:直接调用目标
            // 请注意,最终调用者必须是 InvokerInterceptor,因此我们知道它确实如此
            // 只是对目标进行反射操作,没有热交换或花哨的代理。

            // 如果没有Advice,则直接调用对应方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // We need to create a method invocation...// 需要创建一个方法调用
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.// 通过拦截器链执行方法调用
            retVal = invocation.proceed();
        }

        // Massage return value if necessary.// 如果需要,调整返回值
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
                returnType != Object.class && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            // 特殊情况:如果返回的是目标对象本身,并且方法返回类型是兼容的,则返回代理对象
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.// 来自 TargetSource。释放目标对象
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.// 恢复旧的代理对象
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

ObjenesisCglibAopProxy

  1. 创建Enhancer对象
  2. 设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类
  3. 设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及SpringProxy、Advised、DecoratingProxy接口
  4. 设置Enhancer的Callbacks为DynamicAdvisedInterceptor
  5. 最后创建一个代理对象,代理对象在执行某个方法时,会进入到DynamicAdvisedInterceptor的intercept()方法中
/**
 * 创建 CGLIB 代理对象。
 *
 * @param classLoader 类加载器,用于加载生成的代理类
 * @return 代理对象实例
 * @throws AopConfigException 如果生成代理过程中出现异常
 */
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
    }

    try {
        // 被代理的类
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        Class<?> proxySuperClass = rootClass;
        // 如果被代理类本身就已经是Cglib所生成的代理类了
        if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
            // 获取真正的被代理类
            proxySuperClass = rootClass.getSuperclass();
            // 获取被代理类所实现的接口
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }

        // Validate the class, writing log messages as necessary.
        // 验证类,如果需要的话,记录日志信息
        validateClassIfNecessary(proxySuperClass, classLoader);

        // Configure CGLIB Enhancer...// 配置 CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }

        // 被代理类,代理类的父类
        enhancer.setSuperclass(proxySuperClass);
        // 代理类额外要实现的接口
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

        // 获取和被代理类所匹配的Advisor
        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // fixedInterceptorMap only populated at this point, after getCallbacks call above
        // 在 getCallbacks 调用后才填充 fixedInterceptorMap
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);

        // Generate the proxy class and create a proxy instance.
        // 生成代理类并创建代理对象实例
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    catch (CodeGenerationException | IllegalArgumentException ex) {
        throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
                ": Common causes of this problem include using a final class or a non-visible class",
                ex);
    }
    catch (Throwable ex) {
        // TargetSource.getTarget() failed
        throw new AopConfigException("Unexpected AOP exception", ex);
    }
}
/**
 * Determine the complete set of interfaces to proxy for the given AOP configuration.
 * <p>This will always add the {@link Advised} interface unless the AdvisedSupport's
 * {@link AdvisedSupport#setOpaque "opaque"} flag is on. Always adds the
 * {@link org.springframework.aop.SpringProxy} marker interface.
 * @param advised the proxy config
 * @param decoratingProxy whether to expose the {@link DecoratingProxy} interface
 * @return the complete set of interfaces to proxy
 * @since 4.3
 * @see SpringProxy
 * @see Advised
 * @see DecoratingProxy
 *
 * 确定要为给定的 AOP 配置进行代理的完整接口集合。
 * <p>除非 AdvisedSupport 的 "opaque" 标志打开,否则这将始终添加 Advised 接口。始终添加
 * SpringProxy 标记接口。
 *  *
 * @param advised         代理配置
 * @param decoratingProxy 是否暴露 DecoratingProxy 接口
 * @return 要代理的完整接口集合
 */
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
    // 被代理对象自己所实现的接口
    Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();

    // 如果被代理对象没有实现接口,则判断被代理类是不是接口,或者被代理类是不是已经经过JDK动态代理之后的类从而获取想对应的接口
    if (specifiedInterfaces.length == 0) {
        // No user-specified interfaces: check whether target class is an interface.
        // 没有用户指定的接口:检查目标类是否为接口。
        Class<?> targetClass = advised.getTargetClass();
        if (targetClass != null) {
            if (targetClass.isInterface()) {
                advised.setInterfaces(targetClass);
            }
            else if (Proxy.isProxyClass(targetClass)) {
                advised.setInterfaces(targetClass.getInterfaces());
            }
            specifiedInterfaces = advised.getProxiedInterfaces();
        }
    }

    // 添加三个Spring内置接口:SpringProxy、Advised、DecoratingProxy
    List<Class<?>> proxiedInterfaces = new ArrayList<>(specifiedInterfaces.length + 3);
    for (Class<?> ifc : specifiedInterfaces) {
        // Only non-sealed interfaces are actually eligible for JDK proxying (on JDK 17)
        // 只有非密封的接口实际上才有资格进行 JDK 代理(在 JDK 17 上)
        if (isSealedMethod == null || Boolean.FALSE.equals(ReflectionUtils.invokeMethod(isSealedMethod, ifc))) {
            proxiedInterfaces.add(ifc);
        }
    }
    if (!advised.isInterfaceProxied(SpringProxy.class)) {
        proxiedInterfaces.add(SpringProxy.class);
    }
    if (!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)) {
        proxiedInterfaces.add(Advised.class);
    }
    if (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)) {
        proxiedInterfaces.add(DecoratingProxy.class);
    }
    return ClassUtils.toClassArray(proxiedInterfaces);
}
/**
 * 获取用于创建代理的拦截器回调数组。
 *
 * @param rootClass 根类,被代理的目标类
 * @return 代理的拦截器回调数组
 * @throws Exception 异常
 */
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
    // Parameters used for optimization choices...// 用于优化选择的参数...
    boolean exposeProxy = this.advised.isExposeProxy();
    boolean isFrozen = this.advised.isFrozen();
    boolean isStatic = this.advised.getTargetSource().isStatic();

    // Choose an "aop" interceptor (used for AOP calls).// 选择“aop”拦截器(用于AOP调用)。
    Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

    // Choose a "straight to target" interceptor. (used for calls that are
    // unadvised but can return this). May be required to expose the proxy.
    // 选择“直接到目标”拦截器。(用于未经通知的调用,但可能返回this)。可能需要暴露代理。
    Callback targetInterceptor;
    if (exposeProxy) {
        targetInterceptor = (isStatic ?
                new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
    }
    else {
        targetInterceptor = (isStatic ?
                new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
    }

    // Choose a "direct to target" dispatcher (used for
    // unadvised calls to static targets that cannot return this).
    // 选择“直接到目标”分派程序(用于无法返回this的静态目标的未经通知的调用)。
    Callback targetDispatcher = (isStatic ?
            new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
    // 主要拦截器回调数组
    Callback[] mainCallbacks = new Callback[] {
            aopInterceptor,  // for normal advice,用于常规的 Advice,执行Interceptor链
            // 调用目标而不考虑 Advice,如果进行了优化,则返回 this,将代理对象设置到 ThreadLocal 中,AopContext.setCurrentProxy(proxy)
            targetInterceptor,  // invoke target without considering advice, if optimized 将代理对象设置到ThreadLocal中,AopContext.setCurrentProxy(proxy)
            new SerializableNoOp(),  // no override for methods mapped to this// 对于映射到此的方法,不进行覆盖
            targetDispatcher, this.advisedDispatcher,
            new EqualsInterceptor(this.advised),
            new HashCodeInterceptor(this.advised)
    };

    Callback[] callbacks;

    // If the target is a static one and the advice chain is frozen,
    // then we can make some optimizations by sending the AOP calls
    // direct to the target using the fixed chain for that method.
    // 如果目标是静态的,并且 Advice 链被冻结,
    // 那么我们可以通过使用方法的固定链将 AOP 调用直接发送到目标,以便进行一些优化。
    if (isStatic && isFrozen) {
        Method[] methods = rootClass.getMethods();
        Callback[] fixedCallbacks = new Callback[methods.length];
        this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length);

        // TODO: small memory optimization here (can skip creation for methods with no advice)
        // TODO:此处进行小内存优化(可以跳过没有建议的方法的创建)
        for (int x = 0; x < methods.length; x++) {
            Method method = methods[x];
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
            fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                    chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
            this.fixedInterceptorMap.put(method, x);
        }

        // Now copy both the callbacks from mainCallbacks
        // and fixedCallbacks into the callbacks array.
        // mainCallbacks 和 fixedCallbacks 中的回调复制到 callbacks 数组中。
        callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
        System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
        System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
        this.fixedInterceptorOffset = mainCallbacks.length;
    }
    else {
        callbacks = mainCallbacks;
    }
    return callbacks;
}
DynamicAdvisedInterceptor的intercept()方法
/**
 * CGLIB 的拦截方法,用于拦截被代理对象的方法调用。
 *
 * @param proxy       代理对象
 * @param method      被调用的方法
 * @param args        方法的参数
 * @param methodProxy CGLIB 生成的 MethodProxy 实例,用于调用目标方法
 * @return 方法调用的返回值
 * @throws Throwable 异常
 */
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Object target = null;
    TargetSource targetSource = this.advised.getTargetSource();
    try {
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            // 如果允许暴露代理,则将当前代理对象设置到 AopContext 中
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
        // 尽量晚获取目标对象,以减少我们“拥有”目标对象的时间,以防它来自池...
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        // Check whether we only have one InvokerInterceptor: that is,
        // no real advice, but just reflective invocation of the target.
        // 检查是否只有一个 InvokerInterceptor:即,没有实际的通知,而只是目标的反射调用。
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // We can skip creating a MethodInvocation: just invoke the target directly.
            // Note that the final invoker must be an InvokerInterceptor, so we know
            // it does nothing but a reflective operation on the target, and no hot
            // swapping or fancy proxying.
            // 可以跳过创建 MethodInvocation:直接调用目标方法。
            // 注意,最终的调用者必须是 InvokerInterceptor,所以我们知道它只是对目标的反射操作,没有热交换或复杂的代理。
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
            // We need to create a method invocation... // 需要创建一个方法调用...
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);// 处理方法调用的返回值
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);// 如果目标对象不是静态的,则释放目标对象
        }
        if (setProxyContext) {
            // Restore old proxy.// 恢复之前的代理对象
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

12. 代理对象执行过程

  1. 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor
  2. 代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行匹配筛选
  3. 把和方法所匹配的Advisor适配成MethodInterceptor
  4. 把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前Method对象、方法参数封装为MethodInvocation对象
  5. 调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的对应方法
  6. 按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入invoke()方法
  7. 直到执行完最后一个MethodInterceptor了,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法

13. 各注解对应的MethodInterceptor

  • @Before对应的是AspectJMethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor
    • 先执行advice对应的方法
    • 再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

  • @After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 再执行advice对应的方法

  • @Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor
    • 直接执行advice对应的方法,由@Around自己决定要不要继续往后面调用

  • @AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 如果上面抛了Throwable,那么则会执行advice对应的方法

  • @AfterReturning对应的是AspectJAfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 执行上面的方法后得到最终的方法的返回值
    • 再执行Advice对应的方法

14. AbstractAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator的父类是AbstractAdvisorAutoProxyCreator。

AbstractAdvisorAutoProxyCreator非常强大以及重要,只要Spring容器中存在这个类型的Bean,就相当于开启了AOP,AbstractAdvisorAutoProxyCreator实际上就是一个BeanPostProcessor,所以在创建某个Bean时,就会进入到它对应的生命周期方法中,比如:在某个Bean初始化之后,会调用wrapIfNecessary()方法进行AOP,底层逻辑是,AbstractAdvisorAutoProxyCreator会找到所有的Advisor,然后判断当前这个Bean是否存在某个Advisor与之匹配(根据Pointcut),如果匹配就表示当前这个Bean有对应的切面逻辑,需要进行AOP,需要产生一个代理对象。

15. @EnableAspectJAutoProxy

这个注解主要就是往Spring容器中添加了一个AnnotationAwareAspectJAutoProxyCreator类型的Bean。

AspectJAwareAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,重写了findCandidateAdvisors()方法,AbstractAdvisorAutoProxyCreator只能找到所有Advisor类型的Bean对象,但是AspectJAwareAdvisorAutoProxyCreator除开可以找到所有Advisor类型的Bean对象,还能把@Aspect注解所标注的Bean中的@Before等注解及方法进行解析,并生成对应的Advisor对象。

所以可以理解@EnableAspectJAutoProxy,其实就是像Spring容器中添加了一个AbstractAdvisorAutoProxyCreator类型的Bean,从而开启了AOP,并且还会解析@Before等注解生成Advisor。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1978512.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文心智能体【MBTI速测小精灵】:趣味速测,精准解析你的性格密码!

文章目录 一、文心智能体平台是什么&#xff1f;二、创建文心智能体智能体创建智能体调试分析智能体基础配置智能体高级配置智能体高级调试 三、文心智能体发布四、文心智能体体验总结 一、文心智能体平台是什么&#xff1f; AgentBuilder文心智能体平台是基于文心大模型的智能…

饮料加速稳定性试验

饮料加速稳定性试验概览 饮料加速稳定性试验是一种通过模拟加速条件来预测饮料在市场销售期间稳定性的测试方法。这种测试对于确保饮料产品在整个保质期内的质量和安全性至关重要。它可以帮助生产企业优化产品配方、改进包装材料、调整储存和运输条件&#xff0c;从而确保产品在…

苍穹外面day13(day10)---订单状态定时处理、来单提醒和客户催单

Spring Task 同学们可以看我这篇文章 Spring Task初学-CSDN博客 订单状态定时处理 新建OrderTask /*** 定时任务类&#xff0c;定时处理订单状态*/ Component Slf4j public class OrderTask {Autowiredprivate OrderMapper orderMapper;/*** 处理超时订单的方法*/Scheduled(c…

【嵌入式之RTOS】什么是消息队列

目录 一、FreeRTOS消息队列的基本概念 二、FreeRTOS消息队列的工作原理 三、FreeRTOS消息队列的特点 四、FreeRTOS消息队列的应用 五、示例 消息队列是一种用于任务间通信的机制&#xff0c;它允许一个任务&#xff08;生产者&#xff09;向消息队列发送消息&#xff0c;而…

使用uniapp+Django开发的在线工具网站

引言 在当今数字化时代&#xff0c;在线工具网站为用户提供了便捷的服务和功能&#xff0c;本文分享了我使用UniApp和Django开发的一款多平台在线工具网站。通过这个项目&#xff0c;我探索了跨平台开发与强大的后端框架结合的优势&#xff0c;实现了用户友好的界面和稳健的功…

【C++】二维数组定义方式

二维数组有四种定义方式 1、数据类型 数组名[行数 ][ 列数 ]; 2、数据类型 数组名[ 行数 ][ 列数 ]{{数据1&#xff0c;数据2}&#xff0c;{数据3&#xff0c;数据4 }}; 3、数据类型 数组名[ 行数 ][ 列数 ]{数据1&#xff0c;数据2&#xff0c;数据3&#xff…

C++ 标准库和标准模板库

参考&#xff1a; C STL 教程 | 菜鸟教程 (runoob.com) C标准库和标准模板库 - 星朝 - 博客园 (cnblogs.com) C强大的功能来源于其丰富的类库及库函数资源。C标准库的内容总共在50个标准头文件中定义。在C开发中&#xff0c;要尽可能地利用标准库完成。这样做的直接好处包括&am…

【Jenkins未授权访问漏洞 】

默认情况下 Jenkins面板中用户可以选择执行脚本界面来操作一些系统层命令&#xff0c;攻击者可通过未授权访问漏洞或者暴力破解用户密码等进入后台管理服务&#xff0c;通过脚本执行界面从而获取服务器权限。 第一步&#xff1a;使用fofa语句搜索 搜索语句&#xff1a; port&…

NICE Seminar(2022-7-17)进化约束优化(中南大学王勇教授)

​​​​​​​ ACO偏向于离散 DE和PSO偏向于连续变量 确定性替换和随机替换 存在缺陷&#xff0c;可行域可能有多个。 Pfea为可行解所占比例 目标空间中的可行域与在决策空间的可行域中的对应关系&#xff1f;&#xff1f;&#xff1f; 维度中套维度&#xff1f;&#xff1…

苹果 Safari 的隐私保护与广告追踪问题 :技术进展与挑战

隐私保护的进展与挑战 近年来&#xff0c;浏览器行业在隐私保护技术方面取得了显著进展&#xff0c;尤其是在广告追踪领域。谷歌的 Chrome 浏览器推广了隐私沙盒&#xff0c;通过将用户可能感兴趣的主题分类并推送给广告商。Mozilla Firefox 和 Meta Facebook 则推出了一种名为…

C 语言学习(5) ---- 汇编语法基础

目录 汇编语言基础x86 体系的寄存器说明Intel 和 AT&T 语法"helloworld" 汇编程序分析指令和伪指令TBD 汇编语言基础 汇编程序基本由 4 种类型的组件组成&#xff1a;指令&#xff08;instruction&#xff09;、伪指令&#xff08;directive&#xff09;、标号&…

Python在指定文件夹下创建虚拟环境

基于不同python版本和第三方包版本开发的项目&#xff0c;为了方便学习和管理python环境&#xff0c;可以在指定的文件夹里创建项目所需的虚拟环境。具体流程如下&#xff1a; (1) 以管理员身份打开Ananconda Prompt&#xff0c;查看当前虚拟环境&#xff0c;输入命令如下&…

C#中的Modbus协议

协议介绍 关于Modbus协议&#xff1a; Modbus协议是MODICON&#xff08;莫迪康&#xff09;&#xff08;现施耐德品牌&#xff09;在1979年开发的&#xff0c;是全球第一个真正用于现场的总线协议&#xff1b;Modbus协议是应用于电子控制器上的一种通用语言。通过此协议&…

一、报名与安装软件

声明 首先声明这个考试是需要费用的&#xff0c;目前我缴纳的费用是980元 官网 报名与学习官网为&#xff1a;https://cloud.aigccert.com/web/index 选择此处&#xff0c;点击报名后按要求完善个人信息。 完善个人信息后可以开始学习视频&#xff0c;视频内容看起来多其实…

Servlet开发流程 (里面有Idea项目添加Tomcat依赖详细教程)

目标: 校验注册时&#xff0c;用户名是否被占用。通过客户端向一个Servlet发送请求&#xff0c;携带username&#xff0c;如果用户 名是zhangsan&#xff0c;则向客户端响应 NO&#xff0c;如果是其他&#xff0c;响应YES。 1.在idea中创建web工程 1右键点击父工程-New-Module …

【MySQL 01】在 Ubuntu 22.04 环境下安装 MySQL

文章目录 &#x1f308; 1. 说明&#x1f308; 2. 卸载不必要的环境&#x1f308; 3. 安装 MySQL&#x1f308; 4. 启动和关闭 MySQL 服务&#x1f308; 5. 临时登录 MySQL&#x1f308; 6. 设置 MySQL 密码&#x1f308; 7. 配置 MySQL &#x1f308; 1. 说明 在安装与卸载中…

此卷不包含可识别的文件系统‘及高效数据恢复策略

在数字信息的海洋中遨游时&#xff0c;我们偶尔会遭遇“此卷不包含可识别的文件系统”这一突如其来的警示&#xff0c;它像是一道无形的屏障&#xff0c;阻挡了我们与宝贵数据之间的直接联系。这一错误提示&#xff0c;往往意味着存储设备的文件系统结构出现了异常&#xff0c;…

嵌入式全栈开发学习笔记---数据结构概述

目录 入门级概念 什么是数据结构&#xff1f; 什么是算法&#xff1f;算法和数据结构有什么联系&#xff1f; 为什么要学习数据结构&#xff1f; 逻辑结构 线性结构&#xff08;嵌入式领域的重点&#xff09; 树形结构 图形结构 线性结构&#xff08;嵌入式领域的重点…

【书生大模型实战营(暑假场)】入门任务二 Git 关卡

入门任务二 Git 关卡 参考&#xff1a; 教程任务 1 闯关任务 1.1 使用 Git 完成破冰介绍 本任务将基于开发机实现&#xff0c;重点在于熟悉Git操作。首先要了解 Git操作的常见四部曲&#xff0c;即&#xff1a;舔 Add&#xff0c;提 Commit&#xff0c;拉 Pull&#xff0c…

架构师知识梳理(一):计算机硬件

目录 计算机硬件组成 CPU CPU的组成 CPU的功能 校验码 奇偶校验 CRC CRC计算案例 指令 指令指行过程 指令系统 指令系统分类 指令流水线技术 流水线技术相关计算公式 存储 计算机存储系统设计 高速缓存Cache 缓存的局部性原理 地址映射 替换算法 关于命中…