@[TOC]
系列文档索引
SpringAOP从入门到源码分析大全(一)熟悉动态代理 SpringAOP从入门到源码分析大全(二)熟悉ProxyFactory SpringAOP从入门到源码分析大全(三)ProxyFactory源码分析 SpringAOP从入门到源码分析大全(四)SpringAOP的源码分析 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);
// 设置暴露代理对象到 AopContext
proxyFactory.setExposeProxy(true);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public 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);
}
}
});
// 添加 AfterReturningAdvice
proxyFactory.addAdvice(new AfterReturningAdvice() {
@Override
public 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], // key
args[1], // new value
returnValue // 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 {
@Override
public 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 class
public 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 < Advice
proxyFactory.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 {
@Override
public 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 指令与表达式
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 配置类
*/
@Aspect
public class AspectConfiguration {
@Pointcut("execution(public * *(..))") // 匹配 Join Point
private 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 适配成 Advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(echoServicePointcut, new EchoServiceMethodInterceptor());
DefaultEchoService defaultEchoService = new DefaultEchoService();
ProxyFactory proxyFactory = new ProxyFactory(defaultEchoService);
// 添加 Advisor
proxyFactory.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;
}
@Override
public 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 {
@Override
public 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
@Order
public class AspectConfiguration {
@Pointcut("execution(public * *(..))") // 匹配 Join Point
private 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#invokeAdviceMethodWithGivenArgs
public 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);
// 设置暴露代理对象到 AopContext
proxyFactory.setExposeProxy(true);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public 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);
}
}
});
// 添加 AfterReturningAdvice
proxyFactory.addAdvice(new AfterReturningAdvice() {
@Override
public 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], // key
args[1], // new value
returnValue // 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驱动:按照先后顺序执行。
5、补充:Spring的ProxyFactory集成了jdk与cglib
import org.springframework.aop.framework.ProxyFactory;
public class Test {
public static void main(String[] args) {
UserService userService = new UserService();
// spring 将cglib和jdk动态代理合二为一了,如果有接口,就会走jdk代理,如果只有类,就会走cglib代理
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(userService);
// 可以设置多个Advice,会形成代理链
proxyFactory.addAdvice(new MyBeforeAdvice());
proxyFactory.addAdvice(new MyAroundAdvice());
proxyFactory.addAdvice(new MyAfterAdvice());
UserService proxy = (UserService) proxyFactory.getProxy();
proxy.test();
}
}
public class UserService {
public String test() {
System.out.println("test");
return "success";
}
}
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("方法执行前执行");
}
}
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("返回之后执行");
}
}
import org.springframework.aop.ThrowsAdvice;
public class MyThrowAdvice implements ThrowsAdvice {
/**
* 可以写这些方法:拦截指定的异常
*
* public void afterThrowing(Exception ex)
* public void afterThrowing(RemoteException)
* public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
* public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
*/
}
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class MyAroundAdvice implements MethodInterceptor {
@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
System.out.println("around 前");
// 执行目标方法,或者执行下一个代理链(如果有的话)
Object proceed = invocation.proceed();
System.out.println("around 后");
return proceed;
}
}
6、补充:Advisor的理解
Advisor = pointcut + advice。
跟Advice类似的还有一个Advisor的概念,一个Advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑,比如一个UserService类中有两个方法,按上面的例子,这两个方法都会被代理,被增强,那么我们现在可以通过Advisor,来控制到具体代理哪一个方法,比如:
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(new PointcutAdvisor() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("testAbc");
}
};
}
@Override
public Advice getAdvice() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
@Override
public boolean isPerInstance() {
return false;
}
});
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();
上面代码表示,产生的代理对象,只有在执行testAbc这个方法时才会被增强,会执行额外的逻辑,而在执行其他方法时是不会增强的。
7、补充:ProxyFactoryBean
上面介绍了Spring中所提供了ProxyFactory、Advisor、Advice、PointCut等技术来实现代理对象的创建,但是我们在使用Spring时,我们并不会直接这么去使用ProxyFactory,比如说,我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到UserSerivce的代理对象,而这些,Spring都是支持的,只不过,作为开发者的我们肯定得告诉Spring,那些类需要被代理,代理逻辑是什么。
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean
public ProxyFactoryBean userServiceProxy(){
UserService userService = new UserService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(userService);
proxyFactoryBean.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
return proxyFactoryBean;
}
}
通过这种方法来定义一个UserService的Bean,并且是经过了AOP的。但是这种方式只能针对某一个Bean。它是一个FactoryBean,所以利用的就是FactoryBean技术,间接的将UserService的代理对象作为了Bean。
ProxyFactoryBean还有额外的功能,比如可以把某个Advise或Advisor定义成为Bean,然后在ProxyFactoryBean中进行设置
@Bean
public MethodInterceptor aroundAdvise(){
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
@Bean
public ProxyFactoryBean userService(){
UserService userService = new UserService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(userService);
proxyFactoryBean.setInterceptorNames("aroundAdvise");
return proxyFactoryBean;
}
8、补充:BeanNameAutoProxyCreator
ProxyFactoryBean得自己指定被代理的对象,那么我们可以通过BeanNameAutoProxyCreator来通过指定某个bean的名字,来对该bean进行代理。
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("userSe*"); // beanName的前缀
beanNameAutoProxyCreator.setInterceptorNames("aroundAdvise"); // 指定advise的beanName
beanNameAutoProxyCreator.setProxyTargetClass(true);
return beanNameAutoProxyCreator;
}
通过BeanNameAutoProxyCreator可以对批量的Bean进行AOP,并且指定了代理逻辑,指定了一个InterceptorName,也就是一个Advise,前提条件是这个Advise也得是一个Bean,这样Spring才能找到的,但是BeanNameAutoProxyCreator的缺点很明显,它只能根据beanName来指定想要代理的Bean。
BeanNameAutoProxyCreator 是一个BeanPostProcessor,在对应的方法中进行处理的。
9、补充:DefaultAdvisorAutoProxyCreator
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("test");
DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
defaultPointcutAdvisor.setPointcut(pointcut);
defaultPointcutAdvisor.setAdvice(new MyReturningAdvise());
return defaultPointcutAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
return defaultAdvisorAutoProxyCreator;
}
DefaultAdvisorAutoProxyCreator 是一个BeanPostProcessor,在初始化后的方法中,会加载所有的DefaultPointcutAdvisor 类型的Bean,会匹配指定的Pointcut,来判断是否需要进行代理。
但是,我们发现,通过这种方式,我们得依靠某一个类来实现定义我们的Advisor,或者Advise,或者Pointcut,那么这个步骤能不能更加简化一点呢?
对的,通过注解!
比如我们能不能只定义一个类,然后通过在类中的方法上通过某些注解,来定义PointCut以及Advice,可以的,比如:
@Aspect
@Component
public class MyAspect {
@Before("execution(public void com.service.UserService.test())")
public void zhouyuBefore(JoinPoint joinPoint) {
System.out.println("myBefore");
}
}
通过上面这个类,我们就直接定义好了所要代理的方法(通过一个表达式),以及代理逻辑(被@Before修饰的方法),简单明了,这样对于Spring来说,它要做的就是来解析这些注解了,解析之后得到对应的Pointcut对象、Advice对象,生成Advisor对象,扔进ProxyFactory中,进而产生对应的代理对象,具体怎么解析这些注解就是**@EnableAspectJAutoProxy注解**所要做的事情了,后面详细分析。
而@EnableAspectJAutoProxy注解,就是加了一个AnnotationAwareAspectJAutoProxyCreator。
10、补充:Introduction(@DeclareParents)
AOP的Introduction功能,是一个小的功能但是实际用的很少,因为代码可读性实在是太差了。
它旨在给原对象添加一个接口和一个默认的实现类,这样原对象就相当于直接添加了方法。
举个例子就明白了:
public class UserService {
public String test() {
System.out.println("test");
return "success";
}
}
public interface UserInterface {
void newMethod();
}
public class UserDefaultService implements UserInterface{
@Override
public void newMethod() {
System.out.println("1111");
}
}
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@Aspect
public class Test {
// 手动给UserService加上UserInterface接口,默认实现类为UserDefaultService
@DeclareParents(value = "com.test.UserService", defaultImpl = UserDefaultService.class)
private UserInterface userInterface;
@Bean
public UserService userService() {
return new UserService();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
// 获取userService 的bean,但是由于我们给UserService手动添加了一个接口,所以可以强转为UserInterface
UserInterface userService = (UserInterface) context.getBean("userService");
// 最终执行的是UserDefaultService的newMethod方法
userService.newMethod();
}
}
我们发现,就像是给UserService手动添加了一个方法一样。
未完待续
参考资料
极客时间《小马哥讲 Spring AOP 编程思想》