其他分享
首页 > 其他分享> > 搞懂Spring AOP的前世今生

搞懂Spring AOP的前世今生

作者:互联网

请添加图片描述

Spring AOP概述

在这里插入图片描述
方法调用优于方法执行 Spring目前只支持方法执行这一种类型的Joinpoint

织入

public interface EchoService {

    String echo(String message);
}
public class DefaultEchoService implements EchoService {

    @Override
    public String echo(String message) {
        return message;
    }
}

jdk动态代理

public class CostInvocationHandler implements InvocationHandler {

    private Object target;

    public CostInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        long cost = System.currentTimeMillis() - startTime;
        System.out.println("cost " + cost);
        return result;
    }
}
public static void main(String[] args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    Object proxy = Proxy.newProxyInstance(classLoader,
            new Class[]{EchoService.class},
            new CostInvocationHandler(new DefaultEchoService()));
    EchoService echoService = (EchoService) proxy;
    // cost 0
    // hello world
    System.out.println(echoService.echo("hello world"));
}

cglib

public static void main(String[] args) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(DefaultEchoService.class);
    enhancer.setInterfaces(new Class[] {EchoService.class});
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object source, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            long startTime = System.currentTimeMillis();
            Object result = methodProxy.invokeSuper(source, args);
            long cost = System.currentTimeMillis() - startTime;
            System.out.println("cost " + cost);
            return result;
        }
    });
    EchoService echoService = (EchoService) enhancer.create();
    // cost 29
    // hello world
    System.out.println(echoService.echo("hello world"));
}

Spring AOP一代

Pointcut

public interface Pointcut {

	// 通过类过滤
	ClassFilter getClassFilter();

	// 通过方法过滤
	MethodMatcher getMethodMatcher();

	Pointcut TRUE = TruePointcut.INSTANCE;

}
public class EchoPointcut implements Pointcut {

    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                return EchoService.class.isAssignableFrom(clazz);
            }
        };
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return "echo".equals(method.getName()) &&
                        method.getParameterTypes().length == 1 &&
                        Objects.equals(String.class, method.getParameterTypes()[0]);
            }

            @Override
            public boolean isRuntime() {
                return false;
            }

            @Override
            public boolean matches(Method method, Class<?> targetClass, Object... args) {
                return false;
            }
        };
    }
}

在这里插入图片描述
Spring内部有很多内置的实现,一般情况下我们用内置的实现即可,不用自己定义

// 方法名为 echo 会被拦截
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedName("echo");

Advice

在这里插入图片描述

public interface SayName {

    String getName();
}
public class DefaultSayName implements SayName {

    @Override
    public String getName() {
        return "I am service";
    }
}
public static void main(String[] args) {
    SayName sayName = new DefaultSayName();
    EchoService echoService = new DefaultEchoService();
    DelegatingIntroductionInterceptor interceptor =
            new DelegatingIntroductionInterceptor(sayName);
    Advisor advisor = new DefaultIntroductionAdvisor(interceptor, SayName.class);
    ProxyFactory proxyFactory = new ProxyFactory(echoService);
    proxyFactory.addAdvisor(advisor);
    // hello world
    EchoService proxyService = (EchoService) proxyFactory.getProxy();
    System.out.println(proxyService.echo("hello world"));
    // I am service
    SayName proxySayName = (SayName) proxyFactory.getProxy();
    System.out.println(proxySayName.getName());
}
public static void main(String[] args) {
    JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
    pointcut.setPattern(".*put.*");
    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
    advisor.setPointcut(pointcut);
    advisor.setAdvice(new MethodBeforeAdvice() {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.printf("当前存放的key为 %s,值为 %s", args[0], args[1]);
        }
    });

    ProxyFactory proxyFactory = new ProxyFactory(new HashMap());
    proxyFactory.addAdvisor(advisor);
    Map<String, String> proxyMap = (Map<String, String>) proxyFactory.getProxy();
    // 当前存放的key为 a,值为 a
    proxyMap.put("a", "a");
}

Advisor

织入

Spring AOP的自动动态代理

请添加图片描述
手动配置

public class ProxyConfig {

    // 创建代理对象
    @Bean
    public EchoService echoService() {
        return new DefaultEchoService();
    }

    // 创建advice
    @Bean
    public CostMethodInterceptor costInterceptor() {
        return new CostMethodInterceptor();
    }

    // 使用pointcut和advice创建advisor
    @Bean
    public Advisor advisor() {
        NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
        advisor.setMappedName("echo");
        advisor.setAdvice(costInterceptor());
        return advisor;
    }

    // 创建代理对象
    @Bean("echoProxy")
    public ProxyFactoryBean proxyFactoryBean(EchoService echoService) {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(echoService);
        proxyFactoryBean.setInterceptorNames("advisor");
        return proxyFactoryBean;
    }
}
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProxyConfig.class);
    EchoService echoService = (EchoService) context.getBean("echoProxy");
    // cost 0
    // hello world
    System.out.println(echoService.echo("hello world"));
}

自动配置

public class AutoProxyConfig {

    // 创建代理对象
    @Bean
    public EchoService echoService() {
        return new DefaultEchoService();
    }

    // 创建advice
    @Bean
    public CostMethodInterceptor costInterceptor() {
        return new CostMethodInterceptor();
    }

    // 使用pointcut和advice创建advisor
    @Bean
    public Advisor advisor() {
        NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
        advisor.setMappedName("echo");
        advisor.setAdvice(costInterceptor());
        return advisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator autoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }
}
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutoProxyConfig.class);
    EchoService echoService = context.getBean(EchoService.class);
    // cost 0
    // hello world
    System.out.println(echoService.echo("hello world"));
}

Spring AOP自动动态代理的实现方式

在这里插入图片描述
AbstractAutoProxyCreator重写了如下2个重要的方法
postProcessBeforeInstantiation(Bean实例化前阶段执行)
postProcessAfterInitialization(Bean初始化后阶段执行)

postProcessBeforeInstantiation
请添加图片描述
当用户自定义了TargetSource的实现时,会从TargetSourc
在这里插入图片描述
请添加图片描述

Spring AOP二代(集成了AspectJ)

激活Aspect

注解激活:@EnableAspectJAutoProxy
XML配置:<aop:aspectj-autoproxy>

Spring AOP用AspectJExpressionPointcut桥接了Aspect的筛选能力。其实Aspect有很多种类型的切点表达式,但是Spring AOP只支持如下10种,因为Aspect支持很多种类型的JoinPoint,但是Spring AOP只支持方法执行这一种JoinPoint,所以其余的表达式就没有必要了。
在这里插入图片描述

切点表达式

表达式类型解释
execution匹配方法表达式,首选方式
within限定类型
this代理对象是指定类型
target目标对象是指定类型
args匹配方法中的参数
@target目标对象有指定的注解
@args方法参数所属类型上有指定注解
@within
@annotation有指定注解的方法

execution

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
                throws-pattern?)

拦截Performance类的perform方法的切点表达式如下
在这里插入图片描述
放几个官方的Demo

// The execution of any public method:
execution(public * *(..))

// The execution of any method with a name that begins with set
execution(* set*(..))

// The execution of any method defined by the AccountService interface
execution(* com.xyz.service.AccountService.*(..))

// The execution of any method defined in the service package:
execution(* com.xyz.service.*.*(..))

within

this

target

args

@target

@args

@within

@annotation

参考博客

[1]https://zhuanlan.zhihu.com/p/104520344
大佬分析aop的实现
[2]https://blog.csdn.net/qq_20597727/article/details/84868035
[3]https://blog.csdn.net/qq_20597727/article/details/84800176
Spring AOP之Introduction
[4]https://www.cnblogs.com/lcngu/p/6346777.html
spring aop代理时机
[5]https://www.jianshu.com/p/ef3dc73bc3b1

标签:args,return,Spring,AOP,advisor,new,搞懂,EchoService,public
来源: https://blog.csdn.net/zzti_erlie/article/details/120379790