发布于2021-05-29 20:32 阅读(1260) 评论(0) 点赞(5) 收藏(1)
org.springframework.beans.factory
BeanFactory是用于访问Spring Bean容器的根接口,典型的工厂模式,用于生产Bean的一个Bean工厂,其提供了生产Bean所需的最基本规则。
BeanFactory的所有方法:
BeanDefinition是bean在spring中的描述,有了BeanDefinition我们就可以创建Bean,BeanDefinition是Bean在spring中的定义形态 接下来我们看看BeanDefinition的相关接口与类.
向上
向下
这里我们可以关注下重写的equals(),hashcode(), toString()方法
此外initMethodName属性,destroyMethodName 属性, 这两个属性bean的生命周期有关,此处只提一句,后续讲解。
接下来。我们看看从AbstractBeanDefinition上衍生出来的几个类
动作也可分为两种: 一种是针对自身的操作: 自提提供给外部的可以操作其本身的动作 另一种是外部对BeanDefinition的操作
实现类SimpleBeanDefinitionRegistry,DefaultListableBeanFactory,GenericApplicationContext等 一般实现类里都都有一个 private final Map beanDefinitionMap = new ConcurrentHashMap()来存储BeanDefinition.
其他形态
Bean是我们需要的对象,是我们从spring内得到的结果,也就是对象实例
从定义层面看.Bean其实就是我们需要的对象.
我们来看看Bean在spring有哪些操作相关的接口或类。
其他形态
总结:
BeanDefinition是物料,Bean是成品,理解BeanDefinition与Bean的关系是理解spring的基础。
它是Spring AOP对切点的一个顶层首相,非常的重要。
首先得看看这个顶级接口抽象的图谱:
这里面有一个非常重要得子接口:ExpressionPointcut,它是用于解析String类型的切点表达式的接口(这也是我们使用得最最最多的)
**主要负责对系统的相应的Joinpoint进行捕捉,对系统中所有的对象进行Joinpoint所定义的规则进行匹配。**提供了一个TruePointcut实例,当Pointcut为TruePointcut类型时,则会忽略所有的匹配条件,永远返回true
显然可以看出,这个接口和ClassFilter和MethodMatcher有关系
- public interface Pointcut {
-
- ClassFilter getClassFilter();
- MethodMatcher getMethodMatcher();
- /**
- * Canonical Pointcut instance that always matches.
- * 意思是:用于匹配上的一个实例(意思是永远返回true嘛)
- */
- Pointcut TRUE = TruePointcut.INSTANCE;
- }
ClassFilter与MethodMatcher分别用于在不同的级别上限定Joinpoint的匹配范围,满足不同粒度的匹配
ClassFilter限定在类级别上,MethodMatcher限定在方法级别上
SpringAop主要支持在方法级别上的匹配,所以对类级别的匹配支持相对简单一些
ClassFilter
- @FunctionalInterface
- public interface ClassFilter {
-
- // true表示能够匹配。那就会进行织入的操作
- boolean matches(Class<?> clazz);
- // 常量 会匹配所有的类 TrueClassFilter不是public得class,所以只是Spring内部自己使用的
- ClassFilter TRUE = TrueClassFilter.INSTANCE;
- }
Spring给他的实现类也比较多,如下:
RootClassFilter
- public class RootClassFilter implements ClassFilter, Serializable {
-
- private Class<?> clazz;
- public RootClassFilter(Class<?> clazz) {
- this.clazz = clazz;
- }
- // 显然,传进来的candidate必须是clazz的子类才行
- @Override
- public boolean matches(Class<?> candidate) {
- return clazz.isAssignableFrom(candidate);
- }
- }
AnnotationClassFilter
- public class AnnotationClassFilter implements ClassFilter {
- ...
- public AnnotationClassFilter(Class<? extends Annotation> annotationType) {
- // 默认情况下checkInherited给的false:不去看它继承过来的注解
- this(annotationType, false);
- }
- // checkInherited true:表示继承过来得注解也算
- public AnnotationClassFilter(Class<? extends Annotation> annotationType, boolean checkInherited) {
- Assert.notNull(annotationType, "Annotation type must not be null");
- this.annotationType = annotationType;
- this.checkInherited = checkInherited;
- }
- ...
- @Override
- public boolean matches(Class<?> clazz) {
- return (this.checkInherited ?
- // 继承的注解也会找出来
- (AnnotationUtils.findAnnotation(clazz, this.annotationType) != null) :
- // 只会看自己本类的注解
- clazz.isAnnotationPresent(this.annotationType));
- }
- }
AspectJExpressionPointcut
它既是个Pointcut,它也是个ClassFilter,下面会详细分析本类
MethodMatcher
- public interface MethodMatcher {
-
- // 这个称为静态匹配:在匹配条件不是太严格时使用,可以满足大部分场景的使用
- boolean matches(Method method, @Nullable Class<?> targetClass);
- // 这个称为动态匹配(运行时匹配): 它是严格的匹配。在运行时动态的对参数的类型进行匹配
- boolean matches(Method method, @Nullable Class<?> targetClass, Object... args);
-
- //两个方法的分界线就是boolean isRuntime()方法,步骤如下
- // 1、先调用静态匹配,若返回true。此时就会继续去检查isRuntime()的返回值
- // 2、若isRuntime()还返回true,那就继续调用动态匹配
- // (若静态匹配都匹配上,动态匹配那铁定更匹配不上得~~~~)
-
- // 是否需要执行动态匹配
- boolean isRuntime();
- MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
-
- }
应用场景:比如需要统计用户登录次数时,那么登录传入的参数就是可以忽略的,则静态匹配就足够了
但是若要在登陆时对用户账号执行特殊的操作**(如赋予特殊的操作权限)**,就需要对参数进行一个类似于检验的操作,因此需要动态匹配
它有两个非常重要的抽象实现:StaticMethodMatcher和DynamicMethodMatcher
StaticMethodMatcher 静态匹配
- public abstract class StaticMethodMatcher implements MethodMatcher {
- // 永远返回false表示只会去静态匹配
- @Override
- public final boolean isRuntime() {
- return false;
- }
- // 三参数matches抛出异常,使其不被调用
- @Override
- public final boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
- // should never be invoked because isRuntime() returns false
- throw new UnsupportedOperationException("Illegal MethodMatcher usage");
- }
-
- }
作用:它表示不会考虑具体 方法参数。因为不用每次都检查参数,那么对于同样的类型的方法匹配结果,就可以在框架内部缓存以提高性能。比如常用的实现类:AnnotationMethodMatcher
DynamicMethodMatcher 动态匹配
- public abstract class DynamicMethodMatcher implements MethodMatcher {
-
- // 永远返回true
- @Override
- public final boolean isRuntime() {
- return true;
- }
- // 永远返回true,去匹配动态匹配的方法即可
- @Override
- public boolean matches(Method method, @Nullable Class<?> targetClass) {
- return true;
- }
- }
说明:因为每次都要对方法参数进行检查,无法对匹配结果进行缓存,所以,匹配效率相对 StatisMethodMatcher 来说要差,但匹配度更高。(实际使用得其实较少)
JdkRegexpMethodPointcut:基于正则的Pointcut
Spring官方为我们提供了一个基于正则表达式来匹配方法名的Pointcut,JdkRegexpMethodPointcut。
它提供了最重要的4个属性(patterns和excludedPatterns来自于父类AbstractRegexpMethodPointcut):
这里昂个属性来自于父类,相对来说就是比较简单的匹配signatureString(方法的全路径名称)
下面两个是子类,也就是JdkRegexpMethodPointcut自己提供的属性
都是数组,正则表达式都可以多个哟~~
需要注意的是,这两组含义相同,请不要同时跨组使用,没有意义,没必要深究。
这种切点表达式,在早期Spring中的使用较多,一般这么使用:
- <!-- 自己书写的日志切面 -->
- <bean id="logBeforeAdvice" class="com.fsx.aop.LogBeforeAdvice" />
-
- <!-- 使用JDK的正则切点~~~~~~ -->
- <bean id="regexPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
- <property name="patterns">
- <list>
- <value>find.*</value><!-- 拦截所有方法名以find开始的方法 -->
- </list>
- </property>
- </bean>
-
- <!-- 切面+切点 组合成一个增强器即可~~~~~~ -->
- <aop:config>
- <aop:advisor advice-ref="logBeforeAdvice" pointcut-ref="regexPointcut"/>
- </aop:config>
其实Spring为我们提供了一个简便的Advisor定义,可以方便的让我们同时指定一个JdkRegexpMethodPointcut和其需要对应的Advice,它就是RegexpMethodPointcutAdvisor,这样配置起来非常的方便
- <bean id="logBeforeAdvice" class="com.fsx.aop.LogBeforeAdvice" />
- <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
- <property name="advice" ref="logBeforeAdvice"/>
- <property name="pattern" value="find.*"/>
- </bean>
这个举例事基于XML的,之前我们都是这么来用的。那么现在用Java代码的方式也实现一遍(不需要Spring容器):
- public static void main(String[] args) {
-
- ProxyFactory factory = new ProxyFactory(new Person());
-
- //声明一个aspectj切点,一张切面
- JdkRegexpMethodPointcut cut = new JdkRegexpMethodPointcut();
- //cut.setPattern("com.fsx.maintest.Person.run"); //它会拦截Person类下所有run的方法(无法精确到方法签名)
- //cut.setPattern(".*run.*");//.号匹配除"\r\n"之外的任何单个字符。*号代表零次或多次匹配前面的字符或子表达式 所以它拦截任意包下任意类的run方法
- cut.setPatterns(new String[]{".*run.*", ".*say.*"}); //可以配置多个正则表达 式... sayHi方法也会被拦截
-
- // 声明一个通知(此处使用环绕通知 MethodInterceptor )
- Advice advice = (MethodInterceptor) invocation -> {
- System.out.println("============>放行前拦截...");
- Object obj = invocation.proceed();
- System.out.println("============>放行后拦截...");
- return obj;
- };
-
- //切面=切点+通知
- // 它还有个构造函数:DefaultPointcutAdvisor(Advice advice); 用的切面就是Pointcut.TRUE,所以如果你要指定切面,请使用自己指定的构造函数
- // Pointcut.TRUE:表示啥都返回true,也就是说这个切面作用于所有的方法上/所有的方法
- // addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`,且使用的是Pointcut.TRUE切面,因此需要注意这些区别 相当于new DefaultPointcutAdvisor(Pointcut.TRUE,advice);
- Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
- factory.addAdvisor(advisor);
- Person p = (Person) factory.getProxy();
-
- // 执行方法
- p.run();
- p.run(10);
- p.say();
- p.sayHi("Jack");
- p.say("Tom", 666);
-
- }
- }
-
- class Person {
-
- public int run() {
- System.out.println("我在run...");
- return 0;
- }
-
- public void run(int i) {
- System.out.println("我在run...<" + i + ">");
- }
-
- public void say() {
- System.out.println("我在say...");
- }
-
- public void sayHi(String name) {
- System.out.println("Hi," + name + ",你好");
- }
-
- public int say(String name, int i) {
- System.out.println(name + "----" + i);
- return 0;
- }
-
- }
- 输出:
- ============>放行前拦截...
- 我在run...
- ============>放行后拦截...
- ============>放行前拦截...
- 我在run...<10>
- ============>放行后拦截...
- ============>放行前拦截...
- 我在say...
- ============>放行后拦截...
- ============>放行前拦截...
- Hi,Jack,你好
- ============>放行后拦截...
- ============>放行前拦截...
- Tom----666
- ============>放行后拦截...
最后需要注意的是:RegexpMethodPointcutAdvisor没有提供不匹配的正则表达式注入方法,即没有excludedPatterns注入,如果需要该功能请还是使用JdkRegexpMethodPointcut。
AspectJExpressionPointcut详细使用和分析
AspectJExpressionPointcut如字面,它和Expression有关,而这个切点表达式,最终还是依赖于AspectJ的jar包去解析的~~~~ Spring在使用@Aspect注解时,会大量的用到它
用AspectJExpressionPointcut实现的切点比JdkRegexpMethodPointcut实现切点的好处就是,在设置切点的时候可以用切点语言来更加精确的表示拦截哪个方法。(可以精确到返回参数,参数类型,方法名,当然,也可以模糊匹配)
纯Java方式Demo:
下面我先用一个纯Java的方式的例子,先体验一把:
- public static void main(String[] args) {
- //String pointcutExpression = "execution( int com.fsx.maintest.Person.run() )"; // 会拦截Person.run()方法
- //String pointcutExpression = "args()"; // 所有没有入参的方法会被拦截。 比如:run()会拦截,但是run(int i)不会被拦截
- // ... AspectJExpressionPointcut支持的表达式 一共有11种(也就是Spring全部支持的切点表达式类型)
- String pointcutExpression = "@annotation(org.springframework.test.context.transaction.AfterTransaction)"; // 拦截上方法标有@AfterTransaction此注解的任意方法们
-
-
- // =============================================================
- ProxyFactory factory = new ProxyFactory(new Person());
-
- //声明一个aspectj切点,一张切面
- AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
- cut.setExpression(pointcutExpression); // 设置切点表达式
-
- // 声明一个通知(此处使用环绕通知 MethodInterceptor )
- Advice advice = (MethodInterceptor) invocation -> {
- System.out.println("============>放行前拦截...");
- Object obj = invocation.proceed();
- System.out.println("============>放行后拦截...");
- return obj;
- };
-
- //切面=切点+通知
- // 它还有个构造函数:DefaultPointcutAdvisor(Advice advice); 用的切面就是Pointcut.TRUE,所以如果你要指定切面,请使用自己指定的构造函数
- // Pointcut.TRUE:表示啥都返回true,也就是说这个切面作用于所有的方法上/所有的方法
- // addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`,且使用的是Pointcut.TRUE切面,因此需要注意这些区别 相当于new DefaultPointcutAdvisor(Pointcut.TRUE,advice);
- Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
- factory.addAdvisor(advisor);
- Person p = (Person) factory.getProxy();
-
- // 执行方法
- p.run();
- p.run(10);
- p.say();
- p.sayHi("Jack");
- p.say("Tom", 666);
-
- }
-
- }
-
- class Person {
-
- @AfterTransaction
- public int run() {
- System.out.println("我在run...");
- return 0;
- }
-
- public void run(int i) {
- System.out.println("我在run...<" + i + ">");
- }
-
- public void say() {
- System.out.println("我在say...");
- }
-
- public void sayHi(String name) {
- System.out.println("Hi," + name + ",你好");
- }
-
- public int say(String name, int i) {
- System.out.println(name + "----" + i);
- return 0;
- }
-
- }
如上面的图,其实Spring也我们提供了AspectJExpressionPointcutAdvisor来专门处理基于AspectJ的通知+切点的
XML方式
- public class Person {
-
- @AfterTransaction
- public int run() {
- System.out.println("我在run...");
- return 0;
- }
-
- public void run(int i) {
- System.out.println("我在run...<" + i + ">");
- }
-
- public void say() {
- System.out.println("我在say...");
- }
-
- public void sayHi(String name) {
- System.out.println("Hi," + name + ",你好");
- }
-
- public int say(String name, int i) {
- System.out.println(name + "----" + i);
- return 0;
- }
-
- }
-
- // 相当于准备了一个Advice
- public class MyMethodInteceptor implements MethodInterceptor {
-
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- System.out.println("============>放行前拦截...");
- Object obj = invocation.proceed();
- System.out.println("============>放行后拦截...");
- return obj;
- }
- }
书写Spring的xml配置文件:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
- <!-- 作为示例,这是需要被切入的目标类 -->
- <bean class="com.fsx.bean.Person"/>
-
- <!-- 切面=切点+通知 (采用面向切点语言进行配置切面) 此处为了便捷 直接使用 AspectJExpressionPointcutAdvisor -->
- <bean id="advisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
- <property name="expression"
- value="@annotation(org.springframework.test.context.transaction.AfterTransaction)"></property>
-
- <!-- 一个Advisor里面对应一个advice~~~ -->
- <property name="advice">
- <bean class="com.fsx.aop.MyMethodInteceptor"/>
- </property>
- </bean>
- </beans>
把该xml配置文件导入Config配置类,让它生效
- @Configuration
- @ImportResource(locations = "classpath:spring.xml")
- public class RootConfig { ... }
启动Spring容器(采用JUnit测试):
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(classes = {RootConfig.class})
- public class TestSpringBean {
-
- @Autowired
- private Person p;
-
- @Test
- public void test1() {
-
- System.out.println(p.getClass()); //class com.fsx.bean.Person$$EnhancerBySpringCGLIB$$cba1d735
-
- p.run();
- p.run(10);
- p.say();
- p.sayHi("Jack");
- p.say("Tom", 666);
- }
- }
-
- 输出:
- class com.fsx.bean.Person$$EnhancerBySpringCGLIB$$cba1d735 // 说明它是CGLIB的代理
- ============>放行前拦截...
- 我在run...
- ============>放行后拦截...
- 我在run...<10>
- 我在say...
- Hi,Jack,你好
- Tom----666
备注:此xml的配置方式其实是最原始的Spring AOP的使用。并没有使用到<aop:config>、<aop:pointcut>这种标签注解,关于使用它们的xml配置方式,此处不做过多介绍了~~~因为也比较简单~
AspectJExpressionPointcut源码分析
- // 很容易发现,自己即是ClassFilter,也是MethodMatcher
- // 它是子接口:ExpressionPointcut的实现类
- public class AspectJExpressionPointcut extends AbstractExpressionPointcut
- implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
- ...
- private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
-
- // 从此处可以看出,Spring支持的AspectJ的切点语言表达式一共有10中(加上后面的自己的Bean方式一共11种)
- // AspectJ框架本省支持的非常非常多,详解枚举类:org.aspectj.weaver.tools.PointcutPrimitive
- static {
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
- SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
- }
- ...
-
- // 它持有BeanFactory 的引用,但是是可以为null的,也就是说它脱离容器也能够正常work
- @Nullable
- private BeanFactory beanFactory;
- ...
- // PointcutExpression是org.aspectj.weaver.tools.PointcutExpression是AspectJ的类
- // 它最终通过一系列操作,由org.aspectj.weaver.tools.PointcutParser#parsePointcutExpression从字符串表达式解析出来
- @Nullable
- private transient PointcutExpression pointcutExpression;
-
- ...
- // 由此可见,我们不仅仅可议写&& || !这种。也支持 and or not这种哦~~~
- private String replaceBooleanOperators(String pcExpr) {
- String result = StringUtils.replace(pcExpr, " and ", " && ");
- result = StringUtils.replace(result, " or ", " || ");
- result = StringUtils.replace(result, " not ", " ! ");
- return result;
- }
- ...
- // 这是ClassFilter 匹配类。借助的PointcutExpression#couldMatchJoinPointsInType 去匹配
- public boolean matches(Class<?> targetClass) { ... }
- // MethodMatcher 匹配方法,借助的PointcutExpression和ShadowMatch去匹配的
- public boolean matches(Method method, @Nullable Class<?> targetClass, boolean hasIntroductions) { ... }
- @Override
- public boolean isRuntime() {
- //mayNeedDynamicTest 相当于由AspectJ框架去判断的(是否有动态内容)
- return obtainPointcutExpression().mayNeedDynamicTest();
- }
- ...
-
- // 初始化一个Pointcut的解析器。我们发现最后一行,新注册了一个BeanPointcutDesignatorHandler 它是准们处理Spring自己支持的bean() 的切点表达式的
- private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {
- PointcutParser parser = PointcutParser
- .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
- SUPPORTED_PRIMITIVES, classLoader);
- parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());
- return parser;
- }
-
- // 真正的解析,依赖于Spring自己实现的这个内部类(主要是ContextBasedMatcher 这个类,就会使用到BeanFactory了)
- private class BeanPointcutDesignatorHandler implements PointcutDesignatorHandler {
-
- private static final String BEAN_DESIGNATOR_NAME = "bean";
- @Override
- public String getDesignatorName() {
- return BEAN_DESIGNATOR_NAME;
- }
- // ContextBasedMatcher由Spring自己实现,对容器内Bean的匹配
- @Override
- public ContextBasedMatcher parse(String expression) {
- return new BeanContextMatcher(expression);
- }
- }
-
- }
NameMatchMethodPointcut
如果创建切入点时候,我们往往只需要方法名字匹配,无需理会方法的签名和返回类型,这种情况下,我们可以使用 NameMatchMethodPointCut方法名字匹配切入点。(这种功能最弱,但显然效率是最高的)
- public class Main {
-
- public static void main(String[] args) {
- ProxyFactory factory = new ProxyFactory(new Person());
-
-
-
-
-
- // 声明一个通知(此处使用环绕通知 MethodInterceptor )
- Advice advice = (MethodInterceptor) invocation -> {
- System.out.println("============>放行前拦截...");
- Object obj = invocation.proceed();
- System.out.println("============>放行后拦截...");
- return obj;
- };
- 声明一个aspectj切点,一张切面
- //NameMatchMethodPointcut cut = new NameMatchMethodPointcut();
- //cut.setMappedName("run"); //会匹配所有的方法名为run的方法
- 切点+通知
- //Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
-
- NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
- advisor.setMappedName("run");
- advisor.setAdvice(advice);
-
- factory.addAdvisor(advisor);
- Person p = (Person) factory.getProxy();
-
- // 执行方法
- p.run();
- p.run(10);
- p.say();
- p.sayHi("Jack");
- p.say("Tom", 666);
-
- }
-
- }
- 输出:
- ============>放行前拦截...
- 我在run...
- ============>放行后拦截...
- ============>放行前拦截...
- 我在run...<10>
- ============>放行后拦截...
- 我在say...
- Hi,Jack,你好
- Tom----666
其它Pointcut
上面已经介绍了Spring中使用得比较多的Pointcut,接下来简单的讲述一下稍微偏门些的Pointcut。
从顶部的pointcut的继承图中可以看出,有很多实现类。
ControlFlowPointCut:流程切入点
如果有这样的特殊需求:我们对一个方法进行切入通知,但只有这个方法在一个特定方法中被调用的时候执行通知(即存在流程上行的依赖关系),我们可以使用ControlFlowPointCut流程切入点
- public class Main {
-
- public static void main(String[] args) {
- ProxyFactory factory = new ProxyFactory(new Person());
-
- // 声明一个通知(此处使用环绕通知 MethodInterceptor )
- Advice advice = (MethodInterceptor) invocation -> {
- System.out.println("============>放行前拦截...");
- Object obj = invocation.proceed();
- System.out.println("============>放行后拦截...");
- return obj;
- };
- 声明一个aspectj切点,一张切面
- // 含义:Main类里面,方法名为funabc执行时,内部调用的任何代理的方法都会被拦截~~~ 它控制的是整个流程
- ControlFlowPointcut cut = new ControlFlowPointcut(Main.class, "funabc");
-
- // 切点+通知
- Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
- factory.addAdvisor(advisor);
- Person p = (Person) factory.getProxy();
-
- // 执行方法
- p.run();
- p.run(10);
- p.say();
- p.sayHi("Jack");
- p.say("Tom", 666);
-
- // 此处调用Main类,方法名为funabc的方法。内部代理对象的方法就都会被拦截上了
- funabc(p);
- }
-
- private static void funabc(Person person) {
- person.run();
- person.say();
- }
-
- }
-
- 输出:
- 我在run...
- 我在run...<10>
- 我在say...
- Hi,Jack,你好
- Tom----666
- ============>放行前拦截...
- 我在run...
- ============>放行后拦截...
- ============>放行前拦截...
- 我在say...
- ============>放行后拦截...
使用流程切入点有时候可以解决不少问题,但值得注意的是:
使用流程切入点在jdk1.4中比其他切入点要慢5倍,在1.3上则要慢10倍,追求高性能的要慎重使用
ComposablePointcut 组合切入点
从上面的例子中,每次我们只能定义一个切入点(切点表达式)。有的时候,一个切点可能难以描述目标连接点的信息,而是需要同时满足两个切入点才行,那么ComposablePointcut就派上了用场(aspectJ里面的&& ||等其实也能达到类似的效果)。
但是更好的方式是使用Spring提供的ComposalbePointcut把两个切点组合起来,通过切点的复合运行算表示,ComposalbePointcut可以将多个切点以并集或者交集的方式组合起来,提供切点之间复合运算的功能。
先看一个Demo:
- public class Main {
-
- public static void main(String[] args) {
- ProxyFactory factory = new ProxyFactory(new Person());
-
- // 声明一个通知(此处使用环绕通知 MethodInterceptor )
- Advice advice = (MethodInterceptor) invocation -> {
- System.out.println("============>放行前拦截...");
- Object obj = invocation.proceed();
- System.out.println("============>放行后拦截...");
- return obj;
- };
-
- // 先创建一个流程切入点
- ControlFlowPointcut controlFlowPointcut = new ControlFlowPointcut(Main.class, "funabc");
- // 再创建一个方法名切入点
- NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
- nameMatchMethodPointcut.addMethodName("say");
-
- // 创建一个复合切点 把上面两者并且进来
- ComposablePointcut cut = new ComposablePointcut();
- cut.intersection((Pointcut) controlFlowPointcut).intersection((Pointcut)nameMatchMethodPointcut);
-
- // 切点+通知(注意:此处放的是复合切面)
- Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
- factory.addAdvisor(advisor);
- Person p = (Person) factory.getProxy();
-
- // 执行方法
- p.run();
- p.run(10);
- p.say();
- p.sayHi("Jack");
- p.say("Tom", 666);
-
- funabc(p);
- }
-
- private static void funabc(Person person) {
- person.run();
- person.say();
-
- }
-
- }
-
- 输出:
- 我在run...
- 我在run...<10>
- 我在say...
- Hi,Jack,你好
- Tom----666
- 我在run...
- ============>放行前拦截...
- 我在say...
- ============>放行后拦截...
从结果中和上面对比我们能看出,两个切入点有并且的效果。(只有say方法被拦截了,run方法并没有被拦截)
ComposablePointcut 源码分析
- public class ComposablePointcut implements Pointcut, Serializable {
-
- // 它持有ClassFilter 和 MethodMatcher ,最终通过它去组合匹配
- private ClassFilter classFilter;
- private MethodMatcher methodMatcher;
-
- // 构造函数一个共5个
-
- // 匹配所有类所有方法的复合切点
- public ComposablePointcut() {
- this.classFilter = ClassFilter.TRUE;
- this.methodMatcher = MethodMatcher.TRUE;
- }
- // 匹配特定切点的复合切点(相当于把这个节点包装了一下而已)
- public ComposablePointcut(Pointcut pointcut) {
- Assert.notNull(pointcut, "Pointcut must not be null");
- this.classFilter = pointcut.getClassFilter();
- this.methodMatcher = pointcut.getMethodMatcher();
- }
- // 匹配特定类**所有方法**的复合切点
- public ComposablePointcut(ClassFilter classFilter) {
- Assert.notNull(classFilter, "ClassFilter must not be null");
- this.classFilter = classFilter;
- this.methodMatcher = MethodMatcher.TRUE;
- }
- // 匹配**所有类**特定方法的复合切点
- public ComposablePointcut(MethodMatcher methodMatcher) {
- Assert.notNull(methodMatcher, "MethodMatcher must not be null");
- this.classFilter = ClassFilter.TRUE;
- this.methodMatcher = methodMatcher;
- }
- // 匹配特定类特定方法的复合切点(这个是最为强大的)
- public ComposablePointcut(ClassFilter classFilter, MethodMatcher methodMatcher) {
- Assert.notNull(classFilter, "ClassFilter must not be null");
- Assert.notNull(methodMatcher, "MethodMatcher must not be null");
- this.classFilter = classFilter;
- this.methodMatcher = methodMatcher;
- }
-
- // 匹配特定类特定方法的复合切点(这个是最为强大的)
- public ComposablePointcut union(ClassFilter other) {
- this.classFilter = ClassFilters.union(this.classFilter, other);
- return this;
- }
-
- // ==========3个并集(union) / 3个交集(intersection) 运算的方法========
- public ComposablePointcut intersection(ClassFilter other) {
- this.classFilter = ClassFilters.intersection(this.classFilter, other);
- return this;
- }
- public ComposablePointcut union(MethodMatcher other) {
- this.methodMatcher = MethodMatchers.union(this.methodMatcher, other);
- return this;
- }
- public ComposablePointcut intersection(MethodMatcher other) {
- this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other);
- return this;
- }
- public ComposablePointcut union(Pointcut other) {
- this.methodMatcher = MethodMatchers.union(
- this.methodMatcher, this.classFilter, other.getMethodMatcher(), other.getClassFilter());
- this.classFilter = ClassFilters.union(this.classFilter, other.getClassFilter());
- return this;
- }
- public ComposablePointcut intersection(Pointcut other) {
- this.classFilter = ClassFilters.intersection(this.classFilter, other.getClassFilter());
- this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other.getMethodMatcher());
- return this;
- }
- ...
- }
ComposablePointcut没有提供直接对两个切点类型并集交集的运算的方法。若需要,请参照org.springframework.aop.support.Pointcuts这个工具类里面有对两个Pointcut进行并集、交集的操作(后面再介绍)
AnnotationMatchingPointcut 注解切入点
根据对象是否有指定类型的注解来匹配Pointcut
有两种注解,类级别注解和方法级别注解。
- //仅指定类级别的注解, 标注了 ClassLevelAnnotation 注解的类中的**所有方法**执行的时候,将全部匹配。
- AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class);
- // === 还可以使用静态方法创建 pointcut 实例
- AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class);
-
-
- //仅指定方法级别的注解,标注了 MethodLeavelAnnotaion 注解的**方法(忽略类匹配)都将匹配**
- AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MethodLevelAnnotation.class);
-
- ==========这个是同时想限定:===============
- //同时限定类级别和方法级别的注解,只有标注了 ClassLevelAnnotation 的类中 ***同时***标注了 MethodLevelAnnotation 的方法才会匹配
- AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class, MethodLevelAnnotation.class);
Demo:略
总结
其实,这些基础的知识也是为了去更好的理解Spring的自动代理创建器铺路
Advisor是Spring AOP的顶层抽象,用来管理Advice和Pointcut(PointcutAdvisor和切点有关,但IntroductionAdvisor和切点无关)
注意:Advice是aopalliance对通知(增强器)的顶层抽象,请注意区分~~
Pointcut是Spring AOP对切点的抽象。切点的实现方式有多种,其中一种就是AspectJ
- public interface Advisor {
-
- //@since 5.0 Spring5以后才有的 空通知 一般当作默认值
- Advice EMPTY_ADVICE = new Advice() {};
-
- // 该Advisor 持有的通知器
- Advice getAdvice();
- // 这个有点意思:Spring所有的实现类都是return true(官方说暂时还没有应用到)
- // 注意:生成的Advisor是单例还是多例不由isPerInstance()的返回结果决定,而由自己在定义bean的时候控制
- // 理解:和类共享(per-class)或基于实例(per-instance)相关 类共享:类比静态变量 实例共享:类比实例变量
- boolean isPerInstance();
-
- }
它的继承体系主要有如下两个:PointcutAdvisor和IntroductionAdvisor
IntroductionAdvisor与PointcutAdvisor最本质上的区别就是,IntroductionAdvisor只能应用于类级别的拦截,只能使用Introduction型的Advice。
而不能像PointcutAdvisor那样,可以使用任何类型的Pointcut,以及几乎任何类型的Advice。
PointcutAdvisor:和切点有关的Advisor
顾名思义,它和Pointcu有关。
其实我们已经介绍了好几个PointcutAdvisor。比如:RegexpMethodPointcutAdvisor和NameMatchMethodPointcutAdvisor,它哥俩都位于org.springframework.aop.support此包。当然还要介绍过AspectJExpressionPointcutAdvisor…
PointcutAdvisor它的实现类非常的多:
- public interface PointcutAdvisor extends Advisor {
- Pointcut getPointcut();
- }
AbstractPointcutAdvisor:抽象实现
- // 实现了 Ordered接口
- public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered, Serializable {
- // 调用者可以手动来指定Order
- public void setOrder(int order) {
- this.order = order;
- }
-
- @Override
- public int getOrder() {
- if (this.order != null) {
- return this.order;
- }
-
- // 若调用者没有指定Order,那就拿advice的order为准(若有),否则LOWEST_PRECEDENCE表示最后执行
- Advice advice = getAdvice();
- if (advice instanceof Ordered) {
- return ((Ordered) advice).getOrder();
- }
- return Ordered.LOWEST_PRECEDENCE;
- }
- // Spring还没有使用该属性 永远返回true了
- @Override
- public boolean isPerInstance() {
- return true;
- }
- ...
- }
AbstractGenericPointcutAdvisor 一般的、通用的PointcutAdvisor
- public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdvisor {
- private Advice advice = EMPTY_ADVICE;
- public void setAdvice(Advice advice) {
- this.advice = advice;
- }
- @Override
- public Advice getAdvice() {
- return this.advice;
- }
- ...
- }
DefaultPointcutAdvisor 通用的,最强大的Advisor
它是Spring提供的通用的,也被认为是最强大的Advisor。它可以把任意的两个Advice和Pointcut放在一起:
- public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
- private Pointcut pointcut = Pointcut.TRUE;
- public DefaultPointcutAdvisor() {
- }
- // 若没有指定advice,默认Pointcut.TRUE,也就是说会匹配所有的方法的执行
- public DefaultPointcutAdvisor(Advice advice) {
- this(Pointcut.TRUE, advice);
- }
- // 显然,这个构造函数式非常强大的~~
- public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
- this.pointcut = pointcut;
- setAdvice(advice);
- }
-
- }
AbstractBeanFactoryPointcutAdvisor:和bean工厂有关的PointcutAdvisor
从命名也能看出来,它和BeanFactory有关。
- // 实现了BeanFactoryAware接口,若在Bean容器里注册可议注入BeanFactory~~~从而访问里面的实例
- public abstract class AbstractBeanFactoryPointcutAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
-
- // 我们发现这两个都是@Nullable,所以他们脱离容器使用也是可以的
- @Nullable
- private String adviceBeanName;
- @Nullable
- private BeanFactory beanFactory;
-
- @Nullable
- private transient volatile Advice advice;
- public void setAdviceBeanName(@Nullable String adviceBeanName) {
- this.adviceBeanName = adviceBeanName;
- }
- @Override
- public void setBeanFactory(BeanFactory beanFactory) {
- this.beanFactory = beanFactory;
- // 若在Spring环境下,会给AdviceMonitor重新赋值为:getSingletonMutex()
- resetAdviceMonitor();
- }
-
- // 此处加锁
- public void setAdvice(Advice advice) {
- synchronized (this.adviceMonitor) {
- this.advice = advice;
- }
- }
-
- // 这是它最重要的方法,获取增强器
- @Override
- public Advice getAdvice() {
- Advice advice = this.advice;
- // 非Spring环境一般手动set进来,所以就直接返回吧
- if (advice != null) {
- return advice;
- }
-
- // 显然进来Spring容器环境了,bean工厂和beanName都是不能为null的
- Assert.state(this.adviceBeanName != null, "'adviceBeanName' must be specified");
- Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
-
- // 若bean是单例的 那就没什么好说的 直接去工厂里拿出来就完事了(Advice.class) 有可能返回null哦
- if (this.beanFactory.isSingleton(this.adviceBeanName)) {
- advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class);
- this.advice = advice;
- return advice;
- }
- // 若是多例的,就加锁 然后调用getBean()给他生成一个新的实例即可
- else {
- synchronized (this.adviceMonitor) {
- //这步赋值和判断不能省~~~确保万无一失
- advice = this.advice;
- if (advice == null) {
- advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class);
- this.advice = advice;
- }
- return advice;
- }
- }
- }
- }
DefaultBeanFactoryPointcutAdvisor:通用的BeanFactory的Advisor
这个是和Bean工厂关联的,通用的PointcutAdvisor
- public class DefaultBeanFactoryPointcutAdvisor extends AbstractBeanFactoryPointcutAdvisor {
- private Pointcut pointcut = Pointcut.TRUE;
-
- // 若传进来为null,还是选择 Pointcut.TRUE 匹配所有
- public void setPointcut(@Nullable Pointcut pointcut) {
- this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
- }
- @Override
- public Pointcut getPointcut() {
- return this.pointcut;
- }
- }
在Spring事务相关里,你会看到这个类
位于org.springframework.aop.support包内
BeanFactoryCacheOperationSourceAdvisor:和Cache有关
Spring Cache的@Cachable等注解的拦截,就是采用了它。该类位于:org.springframework.cache.interceptor,显然它和cache相关了。Jar包属于:Spring-context.jar
- // @since 3.1 毕竟Spring的整个org.springframework.cache.Cache体系都是从这里开始的。(@Cacheable...等等)
- public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
-
- // 显然它最重要的是持有这个引用(Cache章节详细介绍了它)
- @Nullable
- private CacheOperationSource cacheOperationSource;
-
- // Pointcut使用的是CacheOperationSourcePointcut
- private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
- @Override
- @Nullable
- protected CacheOperationSource getCacheOperationSource() {
- return cacheOperationSource;
- }
- };
-
-
- public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
- this.cacheOperationSource = cacheOperationSource;
- }
- public void setClassFilter(ClassFilter classFilter) {
- this.pointcut.setClassFilter(classFilter);
- }
- @Override
- public Pointcut getPointcut() {
- return this.pointcut;
- }
-
- }
AsyncAnnotationAdvisor:和@Async有关
位于包为:org.springframework.scheduling.annotation,所属jar包为spring-context.jar
- public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
- // 处理异步发生的异常的====
- private AsyncUncaughtExceptionHandler exceptionHandler;
-
- private Advice advice;
- private Pointcut pointcut;
-
- // 构造函数们
- public AsyncAnnotationAdvisor() {
- this(null, null);
- }
- // executor:可以自己指定异步任务的执行器
- // exceptionHandler:异步异常的处理器
- public AsyncAnnotationAdvisor(@Nullable Executor executor, @Nullable AsyncUncaughtExceptionHandler exceptionHandler) {
- Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
- asyncAnnotationTypes.add(Async.class);
-
- // 支持EJB的注解:@Asynchronous
- try {
- asyncAnnotationTypes.add((Class<? extends Annotation>)
- ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
- } catch (ClassNotFoundException ex) {
- // If EJB 3.1 API not present, simply ignore.
- }
- if (exceptionHandler != null) {
- this.exceptionHandler = exceptionHandler;
- } else {
- // SimpleAsyncUncaughtExceptionHandler:只是一个简单的logger.error的输入打印
- this.exceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
- }
-
- // buildAdvice: new AnnotationAsyncExecutionInterceptor(executor, exceptionHandler) 它是个MethodInterceptor 环绕通知器
- this.advice = buildAdvice(executor, this.exceptionHandler);
- // 把asyncAnnotationTypes交给buildPointcut,它最终是个ComposablePointcut,会把这两种注解都支持。union起来 或者的关系
- this.pointcut = buildPointcut(asyncAnnotationTypes);
- }
-
- public void setTaskExecutor(Executor executor) {
- this.advice = buildAdvice(executor, this.exceptionHandler);
- }
- ...
- }
AbstractAspectJAdvice的实现类如下:这5个实现类完完整整的对应着我们AspectJ的那5个注解。
AspectJPointcutAdvisor
显然是和AspectJ相关的,使用得很是广泛。注意它和AspectJExpressionPointcutAdvisor的区别。有名字也能看出来,AspectJExpressionPointcutAdvisor和表达式语言的切点相关的,而AspectJPointcutAdvisor是无关的。它哥俩都位于包org.springframework.aop.aspectj里。
- public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
-
- // AbstractAspectJAdvice通知:它的子类看下面截图,就非常清楚了
- private final AbstractAspectJAdvice advice;
- // 可以接受任意的Pointcut,可谓非常的通用(当然也包含切点表达式啦)
- private final Pointcut pointcut;
-
- @Nullable
- private Integer order;
-
- // 只有这一个构造函数,包装一个advice
- public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) {
- Assert.notNull(advice, "Advice must not be null");
- this.advice = advice;
- // 然后pointcut根据advice直接给生成了一个。这是AbstractAspectJAdvice#buildSafePointcut的方法
- this.pointcut = advice.buildSafePointcut();
- }
-
- }
InstantiationModelAwarePointcutAdvisor
它是PointcutAdvisor的一个子接口。
- // 由SpringAOP顾问包装AspectJ实现的接口 可能具有延迟初始化策略的方面。
- // 例如,一个PerThis实例化模型意味着对建议的初始化太慢
- public interface InstantiationModelAwarePointcutAdvisor extends PointcutAdvisor {
-
- // 该Advisor是否需要懒加载
- boolean isLazy();
- // 判断此Advisor它所拥有的Advice是否已经初始化了
- boolean isAdviceInstantiated();
- }
它的唯一实现类:InstantiationModelAwarePointcutAdvisorImpl
- // 默认的访问权限,显然是Spring内部自己用的
- class InstantiationModelAwarePointcutAdvisorImpl
- implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation, Serializable {
- private static final Advice EMPTY_ADVICE = new Advice() {};
- // 和AspectJExpression
- private final AspectJExpressionPointcut declaredPointcut;
- ..
-
- // 通知方法
- private transient Method aspectJAdviceMethod;
-
- private final AspectJAdvisorFactory aspectJAdvisorFactory;
- private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;
-
- @Nullable
- private Advice instantiatedAdvice;
- @Nullable
- private Boolean isBeforeAdvice;
- @Nullable
- private Boolean isAfterAdvice;
-
- ...
- @Override
- public boolean isPerInstance() {
- return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON);
- }
- @Override
- public synchronized Advice getAdvice() {
- if (this.instantiatedAdvice == null) {
- this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
- }
- return this.instantiatedAdvice;
- }
- // advice 由aspectJAdvisorFactory去生产 懒加载的效果
- private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
- Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
- this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
- return (advice != null ? advice : EMPTY_ADVICE);
- }
-
- @Override
- public boolean isBeforeAdvice() {
- if (this.isBeforeAdvice == null) {
- determineAdviceType();
- }
- return this.isBeforeAdvice;
- }
- @Override
- public boolean isAfterAdvice() {
- if (this.isAfterAdvice == null) {
- determineAdviceType();
- }
- return this.isAfterAdvice;
- }
-
- // 这里解释根据@Aspect方法上标注的注解,来区分这两个字段的值的
- private void determineAdviceType() {
- AspectJAnnotation<?> aspectJAnnotation =
- AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.aspectJAdviceMethod);
- if (aspectJAnnotation == null) {
- this.isBeforeAdvice = false;
- this.isAfterAdvice = false;
- }
- else {
- switch (aspectJAnnotation.getAnnotationType()) {
- case AtAfter:
- case AtAfterReturning:
- case AtAfterThrowing:
- this.isAfterAdvice = true;
- this.isBeforeAdvice = false;
- break;
- case AtAround:
- case AtPointcut:
- this.isAfterAdvice = false;
- this.isBeforeAdvice = false;
- break;
- case AtBefore:
- this.isAfterAdvice = false;
- this.isBeforeAdvice = true;
- }
- }
- }
- }
这个Advisor是在Spring解析被 @AspectJ注解注释的类时生成的 Advisor,。
而这个 Advisor中的 Pointcut与Advice都是由ReflectiveAspectJAdvisorFactory 来解析生成的(与之对应的 Advice 是 AspectJMethodBeforeAdvice, AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdvice,
Pointcut 则是AspectJExpressionPointcut), 解析的步骤是:
自动代理创建器:AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors() ->
Bean工厂相关的Advisor构建器:BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() ->
ReflectiveAspectJAdvisorFactory.getAdvisors() ->
ReflectiveAspectJAdvisorFactory.getAdvisor() 最终生成了InstantiationModelAwarePointcutAdvisorImpl(当然包括里面的 Pointcut与 advice 也都是由 ReflectiveAspectJAdvisorFactory 解析生成的)
IntroductionAdvisor:引介切面
Spring中有五种增强:BeforeAdvide(前置增强)、AfterAdvice(后置增强)、ThrowsAdvice(异常增强)、RoundAdvice(环绕增强)、IntroductionAdvice(引入增强)
RoundAdvice(环绕增强):就是BeforeAdvide(前置增强)、AfterAdvice(后置增强)的组合使用叫环绕增强。前四种都比较简单。。。
引入增强(Introduction Advice)的概念:一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。(非常强大有木有,A不需要动代码,就能有别的功能,吊炸天有木有)
IntroductionAdvisor纯粹就是为Introduction而生的。
IntroductionAdvisor 和 PointcutAdvisor接口不同,它仅有一个类过滤器ClassFilter 而没有 MethodMatcher,这是因为 `引介切面 的切点是类级别的,而 Pointcut 的切点是方法级别的(细粒度更细,所以更加常用)。
为了更好的了解IntroductionAdvisor,我先有必要讲解下IntroductionInfo和IntroductionInterceptor;
Introduction可以在不改动目标类定义的情况下,为目标类增加新的属性和行为。
IntroductionInfo:引介信息
IntroductionInfo 接口描述了目标类需要实现的新接口。
- // 提供描述引言所需信息的接口
- // IntroductionAdvisor必须实现这个接口。若`org.aopalliance.aop.Advice`直接实现了此接口,
- // 它可议独立的当作introduction来使用而不用依赖IntroductionAdvisor。这种情况下,这个advice可议自描述,不仅提供。。。
- public interface IntroductionInfo {
- //Return the additional interfaces introduced by this Advisor or Advice.
- // 返回额外给Advisor 或者 advice实现的接口们
- Class<?>[] getInterfaces();
- }
它的继承结构如下:
IntroductionInterceptor:引介拦截器
在Spring中,为目标对象添加新的属性和行为必须声明相应的接口以及相应的实现。这样,再通过特定的拦截器将新的接口定义以及实现类中的逻辑附加到目标对象上。然后,目标对象(确切的说,是目标对象的代理对象)就拥有了新的状态和行为
这里面介绍这个非常强大的拦截器:IntroductionInterceptor
- // IntroductionInterceptor它是对MethodInterceptor的一个扩展,同时他还继承了接口DynamicIntroductionAdvice
- public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
-
- }
DynamicIntroductionAdvice:
- public interface DynamicIntroductionAdvice extends Advice {
- boolean implementsInterface(Class<?> intf);
- }
通过DynamicIntroductionAdvice,可以界定当前的 IntroductionInterceptor为哪些接口提供相应的拦截功能。通过MethodInterceptor,IntroductionInterceptor 就可以处理新添加的接口上的方法调用了
打个比方
如果把每个目标对象实例看作盒装牛奶生产线上的那一盒盒牛奶的话,那么生产合格证就是新的Introduction逻辑,而introductionInterceptor 就是把这些生产合格证贴到一盒盒牛奶上的那个人。
要对目标对象进行拦截并添加Introduction的逻辑,我们可以直接扩展IntroductionInterceptor,然后在子类的invoke方法中实现所有的拦截逻辑
除非特殊情况下需要直接扩展IntroductionInterceptor,大多数时候,直接使用Spring提供的两个现成的实现类就可以了:DelegatingIntroductionInterceptor 和 DelegatePerTargetObjectIntroductionInterceptor
例子
下面使用一个例子,加深一下对引介增强的了解:
- // 定义一个新的行为接口,这个行为准备作用在目标对象上
- public interface IOtherInte {
- void doOther();
- }
- // 自己定义一个IntroductionInterceptor来实现IntroductionInterceptor接口
- // 注意:此处也实现了接口IOtherInte(这是类似于增强器部分) 相当于这个interptor目前就只处理 IOtherInte
- public class SomeInteIntroductionInterceptor implements IntroductionInterceptor, IOtherInte {
- /**
- * 判断调用的方法是否为指定类中的方法
- * 如果Method代表了一个方法 那么调用它的invoke就相当于执行了它代表的这个方法
- */
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- if (implementsInterface(invocation.getMethod().getDeclaringClass())) {
- System.out.println("我是引介增强的方法体~~~invoke");
- return invocation.getMethod().invoke(this, invocation.getArguments());
- }
- return invocation.proceed();
- }
-
- /**
- * 判断clazz是否为给定接口IOtherBean的实现
- */
- @Override
- public boolean implementsInterface(Class clazz) {
- return clazz.isAssignableFrom(IOtherInte.class);
- }
-
- @Override
- public void doOther() {
- System.out.println("给人贴标签 doOther...");
- }
- }
-
- // 方法测试
- public static void main(String[] args) {
- ProxyFactory factory = new ProxyFactory(new Person());
- factory.setProxyTargetClass(true); // 强制私用CGLIB 以保证我们的Person方法也能正常调用
-
- // 此处采用IntroductionInterceptor 这个引介增强的拦截器
- Advice advice = new SomeInteIntroductionInterceptor();
-
- // 切点+通知(注意:此处放的是复合切面)
- Advisor advisor = new DefaultIntroductionAdvisor((DynamicIntroductionAdvice) advice, IOtherInte.class);
- //Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
- factory.addAdvisor(advisor);
-
- IOtherInte otherInte = (IOtherInte) factory.getProxy();
- otherInte.doOther();
-
- System.out.println("===============================");
-
- // Person本身自己的方法 也得到了保留
- Person p = (Person) factory.getProxy();
- p.run();
- p.say();
- }
-
- 输出:
- 我是引介增强的方法体~~~invoke
- 给人贴标签 doOther...
- ===============================
- 我在run...
- 我在say...
我们发现,我们没有更改过Person类的任何代码,它竟然就有了doOther()方法的功能,这就是引介增强的强大功能。此处使用的Advisor为DefaultIntroductionAdvisor。它也是我们最常用的Advisor:
- // 它是一个Advisor,同时也是一个IntroductionInfo
- public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
-
- // 它只有ClassFilter,因为它只能作用在类层面上
- ClassFilter getClassFilter();
- // 判断这些接口,是否真的能够增强。 DynamicIntroductionAdvice#implementsInterface()方法
- void validateInterfaces() throws IllegalArgumentException;
-
- }
-
- // 它直接事IntroductionAdvisor的实现类。同时也是一个ClassFilter
- public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFilter, Ordered, Serializable {
- private final Advice advice;
-
- private final Set<Class<?>> interfaces = new LinkedHashSet<>();
- private int order = Ordered.LOWEST_PRECEDENCE;
-
- // 构造函数们
- public DefaultIntroductionAdvisor(Advice advice) {
- this(advice, (advice instanceof IntroductionInfo ? (IntroductionInfo) advice : null));
- }
-
- // 如果IntroductionInfo 不等于null,就会把接口都add进去/
- // IntroductionInfo 的实现类有常用的:DelegatingIntroductionInterceptor和DelegatePerTargetObjectIntroductionInterceptor
- public DefaultIntroductionAdvisor(Advice advice, @Nullable IntroductionInfo introductionInfo) {
- Assert.notNull(advice, "Advice must not be null");
- this.advice = advice;
- if (introductionInfo != null) {
- Class<?>[] introducedInterfaces = introductionInfo.getInterfaces();
- if (introducedInterfaces.length == 0) {
- throw new IllegalArgumentException("IntroductionAdviceSupport implements no interfaces");
- }
- for (Class<?> ifc : introducedInterfaces) {
- addInterface(ifc);
- }
- }
- }
-
- //当然你也可以不使用IntroductionInfo,而自己手动指定了这个接口
- public DefaultIntroductionAdvisor(DynamicIntroductionAdvice advice, Class<?> intf) {
- Assert.notNull(advice, "Advice must not be null");
- this.advice = advice;
- addInterface(intf);
- }
- ...
- @Override
- public void validateInterfaces() throws IllegalArgumentException {
- for (Class<?> ifc : this.interfaces) {
- if (this.advice instanceof DynamicIntroductionAdvice &&
- !((DynamicIntroductionAdvice) this.advice).implementsInterface(ifc)) {
- throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " +
- "does not implement interface [" + ifc.getName() + "] specified for introduction");
- }
- }
- }
- ...
-
- }
DelegatingIntroductionInterceptor和DelegatePerTargetObjectIntroductionInterceptor
这两个类是 Spring AOP 中为 IntroductionInterceptor 介面所提供的实作类别,我们可以直接继承他俩,然后扩展我们自己的行为状态。
- public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport
- implements IntroductionInterceptor {
-
- // 需要被代理的那个对象。因为这个类需要子类继承使用,所以一般都是thid
- @Nullable
- private Object delegate;
- /**
- * Construct a new DelegatingIntroductionInterceptor.
- * The delegate will be the subclass, which must implement
- * additional interfaces.
- * 访问权限事protected,显然就是说子类必须去继承这个类,然后提供空构造函数。代理类就是this
- */
- protected DelegatingIntroductionInterceptor() {
- init(this);
- }
- // 当然,你也可以手动指定delegate
- public DelegatingIntroductionInterceptor(Object delegate) {
- init(delegate);
- }
- private void init(Object delegate) {
- Assert.notNull(delegate, "Delegate must not be null");
- this.delegate = delegate;
- implementInterfacesOnObject(delegate);
-
- // 移除调这些内部标记的接口们
- // We don't want to expose the control interface
- suppressInterface(IntroductionInterceptor.class);
- suppressInterface(DynamicIntroductionAdvice.class);
- }
-
- // 如果你要自定义一些行为:比如环绕通知之类的,子类需要复写此方法(否则没有必要了)
- @Override
- @Nullable
- public Object invoke(MethodInvocation mi) throws Throwable {
- // 判断是否是引介增强
- if (isMethodOnIntroducedInterface(mi)) {
- Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());
-
- // 如果返回值就是delegate 本身,那就把本身返回出去
- if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
- Object proxy = ((ProxyMethodInvocation) mi).getProxy();
- if (mi.getMethod().getReturnType().isInstance(proxy)) {
- retVal = proxy;
- }
- }
- return retVal;
- }
-
- return doProceed(mi);
- }
- ...
- }
因此,上面的例子Demo,我用DelegatingIntroductionInterceptor改造一下(只需要改造SomeInteIntroductionInterceptor即可):
- // 因为我们继承自DelegatingIntroductionInterceptor,所以若我们不做环绕通知个性化,只需要实现接口的方法即可
- public class SomeInteIntroductionInterceptor extends DelegatingIntroductionInterceptor implements IOtherInte {
-
- @Override
- public void doOther() {
- System.out.println("给人贴标签 doOther...");
- }
- }
继承此类,确实少了不少事呢。
DelegatePerTargetObjectIntroductionInterceptor
与DelegatingIntroductionInterceptor不同,DelegatePerTargetObjectIntroductionInterceptor会在内部持有一个目标对象与相应Introduction逻辑实现类之间的映射关系。
当每个目标对象上的新定义的接口方法被调用的时候,它会拦截这些调用。然后以目标对象实例作为键,到它持有的那个映射关系中取得对应当前目标对象实例的Introduction实现类实例。 使用起来和DelegatingIntroductionInterceptor没有太大区别,主要在构造函数上:
- 1: 可以删除掉SomeInteIntroductionInterceptor类了
- 2:单独实现引介接口
- public class OtherImpl implements IOtherInte {
-
- @Override
- public void doOther() {
- System.out.println("我是OtherImpl");
- }
- }
- main方法里修改如下:这样就ok了
- Advice advice = new DelegatePerTargetObjectIntroductionInterceptor(OtherImpl.class, IOtherInte.class);
备注:若你需要复写invoke方法的逻辑,请扩展它即可~~~(它的优点是:每次执行目标对象的时候,都可议缓存起来,提高一点效率吧)
总结
Advisor是Spring AOP中非常重要的一个概念,最终代理对象进行执行什么的,都是适配到此处,因此需要重点掌握。
其中,引介增强平时使用得较少,但是在特殊的场景下,它能够解决某一类问题,还是非常不错的~
DataSourceTransactionManager:
这个接口是在TransactionDefinition的基础上的扩展
// 它继承自TransactionDefinition ,所有可以定义事务的基础属性 public interface TransactionAttribute extends TransactionDefinition { // 返回与此事务属性关联的限定符值 //@since 3.0 @Nullable String getQualifier(); // Should we roll back on the given exception? boolean rollbackOn(Throwable ex); }
它的主要实现类有:DefaultTransactionAttribute、RuleBasedTransactionAttribute、DelegatingTransactionAttribute
DefaultTransactionAttribute
默认的事务属性实现,也是最常用的一个实现。
- // 它继承自DefaultTransactionDefinition
- public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
-
- @Nullable
- private String qualifier;
- @Nullable
- private String descriptor;
-
- // 你自己也可以自定义一个TransactionAttribute other 来替换掉一些默认行为
- public DefaultTransactionAttribute() {
- super();
- }
- public DefaultTransactionAttribute(TransactionAttribute other) {
- super(other);
- }
-
- // @since 3.0
- public void setQualifier(@Nullable String qualifier) {
- this.qualifier = qualifier;
- }
- @Override
- @Nullable
- public String getQualifier() {
- return this.qualifier;
- }
- ...
- // 可以清晰的看到:默认只回滚RuntimeException 或者 Error(比如OOM这种)
- @Override
- public boolean rollbackOn(Throwable ex) {
- return (ex instanceof RuntimeException || ex instanceof Error);
- }
-
- }
RuleBasedTransactionAttribute
这个在基础实现的基础上扩展了一下,扩展了一些规则。
- public class RuleBasedTransactionAttribute extends DefaultTransactionAttribute implements Serializable {
- /** Prefix for rollback-on-exception rules in description strings. */
- public static final String PREFIX_ROLLBACK_RULE = "-";
- /** Prefix for commit-on-exception rules in description strings. */
- public static final String PREFIX_COMMIT_RULE = "+";
-
- // RollbackRuleAttribute:它是个实体类,确定给定异常是否应导致回滚的规则
- // 相当于封装了这个规则的一个实体,内部封装一个异常 提供一个实例变量: 这个变量相当于回滚规则为只回滚RuntimeException
- // public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS = new RollbackRuleAttribute(RuntimeException.class);
- // 所以此类最重要的一个属性,就是这个,它能维护多种回滚的规则~~~~
- @Nullable
- private List<RollbackRuleAttribute> rollbackRules;
- ...
- public List<RollbackRuleAttribute> getRollbackRules() {
- if (this.rollbackRules == null) {
- this.rollbackRules = new LinkedList<>();
- }
- return this.rollbackRules;
- }
-
- // 核心逻辑输入,复写了父类的rollbackOn方法。也就是看看当前异常是否需要回滚呢???
- @Override
- public boolean rollbackOn(Throwable ex) {
- RollbackRuleAttribute winner = null;
- int deepest = Integer.MAX_VALUE;
-
- // 这里getDepth()就是去看看异常栈里面 该类型的异常处于啥位置。
- // 这里用了Integer的最大值,基本相当于不管异常有多深,遇上此异常都应该回滚喽,也就是找到这个winnner了~~~~~
- if (this.rollbackRules != null) {
- for (RollbackRuleAttribute rule : this.rollbackRules) {
- int depth = rule.getDepth(ex);
- if (depth >= 0 && depth < deepest) {
- deepest = depth;
- winner = rule;
- }
- }
- }
-
- // User superclass behavior (rollback on unchecked) if no rule matches.
- // 这句相当于:如果你没有指定回滚规则,那就交给父类吧(只回滚RuntimeException和Error类型)
- if (winner == null) {
- return super.rollbackOn(ex);
- }
-
- // 最终只要找到了,但是不是NoRollbackRuleAttribute类型就成`~~~~
- return !(winner instanceof NoRollbackRuleAttribute);
- }
- }
DelegatingTransactionAttribute
很显然,它就是一个简单的代理,内部持有一个TransactionAttribute的引用。自己也是个抽象类,没做啥事,此处略过。它也继承自:DelegatingTransactionDefinition
TransactionDefinition
事务的定义,上面已经介绍了一个重要分支:TransactionAttribute。接下来继续介绍另外一个分支:
DelegatingTransactionDefinition
一样的也就是个代理抽象类,啥都木有做。内部持有一个TransactionDefinition targetDefinition的引用而已,所有方法都是委托给targetDefinition去做的
ResourceTransactionDefinition
这个子接口非常的新,是Spring5.1才提供的
- // @since 5.1
- // 指示资源事务,尤其是事务性资源是否准备好进行本地优化
- public interface ResourceTransactionDefinition extends TransactionDefinition {
- // 确定事务性资源是否准备好进行本地优化
- // @see #isReadOnly()
- boolean isLocalResource();
- }
它和ResourceTransactionManager的使用相关联。ResourceTransactionManager是PlatformTransactionManager的一个子接口。
我们最常用的事务管理器DataSourceTransactionManager也实现了这个接口~~~~
目前Spring还未提供任何ResourceTransactionDefinition它的具体实现~
TransactionAttributeSource:事务属性源
它位于包:org.springframework.transaction.interceptor
它有点类似于之前讲过的TargetSource,它也是对TransactionAttribute进行了一层包装~~
- public interface TransactionAttributeSource {
- // 通过Method和目标类,拿到事务属性~~~
- // 比如我们的@Transaction是标注在方法上的,可议自定义方法级别的事务属性,用它就特别的方便~
- @Nullable
- TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
-
- }
这里有很多人不明白了,为何都给了Method,为啥还要传入Class呢?难道Method还不知道它所属的类???
这里做如下解释:
这里是有细微差别的:
通常情况下,method的所属类会是targetClass的某个祖先类或者实现的某个接口。(动态代理)
它的实现有多种:
TransactionAttributeSource一般都是作为TransactionInterceptor的一个属性被set进去,然后看看这个事务属性可以作用在不同的方法上面,实现不同方法的个性化定制~
(实际真正处理它的是父类TransactionAspectSupport,它会做匹配~~~~) 具体的在详解TransactionInterceptor的时候会讲述到
NameMatchTransactionAttributeSource
根据名字就能匹配,然后该事务属性就会作用在对应的方法上。比如下面例子:
- // 自定义配置一个事务拦截器(@Transaction注解也会使用此拦截器进行拦截)
- @Bean
- public TransactionInterceptor transactionInterceptor(PlatformTransactionManager transactionManager) {
- Map<String, TransactionAttribute> txMap = new HashMap<>();
- // required事务 适用于觉得部分场景~
- RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
- requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(RuntimeException.class)));
- requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
- txMap.put("add*", requiredTx);
- txMap.put("save*", requiredTx);
- txMap.put("insert*", requiredTx);
- txMap.put("update*", requiredTx);
- txMap.put("delete*", requiredTx);
-
- // 查询 使用只读事务
- RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
- readOnlyTx.setReadOnly(true);
- readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
- txMap.put("get*", readOnlyTx);
- txMap.put("query*", readOnlyTx);
-
- // 定义事务属性的source~~~ 此处使用它 也就是根据方法名进行匹配的~~~
- NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
- source.setNameMap(txMap);
- return new TransactionInterceptor(transactionManager, source);
- }
这个在我们基于XML的配置事务的时候,原理就是这样的~~
注意此处的匹配模式也是基于简单匹配的:PatternMatchUtils.simpleMatch。而非强大的正则匹配。底层getTransactionAttribute()时会根据不同的方法名,来返回不同的事务属性~~~
MethodMapTransactionAttributeSource
它的使用方式和NameMatchTransactionAttributeSource基本相同,但是有一个不同在于:
如果使用NameMatchTransactionAttributeSource配置属性源,比如get*配置为执行事务,那么所有的bean的get方法都会被加上事务,这可能不是我们想要的,因此对于自动代理,我们更好的选择是MethodMapTransactionAttributeSource,它需要指定需要事务化的完整类名和方法名
CompositeTransactionAttributeSource
它代表一种组合模式。
- // @since 2.0 它是Spring2.0后才推出来的
- public class CompositeTransactionAttributeSource implements TransactionAttributeSource, Serializable {
- private final TransactionAttributeSource[] transactionAttributeSources;
- public CompositeTransactionAttributeSource(TransactionAttributeSource... transactionAttributeSources) {
- Assert.notNull(transactionAttributeSources, "TransactionAttributeSource array must not be null");
- this.transactionAttributeSources = transactionAttributeSources;
- }
-
- // 这个实现方法也很容易。多个TransactionAttributeSource放在一起,只要任意一个匹配上就成
- // 备注:若匹配上多个,请注意先后顺序就成 这里面是数组 会保持和你放入的顺序一样~~~
- @Override
- @Nullable
- public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
- for (TransactionAttributeSource source : this.transactionAttributeSources) {
- TransactionAttribute attr = source.getTransactionAttribute(method, targetClass);
- if (attr != null) {
- return attr;
- }
- }
- return null;
- }
- }
MatchAlwaysTransactionAttributeSource
它是TransactionAttributeSource的一个最简单的实现,每次调用,都是返回相同的TransactionAttribute
当然它是可议set一个TransactionAttribute作为通用的事务属性的实现的
AnnotationTransactionAttributeSource
这个就是重点了,它是基于注解驱动的事务管理的事务属性源,和@Transaction相关,也是现在使用得最最多的方式。
它的基本作用为:它遇上比如@Transaction标注的方法时,此类会分析此事务注解,最终组织形成一个TransactionAttribute供随后的调用。
- public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
-
- // 这个是“向下兼容”,JavaEE提供的其余两种注解~~
- private static final boolean jta12Present; //JTA 1.2事务注解
- private static final boolean ejb3Present; //EJB 3 事务注解是
- static {
- ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
- jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
- ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
- }
-
- // true:只处理public方法(基于JDK的代理 显然就只会处理这种方法)
- // false:private/protected等方法都会处理。 基于AspectJ代理得方式可议设置为false
- // 默认情况下:会被赋值为true,表示只处理public的方法
- private final boolean publicMethodsOnly;
- // 保存用于分析事务注解的事务注解分析器 这个注解分析的解析器是重点
- private final Set<TransactionAnnotationParser> annotationParsers;
-
- // 构造函数, publicMethodsOnly 缺省使用 true
- public AnnotationTransactionAttributeSource() {
- this(true);
- }
- public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
- this.publicMethodsOnly = publicMethodsOnly;
- if (jta12Present || ejb3Present) {
- this.annotationParsers = new LinkedHashSet<>(4);
- this.annotationParsers.add(new SpringTransactionAnnotationParser());
- if (jta12Present) {
- this.annotationParsers.add(new JtaTransactionAnnotationParser());
- }
- if (ejb3Present) {
- this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
- }
- }
- // 默认情况下,只添加Spring自己的注解解析器(绝大部分情况都实这里)
- else {
- this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
- }
- }
- // 自己也可以指定一个TransactionAnnotationParser 或者多个也成
- public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) { ... }
- public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) { ... }
- public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) { ... }
-
- // 获取某个类/方法上的事务注解属性(属于 父类的抽象方法)
- @Override
- @Nullable
- protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
- return determineTransactionAttribute(clazz);
- }
- @Override
- @Nullable
- protected TransactionAttribute findTransactionAttribute(Method method) {
- return determineTransactionAttribute(method);
- }
-
- // 具体实现如下:
- // 分析获取某个被注解的元素(AnnotatedElement ),具体的来讲,指的是一个类或者一个方法上的事务注解属性。
- // 实现会遍历自己属性annotationParsers中所包含的事务注解属性分析器试图获取事务注解属性 所以主要还是依赖于TransactionAnnotationParser 去解析的
- @Nullable
- protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
- for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
- TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
- if (attr != null) {
- return attr;
- }
- }
- return null;
- }
-
- /**
- * By default, only public methods can be made transactional.
- */
- @Override
- protected boolean allowPublicMethodsOnly() {
- return this.publicMethodsOnly;
- }
- ...
- }
从源码中可议知道,真正提供给调用的getTransactionAttribute在父类中实现的:
AbstractFallbackTransactionAttributeSource
AbstractFallbackTransactionAttributeSource是接口TransactionAttributeSource的抽象实现,也是上面提到的工具类AnnotationTransactionAttributeSource的父类。
- public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
-
- // 针对没有事务注解属性的方法进行事务注解属性缓存时使用的特殊值,用于标记该方法没有事务注解属性
- // 从而不用在首次缓存在信息后,不用再次重复执行真正的分析 来提高查找的效率
- // 标注了@Transaction注解的表示有事务属性的,才会最终加入事务。但是,但是此处需要注意的是,只要被事务的Advisor切中的,都会缓存起来 放置过度的查找~~~~ 因此才有这个常量的出现
- private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {
- @Override
- public String toString() {
- return "null";
- }
- };
-
- // 方法上的事务注解属性缓存,key使用目标类上的方法,使用类型MethodClassKey来表示
- // 这个Map会比较大,会被事务相关的Advisor拦截下来的方法,最终都会缓存下来。关于事务相关的Advisor,后续也是会着重讲解的~~~
- // 因为会有很多,所以我们才需要一个NULL_TRANSACTION_ATTRIBUTE常量来提高查找的效率~~~
- private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);
-
- // 获取指定方法上的注解事务属性 如果方法上没有注解事务属性,则使用目标方法所属类上的注解事务属性
- @Override
- @Nullable
- public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
- // 如果目标方法是内置类Object上的方法,总是返回null,这些方法上不应用事务
- if (method.getDeclaringClass() == Object.class) {
- return null;
- }
-
- // 先看缓存里有木有,此处使用的非常经典的MethodClassKey作为Map的key
- Object cacheKey = getCacheKey(method, targetClass);
- TransactionAttribute cached = this.attributeCache.get(cacheKey);
- if (cached != null) {
- //目标方法上上并没有事务注解属性,但是已经被尝试分析过并且已经被缓存,
- // 使用的值是 NULL_TRANSACTION_ATTRIBUTE,所以这里再次尝试获取其注解事务属性时,直接返回 null
- if (cached == NULL_TRANSACTION_ATTRIBUTE) {
- return null;
- } else {
- return cached;
- }
- }
- // 缓存没有命中~~~~
- else {
- // 通过方法、目标Class 分析出此方法上的事务属性~~~~~
- TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
- // 如果目标方法上并没有使用注解事务属性,也缓存该信息,只不过使用的值是一个特殊值:
- if (txAttr == null) {
- this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
- }
- // 存在目标属性~ 就put到里面去。
- // 获取到methodIdentification 基本只为了输出日志~~~
- else {
- String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
- if (txAttr instanceof DefaultTransactionAttribute) {
- ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
- }
- this.attributeCache.put(cacheKey, txAttr);
- }
- return txAttr;
- }
- }
-
- //查找目标方法上的事务注解属性 也是上面的核心方法
- @Nullable
- protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
- // 如果事务注解属性分析仅仅针对public方法,而当前方法不是public,则直接返回null
- // 如果是private,AOP是能切入,代理对象也会生成的 但就是事务不回生效的~~~~
- if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
- return null;
- }
-
- // 上面说了,因为Method并不一样属于目标类。所以这个方法就是获取targetClass上的那个和method对应的方法 也就是最终要执行的方法
- Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
-
- // 第一步:去找直接标记在方法上的事务属性~~~ 如果方法上有就直接返回(不用再看类上的了)
- // findTransactionAttribute这个方法其实就是子类去实现的
- TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
- if (txAttr != null) {
- return txAttr;
- }
-
- // 然后尝试检查事务注解属性是否标记在目标方法 specificMethod(注意此处用不是Method) 所属类上
- txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
- if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
- return txAttr;
- }
-
- // 程序走到这里说明目标方法specificMethod,也就是实现类上的目标方法上没有标记事务注解属性(否则直接返回了嘛)
-
- // 如果 specificMethod 和 method 不同,则说明 specificMethod 是具体实现类的方法method 是实现类所实现接口的方法
- // 因此再次尝试从 method 上获取事务注解属性
- // 这也就是为何我们的@Transaction标注在接口上或者接口的方法上都是好使的原因~~~~~~~
- if (specificMethod != method) {
- // Fallback is to look at the original method.
- txAttr = findTransactionAttribute(method);
- if (txAttr != null) {
- return txAttr;
- }
- txAttr = findTransactionAttribute(method.getDeclaringClass());
- if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
- return txAttr;
- }
- }
- return null;
- }
-
- // 可议看到默认值是false 表示private的也是ok的
- // 但是`AnnotationTransactionAttributeSource`复写了它 可以由开发者指定(默认是true了)
- protected boolean allowPublicMethodsOnly() {
- return false;
- }
-
- }
从上文可以看出,真正把@Transaction注解变成一个TransactionAttribute类的是TransactionAnnotationParser,下面有必要来具体看看它
顾名思义,它是解析方法/类上事务注解的。
- // @since 2.5
- public interface TransactionAnnotationParser {
- @Nullable
- TransactionAttribute parseTransactionAnnotation(AnnotatedElement element);
-
- }
它支持上面说的到三个注解,分别对应三个实现类:JtaTransactionAnnotationParser、Ejb3TransactionAnnotationParser、SpringTransactionAnnotationParser。
因为现在基本是Spring的天下了,因此本文只讲述SpringTransactionAnnotationParser,其它的雷同
SpringTransactionAnnotationParser
它专门用于解析Class或者Method上的org.springframework.transaction.annotation.Transactional注解的。
- // @since 2.5 此类的实现相对来说还是比较简单的
- public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
-
- // 此方法对外暴露,表示获取该方法/类上面的TransactionAttribute
- @Override
- @Nullable
- public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
- AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false);
- if (attributes != null) {
- // 此处注意,把这个注解的属性交给它,最终转换为事务的属性类~~~~
- return parseTransactionAnnotation(attributes);
- }
- // 注解都木有,那就返回null
- else {
- return null;
- }
- }
-
- // 顺便提供的一个重载方法,可以让你直接传入一个注解
- public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
- return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false));
- }
-
- // 这个简单的说:就是把注解的属性们 专门为事务属性们~~~~
- protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
- // 此处用的 RuleBasedTransactionAttribute 因为它可议指定不需要回滚的类~~~~
- RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
-
- // 事务的传播属性枚举:内部定义了7种事务传播行为~~~~~
- Propagation propagation = attributes.getEnum("propagation");
- rbta.setPropagationBehavior(propagation.value());
-
- // 事务的隔离级别枚举。一共是4中,枚举里提供一个默认值: 也就是上面我们说的TransactionDefinition.ISOLATION_DEFAULT
- // 至于默认值是哪种隔离界别:这个具体的数据库有关~~~
- Isolation isolation = attributes.getEnum("isolation");
- rbta.setIsolationLevel(isolation.value());
-
- // 设置事务的超时时间
- rbta.setTimeout(attributes.getNumber("timeout").intValue());
- // 是否是只读事务
- rbta.setReadOnly(attributes.getBoolean("readOnly"));
- // 这个属性,是指定事务管理器PlatformTransactionManager的BeanName的,若不指定,那就按照类型找了
- // 若容器中存在多个事务管理器,但又没指定名字 那就报错啦~~~
- rbta.setQualifier(attributes.getString("value"));
-
- // rollbackFor可以指定需要回滚的异常,可议指定多个 若不指定默认为RuntimeException
- // 此处使用的RollbackRuleAttribute包装~~~~ 它就是个POJO没有实现其余接口
- List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
- for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
- rollbackRules.add(new RollbackRuleAttribute(rbRule));
- }
- // 全类名的方式~~
- for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
- rollbackRules.add(new RollbackRuleAttribute(rbRule));
- }
-
-
- // 指定不需要回滚的异常类型们~~~
- // 此处使用的NoRollbackRuleAttribute包装 它是RollbackRuleAttribute的子类
- for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
- rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
- }
- for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
- rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
- }
- // 最后别忘了set进去
- rbta.setRollbackRules(rollbackRules);
-
- return rbta;
- }
- }
通过这个parser就可议把方法/类上的注解,转换为事务属性,然后缓存起来。
这样方法在调用的时候,直接根据Method就能取到事务属性,从而执行不同的事务策略~~~
SavepointManager
管理事务savepoint的编程式API接口。
JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。Connection接口的setSavepoint和releaseSavepoint方法可以设置和释放保存点。
- // @since 1.1
- public interface SavepointManager {
- Object createSavepoint() throws TransactionException;
- void rollbackToSavepoint(Object savepoint) throws TransactionException;
- void releaseSavepoint(Object savepoint) throws TransactionException;
- }
它的主要实现有如下:
TransactionStatus这个分支很重要,后面有着重分析。这里先看看JdbcTransactionObjectSupport这个实现
JdbcTransactionObjectSupport
- // @since 1.1 继承自SmartTransactionObject
- public abstract class JdbcTransactionObjectSupport implements SavepointManager, SmartTransactionObject {
-
- // 这是Spring定义的类,持有java.sql.Connection
- // 所以最支不支持还原点、创建还原点其实都是委托给它来的~
- @Nullable
- private ConnectionHolder connectionHolder;
- ...
- }
它也只是个抽象类,SmartTransactionObject接口相关的方法都没有去实现~~~~但是它的子类DataSourceTransactionObject有去实现的~
关于还原点的实现,整体上还是比较简单的,就是委托给Connection去做~
作者:我睡觉爱打呼噜
链接:http://www.javaheidong.com/blog/article/207421/49b2efe2e26da0e95936/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!