spring aop的實現代碼

跟蹤spring aop的實現

aop核心概念介紹

  • 什麼是切面編程
    在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面編程。

  • Aspect(切面)
    什麼是切面呢?切面這個詞感覺還是挺抽象的,咱們可能知道點構成線,線構成面的數據話術。但是在編程語言中如何體現這個面呢?我的理解是“在程序中切面是由切入點和在切入點的表現的行爲構成”。給個在spring中定義切面的例子如下:

<bean id="advice" class="com.im.sky.spring.aspect.GenericAdvice" />
	
    <aop:config>
        <aop:aspect id="aspect1" ref="advice">
            <aop:pointcut id="p1" expression="execution(* com.im.sky.spring.bean.People.say(..))" />
            <aop:before pointcut-ref="p1" method="before" />
        </aop:aspect>

其中aop:aspect表示的就是一個切面,而aop:pointcut表示的是具體的切入點,即哪些類的哪些放到會被這個切面切到。aop:before表示的就是在切入點的哪一個位置執行什麼行爲。

  • crosscutting concerns(橫切關注點)
    在實現業務的同時,可能會在代碼邏輯中加入日誌、參數校驗、安全檢查,這些通用的操作在aop中就叫做橫切關注點,因爲這類代碼是可以通用的,可以進行抽離,通過藉助於aop,可能將此類代碼和傳統的業務代碼就行解耦。

  • Advice
    在切入點的具體實現,就稱之爲Advice,正如spring中定義的aop:before、aop:after處進行的行爲等。

  • JoinPoint
    Advice在業務代碼執行時加入業務流程的點或者時機稱之爲JoinPoint,目前Spring只支持方法級別,但可以在方法的不同時機加入,比如調用方法之前、之後、返回之後、返回失敗時。

  • Pointcut(切入點)
    Pointcut可以看做是JoinPoint的一個子集,對於每一個方法都會有一些切入的時機,但是呢,你可能只想切入你關注的一些方法,這些方法稱之爲切入點。

Spring中的aop

aop的實現原理還是藉助於動態代理實現的,而目前就是使用JDK動態代理和Cglib動態代理,當一個類實現接口時纔可以使用JDK動態代理,而Cglib不受此限制。

步驟一:定義spring aop配置

<bean id="advice" class="com.im.sky.spring.aspect.GenericAdvice" />

    <aop:config>
        <aop:aspect id="aspect1" ref="advice">
            <aop:pointcut id="p1" expression="execution(* com.im.sky.spring.bean.People.say(..))" />
            <aop:before pointcut-ref="p1" method="before" />
        </aop:aspect>

步驟二:定義基本類

People類

public class People {

    private String name;

    public void setName(String name) {
        System.out.println("name:" + name);
        this.name = name;
    }

    public void say() {
        System.out.println("說人話");
    }

    public void cry() {
        System.out.println("痛苦纔會哭");
    }
}

advice類

public class GenericAdvice {

    public void before() {
        System.out.println("before advice");
    }

    public void after() {
        System.out.println("after advice");
    }

    public void iReturn() {
        System.out.println("return advice");
    }

    public void throwsError() {
        System.out.println("throws advice");
    }

    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around advice for before");
        Object o = pjp.proceed(pjp.getArgs());
        System.out.println("around advice for after");
        return o;
    }
}

執行類Main

public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        People people = context.getBean(People.class);
        people.say();
    }
}

spring中核心類:AbstractAutoProxyCreator

protected Object createProxy(
			Class<?> beanClass, String beanName, 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()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		// 設置目標類,就是上面定義的People的實例(在spring配置文件中定義了一個,沒有粘貼)
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
		//這裏是核心,內部會選擇是採用JDK動態代理還是CgLib動態代理
		return proxyFactory.getProxy(getProxyClassLoader());
	}

proxyFactory.getProxy(ClassLoader loader)源碼

public Object getProxy(ClassLoader classLoader) {
		// 下面看看如何構建aop代理對象的
		return createAopProxy().getProxy(classLoader);
	}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			// 如果目標類是接口或者本身就是一個代理類纔會採用JDK動態代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// 這裏內部用的就是Cglib動態代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

當選擇具體採用哪種代理方式後,就會調用getProxy構成具體的目標類的代理類對象。
JDK動態代理構建目標類代理對象源碼:

public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

Cglib動態代理構建目標類代理對象源碼:

public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
		}

		try {
			Class<?> rootClass = this.advised.getTargetClass();
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

			Class<?> proxySuperClass = rootClass;
			if (ClassUtils.isCglibProxyClass(rootClass)) {
				proxySuperClass = rootClass.getSuperclass();
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// Configure CGLIB Enhancer...
			Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return createProxyClassAndInstance(enhancer, callbacks);
		}
		catch (CodeGenerationException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}

	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		return (this.constructorArgs != null ?
				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
				enhancer.create());
	}

怎麼說呢,spring中的代碼還是很龐大的,如果想仔細的瞭解,就debug進入跟蹤一下吧,我這個寫的也是很少的一部分,中間還涉及預處理等很多東西。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章