Spring源码解析-4.AOP源码解析


一、AOP是什么?

面向切面编程,作为面向对象的一种补充,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行分离,可以减少系统的重复代码和降低模块之间的耦合度。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。

二、Spring AOP简易使用(注解版)

1、定义一个切面类Aspect
即在声明的类上,增加@Aspect、@Component两个注解,在配置类上添加@EnableAspectJAutoProxy注解开启AOP。(Spring是需要手动添加@EnableAspectJAutoProxy注解进行集成的,而SpringBoot中使用自动装配的技术,可以不手动加这个注解就实现集成。)
切面类:

@Aspect
@Component
public class LklAspect {
}

配置类:

@ComponentScan("com.lkl")
@EnableAspectJAutoProxy
public class ContextConfig {
}

2、定义切点Pointcut
定义切点,并定义切点在那些地方执行,采用@Pointcut注解完成,如@Pointcut(public * com.xxx.xxx.* . **(…))。规则:修饰符(可以不写,但不能用 * )+返回类型+哪些包下的类+哪些方法+方法参数 *代表所有,"…"两个点代表参数不限。
自定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
    String value() default "";
}

切点方法:

@Aspect
@Component
public class LklAspect {
    /**
     * 这里演示注解的形式 匹配带有指定注解的连接点
     * 当然,也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     */
    @Pointcut("@annotation(com.lkl.aspect.anno.SysLog)")
    public void myPointcut() {}
 }

3、定义Advice通知
利用通知的5种注解@Before、@After、@AfterReturning、@AfterThrowing、@Around来完成在某些切点的增强动作,如@Before(“myPointcut()”),myPointcut为第二步定义的切点

代码如下:

@Aspect
@Component
public class LklAspect {

	@Before("execution(* com.lkl.service.*.create*(..))")
	public void before(JoinPoint joinPoint) {
		System.out.println("准备执行方法: " + joinPoint.getSignature().getName() + ", 参数列表:" + Arrays.toString(joinPoint.getArgs()));
	}


	@AfterReturning(value = "execution(* com.lkl.service.*.query*(..))", returning = "returnValue")
	public void afterReturning(JoinPoint point, Object returnValue) {
		System.out.println(point.getSignature().getName() + "方法返回:" + returnValue);
	}
}

三、AOP实现原理介绍

Spring AOP是通过动态代理实现的,代理模式,接口 + 真实实现类 + 代理类,其中 真实实现类代理类 都要实现接口,实例化的时候要使用代理类。所以,Spring AOP 需要做的是生成这么一个代理类,然后替换掉真实实现类来对外提供服务。
替换的过程怎么理解呢?在 Spring IOC 容器中非常容易实现,就是在 getBean(…) 的时候返回的实际上是代理类的实例,而这个代理类我们自己没写代码,它是 Spring 采用 JDK Proxy 或 CGLIB 动态生成的。

getBean(…) 方法用于查找实例化容器中的 bean,这也是为什么 Spring AOP 只能作用于 Spring 容器中的 bean 的原因,对于不是使用 IOC 容器管理的对象,Spring AOP 是无能为力的。

Aspect概念
Spring AOP 中主要概念理解:

  • aspect:切面,切面由切点和通知组成,即包括横切逻辑的定义也包括连接点的定义
  • pointcut:切点,每个类都拥有多个连接点,可以理解是连接点得集合
  • joinpoint:连接点,程序执行的某个特定位置,如某个方法调用前后等
  • weaving:织入,将增强添加到目标类的具体连接点的过程
  • advice:通知,是织入到目标类连接点上的一段代码,就是增强到什么地方?增强到什么内容?(五种通知类型
  • target:目标对象,通知织入的目标类
  • aop Proxy:代理对象,即增强后产生的对象

JDK代理与CGLIB代理的区别

  • JDK动态代理实现接口,CGLIB动态继承思想
  • JDK动态代理(目标对象存在接口时)执行效率高于CGLIB
  • 如果对象有接口实现,选择JDK代理,如果没有接口实现选择CGLIB代理

JDK、CGLIB代码简易实现:

  • JDK动态代理:

接口:

public interface Subject {
    void doSomething();
}

真实实现类:

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject do something");
    }
}

代理类:

public class JDKDynamicProxy implements InvocationHandler {

    private Object target;

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

    /**
     * 获取被代理接口实例对象(代理对象)
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Do something before");
        Object result = method.invoke(target, args);
        System.out.println("Do something after");
        return result;
    }
}

测试:

public class Client {

    public static void main(String[] args) {

        RealSubject realSubject = new RealSubject();
        realSubject.doSomething();

		System.out.println("***********************");
        // jdk动态代理测试
        Subject subject = new JDKDynamicProxy(new RealSubject()).getProxy();
        subject.doSomething();
    }
}

结果如下:
在这里插入图片描述
可以发现真实对象,运行还是原本的方法,使用动态代理生成的代理对象,则是增强后的方法。

  • CGLIB动态代理:

父类:

public class RealSubject {
    public void doSomething() {
        System.out.println("RealSubject do something");
    }
}

代理类:

public class CgLibProxy implements MethodInterceptor {

	Object target;

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

	/**
	 * 创建代理对象
	 */
	public Object getProxy() {
		//可以通过Enhancer对象中的create()方法可以去生成一个类,用于生成代理对象
		Enhancer enhancer = new Enhancer();
		//设置父类(将目标类作为代理类的父类)
		enhancer.setSuperclass(target.getClass());
		//设置拦截器(回调对象为本身对象)
		enhancer.setCallback(this);
		//生成一个代理类对象
		return enhancer.create();
	}

	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		System.out.println("方法执行前的增强行为");
		Object result = method.invoke(target, objects);
		System.out.println("方法执行后的增强行为");
		return result;
	}
}

测试:

public class Test {
	public static void main(String[] args) {
		RealSubject realSubject = new RealSubject();
		realSubject.doSomething();

		System.out.println("***********************");
		RealSubject proxy = (RealSubject) new CgLibProxy(new RealSubject()).getProxy();
		proxy.doSomething();

	}
}

结果:
在这里插入图片描述

四、AOP入口(源码解析)

  • spring集成AspectJ AOP主要是@EnableAspectJAutoProxy注解的作用。
    该注解是一个复合注解,如下所示:
    在这里插入图片描述
    @Import注解可以注入一个类到Spring容器当中,上述注解将AspectJAutoProxyRegistrar类注入到spring容器里。spring容器初始化时,执行加载和注册beanDefinition的时候,会使用该类的registerBeanDefinitions方法进行注册beanDefinition。也就来到了AspectJAutoProxyRegistrar中的registerBeanDefinitions方法。
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
该方法的作用就是将AnnotationAwareAspectJAutoProxyCreator注册到容器当中,这个类是Spring AOP的关键。此时Spring中就有了Aspect AOP的功能

  • 然后真正创建bean的方法doCreateBean(...)部分核心代码

	// 创建实例
	instanceWrapper = createBeanInstance(beanName, mbd, args);
	....
	// 依赖注入,主要为属性的依赖注入
	populateBean(beanName, mbd, instanceWrapper);
	// 1、bean执行Aware回调方法 2、执行 beanPostProcessor前置方法 
	// 3、执行类的初始化方法 4、执行beanPostProcessor后置方法
	exposedObject = initializeBean(beanName, exposedObject, mbd);
  • initializeBean(beanName, exposedObject, mbd)初始化bean

	// BeanPostProcessors 前置方法的调用
	wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

	// 调用类的初始化方法
	invokeInitMethods(beanName, wrappedBean, mbd);

	// bean 后置方法调用,在这里有可能对bean进行代理
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  • 后置处理
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
  • 动态代理入口
    在这里插入图片描述
    方法如下,判断是否需要代理
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
		    // 先从缓存中获取
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			    //如果需要对该bean对象进行代理,返回代理对象
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
  • wrapIfNecessary(bean, beanName, cacheKey)
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//判断是否已经被代理
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		//不需要代理,或者配置了conditional跳过
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		//寻找切面
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			//放入缓存
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			//创建代理对象
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		//不需要代理
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
  • 创建代理对象 Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean))
	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (proxyFactory.isProxyTargetClass()) {
			// Explicit handling of JDK proxy targets (for introduction advice scenarios)
			if (Proxy.isProxyClass(beanClass)) {
				// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
				for (Class<?> ifc : beanClass.getInterfaces()) {
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// No proxyTargetClass flag enforced, let's apply our default checks...
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
        //获取代理对象
		return proxyFactory.getProxy(getProxyClassLoader());
	}
  • 最后调用方法,根据不同类型调用不同动态代理模式获取代理对象
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了一种面向切面编程的方式,可以让开发者将一些通用的、横切的关注点(如事务、安全、缓存等)从业务逻辑中剥离出来,使得业务逻辑更加清晰简洁,代码复用更加方便。 Spring AOP的实现原理主要基于Java动态代理和CGLIB动态代理两种方式,其中Java动态代理主要用于接口代理,而CGLIB动态代理则主要用于类代理。Spring AOP中的核心概念是切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)和织入(Weaving)。 在Spring AOP中,切面是一个横向的关注点,它跨越多个对象和方法,通常包含一些通用的功能,如日志记录、安全控制等。连接点则是程序中可以被切面拦截的特定点,如方法调用、异常抛出等。通知是切面在连接点执行前后所执行的动作,包括前置通知(Before)、后置通知(After)、异常通知(AfterThrowing)、返回通知(AfterReturning)和环绕通知(Around)。切点则是用来匹配连接点的规则,它可以指定哪些连接点会被切面拦截。织入则是将切面应用到目标对象中的过程,它可以在编译时、类加载时、运行时等不同的阶段进行。 Spring AOP解析涉及到很多细节,包括代理的生成、通知的执行、切点的匹配等,需要深入了解Spring框架的内部实现和Java的反射机制。对于初学者而言,可以先从Spring AOP的基本概念和用法入手,了解其实现原理的同时,也可以通过调试和查看码来加深理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不进大厂不改名二号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值