基于 XML 配置文件的 AOP
一、创建切入点类
public class PointCutBean {
public void sayHello() {
System.out.println("Hello World!");
}
}
二、创建通知类
public class AdviceBean {
public void writeLine() {
System.out.println("------------------------------");
}
}
三、创建 Spring 配置文件
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<aop:before method="writeLine" pointcut="execution(* chu.yi.bo.PointCutBean.*(..))"/>
</aop:aspect>
</aop:config>
</beans>
四、测试
public void testAOP() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Application.xml");
PointCutBean pointCutBean = (PointCutBean)applicationContext.getBean("pointCutBean");
pointCutBean.sayHello();
}
五、切入点表达式
切入点表达式用来进行切入点(方法)的匹配,语法结构是:execution([修饰符] 返回值类型 包名.类名.方法名(参数))。修饰符是非必选项,返回值类型、包名的任意一级、类名、方法名、参数,都可以用通配符 * 来代替。包名、参数名也可以用通配符 . . 来代替,例子如下:
- 无省略模式
execution(public void chu.yi.bo.PointCutBean.sayHello()) - 省略修饰符
execution(void chu.yi.bo.PointCutBean.sayHello()) - 使用 * 号代替返回值,表示任意返回类型
execution(* chu.yi.bo.PointCutBean.sayHello()) - 使用 * 号替代任意一级包
execution(* *.*.*.PointCutBean.sayHello()) - 使用 .. 表示当前包以及子包
execution(* chu..PointCutBean.sayHello()) - 使用 * 号代替类名,表示任意类
execution(* chu..*.sayHello()) - 使用 * 号代替方法名,表示任意方法
execution(* chu..*.*()) - 方法有参数时,可以使用 * 替代任意类型参数
execution(* chu..*.*(*)) - 无论方法有无参数都可以使用 .. 代替任意类型参数
execution(* chu..*.*(..)) - 全使用通配符
execution(* *..*.*(..))
配置 AOP 相关标签
标签 | 说明 | 属性 |
---|---|---|
aop:config | 用于声明开始 AOP 的配置 | |
aop:aspect | 用于配置切面 | id:给切面提供唯一标识 ref:引用配置好的通知 bean 的 id |
aop:pointcut | 指定对哪些类的哪些方法进行增强,配置切入点表达式 | expression:用于定义切入点表达式 id:给切入点表达式提供唯一标识 |
aop:before | 配置前置通知。指定增强的方法在切入点方法之前执行 | method:用于指定通知类中的增强方法名称 ponitcut-ref:用于指定切入点的表达式的引用 poinitcut:用于指定切入点表达式 |
aop:after-returning | 配置后置通知,切入点方法正常执行之后。它和异常通知只能有一个执行 | 同上 |
aop:after-throwing | 配置异常通知,切入点方法执行产生异常后执行。它和后置通知只能执行一个 | 同上 |
aop:after | 配置最终通知,无论切入点方法执行时是否有异常,它都会在其后面执行 | 同上 |
aop:around | 配置环绕通知 | 同上 |
一、aop: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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pt" expression="execution(* chu.yi.bo.PointCutBean.*(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<aop:before method="writeLine" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
aop:pointcut 标签用于配置切入点,该标签写在 aop:aspect 标签内部只能在当前切面使用,写在 aop:aspect 标签外部可以在所有切面使用。
通知类型
一、前置通知
1、切入点类
public class PointCutBean {
public void beforePointCut() {
System.out.println("beforePointCut");
}
}
2、通知类
public class AdviceBean {
public void beforeAdvice() {
System.out.println("beforeAdvice");
}
}
3、Spring 配置文件
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="beforePt" expression="execution(* chu.yi.bo.PointCutBean.beforePointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<aop:before method="beforeAdvice" pointcut-ref="beforePt"/>
</aop:aspect>
</aop:config>
</beans>
二、后置通知
1、切入点类
public class PointCutBean {
public void afterReturningPointCut() {
System.out.println("afterReturningPointCut");
}
}
2、通知类
public class AdviceBean {
public void afterReturningAdvice() {
System.out.println("afterReturningAdvice");
}
}
3、Spring 配置文件
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="afterReturningPt" expression="execution(* chu.yi.bo.PointCutBean.afterReturningPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<aop:after-returning method="afterReturningAdvice" pointcut-ref="afterReturningPt"/>
</aop:aspect>
</aop:config>
</beans>
三、异常通知
1、切入点类
public class PointCutBean {
public void afterThrowingPointCut() {
System.out.println("afterThrowingPointCut");
throw new RuntimeException();
}
}
2、通知类
public class AdviceBean {
public void afterThrowingAdvice() {
System.out.println("afterThrowingAdvice");
}
}
3、Spring 配置文件
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="afterThrowingPt" expression="execution(* chu.yi.bo.PointCutBean.afterThrowingPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="afterThrowingPt"/>
</aop:aspect>
</aop:config>
</beans>
四、最终通知
1、切入点类
public class PointCutBean {
public void afterPointCut() {
System.out.println("afterPointCut");
}
}
2、通知类
public class AdviceBean {
public void afterAdvice() {
System.out.println("afterAdvice");
}
}
3、Spring 配置文件
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="afterPt" expression="execution(* chu.yi.bo.PointCutBean.afterPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<aop:after method="afterAdvice" pointcut-ref="afterPt"/>
</aop:aspect>
</aop:config>
</beans>
五、环绕通知
1、切入点类
public class PointCutBean {
public void aroundPointCut() {
System.out.println("aroundPointCut");
}
}
2、通知类
public class AdviceBean {
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
Object[] args = pjp.getArgs();// 得到方法执行所需的参数
System.out.println("beforeAdvice 前置");
rtValue = pjp.proceed(args);// 明确调用业务层方法(切入点方法)
System.out.println("afterReturningAdvice 后置");
return rtValue;
} catch (Throwable t) {
System.out.println("afterThrowingAdvice 异常");
throw new RuntimeException(t);
} finally {
System.out.println("afterAdvice 最终");
}
}
}
Spring 框架提供了 ProceedingJoinPoint 接口,程序运行时,Spring 框架会创建该接口的实例,并作为参数传递给通知方法。调用该实例的 proceed() 方法可以执行切入点方法,调用该实例的 getArgs() 方法可以获取切入点方法的参数列表。
3、Spring 配置文件
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="adviceBean" class="chu.yi.bo.AdviceBean"/>
<bean id="pointCutBean" class="chu.yi.bo.PointCutBean"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="aroundPt" expression="execution(* chu.yi.bo.PointCutBean.aroundPointCut(..))"/>
<!-- 配置切面 -->
<aop:aspect id="writeLineAdvice" ref="adviceBean">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<aop:around method="aroundAdvice" pointcut-ref="aroundPt"/>
</aop:aspect>
</aop:config>
</beans>
AOP 相关术语
术语 | 说明 |
---|---|
Joinpoint(连接点) | 连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。 |
Pointcut(切入点) | 所谓切入点是指要对哪些 Joinpoint 进行拦截的定义 |
Advice(通知/ 增强) | 通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。 |
Target(目标对象) | 代理的目标对象 |
Weaving(织入) | 把增强应用到目标对象来创建新的代理对象的过程 |
Proxy (代理) | 一个类被 AOP 织入增强后,就产生一个结果代理类 |
Aspect(切面) | 是切入点和通知的结合 |
基于注解实现 AOP
一、Spring 配置文件
<?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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置 spring 创建容器时要扫描的包 -->
<context:component-scan base-package="chu.yi.bo"/>
<!-- 配置 spring 开启注解 AOP 的支持 -->
<aop:aspectj-autoproxy/>
</beans>
二、前置通知
1、切入点类
@Component("pointCutBean")
public class PointCutBean {
public void beforePointCut() {
System.out.println("beforePointCut");
}
}
2、通知类
@Component("adviceBean")
@Aspect
public class AdviceBean {
@Before("execution(* chu.yi.bo.PointCutBean.beforePointCut(..))")
public void beforeAdvice() {
System.out.println("beforeAdvice");
}
}
三、后置通知
1、切入点类
@Component("pointCutBean")
public class PointCutBean {
public void afterReturningPointCut() {
System.out.println("afterReturningPointCut");
}
}
2、通知类
@Component("adviceBean")
@Aspect
public class AdviceBean {
@AfterReturning("execution(* chu.yi.bo.PointCutBean.afterReturningPointCut(..))")
public void afterReturningAdvice() {
System.out.println("afterReturningAdvice");
}
}
四、异常通知
1、切入点类
@Component("pointCutBean")
public class PointCutBean {
public void afterThrowingPointCut() {
System.out.println("afterThrowingPointCut");
throw new RuntimeException();
}
}
2、通知类
@Component("adviceBean")
@Aspect
public class AdviceBean {
@AfterThrowing("execution(* chu.yi.bo.PointCutBean.afterThrowingPointCut(..))")
public void afterThrowingAdvice() {
System.out.println("afterThrowingAdvice");
}
}
五、最终通知
1、切入点类
@Component("pointCutBean")
public class PointCutBean {
public void afterPointCut() {
System.out.println("afterPointCut");
}
}
2、通知类
@Component("adviceBean")
@Aspect
public class AdviceBean {
@After("execution(* chu.yi.bo.PointCutBean.afterPointCut(..))")
public void afterAdvice() {
System.out.println("afterAdvice");
}
}
六、环绕通知
1、切入点类
@Component("pointCutBean")
public class PointCutBean {
public void aroundPointCut() {
System.out.println("aroundPointCut");
}
}
2、通知类
@Component("adviceBean")
@Aspect
public class AdviceBean {
@Around("execution(* chu.yi.bo.PointCutBean.aroundPointCut(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object rtValue = null;
try{
Object[] args = pjp.getArgs();// 得到方法执行所需的参数
System.out.println("beforeAdvice 前置");
rtValue = pjp.proceed(args);// 明确调用业务层方法(切入点方法)
System.out.println("afterReturningAdvice 后置");
return rtValue;
}catch (Throwable t){
System.out.println("afterThrowingAdvice 异常");
throw new RuntimeException(t);
}finally {
System.out.println("afterAdvice 最终");
}
}
}
七、@Pointcut 注解配置切入点
1、切入点类
@Component("pointCutBean")
public class PointCutBean {
public void beforePointCut() {
System.out.println("beforePointCut");
}
}
2、通知类
@Component("adviceBean")
@Aspect
public class AdviceBean {
@Pointcut("execution(* chu.yi.bo.PointCutBean.beforePointCut(..))")
private void pt(){}
@Before("pt()")
public void beforeAdvice() {
System.out.println("beforeAdvice");
}
}
动态代理
Spring 面向切面编程,底层的实现原理是动态代理。
一、接口
public interface HelloInterface {
void sayHello();
}
二、被代理类
public class Hello implements HelloInterface {
public void sayHello() {
System.out.println("Hello World!");
}
}
三、Java 官方动态代理
public void proxy() {
final HelloInterface hello = new Hello();
HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName());
Object rtValue = method.invoke(hello, args);
System.out.println("After invoke " + method.getName());
return rtValue;
}
}
);
proxyHello.sayHello();
}
三、使用 CGLib 实现动态代理
public void proxy() {
final HelloInterface hello = new Hello();
HelloInterface proxyHello = (HelloInterface) Enhancer.create(hello.getClass(),
new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before invoke " + method.getName());
Object rtValue = method.invoke(hello, objects);
System.out.println("After invoke " + method.getName());
return rtValue;
}
});
proxyHello.sayHello();
}