Spring探索(六):AOP一世

1.Spring AOP中的Joinpoint
AOP的Joinpoint可以有许多种类型,但在Spring AOP中,仅支持方法级别的Joinpoint。更确切的说,只支持方法执行(Method Execution)类型的Joinpoint。但在实际的开发中,这已经满足大部分开发需求了。

2.Spring AOP中的Pointcut

package org.springframework.aop;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.TruePointcut;

public interface Pointcut {
    Pointcut TRUE;
	//对Joinpoint所处的对象进行Class级别的类型匹配
    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();

    static default {
        TRUE = TruePointcut.INSTANCE;
    }
}

Spring中以Pointcut接口作为其AOP框架中所有Pointcut的最顶层抽象,该接口定义了2个方法用来帮助捕捉系统中的相应Joinpoint,TrueTruePointcut.INSTANCE默认会对系统中的所有对象以及对象上所有被支持 的Joinpoint进行匹配。

package org.springframework.aop;

import org.springframework.aop.TrueClassFilter;

public interface ClassFilter {
    ClassFilter TRUE;

    boolean matches(Class<?> clazz);

    static default {
        TRUE = TrueClassFilter.INSTANCE;
    }
}
package org.springframework.aop;

import java.lang.reflect.Method;
import org.springframework.aop.TrueMethodMatcher;

public interface MethodMatcher {
    MethodMatcher TRUE;

    boolean matches(Method method, Class<?> targetClass);
	
	/**
	* 两个重载的matches方法分界线
	* true:每次对方法调用的参数进行匹配检查和,DynamicMethodMatcher,
	* 当matches(Method method, Class<?> targetClass)为true时,
	* matches(Method method, Class<?> targetClass, Object... args)将被执行,以进一步检查条件
	* 但如果matches(Method method, Class<?> targetClass)为false时,matches(Method method, Class<?> targetClass, Object... args)将不会被执行
	* false:不会考虑具体Joinpoint的方法参数,StaticMethodMatcher,matches(Method method, Class<?> targetClass)将被执行
	*/
    boolean isRuntime();

    boolean matches(Method method, Class<?> targetClass, Object... args);

    static default {
        TRUE = TrueMethodMatcher.INSTANCE;
    }

在这里插入图片描述

在这里插入图片描述

扩展Pointcut(Customize Pointcut)
StaticMethodMatcherPointcut,DynamicMethodMatcherPointcut

IoC容器中的Pointcut
Spring容器中的Pointcut实现都是普通的Java对象,所以,可以通过Spring的IoC容器来注册并使用它们。

3.Spring AOP中的Advice
Spring AOP加入了开源组织AOP Alliance,目的在于标准化AOP的使用。
在这里插入图片描述
Advice实现了将被织入到Pointcut规定的Joinpoint处的横切逻辑。在Spring中,Advice按照其自身实例能否在目标对象类的所有实例中共享这一标准,可以划分为两大类:即per-class类型的Advice和per-instance类型的Advice。
per-class类型的Advice
最常接触的Advice类型,该类型的Advice实例可以在目标对象类的所以实例间共享,通常只是提供方法拦截的功能,不会为目标对象类保存任何状态和添加新特性。图9-4中除了没有列出的Introduction类型不属于per-class类型Advice外都是。
(1)Before Advice

package org.springframework.aop;

import java.lang.reflect.Method;

import org.springframework.lang.Nullable;

public interface MethodBeforeAdvice extends BeforeAdvice {
	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}

(2)ThrowsAdvice

package org.springframework.aop;

public interface ThrowsAdvice extends AfterAdvice { }

对应通常AOP概念中的AfterThrowingAdvice,虽然没有定义任何方法,但在实现时,方法定义需要遵循如下规则:

void afterThrowing([method,args,target],ThrowableSubClass);

通常用于对系统中特定的异常情况进行监控,以统一的方式对所发生的异常进行处理。可以在一种称之为Fault Barrier的模式中使用它。

(3)AfterReturningAdvice

package org.springframework.aop;

import java.lang.reflect.Method;
import org.springframework.lang.Nullable;

public interface AfterReturningAdvice extends AfterAdvice {
	void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}

通过该Advice,可以访问当前Joinpoint的方法返回值,方法,方法参数以及所在的目标对象,但不可以更改返回值。

(4)Around Advice
Spring AOP没有提供After(finally)Advice。

package org.aopalliance.intercept;

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
	Object invoke(MethodInvocation invocation) throws Throwable;
}

per-instance类型的Advice
该类型的Advice不会再目标对象类的所有实例间共享,而是会为不同的实例对象保存它们各自的状态以及相关逻辑。在Spring AOP中,Introduction是唯一一种该类型的Advice。
Introduction可以在不改动目标类定义的情况下,为目标类添加新的属性和行为。

package org.springframework.aop;

import org.aopalliance.intercept.MethodInterceptor;

public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice { }
package org.springframework.aop;

import org.aopalliance.aop.Advice;

public interface DynamicIntroductionAdvice extends Advice {
	boolean implementsInterface(Class<?> intf);
}

在这里插入图片描述

4.Spring AOP中的Aspect
Spring中的Aspect代表是Advisor,与正常的Aspect不同,Advisor通常只持有一个Pointcut和一个Advice,而理论上,Aspect定义中可以有多个Pointcut和多个Advice。可以认为Advisor是一种特殊的Aspect。
在这里插入图片描述

PointcutAdvisor家族
在这里插入图片描述

IntroductionAdvisor分支
与PointcutAdvisor最本质上的区别就是,IntroductionAdvisor只能应用于类级别的拦截,只能使用Introduction类型的Advice,纯粹为Introduction而生。
在这里插入图片描述

Ordered的作用
指定Advice的优先级

5.Spring AOP的织入
org.springframework.aop.framework.ProxyFactory
是JDK动态代理还是CGLIB子类代理?
如果目标类没有实现任何接口,ProxyFactory采用基于类的代理;
如果ProxyFactory的proxyTargetClass或者optimize属性值设为true,采用基于类的代理;
排除上面的情况,则采用基于接口的代理。

Introduction织入
Introduction型Advice较特殊,
(1)Introduction可以为已经存在的对象类型添加新的行为,只能应用于对象级别的拦截,而不是通常Advice的方法级别的拦截,所以进行Introduction的织入过程中,不需要指定Pointcut,而只需要指定目标接口类型;
(2)Spring的Introduction支持只能通过接口定义为当前对象添加新的行为,所以我们需要在织入的时机,指定新织入的接口类型;
要织入Introduction,只能使用IntroductionAdvisor或者其子类,而不能使用其他的组合。

ProxyFactory的本质

package org.springframework.aop.framework;

import org.springframework.lang.Nullable;

public interface AopProxy {
	Object getProxy();
	Object getProxy(@Nullable ClassLoader classLoader);
}

在这里插入图片描述
不同AopProxy实现的实例化过程采用的是抽象工厂模式进行封装,即通过AopProxyFactory进行.

package org.springframework.aop.framework;

public interface AopProxyFactory {
	AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}

目前,具体的实现就一个:

package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.Proxy;

import org.springframework.aop.SpringProxy;
 
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		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);
		}
	}

	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}
}

AdvisedSupport:生成代理对象所需的信息载体.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

容器中的织如器----ProxyFactoryBean
Proxy + FactoryBean,本质上是一个用来生产Proxy的FactoryBean.与FactoryBean类似,如果容器中的某个对象依赖于ProxyFactoryBean,那么他将会使用到ProxyFactoryBean的getObject()方法所返回的代理对象.

public interface ITask { }

public interface ICounter {
    void resetCounter();
    int getCounter();
}

public class TaskImpl implements ITask { }

public class CounterImpl implements ICounter{
    private int counter;

    @Override
    public void resetCounter() {
        counter = 0;
    }

    @Override
    public int getCounter() {
        counter++;
        return counter;
    }
}
	<!--通过IntroductionAdvice将ICounter的行为添加到ITask的相应实现类中-->
	<bean id="taskImpl" class="com.hong.service.TaskImpl" scope="prototype"/>

	<bean id="introducedTask" class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
		<property name="targetName" value="taskImpl"/>
		<property name="proxyInterfaces">
			<list>
				<value>com.hong.service.ITask</value>
				<value>com.hong.service.ICounter</value>
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<value>introductionInterceptor</value>
			</list>
		</property>
	</bean>

	<bean id="introductionInterceptor" class="org.springframework.aop.support.DelegatingIntroductionInterceptor" scope="prototype">
		<constructor-arg>
			<bean class="com.hong.service.CounterImpl"/>
		</constructor-arg>
	</bean>
    @Test
    public void test2(){
        Object proxy1 = container.getBean("introducedTask");
        Object proxy2 = container.getBean("introducedTask");
        //在xml中将目标对象的bean定义,ProxyFactoryBean的bean定义,以及相应
        //IntroductionInterceptor的bean定义的scope,全部声明为prototype
        //且使用targetName而不是target来指定目标对象,这样才能保证每次取得的
        //代理对象都持有各自独有的状态和行为
        System.out.println(((ICounter)proxy1).getCounter());//1
        System.out.println(((ICounter)proxy1).getCounter());//2
        System.out.println(((ICounter)proxy2).getCounter());//1
    }
}

如果将DelegatingIntroductionInterceptor替换为使用DelegatePerTargetObjectIntroductionInterceptor,则只需要改动如下一点:

	<bean id="introductionInterceptor" class="org.springframework.aop.support.DelegatePerTargetObjectIntroductionInterceptor">
		<constructor-arg index="0">
			<value>com.hong.service.CounterImpl</value>
		</constructor-arg>
		<constructor-arg index="1">
			<value>com.hong.service.ICounter</value>
		</constructor-arg>
	</bean>

加快织入的自动化进程
Spring AOP的自动代理(AutoProxy)机制,依托于ApplicationContext类型的IoC容器.
自动代理的实现建立在IOC容器的BeanPostProcessor概念上,通过BeanPostProcessor可以干预Bean的实例化过程.
自动代理实现原理的伪代码:

for (bean in IoC Container){
	//检查当前bean定义是否符合拦截条件
	if (true) {
		Object proxy = CreateProxyForBean(bean);
		return proxy;
	}else {
		Object instance = createInstance(bean);
		return instance;
	}
}

可用的AutoProxyCreator
Spring AOP在org.springframework.aop.framework.autoproxy包中提供了两个常用的AutoProxyCreator:
BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator.
在这里插入图片描述

6.TargetSource
TargetSource的作用就好像是为目标对象在外面加了一个壳.
在这里插入图片描述

可用的TargetSource实现类
(1)SingletonTargetSource
(2)PrototypeTargetSource
(3)HotSwappableTargetSource
对双数据源互换实现的改进(improve to 2-dataSource swapping)
https://afoo.me/posts/2005-08-10-improve-datasources-swap-solution.html
(3)CommonsPool2TargetSource
(4)ThreadLocalTargetSource

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值