AOP 基本概念
OOP是自上而下從controller-service-dao-數(shù)據(jù)庫。
AOP(Aspect-Oriented Programming)面向切面編程。AOP工作原理一句話概括:通過代理模式為目標(biāo)對(duì)象生產(chǎn)代理對(duì)象,并將橫切邏輯插入到目標(biāo)方法執(zhí)行的前后。
術(shù)語
切面(Aspect)
切面是切點(diǎn)和通知的集合,一般單獨(dú)作為一個(gè)類。通知和切點(diǎn)共同定義了關(guān)于切面的全部內(nèi)容。
白話文理解:影響了多個(gè)類的公共行為封裝到一個(gè)可重用模塊。
切點(diǎn)(PointCut)
切點(diǎn)是對(duì)一系列代表同種功能(目的)的切入點(diǎn)(連接點(diǎn))的統(tǒng)稱,切點(diǎn)不是一個(gè)點(diǎn),而是代表某一功能的一系列連接點(diǎn)的集合。通常使用明確的類或者方法名稱,或是利用正則表達(dá)式定義所匹配的類和方法來指定這些切點(diǎn)。
通知(Advice)
通知就是我們要在切點(diǎn)執(zhí)行的操作,就是我們要實(shí)現(xiàn)的目的,是要實(shí)現(xiàn)的功能的代碼實(shí)現(xiàn)。一般通知又稱為增強(qiáng)。
通知有物種類型:
Before在方法被調(diào)用之前調(diào)用After在方法完成后調(diào)用通知,無論方法是否執(zhí)行成功After-returning在方法成功執(zhí)行之后調(diào)用通知After-throwing在方法拋出異常后調(diào)用通知Around通知了好、包含了被通知的方法,在被通知的方法調(diào)用之前后調(diào)用之后執(zhí)行自定義的行為
這里需要注意Around不是執(zhí)行兩次。如下代碼,而是在pjp.proceed()方法前后的環(huán)繞。
@Around("controllerAspect()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = new StopWatch();
Object result = null;
Transaction transaction = null;
String uri = "";
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ServletContext ctx = request.getSession().getServletContext();
uri = request.getRequestURI().replace(ctx.getContextPath(), "").toLowerCase();
uri = uri.substring(uri.lastIndexOf("/") + 1);
if (uri.trim().matches("^\\d+$")) {
String numberUri = uri.trim();
uri = request.getRequestURI().replace(ctx.getContextPath(), "")
.replace("/", "").replace(numberUri, "")
.toLowerCase();
}
logger.info(String.format("controller path uri:%s", uri));
transaction = Cat.newTransaction(TYPE, uri);
Cat.logMetric(uri);
// 執(zhí)行方法
result = pjp.proceed();
transaction.setStatus(Transaction.SUCCESS);
// 默認(rèn)執(zhí)行完方法不報(bào)錯(cuò)就是SUCCESS
Cat.logEvent(TYPE, uri, Event.SUCCESS, "");
} catch (Throwable e) {
logger.error("方法執(zhí)行失敗 controller path=" + uri, e);
transaction.setStatus(e);
// 默認(rèn)執(zhí)行完報(bào)錯(cuò)就記失敗
Cat.logEvent(TYPE, uri, e.getMessage(), "");
throw e;
} finally {
transaction.complete();
}
logger.info("controller path={} spent {} seconds", uri, stopWatch.elapsedTime());
return result;
}
連接點(diǎn)(JoinPoint)
連接點(diǎn)是在應(yīng)用執(zhí)行過程中能夠插入切面的一個(gè)點(diǎn)。這個(gè)點(diǎn)可以是調(diào)用方法時(shí),拋出異常時(shí),甚至修改一個(gè)字段時(shí)。切面代碼可以利用這些點(diǎn)插入應(yīng)用的正常流程中,并添加新的行為。Spring只支持方法的連接點(diǎn)
織入(Weaving)
織入是把切面應(yīng)用到目標(biāo)對(duì)象并創(chuàng)建新的代理對(duì)象的過程。切面在指定的連接點(diǎn)被織入到目標(biāo)對(duì)象中。在目標(biāo)對(duì)象的生命周期里有多個(gè)點(diǎn)可以進(jìn)行織入。
Spring AOP的切面織入是在運(yùn)行時(shí)被織入,原理是使用了動(dòng)態(tài)代理技術(shù),Spring支持兩種方式生產(chǎn)代理對(duì)象:JDK動(dòng)態(tài)代理和CGLIB,默認(rèn)的策略是如果目標(biāo)類是接口,則使用JDK動(dòng)態(tài)代理技術(shù),否則使用Cglib來生成代理
引入(Introduction)
添加方法或字段到被通知的類。Spring允許引入新的接口到任何被通知的對(duì)象。Spring中要使用Introduction, 可有通過DelegatingIntroductionInterceptor來實(shí)現(xiàn)通知,通過DefaultIntroductionAdvisor來配置Advice和代理類要實(shí)現(xiàn)的接口。
目標(biāo)對(duì)象(Target Object)
包含連接點(diǎn)的對(duì)象。也被稱作被通知或被代理對(duì)象。
AOP代理(AOP Proxy)
AOP框架創(chuàng)建的對(duì)象,包含通知。 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。
源碼分析
**1、AOPNamespaceHandler中init是在什么時(shí)候調(diào)用的
2、對(duì)于XML配置AOP加載過程已了解,那么對(duì)于SpringBoot配置AOP加載過程呢。**
**XML配置AOP加載過程。
3、AOP有幾種配置方式,每種配置方式,源碼都是怎么走向。**
1、xml配置 <aop:config>標(biāo)簽使用分析
refresh
-> obtainFreshBeanFactory
-> refreshBeanFactory
-> AbstractRefreshableApplicationContext.loadBeanDefinitions(beanFactory)
-> ...
-> XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)
-> DefaultBeanDefinitionDocumentReader.parseBeanDefinitions
對(duì)于<aop:config>是自定義的標(biāo)簽不是defaultNamespace(默認(rèn)是bean標(biāo)簽),那么走的是parseCustomElement方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
那么這個(gè)Handler就對(duì)應(yīng)META/spring.handlers文件中。Spring各個(gè)jar包中的spring.handlers都會(huì)生效。

我們在看下NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);中的resolve方法,此方法拿到namespaceHandler之后會(huì)調(diào)用init方法。
@Override
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}
然后繼續(xù)看parseCustomElement方法handler.parse方法,<aop:config>標(biāo)簽又交給了ConfigBeanDefinitionParser來解析.
首先findParseForElement獲取parser然后進(jìn)行調(diào)用parse方法。
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
我們來看看ConfigBeanDefinitionParser的parse方法:大致是注冊一個(gè)bean,以及切點(diǎn)、增強(qiáng)、切面的解析(pointcunt\advisor\aspect)
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
configureAutoProxyCreator(parserContext, element);一路跟蹤到如下
public static void registerAspectJAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//注冊一個(gè)AspectJAwareAdvisorAutoProxyCreator,
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//解析配置元素,決定代理的模式
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//作為系統(tǒng)組件,把這個(gè)creator這個(gè)bean,放到Spring容器中,讓Spring實(shí)力化,啟動(dòng)這個(gè)Creator
registerComponentIfNecessary(beanDefinition, parserContext);
}
在bean實(shí)例化完成后,會(huì)調(diào)用BeanPostProcessor的postProcessAfterInitialization方法在其父類AbstractAutoProxyCreator中實(shí)現(xiàn),
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
跟蹤wrapIfNecessary方法,會(huì)進(jìn)入到createProxy方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
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.
// 意思就是如果該類有advice則創(chuàng)建proxy,
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 1.通過方法名也能簡單猜測到,這個(gè)方法就是把bean包裝為proxy的主要方法,
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
// 2.返回該proxy代替原來的bean
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
createProxy方法中最后一句proxyFactory.getProxy會(huì)進(jìn)入到DefaultAopProxyFactory的createAopProxy方法。
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);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
@Override
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.");
}
//如果bean的類是接口或者類是JDK內(nèi)部的代理類,則使用JDK的動(dòng)態(tài)代理類。
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
//其他情況使用CGLIB來實(shí)現(xiàn)。
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
創(chuàng)建代理類后,其余過程與bean的生命周期基本一致。
2、注解配置 <aop:aspectj-autoproxy>標(biāo)簽使用分析
與aop:config標(biāo)簽大致類似,區(qū)別在于:
1、此標(biāo)簽交給AspectJAutoProxyBeanDefinitionParser去進(jìn)行解析。
2、AspectJAutoProxyBeanDefinitionParser解析時(shí)候注冊的是AnnotationAwareAspectJAutoProxyCreator的bean。
3、SpringBoot配置AOP
SpringBoot項(xiàng)目自動(dòng)化配置是讀取spring-boot-autoconfigure項(xiàng)目中的/META-INF/spring.factories的文件,以及json配置文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
{
"name": "spring.aop.auto",
"type": "java.lang.Boolean",
"description": "Add @EnableAspectJAutoProxy.",
"defaultValue": true
},
我們看下AopAutoConfiguration
@Configuration
//依賴于EnableAspectJAutoProxy與Aspect與Advice
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
//spring.aop.auto=true此類才會(huì)進(jìn)行加載
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
//proxyTargetClass默認(rèn)為false
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
public static class CglibAutoProxyConfiguration {
}
}
那我們繼續(xù),來看EnableAspectJAutoProxy注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false;
/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;
}
導(dǎo)入了AspectJAutoProxyRegistrar配置我們來看AspectJAutoProxyRegistrar源碼
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
這里是不是很熟悉,相當(dāng)于<aop:aspectj-autoproxy>標(biāo)簽使用,注冊AnnotationAwareAspectJAutoProxyCreator。springboot加載過程就是這樣。
仔細(xì)想想SpringBoot是基于注解的形式,那么肯定是有什么地方調(diào)用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
4、AopNamespaceHandler中init方法
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
一二行上面以及介紹過了,對(duì)于scoped-proxy可以理解為作用域代理。Spring中作用域默認(rèn)是singleton.如果想使用其他除了單例模式以外的作用域,則需要添加scoped-proxy標(biāo)簽,默認(rèn)走CGLIB代理。跟蹤代碼到ScopedProxyBeanDefinitionDecorator類中。
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
boolean proxyTargetClass = true;
if (node instanceof Element) {
Element ele = (Element) node;
if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS));
}
}
// Register the original bean definition as it will be referenced by the scoped proxy
// and is relevant for tooling (validation, navigation).
BeanDefinitionHolder holder =
ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
parserContext.getReaderContext().fireComponentRegistered(
new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
return holder;
}
再看其createScopedProxy方法
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
}
else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
// Copy autowire settings from original bean definition.
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// Register the target bean as separate bean in the factory.
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// Return the scoped proxy definition as primary bean definition
// (potentially an inner bean).
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
對(duì)于創(chuàng)建ScopedProxyFactoryBean要看其中的setBeanFactory方法
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
}
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
this.scopedTargetSource.setBeanFactory(beanFactory);
ProxyFactory pf = new ProxyFactory();
pf.copyFrom(this);
pf.setTargetSource(this.scopedTargetSource);
Class<?> beanType = beanFactory.getType(this.targetBeanName);
if (beanType == null) {
throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
"': Target type could not be determined at the time of proxy creation.");
}
if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
}
// Add an introduction that implements only the methods on ScopedObject.
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
// Add the AopInfrastructureBean marker to indicate that the scoped proxy
// itself is not subject to auto-proxying! Only its target bean is.
pf.addInterface(AopInfrastructureBean.class);
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}
spring-configured不太了解就不多說了,有了解的可以一起交流。
好了寫到這里,喜歡的大家互動(dòng)起來。