1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > SpringAOP从入门到源码分析大全 学好AOP这一篇就够了(二)

SpringAOP从入门到源码分析大全 学好AOP这一篇就够了(二)

时间:2021-12-31 23:07:32

相关推荐

SpringAOP从入门到源码分析大全 学好AOP这一篇就够了(二)

文章目录

系列文档索引四、Spring AOP的使用入门1、激活AspectJ模块(1)注解激活(2)XML激活 2、创建 @AspectJ 代理(了解)(1)编程方式创建 @AspectJ 代理实例(2)XML 配置创建 AOP 代理(3)标准代理工厂 API 3、Pointcut 使用(1)Pointcut 指令与表达式(2)XML 配置 Pointcut(3)API 实现 Pointcut 4、拦截动作(1)注解方式实现(2)XML配置方式实现(3)API方式实现(4)同一个@Before执行顺序 未完待续参考资料

系列文档索引

SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(一)

SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(二)

SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(三)

SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(四)

四、Spring AOP的使用入门

1、激活AspectJ模块

激活AspectJ模块分两种方式,一种是使用注解方式,一种使用xml方式。

• 注解激活 - @EnableAspectJAutoProxy

• XML 配置 - <aop:aspectj-autoproxy/>

(1)注解激活

import org.aspectj.lang.annotation.Aspect;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;@Aspect // 声明为 Aspect 切面@Configuration // Configuration class@EnableAspectJAutoProxy // 激活 Aspect 注解自动代理public class AspectJAnnotationDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(AspectJAnnotationDemo.class);context.refresh();AspectJAnnotationDemo aspectJAnnotationDemo = context.getBean(AspectJAnnotationDemo.class);System.out.println(aspectJAnnotationDemo.getClass());context.close();}}

以上是使用注解激活的实例,使用@EnableAspectJAutoProxy即可激活Aspect注解自动代理。

该AspectJAnnotationDemo类使用@Configuration标注代表是一个配置类,Spring中的配置类都会被代理,默认是使用CGLIB代理。

@Aspect表示该类是一个切面类。

(2)XML激活

在xml文件中使用该标签,即可激活Aspect自动代理:

<aop:aspectj-autoproxy/>

该标签与注解的@EnableAspectJAutoProxy效果相同。

2、创建 @AspectJ 代理(了解)

(1)编程方式创建 @AspectJ 代理实例

实现类: org.springframework.aop.aspectj.annotation.AspectJProxyFactory

import org.springframework.aop.AfterReturningAdvice;import org.springframework.aop.MethodBeforeAdvice;import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;import org.springframework.aop.framework.AopContext;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class AspectJAnnotationUsingAPIDemo {public static void main(String[] args) {// 通过创建一个 HashMap 缓存,作为被代理对象Map<String, Object> cache = new HashMap<>();// 创建 Proxy 工厂(AspectJ)AspectJProxyFactory proxyFactory = new AspectJProxyFactory(cache);// 增加 Aspect 配置类,暂时不需要// proxyFactory.addAspect(AspectConfiguration.class);// 设置暴露代理对象到 AopContextproxyFactory.setExposeProxy(true);proxyFactory.addAdvice(new MethodBeforeAdvice() {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {if ("put".equals(method.getName()) && args.length == 2) {Object proxy = AopContext.currentProxy();System.out.printf("[MethodBeforeAdvice] 当前存放是 Key: %s , Value : %s ," +"代理对象:%s\n", args[0], args[1], proxy);}}});// 添加 AfterReturningAdviceproxyFactory.addAdvice(new AfterReturningAdvice() {@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target)throws Throwable {if ("put".equals(method.getName()) && args.length == 2) {System.out.printf("[AfterReturningAdvice] 当前存放是 Key: %s , 新存放的 Value : %s , 之前关联的 Value : %s\n ",args[0], // keyargs[1], // new valuereturnValue // old value);}}});// 通过代理对象存储数据Map<String, Object> proxy = proxyFactory.getProxy();proxy.put("1", "A");proxy.put("1", "B");System.out.println(cache.get("1"));}}

(2)XML 配置创建 AOP 代理

<aop:aspectj-autoproxy/><bean id="echoService" class="com.demo.DefaultEchoService"></bean><!--拦截器--><bean id="echoServiceMethodInterceptor"class="com.demo.interceptor.EchoServiceMethodInterceptor"/><bean id="echoServiceProxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"><property name="targetName" value="echoService"/> <!--指定targetName为需要代理的bean的Id--><property name="interceptorNames"> <!--指定拦截器--><value>echoServiceMethodInterceptor</value></property></bean>

import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;import java.lang.reflect.Method;public class EchoServiceMethodInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();System.out.println("拦截 EchoService 的方法:" + method);return invocation.proceed();}}

import org.aspectj.lang.annotation.Aspect;import org.springframework.context.annotation.Configuration;import org.springframework.context.support.ClassPathXmlApplicationContext;@Aspect // 声明为 Aspect 切面@Configuration // Configuration classpublic class ProxyFactoryBeanDemo {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/spring-aop-context.xml");EchoService echoService = context.getBean("echoServiceProxyFactoryBean", EchoService.class);System.out.println(echoService.echo("Hello,World"));System.out.println(echoService.getClass());context.close();}}

我们发现,这种方式与编程的方式创建代理是差不多的,只不过是对象的创建交给Spring来处理了。

(3)标准代理工厂 API

实现类 - org.springframework.aop.framework.ProxyFactory

上面XML配置的方式创建ProxyFactoryBean,更多用于Spring中的应用,而ProxyFactory更偏底层。

import org.springframework.aop.framework.ProxyFactory;/*** ProxyFactory使用示例*/public class ProxyFactoryDemo {public static void main(String[] args) {DefaultEchoService defaultEchoService = new DefaultEchoService();// 注入目标对象(被代理)ProxyFactory proxyFactory = new ProxyFactory(defaultEchoService);//proxyFactory.setTargetClass(DefaultEchoService.class);// 添加 Advice 实现 MethodInterceptor < Interceptor < AdviceproxyFactory.addAdvice(new EchoServiceMethodInterceptor());// 获取代理对象EchoService echoService = (EchoService) proxyFactory.getProxy();System.out.println(echoService.echo("Hello,World"));}}

import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;import java.lang.reflect.Method;public class EchoServiceMethodInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();System.out.println("拦截 EchoService 的方法:" + method);return invocation.proceed();}}

3、Pointcut 使用

Spring中的Pointcut只支持方法级别的定义,也就是说只支持Bean的方法拦截。

Pointcut 只是一个筛选,并没有具体的动作。

Advice才是具体的动作,一个Pointcut 可以对应多个Advice。

(1)Pointcut 指令与表达式

Spring AOP 之 aspect表达式详解

import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;/*** Pointcut 示例*/@Configuration // Configuration class@EnableAspectJAutoProxy // 激活 Aspect 注解自动代理public class AspectJAnnotatedPointcutDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(AspectJAnnotatedPointcutDemo.class,AspectConfiguration.class);context.refresh();AspectJAnnotatedPointcutDemo aspectJAnnotationDemo = context.getBean(AspectJAnnotatedPointcutDemo.class);aspectJAnnotationDemo.execute();context.close();}public void execute() {System.out.println("execute()...");}}

/*** Aspect 配置类*/@Aspectpublic class AspectConfiguration {@Pointcut("execution(public * *(..))") // 匹配 Join Pointprivate void anyPublicMethod() {// 方法名即 Pointcut 名System.out.println("@Pointcut at any public method."); // 方法通常设置为空的,不会有具体的动作}@Before("anyPublicMethod()")// Join Point 拦截动作public void beforeAnyPublicMethod() {System.out.println("@Before any public method.");}}

Pointcut定义在切面类的私有方法上,该方法没有任何的动作,只是筛选要切入的方法。该方法名即为Pointcut名。

(2)XML 配置 Pointcut

<!--定义配置类--><bean id="aspectXmlConfig" class="com.demo.AspectXmlConfig"/><aop:config><aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig"><!--引用配置类--><aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/><!--Pointcut--><aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/><!--配置类的方法--></aop:aspect></aop:config>

public class AspectXmlConfig {public void beforeAnyPublicMethod() {System.out.println("@Before any public method.");}}

(3)API 实现 Pointcut

核心 API - org.springframework.aop.Pointcut

• org.springframework.aop.ClassFilter

• org.springframework.aop.MethodMatcher

适配实现 - DefaultPointcutAdvisor

public static void main(String[] args) {EchoServicePointcut echoServicePointcut = new EchoServicePointcut("echo", EchoService.class);// 将 Pointcut 适配成 AdvisorDefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(echoServicePointcut, new EchoServiceMethodInterceptor());DefaultEchoService defaultEchoService = new DefaultEchoService();ProxyFactory proxyFactory = new ProxyFactory(defaultEchoService);// 添加 AdvisorproxyFactory.addAdvisor(advisor);// 获取代理对象EchoService echoService = (EchoService) proxyFactory.getProxy();System.out.println(echoService.echo("Hello,World"));}

public class EchoServicePointcut extends StaticMethodMatcherPointcut {private String methodName;private Class targetClass;public EchoServicePointcut(String methodName, Class targetClass) {this.methodName = methodName;this.targetClass = targetClass;}@Overridepublic boolean matches(Method method, Class<?> targetClass) {return Objects.equals(methodName, method.getName())&& this.targetClass.isAssignableFrom(targetClass);}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}public Class getTargetClass() {return targetClass;}public void setTargetClass(Class targetClass) {this.targetClass = targetClass;}}

public class EchoServiceMethodInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();System.out.println("拦截 EchoService 的方法:" + method);return invocation.proceed();}}

4、拦截动作

Around环绕动作。

Before前置动作。

After后置动作:

• 方法返回后:AfterReturning

• 异常发生后:AfterThrowing

• finally 执行:After

(1)注解方式实现

@Aspect@Orderpublic class AspectConfiguration {@Pointcut("execution(public * *(..))") // 匹配 Join Pointprivate void anyPublicMethod() {// 方法名即 Pointcut 名System.out.println("@Pointcut at any public method.");}@Around("anyPublicMethod()") // Join Point 拦截动作public Object aroundAnyPublicMethod(ProceedingJoinPoint pjp) throws Throwable {System.out.println("@Around any public method.");return pjp.proceed();}@Before("anyPublicMethod()")// Join Point 拦截动作public void beforeAnyPublicMethod() {Random random = new Random();if (random.nextBoolean()) {throw new RuntimeException("For Purpose.");}System.out.println("@Before any public method.");}@After("anyPublicMethod()")public void finalizeAnyPublicMethod() {System.out.println("@After any public method.");}@AfterReturning("anyPublicMethod()")// AspectJAfterReturningAdvice is AfterReturningAdvice// 一个 AfterReturningAdviceInterceptor 关联一个 AfterReturningAdvice// Spring 封装 AfterReturningAdvice -> AfterReturningAdviceInterceptor// AfterReturningAdviceInterceptor is MethodInterceptor// AfterReturningAdviceInterceptor// -> AspectJAfterReturningAdvice//-> AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgspublic void afterAnyPublicMethod() {System.out.println("@AfterReturning any public method.");}@AfterThrowing("anyPublicMethod()")public void afterThrowingAnyPublicMethod() {System.out.println("@AfterThrowing any public method");}public String toString() {return "AspectConfiguration";}private int getValue() {return 0;}}

(2)XML配置方式实现

<aop:config><!-- <aop:pointcut id="allPointcut" expression="execution(* * *(..))"/>--><aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig"><aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/><aop:around method="aroundAnyPublicMethod" pointcut-ref="anyPublicMethod"/><aop:around method="aroundAnyPublicMethod" pointcut="execution(public * *(..))"/><aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/><aop:before method="beforeAnyPublicMethod" pointcut="execution(public * *(..))"/><aop:after method="finalizeAnyPublicMethod" pointcut-ref="anyPublicMethod"/><aop:after-returning method="afterAnyPublicMethod" pointcut-ref="anyPublicMethod"/><aop:after-throwing method="afterThrowingAnyPublicMethod" pointcut-ref="anyPublicMethod"/></aop:aspect></aop:config>

public class AspectXmlConfig {public Object aroundAnyPublicMethod(ProceedingJoinPoint pjp) throws Throwable {Random random = new Random();if (random.nextBoolean()) {throw new RuntimeException("For Purpose from XML configuration.");}System.out.println("@Around any public method : " + pjp.getSignature());return pjp.proceed();}public void beforeAnyPublicMethod() {System.out.println("@Before any public method.");}public void finalizeAnyPublicMethod() {System.out.println("@After any public method.");}public void afterAnyPublicMethod() {System.out.println("@AfterReturning any public method.");}public void afterThrowingAnyPublicMethod() {System.out.println("@AfterThrowing any public method.");}}

(3)API方式实现

为什么 Spring AOP 不需要设计 Around Advice

• AspectJ @Around 与 org.aspectj.lang.ProceedingJoinPoint 配合执行被代理方法

• ProceedingJoinPoint#proceed() 方法类似于 Java Method#invoke(Object,Object…)

• Spring AOP 底层 API ProxyFactory 可通过 addAdvice 方法与 Advice 实现关联

• 接口 Advice 是 Interceptor 的父亲接口,而接口 MethodInterceptor 又扩展了 Interceptor

• MethodInterceptor 的invoke 方法参数 MethodInvocation 与 ProceedingJoinPoint 类似

API 实现 Before Advice

核心接口 - org.springframework.aop.BeforeAdvice

类型:标记接口,与 org.aopalliance.aop.Advice 类似

方法 JoinPoint 扩展 - org.springframework.aop.MethodBeforeAdvice

接受对象 - org.springframework.aop.framework.AdvisedSupport

• 基础实现类 - org.springframework.aop.framework.ProxyCreatorSupport

• 常见实现类:org.springframework.aop.framework.ProxyFactory;org.springframework.aop.framework.ProxyFactoryBean;org.springframework.aop.aspectj.annotation.AspectJProxyFactory

API 实现三种 After Advice

核心接口 - org.springframework.aop.AfterAdvice

类型:标记接口,与 org.aopalliance.aop.Advice 类似

扩展

• org.springframework.aop.AfterReturningAdvice

• org.springframework.aop.ThrowsAdvice

接受对象 - org.springframework.aop.framework.AdvisedSupport

• 基础实现类 - org.springframework.aop.framework.ProxyCreatorSupport

• 常见实现类:org.springframework.aop.framework.ProxyFactory;org.springframework.aop.framework.ProxyFactoryBean;org.springframework.aop.aspectj.annotation.AspectJProxyFactory

// 通过创建一个 HashMap 缓存,作为被代理对象Map<String, Object> cache = new HashMap<>();// 创建 Proxy 工厂(AspectJ)AspectJProxyFactory proxyFactory = new AspectJProxyFactory(cache);// 增加 Aspect 配置类,此处不需要// proxyFactory.addAspect(AspectConfiguration.class);// 设置暴露代理对象到 AopContextproxyFactory.setExposeProxy(true);proxyFactory.addAdvice(new MethodBeforeAdvice() {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {if ("put".equals(method.getName()) && args.length == 2) {Object proxy = AopContext.currentProxy();System.out.printf("[MethodBeforeAdvice] 当前存放是 Key: %s , Value : %s ," +"代理对象:%s\n", args[0], args[1], proxy);}}});// 添加 AfterReturningAdviceproxyFactory.addAdvice(new AfterReturningAdvice() {@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target)throws Throwable {if ("put".equals(method.getName()) && args.length == 2) {System.out.printf("[AfterReturningAdvice] 当前存放是 Key: %s , 新存放的 Value : %s , 之前关联的 Value : %s\n ",args[0], // keyargs[1], // new valuereturnValue // old value);}}});// 通过代理对象存储数据Map<String, Object> proxy = proxyFactory.getProxy();proxy.put("1", "A");proxy.put("1", "B");System.out.println(cache.get("1"));

(4)同一个@Before执行顺序

注解驱动:使用@Order注解标注切面类,同一类中的顺序无法保证。

XML驱动:按照先后顺序执行。

未完待续

参考资料

极客时间《小马哥讲 Spring AOP 编程思想》

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。