Spring入门篇 学习笔记
Spring AOP API 是 Spring 1.2 历史用法,现在仍然支持
这是 Spring AOP 基础,现在的用法也是基于历史的,只是更简便了
Pointcut
实现之一:NameMatchMethodPointcut
根据方法名字进行匹配
成员变量:mappedNames,匹配的方法名集合
1 | <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut"> |
Before advice
只是在进入方法之前被调用,不需要 MethodInvocation 对象
前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值
1 | public class MoocBeforeAdvice implements MethodBeforeAdvice { |
Throws advice
如果连接点抛出异常,throws advice 在连接点返回后被调用
如果 throws-advice 的方法抛出异常,那么它将覆盖原有异常
接口org.springframework.aop.ThrowsAdvice 不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法:void afterThrowing([Method, args, target], ThrowableSubclass)
1 | public class MoocThrowsAdvice implements ThrowsAdvice { |
After Returning advice
可以访问返回值(但不能进行修改)、被调用的方法、方法的参数和目标
如果抛出异常,将会抛出拦截器链,替代返回值
1 | public class MoocAfterReturningAdvice implements AfterReturningAdvice { |
Interception around advice
Spring 的切入点模型使得切入点可以独立与 advice 重用,以针对不同的 advice 可以使用相同的切入点
1 | public class MoocMethodInterceptor implements MethodInterceptor { |
Introduction advice
Spring 把引入通知作为一种特殊的拦截通知
仅使用于类,不能和任何切入点一起使用
需要同时实现 IntroductionAdvisor 和 IntroductionInterceptor
1 | public interface Lockable { |
Advisor
Advisor 是仅包含一个切入点表达式关联的单个通知的方面
除了 introductions, advisor 可以用于任何通知
org.springframework.aop.support.DefaultPointcutAdvisor 是最常用的 advisor 类,它可以与 MethodInterceptor, BeforeAdvice 或者 ThrowsAdvice 一起使用
它可以混合在 Spring 同一个 AOP 代理的 advisor 和 advice
ProxyFactoryBean
创建 Spring AOP 代理的基本方法是使用 ProxyFactoryBean,这样可以完全控制切入点和通知以及它们的顺序
使用 ProxyFactoryBean 或者其他 IoC 相关类来创建 AOP 代理最重要的好处是通知和切入点也可以由 IoC 来管理
如果被代理类没有实现任何接口,使用 CGLIB 代理,否则使用 JDK 代理
通过设置 proxyTargetClass 为 true,可强制使用 CGLIB
如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖 ProxyFactoryBean 的配置
如果 ProxyFactoryBean 的 proxyInterfaces 属性被设置为一个或者多个全限定接口名,基于 JDK 的代理将被创建
如果 ProxyFactoryBean 的 proxyInterfaces 属性没有被设置,但是目标类实现了一个(或者更多)接口,那么 ProxyFactoryBean 将自动检测到这个目标类已经实现了至少一个接口,创建一个基于 JDK 的代理
1 | <!--对应的是 ProxyFactoryBean,target 指向的才是目标类--> |
实例
使用 Pointcut
添加:
1 | public interface BizLogic { |
添加配置:
1 | <?xml version="1.0" encoding="UTF-8"?> |
添加测试类:
1 | @RunWith(BlockJUnit4ClassRunner.class) |
不使用 Pointcut
修改配置:
1 | <?xml version="1.0" encoding="UTF-8"?> |
可以使用匿名内部 bean 来隐藏目标和代理之间的区别(推荐做法,可以避免直接使用 getBean 获取原始对象绕过代理而不会执行 advice),上述配置修改:
- 移除:
<bean id="moocThrowsAdvice" class="com.karonda.aop.api.MoocThrowsAdvice"></bean> <property name="target">修改为:<bean class="com.karonda.aop.api.BizLogicImpl"></bean>
1 | <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> |
Proxying class
CGLIB 代理的工作原理是在运行时生成目标类的子类,Spring 配置这个生成的子类委托方法调用到原来的目标
子类是用 Decorator 模式,织入通知
CGLIB 的代理对用户是透明的,需要注意:
- final 方法不能被通知,因为它们不能被覆盖
- 不用把 CGLIB 添加到 classpath 中,在 Spring 3.2 之后 CGLIB 被重新包装并包含在 Spring 核心的JAR (即基于 CGLIB 的 AOP 就像 JDK 动态代理一样“开箱即用”)
global advisor
用 * 做通配,匹配所有拦截器加入通知链(实现了 MethodInterceptor 接口的)
示例:
moocMethodInterceptor 可以修改为 mooc*:
1 | <property name="interceptorNames"> |
简化的 proxy 定义
使用父子 bean 定义以及内部 bean 定义,可能会带来更清洁和更简洁的代理定义(抽象属性标记父 bean 定义为 abstract,这样它不能被实例化)
示例:
修改配置文件:
1 | <?xml version="1.0" encoding="UTF-8"?> |
使用 ProxyFactory
使用 Spring AOP 而不必依赖于 Spring IoC:
1 | ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); |
大多数情况下最佳实践是使用 IoC 容器创建 AOP 代理
虽然可以硬编码方式实现,但是 Spring 推荐使用配置或注解方式实现
使用 “auto-proxy”
Spring 也允许使用”自动代理“的 bean 定义,它可以自动代理选定的 bean,这是建立在 Spring 的 “bean post processor” 功能基础上的(在加载 bean 的时候就可以修改)
1 | <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> |
使用 DefaultAdvisorAutoPorxyCreator,当前 IoC 容器中自动应用,不用显示声明引用 advisor 的 bean 定义
1 | <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoPorxyCreator"/> |