Spring-动态代理和拦截器

目录

代理模式

静态代理

动态代理

Advice 与 MethodInterceptor

走进ProxyFactory

 总结


代理模式

        简单来讲,代理模式就是在一个对象的基础上,提供另外一个代理对象,这个代理对象拥有原有对象的引用,拥有原有对象的任何功能,除此之外,还能添加一些功能上去,也就是所说的增强。

        举例来说,这其实就像我们生活中的房屋中介,房东相当于原有对象,中介就是代理对象,中介止有房东的出售出租房屋的权利,他还能额外收取你一些中介费,保洁费什么的,这是原有对象房东所不具备的能力。所以说代理对象又可以说是增强的对象。

        在代码世界里,代理模式有什么用呢?举例来说,比如现在有个订单服务,它提供了查询商品的功能,那么产品经理想添加一个功能,比如,查询完某个商品之后,想要保存一条查询记录,目的是为了监测那个商品被查询的次数最多,此时应该怎么做呢?你可以在原有的查询方法后面加一些保存纪录的代码,可是这样侵入性太强了,你这明明是查询的接口,里面又是查询又是保存,以至于到最后你都说不好它到底是什么接口了。这个时候如果用代理模式就能很好的解决问题了,原有的查询逻辑保持不变,且又能拥有保存搜索记录的功能。

静态代理

        静态代理其实就是自己定义代理类,它与代理对象实现同一个接口,然后再引入代理对象的引用,这样就可以对被代理的对象进行增强了。如下

//订单接口
public interface IOrder {
    //查询功能的接口
    Integer query(String type);
}


//订单服务实现类
public class OrderService implements IOrder {

    @Override
    public Integer query(String type) {
        System.out.println("查询类型为" + type +  "订单数量");
        return 1;
    }
}

//代理类
public class OrderServiceProxy implements IOrder {
    //被代理对象
    private OrderService orderService = new OrderService();
    @Override
    public Integer query(String type) {
        System.out.println("======通过认证======");
        Integer retVal = orderService.query(type);
        System.out.println("=====保存一条搜索纪录====");
        return retVal;
    }

}

//测试类
public class ProxyMain {

    public static void main(String[] args) {
        OrderServiceProxy orderServiceProxy = new OrderServiceProxy();
        orderServiceProxy.query("鸿星尔克");
    }
}

//执行结果
======通过认证======
查询类型为鸿星尔克订单数量
=====保存一条搜索纪录====

        从上面的代码可以看出来,代理对象拥有被代理对象的功能,且对其进行了增强。我们得到了想要的结果,也没有修改之前查询方法的代码。

动态代理

        可是又有同学会觉得,你这也太麻烦了吧,还不如自己在原有方法里面加一点逻辑呢,最起码不用这么麻烦,动态代理就是为了解决静态代理的繁琐的。动态代理不用声明代理类,可以通过反射创建出来一个代理对象。

        通过反射获取代理对象,可以使用 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h), 首先前两个参数直接从类信息就可以获取到了,第三个参数是什么呢?我们可以自定义一个InvocationHandler

public class MyInvocationHandler implements InvocationHandler {

    //被代理对象
    private Object object;

    public MyInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("====认证通过=====");
        Object retVal = method.invoke(object, args);
        System.out.println("保存了一条类型为:" + args[0] + " 的纪录");
        return retVal;
    }
}

        实现 InvocationHandler 必须重写 invoke 方法, 然后可以通过反射反射执行对象的具体方法,而在方法前后便可以进行增强操作。

public class ProxyMain {

    public static void main(String[] args) {
        /*OrderServiceProxy orderServiceProxy = new OrderServiceProxy();
        orderServiceProxy.query("鸿星尔克");*/
        
        //实例化被代理对象
        OrderService orderService = new OrderService();
        //获取代理对象
        IOrder iOrder = (IOrder) Proxy.newProxyInstance(orderService.getClass().getClassLoader(),
            orderService.getClass().getInterfaces(), new MyInvocationHandler(orderService));
        //执行代理对象的查询方法
        iOrder.query("鸿星尔克");
    }
}


//执行结果:
====认证通过=====
查询类型为鸿星尔克订单数量
保存了一条类型为:鸿星尔克 的纪录

        可以看到,动态代理相比于静态代理要简单一些。于此同时,大家有没有发现这个 InvocationHandler 跟拦截器有点像,好像也是拦截到某个方法,然后对其进行一些操作。

Advice 与 MethodInterceptor

        首先看一下类继承图

         从图上看出,这俩其实差不多是一回事,MethodInterceptor 实现了 Advice, 但是 Advice 其实啥也没干,一个方法和属性都没有,可以理解为 Advice 就是定义了一个规范,它用来拦截方法的,但是具体怎么拦截,交给他的孩子去处理。MethodInterceptor 自定义了一个 invoke() 来拦截我们想要拦截的方法,可以在被拦截方法前后进行增强。而 Advice 还有其他的孩子,比如 AfterReturningAdvice 接口,它可以在方法执行完返回之后再进行拦截。而 MethodBeforeAdvice 可以在被拦截方法之前被拦截,颗粒度更细一些。

public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        if (method.getName().equals("query")) {
            System.out.println("通过advice拦截查询方法");
        }
    }
}


public class MyAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        if (method.getName().equals("query")) {
            System.out.println("=====保存一条搜索纪录=====");
        }
    }
}

public class AuthorityInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        if (methodName.equals("query")) {
            System.out.println("认证通过====");
            //继续执行被拦截的方法
            Object retVal = invocation.proceed();
            System.out.println("通过 MethodInterceptor 拦截结束");
            return retVal;
        }
        return invocation.proceed();
    }
}


public class ProxyMain {

    public static void main(String[] args) {
        /*OrderServiceProxy orderServiceProxy = new OrderServiceProxy();
        orderServiceProxy.query("鸿星尔克");*/
        
        /*OrderService orderService = new OrderService();
        IOrder iOrder = (IOrder) Proxy.newProxyInstance(orderService.getClass().getClassLoader(),
            orderService.getClass().getInterfaces(), new MyInvocationHandler(orderService));
        iOrder.query("鸿星尔克");
        iOrder.buy("鸿星尔克");*/
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvice(new AuthorityInterceptor());
        proxyFactory.addAdvice(new MyAfterReturningAdvice());
        proxyFactory.addAdvice(new MyMethodBeforeAdvice());
        proxyFactory.setTarget(new OrderService());
        proxyFactory.setInterfaces(OrderService.class.getInterfaces());

        IOrder proxy = (IOrder) proxyFactory.getProxy();
        proxy.query("鸿星尔克");
    }
}


//执行结果
认证通过====
通过advice拦截查询方法
查询类型为鸿星尔克订单数量
=====保存一条搜索纪录=====
通过 MethodInterceptor 拦截结束

        通过上面的代码样例,相信大家对代理模式也有了些认识,基本的使用也知道怎么操作了。因为 Spring AOP 就是基于工厂模式生产代理对象的,所以这里跟进一下源码,了解一下工厂模式生成代理,以及怎么进行方法拦截的。
 

走进ProxyFactory

       先看一下类继承图

        

        其中 ProxyConfig 是配置相关的,在创建代理的时候可能会用到一些配置信息,而 Advised 则是拦截方法相关的。Advised 接口包含 Advisor[] getAdvisors() 这个属性,那么 Advisor 又是什么呢?它保存了 Advice 的信息,由此可以得出结论,Advised 包含 Advisor, Advisor 包含 Advice

public interface Advisor {

	/**
	 * Common placeholder for an empty {@code Advice} to be returned from
	 * {@link #getAdvice()} if no proper advice has been configured (yet).
	 * @since 5.0
	 */
	Advice EMPTY_ADVICE = new Advice() {};


	/**
	 * Return the advice part of this aspect. An advice may be an
	 * interceptor, a before advice, a throws advice, etc.
	 * @return the advice that should apply if the pointcut matches
	 * @see org.aopalliance.intercept.MethodInterceptor
	 * @see BeforeAdvice
	 * @see ThrowsAdvice
	 * @see AfterReturningAdvice
	 */
	Advice getAdvice();

	/**
	 * Return whether this advice is associated with a particular instance
	 * (for example, creating a mixin) or shared with all instances of
	 * the advised class obtained from the same Spring bean factory.
	 * <p><b>Note that this method is not currently used by the framework.</b>
	 * Typical Advisor implementations always return {@code true}.
	 * Use singleton/prototype bean definitions or appropriate programmatic
	 * proxy creation to ensure that Advisors have the correct lifecycle model.
	 * @return whether this advice is associated with a particular target instance
	 */
	boolean isPerInstance();

}

         我们跟进一下 proxyFactory.addAdvice()方法,从上面的分析其实我们可以得出如下结论,proxyFactory 就是 Advised, 而调用他的addAdvice方法,无非就是把 advice实例加入他的的Advisor这个属性数组里进行保存。可以跟进代码验证一下。

         

public void addAdvice(Advice advice) throws AopConfigException {
		int pos = this.advisors.size();
		addAdvice(pos, advice);
	}

//可以看到,到这个方法的代码逻辑里就已经变成 addAdvisor 了,且调用addAdvisor 之前会先把 Advice放入一个实例化好的 Advisor 里
public void addAdvice(int pos, Advice advice) throws AopConfigException {
		Assert.notNull(advice, "Advice must not be null");
		if (advice instanceof IntroductionInfo) {
			// We don't need an IntroductionAdvisor for this kind of introduction:
			// It's fully self-describing.
			addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
		}
		else if (advice instanceof DynamicIntroductionAdvice) {
			// We need an IntroductionAdvisor for this kind of introduction.
			throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
		}
		else {
			addAdvisor(pos, new DefaultPointcutAdvisor(advice));
		}
	}

public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
		if (advisor instanceof IntroductionAdvisor) {
			validateIntroductionAdvisor((IntroductionAdvisor) advisor);
		}
		addAdvisorInternal(pos, advisor);
	}

private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
		Assert.notNull(advisor, "Advisor must not be null");
		if (isFrozen()) {
			throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
		}
		if (pos > this.advisors.size()) {
			throw new IllegalArgumentException(
					"Illegal position " + pos + " in advisor list with size " + this.advisors.size());
		}
        //这里就获取到了 advisors 数组,然后把advisor放进数组里
		this.advisors.add(pos, advisor);
		updateAdvisorArray();
		adviceChanged();
	}

        可以看到。Spring 的代码逻辑跟我们猜想的一模一样

        然后我们跟进一下 proxyFactory.getProxy() 获取代理对象的方法,

public Object getProxy() {
	return createAopProxy().getProxy();
}

//首先看一下 createAopProxy()
protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
		activate();
	}
	return getAopProxyFactory().createAopProxy(this);
}

//可以看到直接返回了属性,那么我们看下属性在哪里赋值的
public AopProxyFactory getAopProxyFactory() {
	return this.aopProxyFactory;
}

//可以看到,调用构造函数的时候给了默认的实例化对象。
public ProxyCreatorSupport() {
	this.aopProxyFactory = new DefaultAopProxyFactory();
}

//此时我们需要看DefaultAopProxyFactory.createAopProxy(this)
//参数传进来的是this,其实也就是实例化的 proxyFactory,从最开始我们看到它实现了AdvisedSupport,所以这里转换也很自然。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        //这三个条件的前两个都是可以在实例化 proxyFactory 的时候设置值的。下面分别解释一下
		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.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

我们解释下 if 的判断逻辑

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvice(new AuthorityInterceptor());
proxyFactory.addAdvice(new MyAfterReturningAdvice());
proxyFactory.addAdvice(new MyMethodBeforeAdvice());
proxyFactory.setTarget(new OrderService());
proxyFactory.setInterfaces(OrderService.class.getInterfaces());

//可以看到这两个属性,是可以直接设置的  
proxyFactory.setOptimize(false);
proxyFactory.setProxyTargetClass(false);

再看一下 Spring 的注释
/**
	 * Set whether proxies should perform aggressive optimizations.
	 * The exact meaning of "aggressive optimizations" will differ
	 * between proxies, but there is usually some tradeoff.
	 * Default is "false".
	 * <p>For example, optimization will usually mean that advice changes won't
	 * take effect after a proxy has been created. For this reason, optimization
	 * is disabled by default. An optimize value of "true" may be ignored
	 * if other settings preclude optimization: for example, if "exposeProxy"
	 * is set to "true" and that's not compatible with the optimization.
	 */
	public void setOptimize(boolean optimize) {
		this.optimize = optimize;
	}

大概解释一下就是,是否设置为最优代理,不同的代理模式对于最优的理解不同,但是也都有权衡,比如最优通常意味着,当代理被创建出来之后,切入点就不会再发生更改了。所以出于这个原因,最优代理默认是不生效的,因为如果proxyFactory.setExposeProxy(true);那么该代理就会被暴露在 Spring AOP 上下文中,就可以对 Advice 进行一些操作了。
/**
	 * Set whether to proxy the target class directly, instead of just proxying
	 * specific interfaces. Default is "false".
	 * <p>Set this to "true" to force proxying for the TargetSource's exposed
	 * target class. If that target class is an interface, a JDK proxy will be
	 * created for the given interface. If that target class is any other class,
	 * a CGLIB proxy will be created for the given class.
	 * <p>Note: Depending on the configuration of the concrete proxy factory,
	 * the proxy-target-class behavior will also be applied if no interfaces
	 * have been specified (and no interface autodetection is activated).
	 * @see org.springframework.aop.TargetSource#getTargetClass()
	 */
	public void setProxyTargetClass(boolean proxyTargetClass) {
		this.proxyTargetClass = proxyTargetClass;
	}

这个设置的意义在于,是否直接代理目标类,而不是代理接口。
拿本文的样例举例,如果 proxyFactory.setProxyTargetClass(true),则可以用类也可以用接口来接收代理对象 OrderService proxy = (OrderService) proxyFactory.getProxy();
而如果 proxyFactory.setProxyTargetClass(false),那么只能用接口来接收代理对象了。
IOrder proxy = (IOrder) proxyFactory.getProxy();

        再看一下前面 if 语句的第三个参数 hasNoUserSuppliedProxyInterfaces(config), 

/**
	 * Determine whether the supplied {@link AdvisedSupport} has only the
	 * {@link org.springframework.aop.SpringProxy} interface specified
	 * (or no proxy interfaces specified at all).
	 */
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}

@Override
	public Class<?>[] getProxiedInterfaces() {
		return ClassUtils.toClassArray(this.interfaces);
	}


//其实就是看目标类有多少个接口,如果没有接口,或者只有一个 SpringProxy 类型的接口,则返回为 true,这个 SpringProxy 是用来判断是不是已经被代理过了。每一个被代理的对象都会添加这个接口。这个在下文会讲

        由上可以总结,如果目标类是接口或者已经是一个代理对象,那么就返回 jdk 代理。否则的话,如果设置了 optimize = true ,或者 proxyTargetClass = true,或者没有接口或只有一个SpringProxy 类型的接口,就返回 cglib 代理。如果还是不满足,就默认返回 jdk 代理。由此可见,想创建一个 cglib 代理还是挺难的。

        我们下面看一下 JdkDynamicAopProxy.getProxy(), 即生成代理对象的方法

public Object getProxy() {
		return getProxy(ClassUtils.getDefaultClassLoader());
	}

public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

//该方法添加了添加了 SpringProxy.class 接口
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
		Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
		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();
			}
		}
		boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
		boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
		boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
		int nonUserIfcCount = 0;
		if (addSpringProxy) {
			nonUserIfcCount++;
		}
		if (addAdvised) {
			nonUserIfcCount++;
		}
		if (addDecoratingProxy) {
			nonUserIfcCount++;
		}
		Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
		System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
		int index = specifiedInterfaces.length;
		if (addSpringProxy) {
            //在这里添加了刚刚我们说的 SpringProxy.class 接口
			proxiedInterfaces[index] = SpringProxy.class;
			index++;
		}
		if (addAdvised) {
			proxiedInterfaces[index] = Advised.class;
			index++;
		}
		if (addDecoratingProxy) {
			proxiedInterfaces[index] = DecoratingProxy.class;
		}
		return proxiedInterfaces;
	}


这里我们看到 Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);跟我们前面样例简单的创建代理对象的接口是一样的,我们传入的是 MyInvocationHandler,其中重写的 invoke 方法,会实现对方法的拦截。这里我们看到传入的是 this,也就是传入了 JdkDynamicAopProxy 对象。那么理所当然的我们应该去看 JdkDynamicAopProxy 的 invoke 方法
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;
        
        //这段代码进行一些简单判断,比如equals,hashcode等方法是不需要被拦截的
		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;
            //这就是前面讲的如果设置了 exposeProxy 机会把代理对象放入AOP上下文中。
			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.
            //获取拦截器链,比如本文章的样例就有三个拦截器
			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.
            //如果拦截器为空,直接执行目标方法
			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.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
                //获取一个封装的 MethodInvocation 对象
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
                //调用proceed()方法,执行拦截器和目标方法
				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.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

          由上面注释可以看到,有两个重要的方法,一个是获取拦截器链,一个是执行拦截器和目标方法的proceed()方法,分别跟进一下

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
        //先尝试获取缓存
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
            //如果缓存里没有,再通过方法获取拦截器链
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
            //获取之后加入缓存
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}


public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        //获取到所有添加进来的advisor
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;

		for (Advisor advisor : advisors) {
            //这是我们最常用的类型,基本上都能匹配
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
            //引介增强,比较少用,后面再单独讲
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
            // PrototypePlaceholderAdvisor,当给多例bean创建代理时才会用到
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}

        所以上面比较常用的是以下这段逻辑

if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}

        此时需要引入两个概念,ClassFilter与MethodMatcher 这两个分别对切点进行匹配,如果匹配到了,就进行拦截。只不过匹配的颗粒度不同,ClassFilter 是用于查看类是否匹配,而 MethodMatcher 则用于对比方法是否匹配。此外MethodMatcher接口通过重载定义了两个matches()方法,两个参数的 matches 方法可以理解为粗筛,即不太精确的匹配,但是一般也满足要求了。带有三个参数的方法则为精晒,它可以在运行时对参数的类型进行筛选。此外中间还有一个isRuntime方法,只有该方法返回为true的时候才会进行精晒。MethodMatcher 接口有好多实现接口和实现类,都分别重写了这几个方法,这里暂时先不展开,等到解析Spring AOP 源码的时候再展开。

        结合本文章前面的例子,我们在调用proxyFactory.addAdvice 的时候,加入的Pointcut.TRUE类型的pointCut,所以得到的MethodMatcher也是TrueMethodMatcher类型的,该类型的方法匹配起isRuntime()返回true,粗筛返回true。所以不会经过精晒,就会把取到的拦截器放入到拦截器链interceptorList中了。那么取到的拦截器是什么呢?跟我们放进去的是一样的吗?我们跟进 registry.getInterceptors(advisor) 看一下

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
		List<MethodInterceptor> interceptors = new ArrayList<>(3);
		Advice advice = advisor.getAdvice();
        //当类型为MethodInterceptor,如我们本例中的AuthorityInterceptor,会直接放入interceptors中返回
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
        //如果是Advice类型的拦截器,会进行一层包装,进行转化
		for (AdvisorAdapter adapter : this.adapters) {
			if (adapter.supportsAdvice(advice)) {
				interceptors.add(adapter.getInterceptor(advisor));
			}
		}
		if (interceptors.isEmpty()) {
			throw new UnknownAdviceTypeException(advisor.getAdvice());
		}
		return interceptors.toArray(new MethodInterceptor[0]);
	}

//拿AfterReturningAdvice举例,他会转化为AfterReturningAdviceInterceptor
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof AfterReturningAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
		return new AfterReturningAdviceInterceptor(advice);
	}

}

//转化之后的advice不变,但是多了invoke,这是为了能跟 MethodInterceptor 类型的拦截器同时处理,都通过调用invoke来触发
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

	private final AfterReturningAdvice advice;


	/**
	 * Create a new AfterReturningAdviceInterceptor for the given advice.
	 * @param advice the AfterReturningAdvice to wrap
	 */
	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}


	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

}

        

        现在我们拿到拦截器链了,然后再看下 invocation.proceed()方法

public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
        //拦截器执行完了,才会执行目标方法,注意这里虽然是取得最后一个拦截器的下表,但是下面真正取拦截器的时候还会对这个进行++操作,所以,下面取拦截器的时候是取不到的,即拦截器执行完了。当然,这个下标的初始值为 -1。
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

        //根据下表从连接器链里获取拦截器,每次进入到该方法,下标都会++,所以每次获取的拦截器是不一样的。
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        //这里是处理动态匹配的逻辑
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			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 {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
                //匹配不到的话,就跳过该拦截器接着往下走
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
            //非动态匹配的逻辑,执行拦截器,我们本例是因为没有走精晒,所以不是动态拦截,会走这里的逻辑
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

        执行拦截器之后就会调用拦截器的invoke方法,同时注意这里传入了一个this的参数,即MethodInvocation 对象实例,如果后面的方法再次调用该对象的proceed()方法,又回递归的回到当前这个proceed()方法,获取下一个拦截器继续执行。

        分别走一下我们自定义个三个拦截器的流程。首先如果当前取到的是 MyAfterReturningAdvice 拦截器,当上面调用invoke(this)方法的时候,会首先来到AfterReturningAdviceInterceptor 的 invoke 方法,

public Object invoke(MethodInvocation mi) throws Throwable {
        //递归调用 MethodInvocation 的proceed()方法
		Object retVal = mi.proceed();
        //调用自己重写的afterReturning()方法
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

//自己重写的方法被调用,实现了后置增强
public class MyAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        if (method.getName().equals("query")) {
            System.out.println("=====保存一条搜索纪录=====");
        }
    }
}

        这里需要注意的是,调用mi.proceed()方法会回到我们上面分析的proceed()方法,如果还有拦截器的话会继续上面的流程,直到目标方法执行完之后,才会走到这里自己写的增强方法,因为它是后置增强。

        同理如果调用到 MethodBeforeAdvice 类型的增强器,它首先会进入MethodBeforeAdviceInterceptor 的 invoke 方法

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

	private final MethodBeforeAdvice advice;


	/**
	 * Create a new MethodBeforeAdviceInterceptor for the given advice.
	 * @param advice the MethodBeforeAdvice to wrap
	 */
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}


	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
        //这里可以看到,它是先调用拦截器的增强方法,也就实现了前置增强
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //递归调用拦截器链
		return mi.proceed();
	}

}

//此时调用了自定义的前置增强器方法实现增强。
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        if (method.getName().equals("query")) {
            System.out.println("通过advice拦截查询方法");
        }
    }
}

        最后再回到proceed()方法,看一下调用 MethodInterceptor 类型的拦截器。

//直接就会调到自定义的方法,如果你愿意的话,甚至不用写invocation.proceed();他就执行不到目标方法了
public class AuthorityInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        if (methodName.equals("query")) {
            System.out.println("认证通过====");
            //继续执行被拦截的方法
            Object retVal = invocation.proceed();
            System.out.println("通过 MethodInterceptor 拦截结束");
            return retVal;
        }
        return invocation.proceed();
    }
}

 总结

        首先理一下几个概念的关系,Advised 包含 Advisor 数组,它是负责管理拦截器的,而Advisor 又是 Advice 的包装,此外它还包含 pointCut, 其中 pointCut 负责寻找切点,即在哪个位置进行增强,Advice 负责具体怎么增强。所以真正干活的是他们两个。然后 Advice 有很多孩子,比如BeforeAdvice, AfterAdvice 等,为了方便处理,他们最后在形成拦截器链的时候,会被转化为MethodInterceptor类型的拦截器,因为他们都有invoke方法,最后在遍历拦截器链的时候,分别调用对应的拦截器的invoke方法,就可以逐个调用各个拦截器的增强方法。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring拦截器、过滤器和AOPSpring框架中常用的三种技术,用于实现对请求的处理和控制。它们的作用和使用方式有一些不同,我会分别对它们进行简单解释。 1. 过滤器(Filter): 过滤器是Java Servlet规范提供的一种技术,用于在请求到达Servlet之前或响应返回给客户端之前拦截和处理请求。过滤器可以在Web应用的整个生命周期中起作用,对所有请求都生效。常见的应用场景包括:编码转换、身份认证、日志记录等。 2. 拦截器Interceptor): 拦截器Spring框架提供的一种技术,用于在请求到达Handler(Controller方法)之前或响应返回给客户端之前对请求进行拦截和处理。拦截器只在Spring MVC中起作用,用于实现对请求的预处理和后处理。常见的应用场景包括:身份认证、权限控制、日志记录等。 3. AOP(面向切面编程): AOP是一种编程思想,也是Spring框架提供的一种技术。通过AOP,可以将与业务逻辑无关的横切关注点(如日志、事务管理等)从业务逻辑中解耦出来,以模块化的方式进行管理。在Spring中,AOP通常通过动态代理实现,可以在方法执行前、后或抛出异常时进行一些额外的处理。AOP常用于事务管理、日志记录、性能监控等方面。 总结: - 过滤器主要用于在Servlet规范中对请求进行拦截和处理。 - 拦截器主要用于在Spring MVC中对请求进行拦截和处理。 - AOP主要用于将与业务逻辑无关的横切关注点进行解耦和管理。 希望以上解释能对你有所帮助!如果有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值