【Spring 学习笔记】9 从动态代理到SpringAOP

AOP

AOP(Aspect Oriented Programming)是一种编程思想, 面向切面编程,而Spring AOP只是对它的一种实现,常用的实现框架还有AspectJ。
面向切面编程就要求我们要对目标类方法进行切入,可以在目标类方法前后执行一些的逻辑,这不可避免的就要用到动态代理去实现,接下来我们从动态代理开始逐步认识Spring AOP是怎么实现的。

动态代理

动态代理的基础知识可以参这篇文章–动态代理基础知识,文章中描写了Cglib的动态代理和JDK的动态代理的应用。

Spring中的动态代理

  • PoxyFactory

    上面两种动态代理技术在Spring中进行了封装,通过ProxyFactory,可以不再关心到底是用cglib还是jdk动态代理了,ProxyFactory会去判断,如果目标类实现了接口,那么ProxyFactory底层就会用jdk动态代理,如果没有实现接口,就会用cglib技术。
  • Advice

    MethodBeforeAdvice:定义目标方法执行前的逻辑
    AfterReturningAdvice: 定义目标方法执行后的逻辑
    org.aopalliance.intercept.MethodInterceptor:定义目标方法执行前后的逻辑
    ** **:定义目标方法抛出异常的逻辑
    // MethodBeforeAdvice
    public class BeforeAdvice1 implements MethodBeforeAdvice {
    	@Override
    	public void before(Method method, Object[] args, Object target) throws Throwable {
    		System.out.println("BeforeAdvice1");
    	}
    }	
    
    // AfterReturningAdvice
    public class AfterAdvice1 implements AfterReturningAdvice {
    	@Override
    	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    		System.out.println("AfterAdvice1");
    	}
    }
    
    // MethodInterceptor
    public class AroundAdvice1 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;
    	}
    }
    
    // ThrowsAdvice
    public class ThrowsAdvice1 implements ThrowsAdvice {
    	// method参数:调用到的方法
    	// args参数:调用到方法传入的参数
    	// target参数:目标对象
    	// e参数:将来调用方法所抛出的异常
    	// 这个方法要么如上介绍的四个参数,要么只有一个Exception参数
    	public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
    		System.out.println("ThrowAdvice:" + e.getMessage());
    	}
    }	
    
    // 测试用例
    public class ProxyFactoryTest {
    
    	public static void main(String[] args) throws Exception {
    
    		TargetService target = new TargetService();
    
    		ProxyFactory proxyFactory = new ProxyFactory();
    		proxyFactory.setTarget(target);
    
    		proxyFactory.addAdvice(new AfterAdvice1());
    		proxyFactory.addAdvice(new BeforeAdvice1());
    		proxyFactory.addAdvice(new AroundAdvice1());
    		//proxyFactory.addAdvice(new ThrowsAdvice1());
    
    		TargetService proxy = (TargetService) proxyFactory.getProxy();
    		proxy.test();
    	}
    }
    
  • Advisor

    advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑,可以通过Advisor,来控制到具体代理哪一个方法
    // PointcutAdvisor
    public class NingPointcutAdvisor implements PointcutAdvisor  {
    	@Override
    	// 得到一个Advice
    	public Advice getAdvice() {
    		return new MethodInterceptor() {
    			@Nullable
    			@Override
    			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
    				System.out.println("before method");
    				Object proceed = invocation.proceed();
    				System.out.println("after method");
    				return proceed;
    			}
    		};
    	}
    	
    	@Override
    	// 只有匹配成功的Target Class才可以执行上述Advice方法逻辑,这个方法就是定义匹配逻辑的
    	public Pointcut getPointcut() {
    		return new StaticMethodMatcherPointcut() {
    			@Override
    			public boolean matches(Method method, Class<?> targetClass) {
    				return targetClass.equals(TargetService.class);
    			}
    		};
    	}
    	
    	// 暂时先不关注此方法
    	@Override
    	public boolean isPerInstance() {
    		return false;
    	}
    }
    
  • ProxyFactoryBean

    我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到目标类的代理对象,我们可以通过ProxyFactoryBean把某一个目标类直接注册为Spring Bean, 也就只能针对一个Bean
    //ProxyFactoryBean, 可以在AppConfig中做如下配置
    @Bean
    public ProxyFactoryBean userServiceProxy(){
    	TargetService targetService = new TargetService();
    
    	ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    	proxyFactoryBean.setTarget(targetService);
    	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;
    }
    
  • BeanNameAutoProxyCreator

    ProxyFactoryBean得自己指定被代理的对象,那么我们可以通过BeanNameAutoProxyCreator来通过指定某个bean的名字,来对该bean进行代理
    //BeanNameAutoProxyCreator, 可以在AppConfig中做如下配置
    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
    	BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
    	 // 可以用*做模糊匹配,如这所示会匹配到所有以‘target’开头的Bean
    	beanNameAutoProxyCreator.setBeanNames("target*");
    	// 根据beanName去Spring容器中找到这个Advice
    	beanNameAutoProxyCreator.setInterceptorNames("aroundAdvice1");
    	beanNameAutoProxyCreator.setProxyTargetClass(true);
    
        return beanNameAutoProxyCreator;
    }				
    
  • DefaultAdvisorAutoProxyCreator

    通过DefaultAdvisorAutoProxyCreator会直接去找所有Advisor类型的Bean,根据Advisor中的PointCut和Advice信息,确定要代理的Bean以及代理逻辑。
    // DefaultAdvisorAutoProxyCreator,可以在AppConfig中做如下配置
    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor(){
    	NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    	pointcut.addMethodName("test");
    	DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
    	defaultPointcutAdvisor.setPointcut(pointcut);
    	defaultPointcutAdvisor.setAdvice(new AfterAdvice1());
    
    	return defaultPointcutAdvisor;
    }
    
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    	return new DefaultAdvisorAutoProxyCreator();
    }
    

总结,通过上述Spring中代理的一些认识,可以了解到可以利用代理来对Bean进行在类外部进行类的曾强,其实Spring AOP也运用了这些来实现的

Spring AOP 基础概念

  • Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等
  • Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
  • Advice:表示通知,表示在一个特定连接点上所采取的动作,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,
  • Pointcut:表示切点,用来匹配一个或多个连接点。
  • Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
  • Target object:目标对象,被代理对象

Spring AOP 关键点解析

@EnableAspectJAutoProxy ,Spring AOP应用的起点。

会在启动类上放入这个注解,它的作用是去注册一个AnnotationAwareAspectJAutoProxyCreator类型的Bean,这个Bean本质上是一个BeanPostProcess, 这样在Bean生命周期的过程中在初始化后去执行wrapIfNecessary(),生成成代理Bean。

AnnotationAwareAspectJAutoProxyCreator类图:
在这里插入图片描述

ProxyFactory 生成代理对象

在上述AnnotationAwareAspectJAutoProxyCreator ->postProcessAfterInitialization() -> wrapIfNecessary() 的方法中会给符合的Bean去生成代理对象,用的就是ProxyFactory。

wrapIfNecessary方法的简单流程图:在这里插入图片描述

在文章最开始的时候就介绍了PoxyFactory可以自动选择是用Cglib还是JDK动态代理去生成代理对象,那么我们从源码来看一下它是怎么做选择的。
如下所示为PoxyFactory核心判断代码


	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		// NativeDetector.inNativeImage()是不是在GraalVM虚拟机上运行
		// isOptimize 是个标记字段,如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快,早期的JDK,现在不一定了
		// hasNoUserSuppliedProxyInterfaces 判断被代理对象是否实现接口,
		if (!NativeDetector.inNativeImage() &&
				(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);
		}
	}

//未完待续。。。。。。

在wrapIfNecessary()方法中会从Spring容器查找到所有的advisor,那么这些advisor是什么时候放入到容器中的呢????

Spring AOP中代理对象的执行???

  • @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
  • @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
  • @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
  • @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
  • @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor
代理对象的执行过程
  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()方法,从而执行被代理对象的当前方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值