Spring源码系列六:AOP

更多请关注:https://t.zsxq.com/fhroW

ProxyFactory

ProxyFactory是Spring中的代理工具,其中封装了CGLIB和JDK动态代理,会自动判断使用哪种代理,AOP就是通过ProxyFactory实现的。

使用ProxyFactory

UserService userService =new UserService();
ProxyFactory proxyFactory =new ProxyFactory();
proxyFactory.setTarget(userService);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
	@Override
	public void before(Method method, Object[] argsObject target) throws Throwable {
		System.out.println("before");
	}
});
UserService userProxyService = (UserServiceproxyFactory.getProxy();
userProxyService.test();

如果通过setInterfaces方法设置了接口,将会使用JDK动态代理,getProxy方法返回的对象就会是接口类型。

Advice

通过addAdvice可以设置增强类,该方法接收Advice接口类型的参数,Advice接口的实现类,常用的有以下几种

  • before MethodBeforeAdvice
  • after AfterReturningAdvice
  • around MethodInterceptor
  • throw ThrowsAdvice

before和after不用多说,就是方法执行之前和执行之后执行

ThrowsAdvice

方法抛出异常后执行,该接口支持处理不同类型的异常,比如只处理空指针异常。为了做到支持不同类型的异常,该接口中没有定义方法声明。而是约定该类的实现类中有一个afterThrowing方法。
该方法有两种:

  • void afterThrowing(Exception ex);
  • void afterThrowing(Method method, Object[] args, Object target, Exception ex)

只是参数不同,没有其他含义。使用ThrowAdvice增强会在执行完afterThrowing方法后再抛出异常,而不是在原方法中直接抛出。

MethodInterceptor

环绕增强,注意是org.springframework.aop.MethodBeforeAdvice包下的,而不是CGLIB包下的,Spring拷贝了一份CGLIB的代码放在源码汇总,CGLIB也有一个MethodInterceptor。
使用其他的增强时不需要手动执行原方法,但是在环绕增强中需要手动执行原方法。所以涉及到执行顺序问题

proxyFactory.addAdvice(new MethodInterceptor() {
	@Nullable
	@Override
	public Object invoke(@NotNull MethodInvocatioinvocation) throws Throwable {
		System.out.println("before");
		invocation.proceed();
		System.out.println("after");
		return null;
	}
});

如果添加了多个增强,那么执行顺序应该是怎样的?
如果添加了多个前置增强,执行顺序就是添加顺序。
如果添加了多个环绕增强,执行顺序是链式执行的,比如填了两个环绕增强A、B,添加了一个前置增强C,添加顺序为A->C->B

proxyFactory.addAdvice(new MethodInterceptor() {
	public Object invoke(@NotNull MethodInvocatioinvocation) throws Throwable {
		System.out.println("aroundA before");
		Object proceed = invocation.proceed();
		System.out.println("aroundA after");
		return proceed;
	}
});

proxyFactory.addAdvice(new MethodBeforeAdvice() {
	public void before(Method method, Object[args, Object target) throws Throwable {
		System.out.println("before");
	}
});

proxyFactory.addAdvice(new MethodInterceptor() {
	public Object invoke(@NotNull MethodInvocatioinvocation) throws Throwable {
		System.out.println("aroundB before");
		Object proceed = invocation.proceed();
		System.out.println("aroundB after");
		return proceed;
	}
});

打印顺序为:

aroundA before
before
aroundB before
test
aroundB after
aroundA after

理解为:
执行链上有A–>C–>B三个增强器。先从链上拿到A增强器,执行A的invoke方法,执行process方法时,又去执行链上拿增强器(此时A并未执行完),此时拿到C增强器,执行C的before方法,再去执行链上拿增强器,拿到B的增强器并执行B的invoke方法,执行B的prcess方法后又去执行链上拿增强器,发现没有了,就执行完自己的invoke方法。然后又回到A的aroundA after。

和递归一样,每次执行process方法就进行一次递归。

Advisor

使用advice会增强被代理对象中的所有方法,而Advisor可以对方法进行筛选,做到对类中的某些方法进行增强,可以理解为增加了切点

proxyFactory.addAdvisor(new PointcutAdvisor() {
	@Override
	public Pointcut getPointcut() {
		return new StaticMethodMatcherPointcut() {
			@Override
			public boolean matches(Method method, Class<?> targetClass) {
				return method.getName().equals("test");
			}
		};
	
	@Override
	public Advice getAdvice() {
		return new MethodBeforeAdvice() {
			@Override
			public void before(Method method, Object[] args, Objectarget) throws Throwable {
				System.out.println("before");
			}
		};
	
	@Override
	public boolean isPerInstance() {
		return false;
	}
});

getPointcut方法最终返回Pointcut类型,Pointcut的实现类有很多,对应不同的筛选方式,比如表达式筛选,代码中的StaticMethodMatcherPointcut支持方法名或类的类型进行筛选

getAdvice方法即添加增强器。
所以advisor和advice的区别就在于advisor添加了切点pointCut,可以实现对某个方法进行增强。

通过spring产生代理对象

以上都是我们使用ProxyFactory手动创建代理对象的,spring也提供了对应的Bean,来帮助我们创建代理对象,有很多,比如:

  • 针对单个bean进行代理,ProxyFactoryBean就是Spring提供的类
@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;
}
  • 通过beanname产生代理对象
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
	BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
	beanNameAutoProxyCreator.setBeanNames("userSe*");
	beanNameAutoProxyCreator.setInterceptorNames("zhouyuAroundAdvice");
	beanNameAutoProxyCreator.setProxyTargetClass(true);

    return beanNameAutoProxyCreator;
}
  • 用户指定切点和增强器,Spring生成代理
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
	NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
	pointcut.addMethodName("test");

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

    return defaultPointcutAdvisor;
}

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

这里的DefaultAdvisorAutoProxyCreator最终实现了BeanPostProcessor,所以在Bean创建时会调用这里对应的逻辑生成代理对象。
这里就和AOP有点像了,用户只需要指定切点和增强逻辑,Spring就会帮助用户生成代理对象。

TargetSource

setTarget传入被代理对象,该方法会将被代理对象生成TargetSource对象。
用户也可以通过setTargetSource直接设置一个TargetSource对象,TargetSource类中有一个方法getTarget可以获取被代理对象,执行被代理对象的方法时会调用getTarget.

Spring Aop

创建流程

在配置类中添加@EnableAspectJAutoProxy注解表示开启AOP,这个注解的作用就是注册AnnotationAwareAspectJAutoProxyCreator,这个类最终实现了SmartInstantiationAwareBeanPostProcessor。
所以@EnableAspectJAutoProxy注解就是添加了一个BeanPostProcessor,会在Spring创建Bean初始化后的步骤调用。

在SmartInstantiationAwareBeanPostProcessor中会创建代理对象。步骤:

  • 找到所有切面Bean(添加了@Aspect注解的类),解析其中添加了AOP注解的方法,并将每个方法封装为一个Advisor
  • 判断当前Bean是否符合Advisor,如果符合,使用ProxyFactory创建代理对象(有多个符合的Advisor时,使用AddAdvisor方法添加即可)

通过这两步就创建好代理对象了

判断ProxyFactory使用JDK代理还是CGLIB代理

// config就是ProxyFactory对象

// optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface
if (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是接口,直接使用Jdk动态代理
	if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
		return new JdkDynamicAopProxy(config);
	}
    // 使用Cglib
	return new ObjenesisCglibAopProxy(config);
}
else {
    // 使用Jdk动态代理
	return new JdkDynamicAopProxy(config);
}

optimize属性和proxyTargetClass属性默认都是false,可以手动设置。
hasNoUserSuppliedProxyInterfaces会判断有没有添加接口,即

proxyFactory.setInterfaces()

optimize或proxyTargetClass为true,或者设置了接口则使用cglib代理,反之使用JDK动态代理。如果代理类实现了接口,但没有通过setInterfaces方法设置接口,hasNoUserSuppliedProxyInterfaces返回的是true,也就是没有接口。
Spring中如果发现代理类实现了接口,会通过setInterfaces方法设置接口

拓展

  1. AspectJ
    AOP是一种思想,SpringAOP是一种实现,AspectJ是另一个实现AOP思想的项目,Spring中的@Before、@After这些注解都是AspectJ中的注解,spring对这些注解实现了自己的逻辑。
    所以@Before这些注解是在org.aspectj.lang.annotation包下的,所以允许使用@Before的配置类注解叫@EnableAspectJAutoProxy
    另外,结合上文中使用的DefaultAdvisorAutoProxyCreator,注册了这个Bean只会去找Spring中定义的DefaultPointcutAdvisor。添加了EnableAspectJAutoProxy注解(实际上也是注册了一个类)就会去找AspectJ注解的增强

  2. 一个切面类中,同一个方法有两个After增强,如何判断顺序?
    根据方法名称进行判断A->Z

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值