7 一起来看AOP

2009年8月,《一起来看流星雨》开播。

2009年9月,《Spring揭秘》出版。

7.1 AOP核心概念

AOP

AOP全称为Aspect-Oriented Programming,面向方面编程。

使用AOP,我们可以对类似于日志记录和安全检查等系统需求进行模块化的组织。

AOL

与OOP需要相应的语言支持一样,AOP也需要某种语言以帮助实现相应的概念实体,我们将实现AOP的语言称为AOL,Aspect-Oriented Language。

常见的一种AOL是扩展自Java的AspectJ。

Aspect:切面

任何一个软件系统,日志记录、安全检查、事务管理等系统需求都像一把刀“恶狠狠”地横切到我们组织良好的各个业务功能模块之上,以AOP的行话来说,这些系统需求就是系统中的 横切关注点(cross-cutting concern)。AOP引入了Aspect的概念,用来以模块化的形式对系统中的横切关注点进行组织和封装。Aspect之于AOP,相当于Class之于OOP。

weave:织入

AOL实现的AOP各个概念实体,最终都需要某种方式集成到系统实现语言所实现的OOP实体组件中,所以,系统实现语言通常称为系统中使用的AOL的“寄生语言”,而将AO组件集成到OOP组件的过程,在AOP中称为织入(weave)过程。

7.2 AOP实现机制

动态代理

JDK提供了动态代理(Dynamic Proxy)机制,可以在运行期间,为相应的接口动态生成对应的代理对象。

我们可以将横切关注点逻辑封装到动态代理的InvocationHandler中,然后在系统运行期间,根据横切关注点需要织入的模块位置,将横切逻辑织入到相应的代理类中。以动态代理类为载体的横切逻辑就可以与系统其他实现模块一起工作了。

使用这种机制,所有需要织入横切关注点逻辑的模块类都得实现相应的接口,因为动态代理机制只针对接口有效。

Spring AOP默认情况下采用这种机制实现AOP机能。

动态字节码增强

通常的class文件都是从Java源代码文件使用Javac编译器编译而成的,但只要符含Java class规范,我们也可以使用ASM或者CGLIB等Java工具库,在程序运行期间,动态构建字节码的class文件。

在此前提下,我们可以为需要织入横切逻辑的模块类在运行期间,通过动态字节码增强技术,为这些系统模块类生成相应的子类,而将横切逻辑加到这些子类中,让应用程序在执行期间使用的是这些动态生成的子类,从而达到将横切逻辑织入系统的目的。

不过,如果需要扩展的类以及类中的实例方法等声明为final的话,则无法对其进行子类化的扩展。

Spring AOP在无法采用动态代理机制进行AOP功能扩展的时候,会使用CGLIB库的动态字节码增强支持来实现AOP的功能扩展。

自定义类加载器

所有的Java程序的class都要通过相应的类加载器加载到Java虚拟机之后才可以运行。我们可以通过自定义类加载器的方式完成横切逻辑到系统的织入,自定义类加载器通过读取外部文件规定的织入规则和必要信息,在加载class文件期间就可以将横切逻辑添加到系统模块类的现有逻辑中,然后将改动后的class交给Java虚拟机运行。

JBoss AOP是采用自定义类加载器的方式实现的。

AOL扩展

我们可以使用扩展过的AOL,实现任何AOP概念实体甚至OOP概念实体。

AOL扩展是最强大的一种方式,但代价是你需要重新学习一门扩展了旧有的语言的AOL。

7.3 AOP基本概念

Joinpoint

Jointpoint是可以进行织入操作的系统执行点。常见的Joinpoint类型有:

方法调用、方法执行、构造方法调用、构造方法执行、字段设置、字段获取、异常处理执行、类初始化。

Pointcut

Pointcut是Jointpoint的表述方式。有点儿像引用。

Pointcut的表述方式有:直接指定Joinpoint所在方法名称、正则表达式、使用特定的Pointcut表述语言。使用得比较普遍的是正则表达式。

Advice

Advice是单一横切关注点逻辑的载体,它代表将会织入到Jointpoint的横切逻辑。如果将Aspect比作OOP中的Class,那么Advice就相当于Class中的Method。

按照Advice在Joinpoint位置执行时机的差异或者完成功能的不同,Advice可以分为:

1) Before Advice:在Joinpoint指定位置之前执行的Advice。

2) After Advice:在Joinpoint指定位置之后执行的Advice。又可以分为:

After returning Advice:只有在当前Joinpoint处执行流程正常完成后才执行。

After throwing Advice:只有在当前Jointpoint处执行过程中抛出异常情况下才执行。

After Advice:不管Joinpoint处执行流程是正常终了还是抛出异常都会执行。

3) Around Advice:对附加其上的Joinpoint进行包裹,可以在Joinpoint之前和之后都指定相应的逻辑,甚至于中断或者忽略Joinpoint处原来程序流程的执行。我们常使用的Filter功能就是Around Advice的一种体现。

4) Introduction:可以为原有的对象添加新的特性或者行为。与之前的几种Advice类型不同,Introduction不是根据横切逻辑在Joinpoint处的执行时机来区分的,而是根据它可以完成的功能而区别于其他Advice类型。

Aspect

Aspect是对系统中的横切关注点逻辑进行模块化封装的AOP概念实体。

通常情况下,Aspect可以包含多个Pointcut以及相关Advice定义。

Weaver

Weaver是织入器,职责是完成横切关注点逻辑到系统的最终织入。

Java平台各AOP实现的织入器形式并不固定:

Spring AOP使用一组类来完成最终的织入操作,ProxyFactory类是Spring AOP中最通用的织入器。

AspectJ有专门的编译器,即ajc,来完成织入操作,所以ajc就是AspectJ的织入器。

JBoss AOP采用自定义的类加载器来完成最终织入,这个自定义的类加载器就是它的织入器。

目标对象

符合Pointcut指定的条件,将被织入横切逻辑的对象。

8 Spring AOP概述及其实现机制

8.1 Spring AOP概述

Spring AOP的设计哲学简单而强大,它不打算将所有的AOP需求全部囊括在内,而是要以有限的20%的AOP支持,来满足80%的AOP需求。如果觉得Spring AOP无法满足你需要的那80%之外的需求,Spring AOP对AspectJ也提供了很好的集成,就求助于AspectJ好了。

Spring AOP的AOL是Java。

Spring AOP的实现主要是使用动态代理机制。

8.2 Spring AOP的实现机制

代理模式

代理模式通常涉及4种角色:

ISubject:对被访问资源的抽象。

SubjectImpl:被访问资源的具体实现类。

SubjectProxy:被访问资源的代理实现类。

Client:对访问者的抽象。

SubjectImpl和SubjectProxy都实现了ISubject,而SubjectProxy内部持有SubjectImpl的引用。当Client通过request()请求服务的时候,SubjectProxy将请求转发给SubjectImpl。从这个角度来说,SubjectProxy倒是有多此一举之嫌了。不过,SubjectProxy可以在转发请求的同时,对请求添加更多的访问限制,Spring AOP本质上就是采用这种代理机制实现的。

动态代理

使用静态代理机制有什么问题?

针对不一样的目标对象类型,我们要为其单独实现一个代理对象。而实际上,这些代理对象要添加的横切逻辑是一样的。

使用动态代理机制,我们可以为指定的缺口在系统运行期间动态地生成代理对象。

动态代理机制的实现主要由一个类和一个接口组成,即java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。

代码示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.LocalTime;public class Test {public static void main(String[] args) {ISubject subjectProxy = (ISubject) Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[] { ISubject.class }, new RequestCtrlInvocationHandler(new SubjectImpl()));subjectProxy.request();}
}interface ISubject {void request();
}class SubjectImpl implements ISubject {@Overridepublic void request() {System.out.println("ok");}
}class RequestCtrlInvocationHandler implements InvocationHandler {private Object target;public RequestCtrlInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equals("request")) {LocalTime startTime = LocalTime.MIN;LocalTime endTime = LocalTime.of(7, 59, 59);LocalTime currentTime = LocalTime.now();if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) {System.out.println("service is not available now.");return null;}return method.invoke(target, args);}return null;}
}

动态代理的缺点是只能对实现了interface的类使用。默认情况下,如果Spring AOP发现目标实现了相应interface,则采用动态代理机制为其生成代理对象实例,而如果目标对象没有实现任何interface,Spring AOP会尝试使用一个称为CGLIB(Code Generation Library)的开源的动态字节码生成类库,为目标对象生成动态的代理对象实例。

动态字节码生成

使用动态字节码生成技术扩展对象行为的原理是,我们可以对目标对象进行继承扩展,为其生成相应的子类,而子类可以通过覆写来扩展父类的行为,只要将横切逻辑的实现放到子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式相同的效果了。

但是,使用继承的方式来扩展对象定义,也不能像静态代理模式那样,为每个不同类型的目标对象都单独创建相应的扩展子类。所以我们要借助于CGLIB这样的动态字节码生成库,在系统运行期间动态地为目标对象生成相应的扩展子类。

要对目标类进行扩展,首先需要实现一个org.springframework.cglib.proxy.Callback接口,不过更多的时候,我们会直接使用扩展了Callback接口的org.springframework.cglib.proxy.MethodInterceptor接口。

代码示例:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;
import java.time.LocalTime;public class Test {public static void main(String[] args) {// 通过CGLIB的Enhancer为目标对象动态地生成一个子类Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Requestable.class);enhancer.setCallback(new RequestCtrlCallback());Requestable proxy = (Requestable) enhancer.create();proxy.request();}
}class Requestable {public void request() {System.out.println("rq in Requestable without implementint any interface");}
}class RequestCtrlCallback implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {if (method.getName().equals("request")) {LocalTime startTime = LocalTime.MIN;LocalTime endTime = LocalTime.of(7, 59, 59);LocalTime currentTime = LocalTime.now();if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) {System.out.println("service is not available now.");return null;}return proxy.invokeSuper(obj, args);}return null;}
}

使用CGLIB对类进行扩展的唯一限制是无法对final方法进行覆写。

9 Spring AOP一世

这章的方法已经过时了,大概看下就行了。

9.1 Spring AOP中的Joinpoint

在Spring AOP中,只支持方法执行(Method Execution)类型的Joinpoint。

9.2 Spring AOP中的Pointcut

org.springframework.aop.Pointcut:

public interface Pointcut {Pointcut TRUE = TruePointcut.INSTANCE;ClassFilter getClassFilter();MethodMatcher getMethodMatcher();
}

ClassFilter:

public interface ClassFilter {ClassFilter TRUE = TrueClassFilter.INSTANCE;boolean matches(Class<?> var1);
}

MethodMatcher:

public interface MethodMatcher {MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;boolean matches(Method var1, Class<?> var2);boolean isRuntime();boolean matches(Method var1, Class<?> var2, Object... var3);
}

NameMatchMethodPointcut

名称匹配。

NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedName("matches");
// 匹配多个方法名
pointcut.setMappedNames(new String[]{ "matches", "isRuntime" });
// 使用 * 进行模糊匹配
pointcut.setMappedNames(new String[]{ "*match*", "ma*es" });

AbstractRegexpMethodPointcut实现类

正则匹配。

以抽象类AbstractRegexpMethodPointcut为统帅,其下设JdkRegexpMethodPointcut和Perl5RegexpMethodPointcut两种具体实现。

JdkRegexpMethodPointcut的简单使用示例如下:

JdkregexpMethodPointcut pointcut = new JdkregexpMethodPointcut();
pointcut.setPattern(".*match.*");
pointcut.setPatterns(new String[]{ ".*match.*", ".*matches" });

AnnotationMatchingPointcut

注解匹配。

两个注解:

@ClassLevelAnnotation:用于类层次。

@MethodLevelAnnotation:用于方法层次。

注解的匹配规则:

1) Pointcut指定类级别的注解,匹配标注了@ClassLevelAnnotation的类中的所有方法。

AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class);
// 等价于
// AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class);

2) Pointcut指定方法级别的注解,仅匹配标注了@MethodLevelAnnotation的方法。

AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MethodLevelAnnotation.class);

3) Pointcut同时指定类级别和方法级别的注解,仅匹配标注了@ClassLevelAnnotation的类中同时标注了@MethodLevelAnnotation的方法。

AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class, MethodLevelAnnotation.class);

ComposablePointcut

Pointcut提供了逻辑运算功能,ComposablePointcut是Spring AOP提供的Pointcut逻辑运算的实现。

9.3 Spring AOP中的Advice

Spring AOP加入了开源组织AOP Alliance,目的在于标准化AOP的使用,促进各个AOP实现产品之间的可交互性。Spring中的Advice实现遵循AOP Alliance规定的接口org.aopalliance.aop.Advice。

在Spring中,Advice按照其自身实例(instance)能否在目标对象类的所有实例中共享这一标准,可以划分为两大类:per-class类型和per-instance类型。

per-class类型的Advice

1) Before Advice。

Before Advice实现的横切逻辑将在相应的Joinpoint之前执行。

要在Spring中实现Before Advice,我们通常只需要实现org.springframework.aop.MethodBeforeAdvice接口即可。

2) ThrowsAdvice。

ThrowsAdvice通常用于对系统中特定的异常情况进行监控,以统一的方式对所发生的异常进行处理。

我们通常只需要实现org.springframework.aop.ThrowsAdvide接口即可。

3) AfterReturningAdvice。

方法正常返回的情况下,执行AfterReturningAdvice。

实现org.springframework.aop.AfterReturningAdvice接口即可。

虽然AfterReturningAdvice可以访问到方法的返回值,但不能对其进行修改。

4) Around Advice。

强大。之前提到的几种Advice能完成的事情,都不在话下。

Spring中没有直接定义对应Around Advice的实现接口,而是直接采用AOP Alliance的标准接口org.aopalliance.intercept.MethodInterceptor。

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

per-instance类型的Advice

在Spring AOP中,Introduction是唯一一种per-instance型Advice。

Introduction可以在不改动目标类定义的情况下,为目标类添加新的属性以及行为。

实现 org.springframework.aop.IntroductionInterceptor。

9.4 Spring AOP中的Aspect

Advisor代表Spring中的Aspect。

Advisor通常只持有一个Pointcut和一个Advice。

Advisor分为两个分支,一个分支以org.springframework.aop.PointcutAdvisor为首,另一个分支则以org.springframework.aop.IntroductionAdvisor为首。

PointcutAdvisor分支

PointcutAdvisor常用的实现有:

1) DefaultPointcutAdvisor。

最通用的PointcutAdvisor实现,除了不能为其指定Introduction类型的Advice,剩下的任何类型的Pointcut、任何类型的Advice都可以通过DefaultPointcutAdvisor使用。

2) NameMatchMethodPointcutAdvisor。

与DefaultPointcutAdvisor相比,限定了可以使用的Pointcut类型为NameMatchMethodPointcut。

3) RegexpMethodPointcutAdvisor。

与DefaultPointcutAdvisor相比,限定了可以使用的Pointcut类型为AbstractRegexpMethodPointcut。

IntroductionAdvisor分支

IntroductionAdvisor与PointcutAdvisor最本质的区别在于,IntroductionAdvisor只能应用于类级别的拦截,只能使用Introduction型的Advice。

IntroductionAdvisor只有一个默认实现DefaultIntroductionAdvisor。

9.5 Spring AOP的织入

在Spring AOP中,可以使用类org.springframework.aop.framework.ProxyFactory作为织入器。

ProxyFactory weaver = new ProxyFactory(yourTargetObject);
// 或者
// ProxyFactory weaver = new ProxyFactory();
// weaver.setTarget(task);
Advisor advisor = ...;
weaver.addAdvisor(advisor);
Object projectObject = weaver.getProxy();
// 使用projectObject

代码示例:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
import org.springframework.util.StopWatch;public class Test {public static void main(String[] args) {ITask task = new MockTask();ProxyFactory weaver = new ProxyFactory(task);// 可有可无: weaver.setInterfaces(new Class[] { ITask.class });NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();advisor.setMappedName("execute");advisor.setAdvice(new PerformanceMethodInterceptor());weaver.addAdvisor(advisor);ITask proxyObject = (MockTask) weaver.getProxy();proxyObject.execute();}
}interface ITask {void execute();
}// 若不实现任何接口, 则采用CGLIB动态字节码生成
// 只需要把main方法中所有的ITask改成MockTask
class MockTask implements ITask {@Overridepublic void execute() {System.out.println("task executed");}
}class PerformanceMethodInterceptor implements MethodInterceptor {private final Log logger = LogFactory.getLog(this.getClass());@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {StopWatch watch = new StopWatch();try {watch.start();return invocation.proceed();} finally {watch.stop();if (logger.isInfoEnabled()) {logger.info(watch.toString());}}}
}

10 Spring AOP二世

10.1 @AspectJ形式的Spring AOP

编程方式织入

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.util.StopWatch;public class Test {public static void main(String[] args) {AspectJProxyFactory weaver = new AspectJProxyFactory();weaver.setProxyTargetClass(true);weaver.setTarget(new Foo());weaver.addAspect(PerformanceTraceAspect.class);Foo proxy = weaver.getProxy();proxy.method1();proxy.method2();}
}class Foo {public void method1() {System.out.println("method1 execution");}public void method2() {System.out.println("method2 execution");}
}@Aspect
class PerformanceTraceAspect {private final Log logger = LogFactory.getLog(PerformanceTraceAspect.class);@Pointcut("execution(public void *.method1()) || execution(public void *.method2())")public void pointcutName() {}@Around("pointcutName()")public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {StopWatch watch = new StopWatch();try {watch.start();return joinPoint.proceed();} finally {watch.stop();if (logger.isInfoEnabled()) {logger.info("PT in method[" + joinPoint.getSignature().getName() + "] >>>> "+ watch.toString());}}}
}

通过自动代理织入

配置:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><!-- 这里可以基于Schema简化 --><bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"><property name="proxyTargetClass" value="true"></property></bean><bean id="performanceAspect" class="com.test.PerformanceTraceAspect"/><bean id="target" class="com.test.Foo"/>
</beans>

测试代码:

ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");
Foo proxy = (Foo) ctx.getBean("target");
proxy.method1();
proxy.method2();

@AspectJ形式的Pointcut

@AspectJ形式的Pointcut声明包含两个部分:

Pointcut Expression:Pointcut表达式。指定具体的匹配模式。

Pointcut Signature:Pointcut签名。是一个方法定义,返回类型必须是void。

Pointcut Expression匹配模式:

1) execution。

Spring AOP仅支持方法执行类型的Joinpoint。使用它,将帮助我们匹配拥有指定方法签名的JoinPoint。

假设我们拥有以下类定义:

public class Foo {public void doSomething(String arg) { }
}

那么可以使用如下Pointcut表达式来匹配Foo.doSomething方法:

execution(public void Foo.doSomething(String))

其中方法的返回类型、方法名以及参数的匹配模式是必须指定的,其他部分可以省略:

execution(void doSomething(String))

在execution表达式中还可以使用两种通配符:* 和 .. 。

2) within。

within标识符只接受类型声明,它会匹配指定类型下所有的Joinpoint。不过,因为Spring AOP只支持方法级别的Joinpoint,所以在使用within指定某个类后,它将匹配指定类所声明的所有方法执行。

3) this和target。

this指代目标对象的代理对象。

target指代目标对象。

4) args。

帮助我们捕捉拥有指定参数类型、指定参数数量的方法级Joinpoint,而不管该方法在什么类型中被声明。

5) @within。

指定某种类型,只要对象标注了该类型的注解,将匹配该对象内部所有Joinpoint。

6) @target。

如果目标对象拥有@target标识符所指定的注解类型,那么目标对象中所有的方法级别JoinPoint将被匹配。

7) @args。

尝试检查当前方法级的Joinpoint的方法参数类型,如果该次传入的参数类型拥有@args所指定的注解,当前Joinpoint将被匹配,否则将不会被匹配。

8) @annotation。

尝试检查系统中所有对象的所有方法级别Joinpoint。如果被检测的方法标注有@annotation标识符所指定的注解类型,那么当前方法所在的Joinpoint将被匹配。

@AspectJ形式的Advice

用于标注对应Advice定义方法的注解包括:

@Before。

@AfterReturning。

@AfterThrowing。

@After。

@Around。标注拦截器类型的Advice。

@DeclareParents。

10.2 基于Schema的AOP

Spring框架从1.x版本升级到2.x版本后,提倡的容器配置方式从基于DTD的XML基于Schema的XML,进一步提高了配置方式的灵活性和可扩展性。

命名空间声明

要使用基于Schema的AOP,IoC容器的配置文件应该使用基于Schema的XML,同时在文件头中增加针对AOP的命名空间声明:

<?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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd"><!-- 相关bean定义 --></beans>

<aop:config>

添加了aop的命名空间后,就可以使用aop命名空间的各种配置元素了。

基于Schema的AOP配置方式,针对Pointcut、Advisor以及Aspect等概念提供了独立的配置元素,所有这些配置元素都包含在统一的配置元素<aop:config>中。

<aop:config>只有一个属性proxy-target-class,对应ProxyConfig中的proxyTargetClass属性,使我们可以控制是使用基于接口的代理还是基于类的代理。

<aop:config>内部可以有三个子元素,分别是<aop:pointcut>、<aop:advisor>和<aop:aspect>。它们必须按顺序进行配置。

<aop:config proxy-target-class="false"><aop:pointcut/><aop:advisor/><aop:aspect></aop:aspect>
</aop:config>

代码示例

Java代码:

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StopWatch;public class Test {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");MyTask task = (MyTask) ctx.getBean("task");task.doSth();}
}class MyTask {public void doSth() {System.out.println("doSth");}
}class SchemaBaseAspect {private final Logger logger = Logger.getLogger(SchemaBaseAspect.class);public void doBefore(JoinPoint jp) {if (logger.isInfoEnabled()) {logger.info("before method[" + jp.getSignature().getName() + "] execution.");}}public void doAfterReturning(JoinPoint jp) {if (logger.isInfoEnabled()) {logger.info("method[" + jp.getSignature().getName() + "] completed successfully.");}}public void doAfterThrowing(RuntimeException e) {logger.error(ExceptionUtils.getFullStackTrace(e));}public void doAfter() {logger.warn("release system resources, etc.");}public Object doProfile(ProceedingJoinPoint pjp) throws Throwable {StopWatch watch = new StopWatch();try {watch.start();return pjp.proceed();} finally {watch.stop();if (logger.isInfoEnabled()) {logger.info(watch);}}}
}

配置:

<?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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd"><aop:config><aop:aspect id="myAspect" ref="schemaBaseAspect" order="2"><aop:pointcut id="privatePointcut" expression="execution(public void *.doSth())"/><aop:before pointcut-ref="privatePointcut" method="doBefore"/><aop:after-returning pointcut-ref="privatePointcut" method="doAfterReturning"/><aop:after-throwing pointcut-ref="privatePointcut" method="doAfterThrowing" throwing="e"/><aop:after pointcut-ref="privatePointcut" method="doAfter"/><aop:around pointcut-ref="privatePointcut" method="doProfile"/></aop:aspect></aop:config><bean id="schemaBaseAspect" class="com.test.SchemaBaseAspect"/><bean id="task" class="com.test.MyTask"/>
</beans>

输出:

[01-17 14:33:58] [INFO] [com.test.SchemaBaseAspect:30] before method[doSth] execution.
doSth
[01-17 14:33:58] [INFO] [com.test.SchemaBaseAspect:56] StopWatch '': running time = 11791700 ns; [] took 11791700 ns = 100%
[01-17 14:33:58] [WARN] [com.test.SchemaBaseAspect:45] release system resources, etc.
[01-17 14:33:58] [INFO] [com.test.SchemaBaseAspect:36] method[doSth] completed successfully.

11 AOP应用案例

11.1 异常处理

Java中的异常分为两类:

1) unchecked exception。

java.lang.Error和java.lang.RuntimeException及其子类。编译器不会对这些类型的异常进行编译期检查。

unchecked exception通常对应系统中的严重异常情况,这些情况应用程序一般无法恢复,比如数据库挂掉、网线连接断开、服务器崩溃等。

2) checked exception。

java.lang.Exception及其子类,除去java.lang.RuntimeException分支。编译器会在编译期间对这些异常类型进行检查。

checked exception通常用于表明系统中的某些罕见的非正常状态,要求调用发对这些非正常状态进行处理。通常是可恢复的。

unchecked exception实际上可以做的事很少,通常就是记录日志、通知相应人员。所以,这些相同的逻辑实现可以归并于一处进行处理,而不是让它们散落在系统的各处,也就是说,对于unchecked exception来说,它实际上就是一种横切关注点(cross-cutting concern)。

鉴于此,我们完全可以实现一个对应unchecked exception处理的Aspect,让其对系统中的所有可能的unchecked exception进行统一的处理。这个Aspect我们一般称为Fault Barrier。

11.2 安全检查

Spring Security。

11.3 缓存

Spring Cache。

《Spring揭秘》读书笔记 2:Spring AOP相关推荐

  1. Spring揭秘 读书笔记 三 bean的scope与FactoryBean

    本书可作为王富强所著<<Spring揭秘>>一书的读书笔记  第四章 BeanFactory的xml之旅 bean的scope scope有时被翻译为"作用域&quo ...

  2. 读书笔记《Spring Boot+Vue全栈开发实战》(下)

    本书将带你全面了解Spring Boot基础与实践,带领读者一步步进入 Spring Boot 的世界. 前言 第九章 Spring Boot缓存 第十章 Spring Boot安全管理 第十一章 S ...

  3. Spring Cloud学习笔记—网关Spring Cloud Gateway官网教程实操练习

    Spring Cloud学习笔记-网关Spring Cloud Gateway官网教程实操练习 1.Spring Cloud Gateway介绍 2.在Spring Tool Suite4或者IDEA ...

  4. springaop事务逻辑原理_太狠了!阿里大牛手写的Spring核心面试笔记:IOC+AOP+MVC+事务...

    Spring作为现在最流行的java 开发技术,其内部源码设计非常优秀.如果你不会Spring,那么很可能面试官会让你回家等通知. Spring是什么? 有一个工地,几百号人在用铁锹铲子挖坑. 如果开 ...

  5. 读书笔记《Spring Boot实战 —— Java EE 开发的颠覆者》

    Spring框架是轻量级的企业级开发一站式解决方案 Spring使用简单的POJO Plain Old Java Object 无限制的普通Java对象 Spring Framework Runtim ...

  6. Spring读书笔记-----使用Spring容器(二)

    一.使用ApplicationContext 前面介绍了,我们一般不会使用BeanFactory实例作为Spring容器,而是使用ApplicationContext实例作为容器,它增强了BeanFa ...

  7. Spring框架学习笔记(1) ---[spring框架概念 , 初步上手使用Spring , 控制反转 依赖注入初步理解 ]

    spring官网 -->spring官网 spring5.3.12–>spring-framework 在线文档 --> Spring 5.3.12 文章目录 1.Spring概论 ...

  8. 读书笔记:Spring in action 第2章

    第二章 装配Bean 2.1Spring配置的可选方案 2.2自动化装配bean 2.2.1创建可被发现的bean 2.2.2 为组件扫描的bean命名 2.2.3设置组件扫描的基础包 2.2.4 通 ...

  9. Spring 读书笔记-----使用Spring容器(一)

    Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口.他们都可代表Spring容器,Spr ...

  10. Spring实战读书笔记 高级装配(1)

    一.条件化装配 1. 当你希望你的bean在特殊条件下才能装配时,比如在声明了特定的bean时,或者配置了特定的环境变量的时候.那么就可以使用 @Conditional注解,可以用在 @Bean注解下 ...

最新文章

  1. PL/SQL学习笔记-常量变量及数据类型初步
  2. Basic005. Intro to statistics basic terms统计名词介绍
  3. 找不到具有绑定 MetadataExchangeHttpBinding 的终结点的与方案 http 匹配的基址。注册的基址方案是 [https]...
  4. 2018年4月22日笔记
  5. asp.net页面处理过程文章整理
  6. Qt IFW框架简介
  7. JVM上的高并发HTTP客户端
  8. linux 信号_Linux信号量(1)-SYSTEM V
  9. Facebook去年从中国获50亿美元广告收入 占营收10%
  10. adb shell 运行时报错“adb server version (26) doesn‘t match this client (39); killing...“的解决方案
  11. iOS 的TextView的常规用法
  12. R语言数据最大最小归一化
  13. 网络系统设计综合布线方案
  14. IP地址和MAC地址简介
  15. 优秀课程案例:使用Scratch制作贪吃蛇大战游戏
  16. 射频芯片ATE测试从入门到放弃之收发机
  17. 给2011年的MacBook Pro和MacBook Air升级到最新系统和做双系统时遇到的问题
  18. 我们向印度人学习什么?
  19. daimayuan每日一题#810 最短路计数
  20. Win32:编译64位程序的注意点

热门文章

  1. uniapp picker时间选择器
  2. C++排序中的谓语使用
  3. 不同域名网站携带信息跳转并保存到localStorage中
  4. 苹果手机壳_苹果客服再秀下限:iPhone 12保护壳没开孔是正常的|保护壳|客服|手机壳|手机...
  5. CC1312R低功耗高集成度M4F内核Sub-1G收发芯片
  6. ListView中嵌套checkbox实现多选
  7. c语言编程输出1000以内能被3整除的数,【C语言】找出1000以内可以被3整除的数
  8. Word转PDF失败,报错Adobe Acrobat没激活!!!我如此穷,肯定会换个办法解决呀!
  9. xxx2xxx转换工具邪恶八进制收集整理上传专用主题(不断更新)https://forum.eviloctal.com/viewthread.php?tid=14426
  10. 微信小程序会员卡开发(开发效果示例图+详细介绍+接口说明)