Spring之AOP底层源码解析

news2024/10/1 17:27:30

Spring之AOP底层源码解析

1、动态代理

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

举个例子

public class UserService {

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

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

Untitled

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

		public static void main(String[] args) {
				// 被代理对象
        UserService target = new UserService();
        // 通过cglib技术
        Enhancer enhancer = new Enhancer();
				// 设置要代理的类
        enhancer.setSuperclass(UserService.class);
        // 定义额外的逻辑,也就是代理逻辑
        enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
            /**
             *
             * @param o 代理对象
             * @param method 当前正在执行的方法
             * @param objects 方法所需要的参数
             * @param methodProxy
             * @return
             * @throws Throwable
             */
						@Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
								// 执行被代理对象的原始方法
                Object result = methodProxy.invoke(target, objects);
								// Object result = methodProxy.invokeSuper(o, objects); 这种方式也可以
                System.out.println("after...");
                return result;
            }
        }});

        // 动态代理所创建出来的UserService代理对象
        UserService userService = (UserService) enhancer.create();
        // 执行这个userService的test方法时,就会额外执行一些其他逻辑
        userService.test();
    }

Untitled

得到的都是 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 动态代理来生成一个代理对象:

		public static void main(String[] args) {
				// 被代理对象
        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();
    }

Untitled

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

Untitled

Untitled

表示一定要是个接口。

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

2、ProxyFactory

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

		public static void main(String[] args) {
				// 被代理对象
        UserService target = new UserService();
				// 创建代理对象工厂
        ProxyFactory proxyFactory = new ProxyFactory();
				// 设置要代理的目标类
        proxyFactory.setTarget(target);
				/**
         * 注意:如果没有加上下面这行代码,那么默认走的是cglib动态代理;
         * 而如果我们设置了接口,那么走的就是jdk动态代理
         */
        proxyFactory.setInterfaces(UserInterface);
				// 设置代理逻辑,MethodInterceptor表示方法拦截器
        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();
    }

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

3、Advice的分类

1、Before Advice:方法调用前执行

public class XiexuBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法调用前执行");
    }
}

2、After Returning Advice:方法 Return 后执行

		public class XiexuAfterReturningAdvice implements AfterReturningAdvice {
        /**
         * @param returnValue 执行完被代理方法之后的返回值
         * @param method      被代理方法
         * @param args        方法所需要的参数
         * @param target      被代理对象
         * @throws Throwable
         */
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("方法return后执行");
        }
    }

3、After Throwing Advice:方法抛异常后执行

Untitled

public class XiexuThrowsAdvice implements ThrowsAdvice {
		
		/**
     * NullPointerException ex:当方法抛出的异常类型为NullPointerException类型时,才会调用该方法
     */
    public void afterThrowing(Method method, Object[] args, Object target, NullPointerException ex) {
        System.out.println("方法抛出异常后执行");
    }

}

4、After Advice:方法执行完之后执行,不管当前方法有没有抛出异常,这个 Advice 方法都会执行

5、Around Advice:这是功能最强大的 Advice,可以自定义执行顺序

public class XiexuAroundAdvice 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;
    }
}

3.1、MethodInterceptor链

除了Around advice,其他 advice 在执行完各自的逻辑代码后,都会自动调用 proceed() 去执行被代理方法,而每次调用 proceed() 就会去看还有没有设置其他的 advice,如果有就会继续执行其他 advice 的代理逻辑。

这里就是用到了「责任链」设计模式。

	public static void main(String[] args) {
				// 被代理对象
        UserService target = new UserService();
				// 代理对象工厂
        ProxyFactory proxyFactory = new ProxyFactory();
				// 设置目标对象
        proxyFactory.setTarget(target);
        proxyFactory.addAdvice(new XiexuBeforeAdvice());
        proxyFactory.addAdvice(new XiexuAroundAdvice());
        proxyFactory.addAdvice(new XiexuAroundAdvice());
				// 获取代理对象
        UserService proxy = (UserService) proxyFactory.getProxy();
        proxy.test(); // 执行这行代码的时候,底层就会去执行invocation.proceed()
    }

Untitled

proceed() 方法源码:

	@Override
	@Nullable
	public Object proceed() throws Throwable {

		/**
		 * currentInterceptorIndex初始值为-1,每调用一个Interceptor就会加1
		 * 当调用完了最后一个Interceptor后就会执行被代理的方法
		 */
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			// 执行被代理的方法,点进invokeJoinpoint()看看
			return invokeJoinpoint();
		}

		// currentInterceptorIndex初始值为-1
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

		/**
		 * 如果当前interceptor是InterceptorAndDynamicMethodMatcher,则先进行匹配,匹配成功后再调用该interceptor
		 * 如果没有匹配则递归调用proceed()方法,调用下一个interceptor
		 */
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			// 动态匹配,根据方法参数进行匹配
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			} else {
				// 不匹配则执行下一个MethodInterceptor
				return proceed();
			}
		} else {
			/**
			 * 直接调用MethodInterceptor,传入this,在内部会再次调用proceed()方法进行递归
			 * 比如MethodBeforeAdviceInterceptor
			 */
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

4、Advisor的理解

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

public class Test {

    public static void main(String[] args) {
				// 被代理对象
        UserService target = new UserService();
				// 代理对象工厂
        ProxyFactory proxyFactory = new ProxyFactory();
				// 设置目标对象
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(new PointcutAdvisor() {
            /**
             * Pointcut可以去定义我们的代理逻辑要应用到哪个方法或哪个类上面
             * @return
             */
            @Override
            public Pointcut getPointcut() {
                return new StaticMethodMatcherPointcut() {
                    @Override
                    public boolean matches(Method method, Class<?> targetClass) {
												// 表示只有test()方法才需要走代理逻辑
                        return method.getName().equals("test");
                    }
                };
            }

            /**
             * Advice只是表示一段代理逻辑
             * @return
             */
            @Override
            public Advice getAdvice() {
                return new XiexuAroundAdvice();
            }

            /**
             * 这个方法可以忽略
             * @return
             */
            @Override
            public boolean isPerInstance() {
                return false;
            }
        });
				// 获取代理对象
        UserInterface userService = (UserInterface) proxyFactory.getProxy();
        userService.test();
    }

}

Untitled

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

5、创建代理对象的方式

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

5.1、ProxyFactoryBean

		// 将产生的代理对象成为一个Bean
		@Bean
    public ProxyFactoryBean userService() {
        UserService userService = new UserService();
				// 创建ProxyFactoryBean对象
        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 还有额外的功能,比如可以把某个 Advice 或 Advisor 定义成为 Bean,然后在 ProxyFactoryBean 中进行设置:

		@Bean
    public MethodInterceptor XiexuAroundAdvice() {
        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("XiexuAroundAdvice");
        return proxyFactoryBean;
    }

5.2、BeanNameAutoProxyCreator

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

		@Bean
    public MethodInterceptor XiexuAroundAdvice() {
        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 BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setBeanNames("userSe*");
        beanNameAutoProxyCreator.setInterceptorNames("XiexuAroundAdvice");
        beanNameAutoProxyCreator.setProxyTargetClass(true);
        return beanNameAutoProxyCreator;
    }

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

5.3、DefaultAdvisorAutoProxyCreator

public class AppConfig {

		/**
     * 定义一个Advisor类型的Bean
     *
     * @return
     */
    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.addMethodName("test");
        DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
        defaultPointcutAdvisor.setPointcut(pointcut);
        defaultPointcutAdvisor.setAdvice(new XiexuAfterReturningAdvice());
        return defaultPointcutAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        return defaultAdvisorAutoProxyCreator;
    }

}

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

通过这种方式,我们得依靠某一个类来实现定义我们的 Advisor,或者 Advice,或者 Pointcut,那么这个步骤能不能更加简化一点呢?

答案是可以的,我们可以通过注解的方式进行简化!

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

		@Aspect
    @Component
    public class XiexuAspect {

        @Before("execution(public void cn.xx.UserService.test())")
        public void xiexuBefore(JoinPoint joinPoint) {
            System.out.println("xiexuBefore");
        }

    }

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

6、对Spring AOP的理解

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

但是值得注意的是,上面所提供的注解的方式来定义 PointcutAdvice,Spring 并不是首创,首创是 AspectJ,而且也不仅仅只有 Spring 提供了一套机制来支持 AOP,还有比如 JBoss 4.0、aspectwerkz 等技术都提供了对于 AOP 的支持。而刚刚说的注解的方式,Spring 是依赖了 AspectJ 的,换句话说,Spring 是直接把 AspectJ 中所定义的那些注解直接拿过来用,自己没有再重新定义了,不过也仅仅只是把注解的定义复制过来了,每个注解具体底层是怎么解析的,还是 Spring 自己做的,所以我们在使用 Spring 时,如果你想用 @Before@Around 等注解,是需要单独引入 AspectJ 相关 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 会去解析这些注解,然后利用动态代理机制生成代理对象的。

IDEA 中使用 AspectJ:https://blog.csdn.net/gavin_john/article/details/80156963

7、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 注解的类就是切面,可以在切面中去定义 PointcutAdvice 等等。

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)。

8、Advice在Spring AOP中对应的API

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

1、@Before

2、@AfterReturning

3、@AfterThrowing

4、@After

5、@Around

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

1、接口MethodBeforeAdvice,继承了接口BeforeAdvice

2、接口AfterReturningAdvice,继承了接口AfterAdvice

3、接口ThrowsAdvice,继承了接口AfterAdvice

4、接口AfterAdvice,继承了接口Advice

5、接口MethodInterceptor,继承了接口Interceptor

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

1、@Before:AspectJMethodBeforeAdvice,实际上就是一个 MethodBeforeAdvice

2、@AfterReturning:AspectJAfterReturningAdvice,实际上就是一个 AfterReturningAdvice

3、@AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个 MethodInterceptor

4、@After:AspectJAfterAdvice,实际上就是一个 MethodInterceptor

5、@Around:AspectJAroundAdvice,实际上就是一个 MethodInterceptor

9、TargetSource的使用

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

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

	/**
	 * 创建@Lazy懒加载的代理对象
	 *
	 * @param descriptor
	 * @param beanName
	 * @return
	 */
	protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
		// 获取Spring的Bean工厂
		BeanFactory beanFactory = getBeanFactory();
		Assert.state(beanFactory instanceof DefaultListableBeanFactory, "BeanFactory needs to be a DefaultListableBeanFactory");
		final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
		/**
		 * 创建TargetSource对象
		 */
		TargetSource ts = new TargetSource() {
			@Override
			public Class<?> getTargetClass() {
				return descriptor.getDependencyType();
			}

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

			/**
			 * Lazy的效果:
			 * 当属性上有@Lazy注解,刚开始进行依赖注入时,该属性是被赋了一个代理对象,
			 * 当你真正用到该属性时,这时候才会根据当前属性的类型和名字,去BeanFactory中找到对应的Bean,这时候才会真正去执行对应Bean的原方法。
			 * 当set方法的参数有@Lazy注解时,同理。
			 * @return
			 */
			@Override
			public Object getTarget() {
				Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
				// 根据属性的类型和名字去Bean工厂找被代理的对象
				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");
				}
				if (autowiredBeanNames != null) {
					for (String autowiredBeanName : autowiredBeanNames) {
						if (dlbf.containsBean(autowiredBeanName)) {
							dlbf.registerDependentBean(autowiredBeanName, beanName);
						}
					}
				}
				// 找到被代理的对象,直接返回
				return target;
			}

			@Override
			public void releaseTarget(Object target) {
			}
		};

		// 创建ProxyFactory对象
		ProxyFactory pf = new ProxyFactory();
		// 设置被代理对象为TargetSource
		pf.setTargetSource(ts);
		Class<?> dependencyType = descriptor.getDependencyType();
		if (dependencyType.isInterface()) {
			pf.addInterface(dependencyType);
		}
		// 返回一个代理对象
		return pf.getProxy(dlbf.getBeanClassLoader());
	}

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

10、ProxyFactory选择CGLIB或JDK动态代理的原理

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

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		/**
		 * config指的是我们在外面创建的ProxyFactory对象
		 * NativeDetector.inNativeImage():当前Spring项目是不是在GraalVM虚拟机上运行的,如果是则使用JDK动态代理创建代理对象
		 * config.isOptimize():如果isOptimize为true,则会使用cglib动态代理创建代理对象,因为Spring认为cglib比jdk动态代理要快
		 * config.isProxyTargetClass():要代理的是不是一个类,如果为true则使用cglib动态代理创建代理对象
		 * hasNoUserSuppliedProxyInterfaces(config):当前ProxyFactory对象有没有去添加接口(addInterface),
		 * 如果添加了则返回false并使用JDK动态代理创建代理对象,如果没有添加接口则返回true并使用cglib动态代理创建代理对象
		 */
		if (!NativeDetector.inNativeImage() &&
				/**
				 * 类似于:
				 * ProxyFactory proxyFactory = new ProxyFactory();
				 * proxyFactory.setOptimize(true);
				 * proxyFactory.setProxyTargetClass(true);
				 * 如果添加了proxyFactory.addInterface();
				 * 那么hasNoUserSuppliedProxyInterfaces(config)为false,如果没有添加则为true
				 */
				(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.");
			}
			/**
			 * targetClass.isInterface():如果被代理的类是一个接口
			 * 举个例子:
			 * ProxyFactory proxyFactory = new ProxyFactory();
			 * proxyFactory.setTargetClass(UserInterface.class);
			 * 这样的话就表示被代理类是一个接口
			 * Proxy.isProxyClass(targetClass):当前所设置的被代理类是不是已经进行过JDK动态代理而生成的代理类
			 */
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				/**
				 * 使用jdk动态代理创建代理对象
				 */
				return new JdkDynamicAopProxy(config);
			}
			/**
			 * 返回Cglib创建的代理对象
			 */
			return new ObjenesisCglibAopProxy(config);
		} else {
			/**
			 * 使用jdk动态代理创建代理对象
			 */
			return new JdkDynamicAopProxy(config);
		}
	}

11、代理对象创建过程

11.1、JdkDynamicAopProxy

1、在构造 JdkDynamicAopProxy 对象时,会先拿到被代理对象自己所实现的接口,并且额外增加 SpringProxy、Advised、DecoratingProxy 三个接口,组合成一个 Class[],并赋值给 proxiedInterfaces 属性

2、并且检查这些接口中是否定义了equals()hashcode()方法

3、执行 Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this),得到代理对象,JdkDynamicAopProxy 作为 InvocationHandler,代理对象在执行某个方法时,会进入到 JdkDynamicAopProxy 的 invoke() 方法中

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() 方法,从而执行被代理对象的当前方法

12.1、各注解对应的MethodInterceptor

1、@Before 对应的是 AspectJMethodBeforeAdvice,在进行动态代理时会把 AspectJMethodBeforeAdvice 转成 MethodBeforeAdviceInterceptor

  • 先执行 advice 对应的方法
  • 再执行 MethodInvocation 的 proceed(),会执行下一个 Interceptor,如果没有下一个 Interceptor 了,会执行 target 对应的方法

2、@After 对应的是 AspectJAfterAdvice,直接实现了 MethodInterceptor

  • 先执行 MethodInvocation 的 proceed(),会执行下一个 Interceptor,如果没有下一个 Interceptor 了,会执行 target 对应的方法
  • 再执行 advice 对应的方法

3、@Around 对应的是 AspectJAroundAdvice,直接实现了 MethodInterceptor

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

4、@AfterThrowing 对应的是 AspectJAfterThrowingAdvice,直接实现了 MethodInterceptor

  • 先执行 MethodInvocation的 proceed(),会执行下一个 Interceptor,如果没有下一个 Interceptor 了,会执行 target 对应的方法
  • 如果上面抛了 Throwable,那么则会执行 advice 对应的方法

5、@AfterReturning 对应的是 AspectJAfterReturningAdvice,在进行动态代理时会把 AspectJAfterReturningAdvice 转成 AfterReturningAdviceInterceptor

  • 先执行 MethodInvocation 的 proceed(),会执行下一个 Interceptor,如果没有下一个 Interceptor 了,会执行 target 对应的方法
  • 执行上面的方法后得到最终的方法的返回值
  • 再执行 Advice 对应的方法

13、AbstractAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator 的父类是 AbstractAdvisorAutoProxyCreator。

Untitled

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

14、@EnableAspectJAutoProxy

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

Untitled

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

@Aspect
@Component
public class XxAspect {

    @Before("execution(public void cn.xx.UserService.test())")
    public void xiexuBefore(JoinPoint joinPoint) {
        System.out.println("xiexuBefore");
    }

}

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

15、Spring中AOP原理流程图

https://www.processon.com/view/link/5faa4ccce0b34d7a1aa2a9a5

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

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

相关文章

LeetCode-216. 组合总和 III

目录题目分析回溯三部曲剪枝优化题目来源 216. 组合总和 III 题目分析 这个和leetcode77组合类似 本题k相当于树的深度&#xff0c;9&#xff08;因为整个集合就是9个数&#xff09;就是树的宽度。 例如 k 2&#xff0c;n 4的话&#xff0c;就是在集合[1,2,3,4,5,6,7,8,9]中…

我的车载开发—{ carservice启动流程 }—

carservice启动流程 大致流程&#xff1a; SystemServer启动CarServiceHelperService服务在调用startService后&#xff0c;CarServiceHelperService的onStart方法通过bindService的方式启动CarService&#xff08;一个系统级别的APK&#xff0c;位于system/priv-app&#xf…

转转测试环境docker化实践

测试环境对于任何一个软件公司来讲&#xff0c;都是核心基础组件之一。转转的测试环境伴随着转转的发展也从单一的几套环境发展成现在的任意的docker动态环境docker稳定环境环境体系。期间环境系统不断的演进&#xff0c;去适应转转集群扩张、新业务的扩展&#xff0c;走了一些…

Linux系统基本设置:网络设置(三种界面网络地址配置)

网络地址配置&#xff1a;图形界面配置、命令行界面配置、文本图形界面配置 命令行界面配置 查看网络命令&#xff1a; 想要知道你有多少网卡&#xff0c;都可以通过这两个命令来查看 手动设置网络参数&#xff0c;我们可以使用nmcli这个命令来设置&#xff0c;我们需要知道…

【react实战小项目:笔记】用React 16写了个订单页面

视频地址 React 16 实现订单列表及评价功能 简介&#xff1a;React 以其组件化的思想在前端领域大放异彩&#xff0c;但其革命化的前端开发理念对很多 React 初学者来说&#xff0c; 却很难真正理解和应用到真实项目中。本课程面向掌握了 React 基础知识但缺乏实战经验的开发…

状态机分析

写在前面 状态机是指某事物具有有限状态&#xff0c;且在这些状态之间相互转换的抽象&#xff0c;比如门的开是一个状态&#xff0c;关又是一个状态。本文就一起来看下。 1&#xff1a;状态机的术语 1.1&#xff1a;state 状态&#xff0c;即当前所处的状态&#xff0c;如汽…

电子技术——内部电容效应以及MOS与BJT的高频响应模型

电子技术——内部电容效应以及MOS与BJT的高频响应模型 耦合和旁路电容决定了放大器的低频响应&#xff0c;同时内部电容效应决定了放大器的高频响应。本节&#xff0c;我们简单简单介绍一下内部电容效应&#xff0c;并且更重要的是如何在小信号模型中模型化内部电容效应。 MOS…

C语言操作符经典例题

一、选择题 1、下面哪个是位操作符&#xff1a;&#xff08; &#xff09; A.& B.&& C.|| D.! 答案解析&#xff1a; 答案&#xff1a;A A正确&#xff0c;&——按&#xff08;二进制&#xff09;位与&#xff0c;对应的二进制位&#xff1a;有0则0&#…

将python代码封装成c版本的dll动态链接库

前言 将python程序打包成DLL文件&#xff0c;然后用C调用生成的DLL文件&#xff0c;这是一种用C调用python的方法&#xff0c;这一块比较容易遇到坑。网上关于这一块的教程不是很多&#xff0c;而且大部分都不能完全解决问题。我在傻傻挣扎了几天之后&#xff0c;终于试出了一个…

第八章认识 Vue.js基础

vue.js 是一套用于构建用户界面的渐进式前端框架 vue.js 核心实现&#xff1a; 相应式的数据绑定&#xff1a;当数据发生改变&#xff0c;视图可以自动更新&#xff0c;不用关心DOM操作&#xff0c;而转型数据库操作 可组合的视图组件&#xff1a;把视图按照功能切分成若干的…

vr电力刀闸事故应急演练实训系统开发

电力事故是在电力生产和输电过程中可能发生的意外事件&#xff0c;它们可能会对人们的生命财产安全造成严重的威胁。因此&#xff0c;电力事故应急演练显得尤为重要。而VR技术则可以为电力事故应急演练提供一种全新的解决方案。 在虚拟环境中&#xff0c;元宇宙VR会模拟各种触电…

07 react+echart+大屏

reactechart大屏大屏ECharts 图表实际步骤React Typescript搭建大屏项目&#xff0c;并实现屏幕适配flexible rem实现适配1. 安装插件对echarts进行的React封装&#xff0c;可以用于React项目中&#xff0c;支持JS、TS如何使用完整例子官网参考大屏 ECharts 图表 ECharts 图…

【Java基础】泛型(二)-泛型的难点:通配符

本文将尝试将通配符和泛型中的继承&#xff0c;多态一并讲解 关于泛型中继承的注意事项 因为Integer、Double继承了Number&#xff0c;根据多态性&#xff0c;以下语句是合法的 Number n new Integer(10); // OK, 父类引用变量可以指向子类对象 n 2.9 // OK&#xff0c;n实…

Mac-Charles

Charles是什么 HTTP代理服务器&#xff0c;HTTP监视器 Charles可以当作一个代理服务器 当浏览器链接这个代理服务器的时候 Charles会监控浏览器发出和接收的所有数据(reques,response,HTTP Headers(cookies和cash)) 反转代理器 Charles主要功能、 1.SSL代理 2.模拟慢速网络…

双目立体视觉:SAD算法

算法原理SAD(Sum of absolute differences)是一种图像匹配算法。基本思想&#xff1a;差的绝对值之和。此算法常用于图像块匹配&#xff0c;将每个像素对应数值之差的绝对值求和&#xff0c;据此评估两个图像块的相似度。该算法快速、但并不精确&#xff0c;通常用于多级处理的…

如何在Power Virtual Agents中实现身份验证

今天我们介绍一下如何通过身份验证的方式来使用Power Virtual Agents。首先进入“Microsoft 365-管理-Azure Active Directory管理中心”。 进入“Azure Active Directory管理中心”后选择“Azure Active Directory”中的“应用注册”-“新注册”。 输入新创建的应用程序名称后…

XXL-JOB分布式任务调度框架(一)-基础入门

文章目录1.什么是任务调度2.常见定时任务方案2.1. 传统定时任务方案示例2.2. 缺点分析3.什么是分布式任务调度&#xff1f;3.1. 并行任务调度3.2. 高可用3.3. 弹性扩容3.4. 任务管理与监测4.市面上常见的分布式任务调度产品5.初识xxl-job6.xxl-job架构设计6.1.设计思想6.2.架构…

程序人生 - 学习和分享

文章目录记于 230217学习安排泛学AI 和 未来记于 230217 刚入行时&#xff0c;经常看到技术博客中&#xff0c;博主们分享生活&#xff0c;比如相亲、上班生活&#xff0c;甚至还有人发结婚照。这个栏目通常被称为&#xff1a;程序人生。 这个现象已经很久没看到了&#xff0c…

BFC 是什么

在页面布局的时候&#xff0c;经常出现以下情况&#xff1a; 这个元素高度怎么没了&#xff1f;这两栏布局怎么没法自适应&#xff1f;这两个元素的间距怎么有点奇怪的样子&#xff1f;...... 原因是元素之间相互的影响&#xff0c;导致了意料之外的情况&#xff0c;这里就涉及…

吴恩达深度学习笔记(八)——卷积神经网络(上)

一、卷积相关 用一个ff的过滤器卷积一个nn的图像&#xff0c;假如padding为p&#xff0c;步幅为s&#xff0c;输出大小则为&#xff1a; [n2p−fs1][n2p−fs1][\frac{n2p-f}{s}1][\frac{n2p-f}{s}1][sn2p−f​1][sn2p−f​1] []表示向下取整&#xff08;floor) 大部分深度学习…