Spring之Aop代理对象的产生(一)
目录
- 1. 准备
- 1.1. 接口
- 1.2. 接口的实现
- 1.3. 切面类
- 1.4. 测试类
- 1.5. 测试结果
- 2. `Spring Aop` 代理对象的产生
- 2.1. 源码解读之前
- 2.2. 抽象类 `AbstractBeanFactory` 的 `doGetBean()`
- 2.2.1. 单例 `bean` 的创建
- 2.2.2. 单例 `bean` 的创建 `createBean()`
- 2.2.2.1 单例 `bean` 具体创建的 `doCreateBean()`
- 2.2.2.1.1 单例 `bean` 初始化的 `initializeBean()`
- 2.2.2.1.2. 进入 `initializeBean()`
- 2.2.2.1.3. `initializeBean()` 流程步骤
- 3. 代理对象产生流程步骤
1. 准备
1.1. 接口
public interface UserService {String query();
}
1.2. 接口的实现
@Service
public class UserServiceImpl implements UserService {@Overridepublic String query() {System.out.println("------------ yz ------------");return "yz";}
}
1.3. 切面类
@Aspect
@Component
public class AopConfig {@Pointcut(value = "execution(* com.atguigu.springIOC.UserServiceImpl.*(..))")public void businessService() {}@Before(value = "businessService()")public void doBefore() {System.out.println("----------------- doBefore -----------------");}@AfterReturning(value = "businessService()")public void doAfterReturning() {System.out.println("----------------- doAfterReturning -----------------");}@After(value = "businessService()")public void doAfter() {System.out.println("----------------- doAfter -----------------");}
}
1.4. 测试类
// 该注解在测试时必须添加;在使用 springboot 较高版本启动运行程序时,可以不添加
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.atguigu.springIOC"})
public class BeanTest {public static void main(String[] args) {// 获取 spring IOC 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanTest.class);// 从容器中获取 beanUserService userService = (UserService) applicationContext.getBean("userServiceImpl");String query = userService.query();System.out.println(query);}
}
1.5. 测试结果
----------------- doBefore -----------------
------------ yz ------------
----------------- doAfter -----------------
----------------- doAfterReturning -----------------
yz
2. Spring Aop
代理对象的产生
2.1. 源码解读之前
既然要产生代理对象,我们进行源码分析,无非就是从下面两行代码入手
// 获取 spring IOC 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanTest.class);
// 从容器中获取 bean
UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
通过 spring 初始化 bean(单例) 过程 的文章分析,userServiceImpl
这个 bean
会通过 getBean()
从一级缓存 singletonObjects
中能获取到。那么 userServiceImpl
的代理对象肯定从下面的代码中产生
// 获取 spring IOC 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanTest.class);
2.2. 抽象类 AbstractBeanFactory
的 doGetBean()
关于代码
// 获取 spring IOC 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanTest.class);
的过程,鉴于篇幅本篇文章不再详细赘述,详细过程请查看,我们从这个 doGetBean
方法开始
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {// 通过 name 获取 beanName。这里不使用 name 直接作为 beanName 有两个原因// 1、name 可能会以 & 字符开头,表明调用者想获取 FactoryBean 本身,而非 FactoryBean// 实现类所创建的 bean。在 BeanFactory 中,FactoryBean 的实现类和其他的 bean 存储// 方式是一致的,即 <beanName, bean>,beanName 中是没有 & 这个字符的。所以我们需要// 将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。// 2、还是别名的问题,转换需要 &beanNamefinal String beanName = transformedBeanName(name);Object bean;// 先从缓存中获取,因为在容器初始化的时候或者其他地方调用过getBean,已经完成了初始化Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}// 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果// sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的// bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回// 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// 如果是原型不应该在初始化的时候创建,在这里直接抛出异常if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 获取parentBeanFactoryBeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {String nameToLookup = originalBeanName(name);// 如果parentBeanFactory存在,并且beanName在当前BeanFactory不存在Bean定义,则尝试从parentBeanFactory中获取bean实例if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {return (T) parentBeanFactory.getBean(nameToLookup, args);}else {return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {// 添加到alreadyCreated set集合当中,表示他已经创建过一次,做标记markBeanAsCreated(beanName);}try {// 根据beanName重新获取MergedBeanDefinition(步骤6将MergedBeanDefinition删除了,这边获取一个新的)final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 检查MergedBeanDefinitioncheckMergedBeanDefinition(mbd, beanName, args);// 拿到当前bean依赖的bean名称集合,在实例化自己之前,需要先实例化自己依赖的beanString[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {// 遍历当前bean依赖的bean名称集合for (String dep : dependsOn) {// 检查dep是否依赖于beanName,即检查是否存在循环依赖if (isDependent(beanName, dep)) {// 如果是循环依赖则抛异常throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 将dep和beanName的依赖关系注册到缓存中registerDependentBean(dep, beanName);try {// 获取dep对应的bean实例,如果dep还没有创建bean实例,则创建dep的bean实例getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// scope为 singleton 的bean创建if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> {try {// 创建Bean实例return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});// 返回beanName对应的实例对象bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// scope为 prototype 的bean创建 else if (mbd.isPrototype()) { Object prototypeInstance = null;try {// 创建实例前的操作(将beanName保存到prototypesCurrentlyInCreation缓存中)beforePrototypeCreation(beanName);// 创建Bean实例prototypeInstance = createBean(beanName, mbd, args);}finally {// 创建实例后的操作(将创建完的beanName从prototypesCurrentlyInCreation缓存中移除)afterPrototypeCreation(beanName);}// 返回beanName对应的实例对象bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {// 既不是单例也不是原型的 bean创建,可能是 request之类的// 根据scopeName,从缓存拿到scope实例String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {// 既不是单例也不是原型的bean创建(新建了一个ObjectFactory,并且重写了getObject方法)Object scopedInstance = scope.get(beanName, () -> {// 创建实例前的操作(将beanName保存到prototypesCurrentlyInCreation缓存中)beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {// 创建实例后的操作(将创建完的beanName从prototypesCurrentlyInCreation缓存中移除)afterPrototypeCreation(beanName);}});// 返回beanName对应的实例对象bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {// 如果创建bean实例过程中出现异常,则将beanName从alreadyCreated缓存中移除cleanupAfterBeanCreationFailure(beanName);throw ex;}}// 检查所需类型是否与实际的bean对象的类型匹配if (requiredType != null && !requiredType.isInstance(bean)) {try {// 类型不对,则尝试转换bean类型T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;
}
注意下面的代码,第一次(启动初始化 IOC
容器时 )sharedInstance
肯定为 null
// 先从缓存中获取,因为在容器初始化的时候或者其他地方调用过getBean,已经完成了初始化
Object sharedInstance = getSingleton(beanName);
2.2.1. 单例 bean
的创建
由于 userServiceImpl
是单例的,所以我们重点关注
if (mbd.isSingleton()) {// scope为 singleton 的bean创建(新建了一个ObjectFactory,并且重写了getObject方法)sharedInstance = getSingleton(beanName, () -> {try {// 创建Bean实例return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});// 返回beanName对应的实例对象// 这里主要处理实现了FactoryBean的情况,需要调用重写的getObject()方法来获取实际的Bean实例bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
在这里使用 idea
条件断点进行代码调试,在下面这行打断点
sharedInstance = getSingleton(beanName, () -> {
进入 getSingleton
方法如下图
重新打条件断点,进入 singletonFactory.getObject()
方法
2.2.2. 单例 bean
的创建 createBean()
来到 AbstractAutowireCapableBeanFactory
的 createBean
方法
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {if (logger.isDebugEnabled()) {logger.debug("Creating instance of bean '" + beanName + "'");}// 确保对应BeanClass完成解析,具体表现是进行了ClassLoder.loadClass或Class.forName完成了类加载resolveBeanClass(mbd, beanName);try {// 准备方法覆盖,主要为lookup-method,replace-method等配置准备mbd.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// 供特定后置处理器拓展,如果直接生成了一个Bean,就直接返回不走正常创建流程。// 具体逻辑是判断当前Spring容器是否注册了实现了InstantiationAwareBeanPostProcessor接口的后置处理器// 如果有,则依次调用其中的applyBeanPostProcessorsBeforeInstantiation方法,如果中间任意一个方法返回不为null,直接结束调用。// 然后依次所有注册的BeanPostProcessor的postProcessAfterInitialization方法(同样如果任意一次返回不为null,即终止调用。Object bean = resolveBeforeInstantiation(beanName, mbd);// 如果不为空,说明提前生成了实例,直接返回if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}// 具体创建Bean逻辑Object beanInstance = doCreateBean(beanName, mbd, args);if (logger.isDebugEnabled()) {logger.debug("Finished creating instance of bean '" + beanName + "'");}return beanInstance;
}
断点调试发现,下面会产生代理对象,继续追踪
// 具体创建Bean逻辑
Object beanInstance = doCreateBean(beanName, mbd, args);
2.2.2.1 单例 bean
具体创建的 doCreateBean()
来到了 AbstractAutowireCapableBeanFactory
的 doCreateBean
方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// BeanWrapper封装了具体的Bean实例,然后可以很方便地通过调用getPropertyValue和setPropertyValue等方法反射读写Bean的具体属性BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {// 先尝试从缓存中取instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {// 调用构造方法创建一个空实例对象,并用BeanWrapper进行包装instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// 获取所有的后置处理器,如果后置处理器实现了MergedBeanDefinitionPostProcessor接口,则一次调用其postProcessMergedBeanDefinition方法synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// 如果满足循环依赖缓存条件,先缓存具体对象boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");} // 循环依赖处理逻辑:将已完成实例化,但是未完成属性赋值和相关的初始化的一个不完整的 bean 添加到三级缓存 singletonFactories 中// 具体内部会遍历后置处理器,判断是否有SmartInstantiationAwareBeanPostProcessor的实现类,然后调用里面getEarlyBeanReference覆盖当前Bean// 默认不做任何操作返回当前Bean,作为拓展,这里比如可以供AOP来创建代理类addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// 开始对Bean实例进行初始化Object exposedObject = bean;try {// 对bean进行属性填充,在这里面完成依赖注入的相关内容populateBean(beanName, mbd, instanceWrapper);// 完成属性依赖注入后,进一步初始化Bean// 具体进行了以下操作:// 1.若实现了BeanNameAware, BeanClassLoaderAware,BeanFactoryAwareAware等接口,则注入相关对象// 2.遍历后置处理器,调用实现的postProcessBeforeInitialization方法,// 3.如果实现了initialzingBean,调用实现的 afterPropertiesSet()// 4.如果配置了init-mothod,调用相应的init方法// 5.遍历后置处理器,调用实现的postProcessAfterInitializationexposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// 如果实现了Disposable接口,会在这里进行注册,最后在销毁的时候调用相应的destroy方法try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;
}
2.2.2.1.1 单例 bean
初始化的 initializeBean()
在 doCreateBean()
中会产生代理对象的代码(注意:代理对象的产生是在实例化 bean
和 bean
的属性填充之后进行的)
exposedObject = initializeBean(beanName, exposedObject, mbd);
doCreateBean(beanName, mbd, args)
的主要方法流程
createBeanInstance(beanName, mbd, args)
:实例化bean
,调用对象的构造方法实例化对象addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName,mbd, bean))
:将已完成实例化,但是未完成属性赋值和相关的初始化的一个不完整的bean
添加到三级缓存singletonFactories
中populateBean(beanName, mbd, instanceWrapper)
:对bean
进行属性填充initializeBean(beanName, exposedObject, mbd)
:完成bean
的属性填充注入后,进一步初始化bean
,在此过程中产生代理对象。此时bean
的创建工作正式完成,已经可以在项目中使用了registerDisposableBeanIfNecessary(beanName, bean, mbd)
:如果符合bean
的销毁条件,则执行单例bean
的销毁工作
2.2.2.1.2. 进入 initializeBean()
进入 AbstractAutowireCapableBeanFactory
的 initializeBean
方法
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {// 第一步:先执行所有的AwareMethodsif (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());} else {// 如果实现了Aware接口,就对该bean进行一些设置// 比如实现了BeanNameAware接口,那么对其bean的属性beanName上设置对应的beanName// 如果实现了BeanFactoryAware接口,那么对其beanFactory属性设置上创建该bean使用的bean工厂invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {// 执行所有的BeanPostProcessor#postProcessBeforeInitialization 初始化之前的处理器方法// 规则:只要谁反悔了null,后面的就都不要执行了// 这里面实现postProcessBeforeInitialization 的处理器就很多了,有很多对Aware进行了扩展的wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {// 如果bean实现了InitializingBean或者用户自定义的init方法方法,那么调用这些初始化方法对bean的属性进行一些个性化设置invokeInitMethods(beanName, wrappedBean, mbd);} catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {// 执行后置处理器的postProcessAfterInitialization方法。AOP的原理和实现就在其中wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;
}
2.2.2.1.3. initializeBean()
流程步骤
- 调用各类感知
Aware
接口 - 执行
applyBeanPostProcessorsBeforeInitialization
初始化前的处置操作 - 调用
InitializingBean
接口初始化 (如果配置了method-init
,则调用其方法初始化 ) - 调用
applyBeanPostProcessorsAfterInitialization
初始化之后的处置操作,AOP
的原理和实现就在其中
3. 代理对象产生流程步骤
- 在
IOC
容器启动初始化时,首先会去一级缓存singletonObjects
中去获取(肯定没有) - 在根据当前
bean
的具体作用域,去初始化bean
- 在做完
bean
的一些校验之后,会进行bean
的实例化,即调用createBeanInstance()
方法 - 如果存在
bean
的循环依赖的情况,会将当前已实例化的bean put
进三级缓存singletonFactories
中 - 接着会进行
bean
的相关属性填充,即调用populateBean()
方法,注意此时还并没有产生代理对象 - 再接着会进行
bean
的初始化操作,即调用initializeBean()
方法 - 后续更新查看
Spring之Aop代理对象的产生(一)相关推荐
- Spring之AOP代理模式
代理模式分类: 静态代理 动态代理 你要租房,不找房东,找中介,中介是房东的代理. 1.1静态代理 角色分析 抽象角色:一般会使用接口或者抽象类来解决 /*** @author LongXi* @cr ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象【两万字】
基于最新Spring 5.x,介绍了Spring AOP中的AspectJAwareAdvisorAutoProxyCreator自动代理创建者的工作流程,对于创建代理对象的源码进行了深度分析! ...
- spring事务--使用aop事务代理对象调用方法示例
第一种获取aop代理对象的方式: 第二种方式获取aop代理对象:
- 动态代理以及对应Spring中AOP源码分析
AOP(面向切面编程)在Spring中是被广泛应用的(例如日志,事务,权限等),而它的基本原理便是动态代理. 我们知道动态代理有两种:基于JDK的动态代理以及基于CGlib动态代理.以下是两种动态代理 ...
- spring的aop的动态代理机制都有哪些_Spring学习(4):Spring AOP
Spring AOP说明 AOP(Aspect Oriented Pragraming)面向切面编程,AOP采用横向抽取机制,取代了传统纵向继承体系的重复性代码(性能监视.事务管理.安全检查.缓存). ...
- Spring通知类型及使用ProxyFactoryBean创建AOP代理
Spring 通知类型 通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口. Spr ...
- 手写Spring-第十五章-我也要注入?为代理对象注入属性,完善AOP
前言 我们上一章实现了用注解为对象注入属性.但是还有一个地方我们没有照顾到,那就是代理对象属性的注入.你可能会疑惑,我们的Bean,不都是通过cglib代理出来的吗?那不都是代理对象?但我们这里说的代 ...
- AOP之proceedingjoinpoint和joinpoint区别(获取各对象备忘)、动态代理机制及获取原理代理对象、获取Mybatis Mapper接口原始对象...
现在AOP的场景越来越多,所以我们有必要理解下和AOP相关的一些概念和机制. import org.aspectj.lang.reflect.SourceLocation; public interf ...
- Spring学习总结——Spring实现AOP的多种方式
目录 一.基于XML配置的Spring AOP 二.使用注解配置AOP 三.AspectJ切点函数 四.AspectJ通知注解 五.零配置实现Spring IoC与AOP 六.示例下载 AOP(Asp ...
最新文章
- JAVA拾遗--关于SPI机制
- 生成Base58格式的UUID(Hibernate Base64格式的UUID续)
- java修饰符继承_Java修饰符和继承
- JAVA_出神入化学习路线大纲
- 《用python写网络爬虫》完整版+源码
- 智能客户端(Smart Client )中文文档及案例(转贴)
- 解决vbox挂载VBoxGuestAdditions失败
- windos不能在本地计算机启动服务器,WDS 服务器可能无法启动 - Windows Server | Microsoft Docs...
- Livereload介绍
- Automative SPICE 之五 过程能力层次和过程属性
- 计算单词的长度C++
- Javascript带按钮的轮播广告
- c++多线程之packaged_task
- Apple Watch更懂女人心
- 5.PS-快速选择和魔棒
- 分发服务器性能,高性能P2P流媒体内容分发服务器的设计与实现
- qlv视频转换器免费版_腾讯视频素材下载和转换教程
- Java实现hanoi塔
- 7-6 7-7 7-8 7-9 7-10
- windows 修改磁盘盘符教程
热门文章
- 阿里灵杰问天引擎电商搜索 -- 数据说明
- CTR介绍,数据集往往为表格形式,训练集使用历史的日志数据,然后进行特征归一化、离散化和特征哈希等操作,最终一条训练集为一行多列的二分类任务。
- android camera调试打印信息,Android : 高通平台Camera调试
- flutter能开发游戏吗_Flutter开发游戏初体验,喜大普奔
- 顺序容器和关联容器添加新元素方法详解
- java打开文件对话框
- 基于python快速实现排列组合算法
- 基础集合论笔记 目录
- 锁php_php+redis实现分布式锁
- android获取当前显示的view,Android中ViewPager获取当前显示的Fragment