这里先说一点题外话。由于明年一月份火焰纹章系列新作-火焰纹章Engage就要发售了,我作为一个老火纹厨表示,这个2022年真的一秒钟也待不下去了(恼)。所以就用了这么个标题。也算是强行契合了这次的主题。Engage,有结合的意思。我们上一章用动态代理,实现了AOP的核心功能。但是这个功能的实现相对比较独立,和前面的内容有很强的割裂感。那我们自然是不能满足于这样的现状的,直接把这样的内容交给用户,是很难使用的。我们需要把它和Spring的生命周期结合起来,以配置的方式,达到润物细无声的效果。さあ、私と、Engage!(快到2023年吧~
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─akitsuki
│ │ │ └─springframework
│ │ │ ├─aop
│ │ │ │ │ AdvisedSupport.java
│ │ │ │ │ Advisor.java
│ │ │ │ │ BeforeAdvice.java
│ │ │ │ │ ClassFilter.java
│ │ │ │ │ MethodBeforeAdvice.java
│ │ │ │ │ MethodMatcher.java
│ │ │ │ │ Pointcut.java
│ │ │ │ │ PointcutAdvisor.java
│ │ │ │ │ TargetSource.java
│ │ │ │ │
│ │ │ │ ├─aspect
│ │ │ │ │ AspectJExpressionPointcut.java
│ │ │ │ │ AspectJExpressionPointcutAdvisor.java
│ │ │ │ │
│ │ │ │ └─framework
│ │ │ │ │ AopProxy.java
│ │ │ │ │ Cglib2AopProxy.java
│ │ │ │ │ JdkDynamicAopProxy.java
│ │ │ │ │ ProxyFactory.java
│ │ │ │ │ ReflectiveMethodInvocation.java
│ │ │ │ │
│ │ │ │ ├─adapter
│ │ │ │ │ MethodBeforeAdviceInterceptor.java
│ │ │ │ │
│ │ │ │ └─autoproxy
│ │ │ │ DefaultAdvisorAutoProxyCreator.java
│ │ │ │
│ │ │ ├─beans
│ │ │ │ ├─exception
│ │ │ │ │ BeanException.java
│ │ │ │ │
│ │ │ │ └─factory
│ │ │ │ │ Aware.java
│ │ │ │ │ BeanClassLoaderAware.java
│ │ │ │ │ BeanFactory.java
│ │ │ │ │ BeanFactoryAware.java
│ │ │ │ │ BeanNameAware.java
│ │ │ │ │ ConfigurableListableBeanFactory.java
│ │ │ │ │ DisposableBean.java
│ │ │ │ │ FactoryBean.java
│ │ │ │ │ HierarchicalBeanFactory.java
│ │ │ │ │ InitializingBean.java
│ │ │ │ │ ListableBeanFactory.java
│ │ │ │ │
│ │ │ │ ├─config
│ │ │ │ │ AutowireCapableBeanFactory.java
│ │ │ │ │ BeanDefinition.java
│ │ │ │ │ BeanDefinitionRegistryPostProcessor.java
│ │ │ │ │ BeanFactoryPostProcessor.java
│ │ │ │ │ BeanPostProcessor.java
│ │ │ │ │ BeanReference.java
│ │ │ │ │ ConfigurableBeanFactory.java
│ │ │ │ │ DefaultSingletonBeanRegistry.java
│ │ │ │ │ InstantiationAwareBeanPostProcessor.java
│ │ │ │ │ PropertyValue.java
│ │ │ │ │ PropertyValues.java
│ │ │ │ │ SingletonBeanRegistry.java
│ │ │ │ │
│ │ │ │ ├─support
│ │ │ │ │ AbstractAutowireCapableBeanFactory.java
│ │ │ │ │ AbstractBeanDefinitionReader.java
│ │ │ │ │ AbstractBeanFactory.java
│ │ │ │ │ BeanDefinitionReader.java
│ │ │ │ │ BeanDefinitionRegistry.java
│ │ │ │ │ CglibSubclassingInstantiationStrategy.java
│ │ │ │ │ DefaultListableBeanFactory.java
│ │ │ │ │ DisposableBeanAdapter.java
│ │ │ │ │ FactoryBeanRegistrySupport.java
│ │ │ │ │ InstantiationStrategy.java
│ │ │ │ │ SimpleInstantiationStrategy.java
│ │ │ │ │
│ │ │ │ └─xml
│ │ │ │ XmlBeanDefinitionReader.java
│ │ │ │
│ │ │ ├─context
│ │ │ │ │ ApplicationContext.java
│ │ │ │ │ ApplicationContextAware.java
│ │ │ │ │ ApplicationEvent.java
│ │ │ │ │ ApplicationEventPublisher.java
│ │ │ │ │ ApplicationListener.java
│ │ │ │ │ ConfigurableApplicationContext.java
│ │ │ │ │
│ │ │ │ ├─event
│ │ │ │ │ AbstractApplicationEventMulticaster.java
│ │ │ │ │ ApplicationContextEvent.java
│ │ │ │ │ ApplicationEventMulticaster.java
│ │ │ │ │ ContextClosedEvent.java
│ │ │ │ │ ContextRefreshEvent.java
│ │ │ │ │ SimpleApplicationEventMulticaster.java
│ │ │ │ │
│ │ │ │ └─support
│ │ │ │ AbstractApplicationContext.java
│ │ │ │ AbstractRefreshableApplicationContext.java
│ │ │ │ AbstractXmlApplicationContext.java
│ │ │ │ ApplicationContextAwareProcessor.java
│ │ │ │ ClasspathXmlApplicationContext.java
│ │ │ │
│ │ │ ├─core
│ │ │ │ └─io
│ │ │ │ ClasspathResource.java
│ │ │ │ DefaultResourceLoader.java
│ │ │ │ FileSystemResource.java
│ │ │ │ Resource.java
│ │ │ │ ResourceLoader.java
│ │ │ │ UrlResource.java
│ │ │ │
│ │ │ └─util
│ │ │ ClassUtils.java
│ │ │
│ │ └─resources
│ └─test
│ ├─java
│ │ └─com
│ │ └─akitsuki
│ │ └─springframework
│ │ │ ApiTest.java
│ │ │
│ │ └─bean
│ │ IUserDao.java
│ │ UserDao.java
│ │ UserDaoBeforeAdvice.java
│ │
│ └─resources
│ spring.xml
我们讨论AOP,就不能不讨论Advice。如果我们通览一遍代码的话,会发现与Advice相关的内容非常多。那么到底什么是Advice呢?这里的Advice,可以被解释为【通知】。我们知道,实际上AOP就是我们对原有bean的一系列增强,那么Advice就是我们要增强的逻辑。当然就我个人而言,我也倾向于把它感性地解释为【建议】,我给你的建议,就是对你功能的增强,这样去理解。那么接下来,我们就来完成AOP中的Advice系列内容。
这次我们准备做一个方法执行前的切面,所以我们需要有一个BeforeAdvice。
package com.akitsuki.springframework.aop;
import org.aopalliance.aop.Advice;
/**
* @author [email protected]
* @date 2022/12/6 14:52
*/
public interface BeforeAdvice extends Advice {
}
可以看到,它继承了一个标记接口Advice,代表我们的BeforeAdvice,也是一个Advice。那么我们还需要更加具体的去描述它,具体到哪里呢?方法。
package com.akitsuki.springframework.aop;
import java.lang.reflect.Method;
/**
* @author [email protected]
* @date 2022/12/6 15:00
*/
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, Object target) throws Throwable;
}
这次我们有了一个方法,before
。那么我们将要实现的before方法,就是我们要对真正bean要执行的方法,进行的增强内容了。我们在调用真正的bean方法之前,这里的before会先执行。
有了【建议】,那么提出建议的人是谁?自然是【建议者】了
package com.akitsuki.springframework.aop;
import org.aopalliance.aop.Advice;
/**
* @author [email protected]
* @date 2022/12/6 14:56
*/
public interface Advisor {
Advice getAdvice();
}
可以看到我们通过getAdvice方法,就可以获取到对应的【建议】。我们接下来,还要对这个概念进行细分。建议者,是谁的建议者呢?是【切点】的建议者
package com.akitsuki.springframework.aop;
/**
* @author [email protected]
* @date 2022/12/6 15:02
*/
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
我们上一章中也介绍了 Pointcut
,切点。它提供了两个方法,分别是获取类过滤器和方法匹配器。通过这两个方法,我们也可以看出,它的作用是用来判断【应该切谁】这件事情。通过类和方法的筛选,我们就可以确定到一个具体的对象了。
那么有了这么个接口,我们自然需要一个类去实现它,来让它更加具体。我们这次的实现,都是用AspectJ表达式来进行的,所以我们需要一个基于AspectJ的实现类,来实现这个接口。
package com.akitsuki.springframework.aop.aspect;
import com.akitsuki.springframework.aop.Pointcut;
import com.akitsuki.springframework.aop.PointcutAdvisor;
import org.aopalliance.aop.Advice;
/**
* @author [email protected]
* @date 2022/12/6 15:06
*/
public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {
/**
* 切面
*/
private AspectJExpressionPointcut pointcut;
/**
* 具体的拦截方法
*/
private Advice advice;
/**
* 表达式
*/
private String expression;
public void setExpression(String expression) {
this.expression = expression;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
@Override
public Advice getAdvice() {
return advice;
}
@Override
public Pointcut getPointcut() {
if (null == pointcut) {
pointcut = new AspectJExpressionPointcut(expression);
}
return pointcut;
}
}
这里有一些我们的老朋友,AspectJExpressionPointcut
是我们上一章实现的,它的主要内容是对类过滤器和方法匹配器的管理和使用,用它来判断一个bean是否应该被当前表达式进行切入。expression
则是我们的表达式,用来指定要切的位置的属性。
MethodInterceptor,方法拦截器。可以把即将要执行的方法拦截下来,在需要的时候再去执行(当然,你也可以不让它执行)。对于我们的Before切面来说,我们需要在方法执行前,执行我们的切面逻辑,然后再执行方法的正常逻辑。所以,我们需要一个Before的方法拦截器。
package com.akitsuki.springframework.aop.framework.adapter;
import com.akitsuki.springframework.aop.MethodBeforeAdvice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* @author [email protected]
* @date 2022/12/6 15:15
*/
public class MethodBeforeAdviceInterceptor implements MethodInterceptor {
private MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor() {}
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
this.advice.before(methodInvocation.getMethod(), methodInvocation.getArguments(), methodInvocation.getThis());
return methodInvocation.proceed();
}
}
可以看到,这里的逻辑,就是在方法执行前,调用了advice的before方法,执行了切面逻辑。
我们知道,实现代理有多种方式,所以我们需要一个工厂来帮我们生产对应的代理对象。
package com.akitsuki.springframework.aop.framework;
import com.akitsuki.springframework.aop.AdvisedSupport;
import lombok.AllArgsConstructor;
/**
* @author [email protected]
* @date 2022/12/6 15:19
*/
@AllArgsConstructor
public class ProxyFactory {
private AdvisedSupport advisedSupport;
public Object getProxy() {
return createProxy().getProxy();
}
private AopProxy createProxy() {
if (advisedSupport.isProxyTargetClass()) {
return new Cglib2AopProxy(advisedSupport);
}
return new JdkDynamicAopProxy(advisedSupport);
}
}
可以看到,我们具体使用什么方法来生产代理对象,是取决于 adviceSupport
的 isProxyTargetClass
的。通过true和false两种状态,来选择 cglib
和 jdk
两种方式。我们在使用时,只需要调用 getProxy
方法,就可以获得最终的被代理后的对象了。
写到这儿,都快结束了,终于扯到我们的主题了:如何与Spring的生命周期结合起来。我们知道,Spring的核心是Bean。我们生产的代理对象,终究也是Bean。而且,我们的这些Advice、Interceptor、Advisor之类的,也都需要成为一个Bean,才能够方便Spring进行管理。
但是仅仅作为Bean还不够,我们还需要将这个过程变得自动化。让Spring初始化完成这些Bean之后,我们的这一系列功能就顺带着被搞定了。所以这里就要用到我们前面几章学习到的工具:后置处理器PostProcessor。既然要在初始化Bean时候的完成,那么我们自然就要在这一步,加入处理器,来帮我们搞定AOP的一系列内容。
我们需要两样东西:一个处理类,一个后置处理器。处理类是真正干活的,处理器则是用来插入Bean创建过程的。这里我们需要新加入一种后置处理器,它在Bean创建的最开始进行工作,它用于提供一种特殊的Bean实例化过程,通过这种过程,Bean的实例化不需要走默认的那套实例化。
package com.akitsuki.springframework.beans.factory.config;
import com.akitsuki.springframework.beans.exception.BeanException;
/**
* @author [email protected]
* @date 2022/12/6 15:46
*/
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
/**
* bean实例化之前处理
* @param beanClass
* @param beanName
* @return
* @throws BeanException
*/
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException;
}
有了这么一个后置处理器,我们就来实现真正的处理类吧。
package com.akitsuki.springframework.aop.framework.autoproxy;
import com.akitsuki.springframework.aop.*;
import com.akitsuki.springframework.aop.aspect.AspectJExpressionPointcutAdvisor;
import com.akitsuki.springframework.aop.framework.ProxyFactory;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.BeanFactory;
import com.akitsuki.springframework.beans.factory.BeanFactoryAware;
import com.akitsuki.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import java.util.Collection;
/**
* @author [email protected]
* @date 2022/12/6 15:31
*/
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
private boolean isInfrastructureClass(Class<?> beanClass) {
return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeanException {
if (isInfrastructureClass(beanClass)) {
return null;
}
Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
for (AspectJExpressionPointcutAdvisor advisor : advisors) {
ClassFilter classFilter = advisor.getPointcut().getClassFilter();
if (!classFilter.matches(beanClass)) {
continue;
}
AdvisedSupport advisedSupport = new AdvisedSupport();
TargetSource targetSource = null;
try {
targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
advisedSupport.setProxyTargetClass(false);
return new ProxyFactory(advisedSupport).getProxy();
}
return null;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
}
好长好长,我们从上往下分析。
首先我们看到,这个类实现了我们上面的那个处理器,还有BeanFactoryAware接口,以便将BeanFactory注入进来。再往下是一个私有的判断方法,判断是否为这几种AOP中的基本类型。然后由于 InstantiationAwareBeanPostProcessor
继承了 BeanPostProcessor
,所以也有bean实例化前后的处理方法。不过在这里我们用不到,所以直接返回即可,我们的重头戏在 postProcessBeforeInstantiation
的实现。
在方法的开头,就对这些AOP的基本类型进行了判断,如果是这些基本类型,是不走代理的,所以直接返回。接下来是对所有Advisor的一个遍历,主要是为了找到一个满足当前Bean的class的一个Advisor,然后构造出一个AdvisedSupport,将TargetSource、方法拦截器、方法匹配器、代理类型等数据给封装进去。这里需要注意的是,真实对象的创建,是在构建TargetSource的时候,通过反射进行的。这里进行了一些简化处理,只调用了无参的构造方法,实际上这里应该更加完善。最后,新建一个代理工厂,生产出最终的代理对象进行返回,我们的处理就算完成了。
我们要做的最后一步,就是进行织入,结合,Engage!因为我们虽然有了后置处理器,但我们的这个后置处理器实际上还没有发挥作用,我们要将其融入Bean的创建过程中。所以我们还需要回到我们的老朋友 AbstractAutowireCapableBeanFactory
那里。
package com.akitsuki.springframework.beans.factory.support;
/**
* 抽象的实例化Bean类,提供创建Bean的能力,创建完成后,放入单例bean map中进行缓存
*
* @author [email protected]
* @date 2022/11/7 10:12
*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException {
Object bean;
try {
bean = resolveBeforeInstantiation(beanName, beanDefinition);
if (null != bean) {
return bean;
}
bean = createBeanInstance(beanDefinition, beanName, args);
//设置bean属性
applyPropertyValues(beanName, beanDefinition, bean);
//初始化bean,执行beanPostProcessor的前置和后置方法
initializeBean(beanName, bean, beanDefinition);
//注册实现了DisposableBean接口的对象
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
if (beanDefinition.isSingleton()) {
//创建好的单例bean,放入缓存
addSingleton(beanName, bean);
}
} catch (Exception e) {
throw new BeanException("创建bean失败", e);
}
return bean;
}
protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
if (null != bean) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
return bean;
}
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor processor : getBeanPostProcessors()) {
if (processor instanceof InstantiationAwareBeanPostProcessor) {
Object result = ((InstantiationAwareBeanPostProcessor) processor).postProcessBeforeInstantiation(beanClass, beanName);
if (null != result) {
return result;
}
}
}
return null;
}
}
这里我做了一些精简,只保留了有变化的方法,毕竟这个类经过多次扩充完善,已经变得越来越庞大了。
我们首先看织入点:createBean
方法。这里我们其实只需要看前几行就可以了。resolveBeforeInstantiation
是我们要做事情的方法,如果这个方法成功返回了一个Bean,那么就可以直接返回了,不需要走下面的那一套标准的Bean创建流程。这就是我们上面说的,提供一个特殊的Bean创建流程。那接下来我们看 resolveBeforeInstantiation
的内容。它的内容也很简单,是调用 applyBeanPostProcessorsBeforeInstantiation
,同样的,如果成功返回了一个Bean,则去调用实例化完成后置处理器,就完成了。看来我们的真正逻辑,还得跟踪到 applyBeanPostProcessorsBeforeInstantiation
中。去看一眼,原来如此,是拿到了所有的Bean后置处理器,然后挑出来 InstantiationAwareBeanPostProcessor
类型的,去挨个调用处理方法。如果有一个方法返回的结果不为空(也就意味着成功生产出了Bean),就返回。否则,继续找,直到找到为止,或者循环结束,最后返回null。结合我们上面的处理,可以看出,这里如果返回了null,就会走正常的bean创建逻辑。那么我们的这个处理器的逻辑就算织入完毕了。
这个系列写到现在,唯一不变的可能就是各种意义不明的标题了。无奈作者我是一个中二少年(少年?),就算成为了码农,还是会时不时泄露出来一些中二气息(笑。
好了,我们来看测试。上次测试中的接口和Dao,我们不需要修改。这次我们需要实现一个MethodBeforeAdvice,作为我们的切面处理。
package com.akitsuki.springframework.bean;
import com.akitsuki.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @author [email protected]
* @date 2022/12/7 10:09
*/
public class UserDaoBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("方法执行了:" + method);
}
}
实现了MethodBeforeAdvice接口,并且实现了before方法。可以推断,当方法被拦截下来之后,这里的before方法就会先执行,打印出相应的方法名。
然后,这次我们的配置文件,终于有用武之地了。我已经等不及了,快点端上来罢(无端联想
<beans>
<bean id="userDao" class="com.akitsuki.springframework.bean.UserDao"/>
<bean class="com.akitsuki.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean id="beforeAdvice" class="com.akitsuki.springframework.bean.UserDaoBeforeAdvice"/>
<bean id="methodInterceptor" class="com.akitsuki.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
<property name="advice" ref="beforeAdvice"/>
bean>
<bean id="pointcutAdvisor" class="com.akitsuki.springframework.aop.aspect.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* com.akitsuki.springframework.bean.IUserDao.*(..))"/>
<property name="advice" ref="methodInterceptor"/>
bean>
beans>
这次可不得了,一下子配置了好多的bean。首先是我们的老朋友,UserDao
。紧接着是我们的处理类,也就是实现了后置处理器,要去生产代理Bean的类,我们的小英雄。再往下,是我们刚才写的 UserDaoBeforeAdvice
,也就是真正AOP增强的内容。然后是方法拦截器,有了它我们才能拦截下方法,同时它也配置了一个属性advice,毕竟把方法拦截下来之后,具体做什么,还得看advice,自然就是我们上面的那个advice了。最后是我们的Advisor,它里面配置了表达式,用来表明我们具体要拦截哪个方法。这里的配置是拦截 IUserDao
的所有方法。然后它也有一个Advice,是我们的方法拦截器。到这里可能会有点乱,我们理一下。
我们的Advice,有两个:一个是 UserDaoBeforeAdvice
,这个是增强的内容。一个是 MethodBeforeAdviceInterceptor
,这个是方法拦截器,用来拦下方法的执行。具体的依赖关系是下面这样:
AspectJExpressionPointcutAdvisor
通过表达式进行匹配,依赖 MethodBeforeAdviceInterceptor
进行方法的拦截。MethodBeforeAdviceInterceptor
完成拦截后,则依赖 UserDaoBeforeAdvice
来执行增强内容。而这一系列过程,则都是通过 DefaultAdvisorAutoProxyCreator
在前置处理器中进行组装完成的。这就是整体的结构。
最后是我们的主要测试类
package com.akitsuki.springframework;
import com.akitsuki.springframework.bean.IUserDao;
import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import org.junit.Test;
/**
* @author [email protected]
* @date 2022/12/5 16:46
*/
public class ApiTest {
@Test
public void test() {
ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
IUserDao userDao = context.getBean("userDao", IUserDao.class);
System.out.println(userDao.queryUserName(1L));
}
}
嗯,非常简单,从这里完全看不出AOP的影子
测试结果
方法执行了:public abstract java.lang.String com.akitsuki.springframework.bean.IUserDao.queryUserName(java.lang.Long)
akitsuki
Process finished with exit code 0
结合的非常好~方法名被成功打印出来了。而且在使用中完全感觉不到AOP的存在,真正的润如细无声,无侵入性。
相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring
,这里对应的代码是mini-spring-12