【转载】面向切面编程(AOP)学习
看到这篇文章,学习一下:http://www.ciaoshen.com/2016/10/28/aop/
想理清一下从“动态代理”,到 “注释”,到“面向切面编程”这么一个技术演进的脉络。
只想讲清楚两个问题:
- 什么是面向切面编程?为什么要面向切面?
- 动态代理技术是怎么实现面向切面的?注释在其中扮演了什么角色?
提到另一篇文章:https://my.oschina.net/huangyong/blog/161338
目前最知名最强大的 Java 开源项目就是 AspectJ 了。Rod Johnson(老罗)写了一个叫做 Spring 框架,从此一炮走红,成为了 Spring 之父。
他在自己的 IOC 的基础之上,又实现了一套 AOP 的框架,后来仿佛发现自己越来越走进深渊里,在不能自拔的时候,有人建议他还是集成 AspectJ 吧,他在万般无奈之下才接受了该建议。于是,我们现在用得最多的想必就是 Spring + AspectJ 这种 AOP 框架了。
Spring AOP:抛出增强
程序报错,抛出异常了,一般的做法是打印到控制台或日志文件中,这样很多地方都得去处理,有没有一个一劳永逸的方法呢?那就是 Throws Advice(抛出增强),它确实很强:
@Component
public class GreetingImpl implements Greeting { @Override
public void sayHello(String name) {
System.out.println("Hello! " + name); throw new RuntimeException("Error"); // 故意抛出一个异常,看看异常信息能否被拦截到
}
}
下面是抛出增强类的代码:
@Component
public class GreetingThrowAdvice implements ThrowsAdvice { public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
System.out.println("---------- Throw Exception ----------");
System.out.println("Target Class: " + target.getClass().getName());
System.out.println("Method Name: " + method.getName());
System.out.println("Exception Message: " + e.getMessage());
System.out.println("-------------------------------------");
}
}
8. Spring AOP:引入增强
这个功能确实太棒了!但还有一个更厉害的增强。如果某个类实现了 A 接口,但没有实现 B 接口,那么该类可以调用 B 接口的方法吗?如果您没有看到下面的内容,一定不敢相信原来这是可行的!
以上提到的都是对方法的增强,那能否对类进行增强呢?用 AOP 的行话来讲,对方法的增强叫做 Weaving(织入),而对类的增强叫做 Introduction(引入)。而 Introduction Advice(引入增强)就是对类的功能增强,它也是 Spring AOP 提供的最后一种增强。
定义了一个新接口 Apology(道歉):
public interface Apology { void saySorry(String name);
}
借助 Spring 的引入增强。
@Component
public class GreetingIntroAdvice extends DelegatingIntroductionInterceptor implements Apology { @Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return super.invoke(invocation);
} @Override
public void saySorry(String name) {
System.out.println("Sorry! " + name);
}
}
如何配置
<?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"
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"> <context:component-scan base-package="aop.demo"/> <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="aop.demo.Apology"/> <!-- 需要动态实现的接口 -->
<property name="target" ref="greetingImpl"/> <!-- 目标类 -->
<property name="interceptorNames" value="greetingIntroAdvice"/> <!-- 引入增强 -->
<property name="proxyTargetClass" value="true"/> <!-- 代理目标类(默认为 false,代理接口) -->
</bean> </beans>
客户端代码
public class Client { public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml");
GreetingImpl greetingImpl = (GreetingImpl) context.getBean("greetingProxy");
// 注意:转型为目标类,而并非它的 Greeting 接口
greetingImpl.sayHello("Jack"); Apology apology = (Apology) greetingImpl;
// 将目标类强制向上转型为 Apology 接口(这是引入增强给我们带来的特性,也就是“接口动态实现”功能)
apology.saySorry("Jack");
}
}
以上的示例代码都已经下载:
/Users/baidu/Documents/Data/Work/Code/Demo_Code/aop_demo
下面这篇是续集:
https://my.oschina.net/huangyong/blog/161402
9. Spring AOP:切面
只需要拦截特定的方法就行了,没必要拦截所有的方法。借助了 AOP 的一个很重要的工具,Advisor(切面),来解决这个问题。它也是 AOP 中的核心。
这里提到这个“拦截匹配条件”在 AOP 中就叫做 Pointcut(切点),其实说白了就是一个基于表达式的拦截条件。
归纳一下,Advisor(切面)封装了 Advice(增强)与 Pointcut(切点 )。
下面要做的就是拦截这两个新增的方法,而对 sayHello() 方法不作拦截。
@Component
public class GreetingImpl implements Greeting { @Override
public void sayHello(String name) {
System.out.println("Hello! " + name);
} public void goodMorning(String name) {
System.out.println("Good Morning! " + name);
} public void goodNight(String name) {
System.out.println("Good Night! " + name);
}
}
Spring AOP 中,提供了许多切面类了,这些切面类就有基于正则表达式的切面类。
<?xml version="1.0" encoding="UTF-8"?>
<beans ..."> <context:component-scan base-package="aop.demo"/> <!-- 配置一个切面 -->
<bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="greetingAroundAdvice"/> <!-- 增强 -->
<property name="pattern" value="aop.demo.GreetingImpl.good.*"/> <!-- 切点(正则表达式) -->
</bean> <!-- 配置一个代理 -->
<bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="greetingImpl"/> <!-- 目标类 -->
<property name="interceptorNames" value="greetingAdvisor"/> <!-- 切面 -->
<property name="proxyTargetClass" value="true"/> <!-- 代理目标类 -->
</bean> </beans>
注意以上代理对象的配置中的 interceptorNames,它不再是一个增强,而是一个切面,因为已经将增强封装到该切面中了。此外,切面还定义了一个切点(正则表达式),其目的是为了只将满足切点匹配条件的方法进行拦截。
除了 RegexpMethodPointcutAdvisor 以外,在 Spring AOP 中还提供了几个切面类,比如:
DefaultPointcutAdvisor:默认切面(可扩展它来自定义切面) NameMatchMethodPointcutAdvisor:根据方法名称进行匹配的切面 StaticMethodMatcherPointcutAdvisor:用于匹配静态方法的切面
10. Spring AOP:自动代理(扫描 Bean 名称)
能否让 Spring 框架为我们自动生成代理呢?Spring AOP 提供了一个可根据 Bean 名称来自动生成代理的工具,它就是 BeanNameAutoProxyCreator。是这样配置的:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...> ... <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Impl"/> <!-- 只为后缀是“Impl”的 Bean 生成代理 -->
<property name="interceptorNames" value="greetingAroundAdvice"/> <!-- 增强 -->
<property name="optimize" value="true"/> <!-- 是否对代理生成策略进行优化 -->
</bean> </beans>
关于CGLib和JDK动态代理:
根据多年来实际项目经验得知:CGLib 创建代理的速度比较慢,但创建代理后运行的速度却非常快,而 JDK 动态代理正好相反。
如果在运行的时候不断地用 CGLib 去创建代理,系统的性能会大打折扣,所以建议一般在系统初始化的时候用 CGLib 去创建代理,
并放入 Spring 的 ApplicationContext 中以备后用。
11. Spring AOP:自动代理(扫描切面配置)
Spring AOP 基于切面也提供了一个自动代理生成器:DefaultAdvisorAutoProxyCreator。
<?xml version="1.0" encoding="UTF-8"?>
<beans ...> ... <bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="pattern" value="aop.demo.GreetingImpl.good.*"/>
<property name="advice" ref="greetingAroundAdvice"/>
</bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="optimize" value="true"/>
</bean> </beans>
这里无需再配置代理了,因为代理将会由 DefaultAdvisorAutoProxyCreator 自动生成。也就是说,这个类可以扫描所有的切面类,并为其自动生成代理。
12. Spring + AspectJ(基于注解:通过 AspectJ execution 表达式拦截方法)
@Aspect
@Component
public class GreetingAspect { @Around("execution(* aop.demo.GreetingImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
before();
Object result = pjp.proceed();
after();
return result;
} private void before() {
System.out.println("Before");
} private void after() {
System.out.println("After");
}
}
方法的参数中包括一个 ProceedingJoinPoint 对象,它在 AOP 中称为 Joinpoint(连接点),可以通过该对象获取方法的任何信息,例如:方法名、参数等。
总结如下图:
再来一张表格,总结一下各类增强类型所对应的解决方案:
增强类型 | 基于 AOP 接口 | 基于 @Aspect | 基于 <aop:config> |
Before Advice(前置增强) | MethodBeforeAdvice | @Before | <aop:before> |
AfterAdvice(后置增强) | AfterReturningAdvice | @After | <aop:after> |
AroundAdvice(环绕增强) | MethodInterceptor | @Around | <aop:around> |
ThrowsAdvice(抛出增强 | ThrowsAdvice | @AfterThrowing | <aop:after-throwing> |
IntroductionAdvice(引入增强) | DelegatingIntroductionInterceptor | @DeclareParents | <aop:declare-parents> |
最后给一张 UML 类图描述一下 Spring AOP 的整体架构:
以上,需要结合例子深入学习。
(完)
【转载】面向切面编程(AOP)学习的更多相关文章
- Spring学习手札(二)面向切面编程AOP
AOP理解 Aspect Oriented Program面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 但是,这种说法有些片面,因为在软件工程中,AOP的价值体现的并 ...
- Spring学习笔记:面向切面编程AOP(Aspect Oriented Programming)
一.面向切面编程AOP 目标:让我们可以“专心做事”,避免繁杂重复的功能编码 原理:将复杂的需求分解出不同方面,将公共功能集中解决 *****所谓面向切面编程,是一种通过预编译方式和运行期动态代理实现 ...
- Spring框架学习笔记(2)——面向切面编程AOP
介绍 概念 面向切面编程AOP与面向对象编程OOP有所不同,AOP不是对OOP的替换,而是对OOP的一种补充,AOP增强了OOP. 假设我们有几个业务代码,都调用了某个方法,按照OOP的思想,我们就会 ...
- Spring之控制反转——IoC、面向切面编程——AOP
控制反转——IoC 提出IoC的目的 为了解决对象之间的耦合度过高的问题,提出了IoC理论,用来实现对象之间的解耦. 什么是IoC IoC是Inversion of Control的缩写,译为控制 ...
- Spring框架系列(4) - 深入浅出Spring核心之面向切面编程(AOP)
在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式.@pd ...
- 设计模式之面向切面编程AOP
动态的将代码切入到指定的方法.指定位置上的编程思想就是面向切面的编程. 代码只有两种,一种是逻辑代码.另一种是非逻辑代码.逻辑代码就是实现功能的核心代码,非逻辑代码就是处理琐碎事务的代码,比如说获取连 ...
- 【串线篇】面向切面编程AOP
面向切面编程AOP 描述:将某段代码“动态”的切入到“指定方法”的“指定位置”进行运行的一种编程方式 (其底层就是Java的动态代理)spring对其做了简化书写 场景: 1).AOP加日志保存到数据 ...
- 04 Spring:01.Spring框架简介&&02.程序间耦合&&03.Spring的 IOC 和 DI&&08.面向切面编程 AOP&&10.Spring中事务控制
spring共四天 第一天:spring框架的概述以及spring中基于XML的IOC配置 第二天:spring中基于注解的IOC和ioc的案例 第三天:spring中的aop和基于XML以及注解的A ...
- [译]如何在ASP.NET Core中实现面向切面编程(AOP)
原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE 原文作者:ZANID HAYTAM 译文地址:如何在ASP.NET Cor ...
随机推荐
- excludeFromRecents标签
Android:excludeFromRecents控制在不在recent列表中显示. true时不显示:false显示,默认. 运行如下activity后,不会显示在recent列表中. <a ...
- Python 基础(一)
本章内容 1.编译和解释型语言的区别 2.Python的解释器 3.pyc文件 4.运行环境 5.变量 6.数据类型 7.字符编码 8.三元运算 编译和解释型语言的区别 编译器是把源程序的每一条语句都 ...
- bzoj3039 joyoi1939 玉蟾宫 悬线法
悬线法 #include <iostream> #include <cstring> #include <cstdio> using namespace std; ...
- luogu3808 luogu3796 AC自动机(简单版) AC自动机(加强版)
纪念一下我一晚上写了八遍AC自动机 这是加强版的: #include <iostream> #include <cstring> #include <cstdio> ...
- 转:获取GridView中RowCommand的当前索引行
获取GridView中RowCommand的当前索引行 前台添加一模版列,里面添加一个LinkButton 前台 (如果在后台代码中用e.CommandArgument取值的话前台代码就必须在按钮中设 ...
- [已解决] wordpress 修改 permalink 后 页面 404 问题
功能说明 为了利于SEO优化,我们需要将地址设置为永久链接,在层级不要太深的情况下实现伪静态页面的目的,例如wordpress 默认页面地址为: https://www.ryanzoe.top/?p= ...
- ip 核生成 rom 及questasim仿真时需要注意的问题
IP 核生成 ROM 步骤1:Tools --> MegaWizard Plug-In Manager 步骤2:Create a new custom megafuction variation ...
- LinearLayout 滚动条
比如文件列表, 显示不下,需要出滚动条:外面加一个scrollview 直接上代码: <?xml version="1.0" encoding="utf-8&quo ...
- phpcms调用一个指定的栏目
很多初学者在学习phpcms的时候在模板中都是静态的写导航的url,那样在后期维护的时候可是个巨大的工程啊,所以我在学的时候就想起织梦是能指定调用栏目的url,借鉴了这一想法, {$CATEGORY[ ...
- oracle中的dual表
dual表是和Oracle数据字典一起创建的.它实际上只包含dummy这一个column,并且只有一条记录,这条记录的值是X. X dual表的owner是SYS,但所有用户都可以访问它.Althou ...