学习 Spring (十六) AOP API
Spring入门篇 学习笔记
Spring AOP API 是 Spring 1.2 历史用法,现在仍然支持
这是 Spring AOP 基础,现在的用法也是基于历史的,只是更简便了
Pointcut
实现之一:NameMatchMethodPointcut
根据方法名字进行匹配
成员变量:mappedNames,匹配的方法名集合
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
Before advice
只是在进入方法之前被调用,不需要 MethodInvocation 对象
前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值
public class MoocBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("MoocBeforeAdvice : " + method.getName() + ", " +
target.getClass().getName());
}
}
Throws advice
如果连接点抛出异常,throws advice 在连接点返回后被调用
如果 throws-advice 的方法抛出异常,那么它将覆盖原有异常
接口org.springframework.aop.ThrowsAdvice 不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法:void afterThrowing([Method, args, target], ThrowableSubclass)
public class MoocThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex) throws Throwable {
System.out.println("MoocThrowsAdvice afterThrowing 1");
}
public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
System.out.println("MoocThrowsAdvice afterThrowing 2 : " + method.getName() + ", " +
target.getClass().getName());
}
}
After Returning advice
可以访问返回值(但不能进行修改)、被调用的方法、方法的参数和目标
如果抛出异常,将会抛出拦截器链,替代返回值
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("MoocAfterReturningAdvice : " + method.getName() + ", " +
target.getClass().getName() + ", " + returnValue);
}
}
Interception around advice
Spring 的切入点模型使得切入点可以独立与 advice 重用,以针对不同的 advice 可以使用相同的切入点
public class MoocMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("MoocMethodInterceptor 1 : " + invocation.getMethod().getName() + ", " +
invocation.getStaticPart().getClass().getName());
Object obj = invocation.proceed();
System.out.println("MoocMethodInterceptor 2 : " + obj);
return obj;
}
}
Introduction advice
Spring 把引入通知作为一种特殊的拦截通知
仅使用于类,不能和任何切入点一起使用
需要同时实现 IntroductionAdvisor 和 IntroductionInterceptor
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
// 通常不是直接去实现 IntroductionInterceptor 接口,而是继承 DelegatingIntroductionInterceptor
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
throw new RuntimeException();
}
return super.invoke(invocation);
}
}
// 持有独立的 LockMixin 实例
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
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 的代理
<!--对应的是 ProxyFactoryBean,target 指向的才是目标类-->
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="bizLogicImplTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>defaultAdvisor</value>
<value>moocAfterReturningAdvice</value>
<value>moocMethodInterceptor</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean>
实例
使用 Pointcut
添加:
public interface BizLogic {
String save();
}
public class BizLogicImpl implements BizLogic {
public String save() {
System.out.println("BizLogicImpl : BizLogicImpl save.");
return "BizLogicImpl save.";
// throw new RuntimeException();
}
}
添加配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="moocBeforeAdvice" class="com.karonda.aop.api.MoocBeforeAdvice"></bean>
<bean id="moocAfterReturningAdvice" class="com.karonda.aop.api.MoocAfterReturningAdvice"></bean>
<bean id="moocMethodInterceptor" class="com.karonda.aop.api.MoocMethodInterceptor"></bean>
<bean id="moocThrowsAdvice" class="com.karonda.aop.api.MoocThrowsAdvice"></bean>
<bean id="bizLogicImplTarget" class="com.karonda.aop.api.BizLogicImpl"></bean>
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
<bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="moocBeforeAdvice" />
<property name="pointcut" ref="pointcutBean" />
</bean>
<!--对应的是 ProxyFactoryBean,target 指向的才是目标类-->
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="bizLogicImplTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>defaultAdvisor</value>
<value>moocAfterReturningAdvice</value>
<value>moocMethodInterceptor</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean>
</beans>
添加测试类:
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPAPI extends UnitTestBase {
public TestAOPAPI() {
super("classpath:spring-aop-api.xml");
}
@Test
public void testSave() {
BizLogic logic = (BizLogic)super.getBean("bizLogicImpl");
logic.save();
}
}
不使用 Pointcut
修改配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="moocBeforeAdvice" class="com.karonda.aop.api.MoocBeforeAdvice"></bean>
<bean id="moocAfterReturningAdvice" class="com.karonda.aop.api.MoocAfterReturningAdvice"></bean>
<bean id="moocMethodInterceptor" class="com.karonda.aop.api.MoocMethodInterceptor"></bean>
<bean id="moocThrowsAdvice" class="com.karonda.aop.api.MoocThrowsAdvice"></bean>
<bean id="bizLogicImplTarget" class="com.karonda.aop.api.BizLogicImpl"></bean>
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.karonda.aop.api.BizLogic</value>
</property>
<property name="target">
<ref bean="bizLogicImplTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>moocBeforeAdvice</value>
<value>moocAfterReturningAdvice</value>
<value>moocMethodInterceptor</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean>
</beans>
可以使用匿名内部 bean 来隐藏目标和代理之间的区别(推荐做法,可以避免直接使用 getBean 获取原始对象绕过代理而不会执行 advice),上述配置修改:
- 移除:
<bean id="moocThrowsAdvice" class="com.karonda.aop.api.MoocThrowsAdvice"></bean>
<property name="target">
修改为:<bean class="com.karonda.aop.api.BizLogicImpl"></bean>
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.karonda.aop.api.BizLogic</value>
</property>
<property name="target">
<bean class="com.karonda.aop.api.BizLogicImpl"></bean>
</property>
<property name="interceptorNames">
<list>
<value>moocBeforeAdvice</value>
<value>moocAfterReturningAdvice</value>
<value>moocMethodInterceptor</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean>
Proxying class
CGLIB 代理的工作原理是在运行时生成目标类的子类,Spring 配置这个生成的子类委托方法调用到原来的目标
子类是用 Decorator 模式,织入通知
CGLIB 的代理对用户是透明的,需要注意:
- final 方法不能被通知,因为它们不能被覆盖
- 不用把 CGLIB 添加到 classpath 中,在 Spring 3.2 之后 CGLIB 被重新包装并包含在 Spring 核心的JAR (即基于 CGLIB 的 AOP 就像 JDK 动态代理一样“开箱即用”)
global advisor
用 * 做通配,匹配所有拦截器加入通知链(实现了 MethodInterceptor 接口的)
示例:
moocMethodInterceptor 可以修改为 mooc*:
<property name="interceptorNames">
<list>
<value>moocBeforeAdvice</value>
<value>moocAfterReturningAdvice</value>
<!--<value>moocMethodInterceptor</value>-->
<value>mooc*</value>
<value>moocThrowsAdvice</value>
</list>
</property>
简化的 proxy 定义
使用父子 bean 定义以及内部 bean 定义,可能会带来更清洁和更简洁的代理定义(抽象属性标记父 bean 定义为 abstract,这样它不能被实例化)
示例:
修改配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="moocBeforeAdvice" class="com.karonda.aop.api.MoocBeforeAdvice"></bean>
<bean id="moocAfterReturningAdvice" class="com.karonda.aop.api.MoocAfterReturningAdvice"></bean>
<bean id="moocMethodInterceptor" class="com.karonda.aop.api.MoocMethodInterceptor"></bean>
<bean id="moocThrowsAdvice" class="com.karonda.aop.api.MoocThrowsAdvice"></bean>
<bean id="baseProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"
lazy-init="true" abstract="true"></bean>
<bean id="bizLogicImpl" parent="baseProxyBean">
<property name="target">
<bean class="com.karonda.aop.api.BizLogicImpl"></bean>
</property>
<property name="proxyInterfaces">
<value>com.karonda.aop.api.BizLogic</value>
</property>
<property name="interceptorNames">
<list>
<value>moocBeforeAdvice</value>
<value>moocAfterReturningAdvice</value>
<value>moocMethodInterceptor</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean>
</beans>
使用 ProxyFactory
使用 Spring AOP 而不必依赖于 Spring IoC:
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface)factory.getProxy();
大多数情况下最佳实践是使用 IoC 容器创建 AOP 代理
虽然可以硬编码方式实现,但是 Spring 推荐使用配置或注解方式实现
使用 "auto-proxy"
Spring 也允许使用”自动代理“的 bean 定义,它可以自动代理选定的 bean,这是建立在 Spring 的 "bean post processor" 功能基础上的(在加载 bean 的时候就可以修改)
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk"/>
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
</bean>
使用 DefaultAdvisorAutoPorxyCreator,当前 IoC 容器中自动应用,不用显示声明引用 advisor 的 bean 定义
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoPorxyCreator"/>
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>
<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>
<bean id="bussinessObject1" class="com.mycompany.BusinessObject1">
<!-- 可以省略 Properties 定义-->
</bean>
源码:learning-spring
学习 Spring (十六) AOP API的更多相关文章
- 学习 Spring (十二) AOP 基本概念及特点
Spring入门篇 学习笔记 AOP: Aspect Oriented Programming, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 主要功能是:日志记录.性能统计.安全控 ...
- python3.4学习笔记(十六) windows下面安装easy_install和pip教程
python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...
- 201671010140. 2016-2017-2 《Java程序设计》java学习第十六周
java学习第十六周-并发 本周,学习了Java中线程,并发的知识,在老师的带领下,进行了对知识的理解学习,以及对实验的运行讲解,对这一块内容掌握的还可以,在自主编程中,也能够完成.线, ...
- 学习笔记:CentOS7学习之十六:LVM管理和ssm存储管理器使用
目录 学习笔记:CentOS7学习之十六:LVM管理和ssm存储管理器使用 16.1 LVM的工作原理 16.1.1 LVM常用术语 16.1.2 LVM优点 16.2 创建LVM的基本步骤 16.2 ...
- 风炫安全WEB安全学习第二十六节课 XSS常见绕过防御技巧
风炫安全WEB安全学习第二十六节课 XSS常见绕过防御技巧 XSS绕过-过滤-编码 核心思想 后台过滤了特殊字符,比如说
- 风炫安全Web安全学习第十六节课 高权限sql注入getshell
风炫安全Web安全学习第十六节课 高权限sql注入getshell sql高权限getshell 前提条件: 需要知道目标网站绝对路径 目录具有写的权限 需要当前数据库用户开启了secure_file ...
- Java开发学习(三十六)----SpringBoot三种配置文件解析
一. 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时 ...
- (C/C++学习笔记) 十六. 预处理
十六. 预处理 ● 关键字typeof 作用: 为一个已有的数据类型起一个或多个别名(alias), 从而增加了代码的可读性. typedef known_type_name new_type_nam ...
- Java开发学习(十六)----AOP切入点表达式及五种通知类型解析
一.AOP切入点表达式 对于AOP中切入点表达式,总共有三个大的方面,分别是语法格式.通配符和书写技巧. 1.1 语法格式 首先我们先要明确两个概念: 切入点:要进行增强的方法 切入点表达式:要进行增 ...
随机推荐
- linux内存源码分析 - 零散知识点
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 直接内存回收中的等待队列 内存回收详解见linux内存源码分析 - 内存回收(整体流程),在直接内存回收过程中, ...
- 震惊!!!python可以用中文来写代码
python可以用中文来写代码 说明: 偶尔间试了一下,python可以用中文来写代码,除了一些python内置函数,和运算符不能用中文外,其它的比如新定义的类名.函数名.变量名,甚至是函数间传的参数 ...
- for 循环 以及基本的数据类型
for 循环: for 关键字 i 变量(此处可以更改 更改规则参考变量命名规则) in 关键字 可迭代对象 (想要循环谁就放谁,注意:数字除外 因为数字不可迭代) for 循环内可以进行任意操作,可 ...
- node express使用
var express = require('express'); var app = express(); app 对象具有以下的方法: 路由HTTP请求:app.METHOD和app.param. ...
- 1000/problem/A
传送门: [http://codeforces.com/contest/1000/problem/A] 题意: 一个比赛颁奖,要准备T-Shirt给获奖者,但有的去年获奖过,衣服尺寸可以不改,有的需要 ...
- Yii框架的增删改查总结分析
一.查询数据 1.findAll(根据一个条件查询一个集合) $admin=Admin::model()->findAll($condition,$params); $admin=Admin:: ...
- InvalidDataAccessResourceUsageException:mysql保留字引发的血案
org.springframework.dao.InvalidDataAccessResourceUsageException: could NOT EXECUTE statement; SQL [n ...
- PS调出清新淡雅外景女生背影照
首先,依然是前期事项. 这套图是八月份下午三点多在草地上拍的(好像标题的秋日欺骗了大众XD),阳光很烈,不过也因为这样,能拍出比较清新的蓝天.用的是腾龙70-200 2.8VC拍摄,长焦在拍这种空旷大 ...
- UITableView加载数据,没有数据,没有网络界面处理
https://blog.csdn.net/chmod_r_755/article/details/53231461 俗话说的好,傻逼的APP都是相似的,牛逼的APP各有各的牛逼...但是UITabl ...
- 学习mongoDB的一些感受(转自:http://blog.csdn.net/liusong0605/article/details/11581019)
曾经使用过mongoDB来保存文件,最一开始,只是想总结一下在开发中如何实现文件与mongoDB之间的交互.在此之前,并没有系统的了解过mongoDB,虽然知道我们用它来存储文件这些非结构化数据,但是 ...