Object-oriented programming (OOP) is what we use most in business development, because its code is logical and intuitive, and you can see the complete execution link from the top down.
Extending this foundation, there is the emergence of section-oriented programming (AOP), which extracts repeatable crosscutting logic into unified modules.
For example, log printing, security monitoring, if the IDEA of OOP is to add duplicate code before and after each method, then there will be too many changes to maintain. So AOP programming came along, and AOP’s focus was horizontal, as opposed to OOP’s vertical.
So let’s learn how AOP is used and the processing logic in the Spring container
How to use
Before the business development needs to use AOP, so I also compiled a Spring custom annotations to implement AOP, interested students can go to see ~
Here’s an example from the book:
Create the bean for interception
public class TestAopBean {
private String testStr = "testStr";
public void testAop(a) {
// The intercepted method, simply print
System.out.println("I am the true aop bean"); }}Copy the code
Create the Advisor
@Aspect
public class AspectJTest {
@Pointcut("execution(* *.testAop(..) )")
public void test(a) {}@Before("test()")
public void beforeTest(a) {
System.out.println("before Test");
}
@After("test()")
public void afterTest(a) {
System.out.println("after Test");
}
@Around("test()")
public Object aroundTest(ProceedingJoinPoint joinPoint) {
System.out.println("around Before");
Object o = null;
try {
// Call the method of the aspect
o = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around After");
returno; }}Copy the code
First, the @aspect annotation makes Spring realize that this is a Aspect bean and marks the method @pointcut (“execution(* *.testaop (..)). The expression inside execution() indicates the intercepted method. Before, After, and Around indicate execution Before and After the intercepted method, respectively.
Create the configuration file aop.xml
<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<! Enable AOP functionality -->
<aop:aspectj-autoproxy />
<bean id="aopTestBean" class="aop.TestAopBean"/>
<bean class="aop.AspectJTest" />
</beans>
Copy the code
Test the Demo
public class AopTestBootstrap {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop/aop.xml");
TestAopBean bean = (TestAopBean) context.getBean("aopTestBean");
bean.testAop();
// The output sequence of the enhancement method is as follows:
// Around proceed -> Before -> Around proceed -> After
//around Before
//before Test
//I am the true aop bean
//around After
//after Test}}Copy the code
Based on the startup example above, we found that the core business method testAop() we wrote simply printed I am the True AOP Bean, but the execution result printed something else, indicating that the class has been enhanced. We extended it without modifying the core business method. It is proved that AOP can make the auxiliary functions independent of the core business, which facilitates the expansion and decoupling of programs.
Easy to use, let’s see how Spring implements AOP functionality
Dynamic AOP custom tags
The AOP implementation also uses custom annotations, according to the IDEA of custom tags: for each custom tag, there is a corresponding parser, and then with the help of the powerful development tool IDEA positioning function, find where the parser registered:
- Hold down the
ctrl
, corresponding to the positioning labelxsd
file - According to the named file, in
META-INF
I found it in the directoryspring.handlers
file - The processor was found in the processor file
AopNamespaceHandler
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init(a) {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config".new ConfigBeanDefinitionParser());
// Note 8.1 Custom annotations, registered parser, element name is AspectJ-AutoProxy
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".newSpringConfiguredBeanDefinitionParser()); }}Copy the code
The processor inherits fromNamespaceHandlerSupport
, will be executed during loadinginit
The initialization method, in this case, registersaspectj-autoproxy
Type resolverAspectJAutoProxyBeanDefinitionParser
You’ve already seen how to register a custom parser, so let’s take a quick look at how the application resolves a bean of type AspectJ-AutoProxy.
Registered AnnotationAwareAspectJAutoProxyCreator
When parsing, its entry method is as follows:
public BeanDefinition parse(Element element, ParserContext parserContext) {
/ / aop annotation parsing entrance, registered AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// Handling of subclasses in annotations
extendBeanDefinition(element, parserContext);
return null;
}
Copy the code
The entry method is as concise as ever, telling you what to do, and then handing over the complex logic to the utility class or subclass to implement it, so the next thing to look at is how to registerAnnotationAwareAspectJAutoProxyCreator
.
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
/ / by tools, AspectJAnnotationAutoProxyCreator registration or upgrade
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// Handle proxy-target-class and expose-proxy properties
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// Register the component and notify it for the listener to process
registerComponentIfNecessary(beanDefinition, parserContext);
}
Copy the code
We can see that there are three processing logic in this method, so we will analyze them one by one:
AnnotationAwareAspectJAutoProxyCreator register or upgrade
For the realization of AOP, basically all is by AnnotationAwareAspectJAutoProxyCreator to complete, it can automatically according to the tangent Point of the @ Point annotations define agent matching bean.
Due to theSpring
Did a lot of work for us, so developmentAOP
Business can be so simple, even the configuration is also simplified a lot, so take a lookSpring
How to use custom configuration to help us automatically registerAnnotationAwareAspectJAutoProxyCreator
.
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {
/ / AnnotationAwareAspectJAutoProxyCreator actually registered type of bean
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired( Class
cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// If an automatic proxy creator already exists in Registry and the type of the agent passed in is not the same as the registered one, determine whether it needs to be modified based on priority
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if(! cls.getName().equals(apcDefinition.getBeanClassName())) {// Select which one to use according to the priority
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
// Change the registered beanName to use the passed proxy creatorapcDefinition.setBeanClassName(cls.getName()); }}// Since the proxy already exists, the default Settings are not required
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
// The default is the minimum priority
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
Automatic proxy creator / / registered name is always org. Springframework. Aop. Config. InternalAutoProxyCreator
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
Copy the code
In this step, and realized automatic registration AnnotationAwareAspectJAutoProxyCreator class, at the same time can see involves the concept of the priority has always been AUTO_PROXY_CREATOR_BEAN_NAME and registered name.
Handles proxy-target-class and expose-proxy properties
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if(sourceElement ! =null) {
// This method simply resolves the following two properties and, if true, adds them to the properties list of the proxy registry
// definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE)
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
// Handle proxy-target-class attributes
// Depending on how the code is generated, use JDK dynamic proxy or Cglib in the next step
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
// Handle the expose-proxy property
// Extend the enhancement, sometimes the target object internal self-call does not implement the enhancement in the aspect, through this property can be enhanced to both methodsAopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}}Copy the code
About AopConfigUtils. ForceAutoProxyCreatorToUseClassProxying (registry); Method, which is the process of setting properties. If the resolved properties are true, they are added to the property list of the proxy registry, which is not covered here.
Familiarize these two properties separately:
proxy-target-class
Spring AOP
Some useJDK
Dynamic Proxy + InvocationHandler, orCGLIB
(Code Generation LIB) to create a proxy for the target object. According to the book, the recommended use isJDK
Dynamic proxy.
JDK dynamic proxies are used if the proxied target object implements at least one interface. All interfaces implemented by this target type will be proxied.
If the target object ** does not implement any interface, a CGLIB proxy is created. ** If you want to proxy all the methods of the target object, not just those implemented from the interface, you can use this proxy-target-class property to enable the mandatory use of the CGLIB proxy.
However, forcibly enabling CGLIB causes the following two problems:
- Final methods cannot be advised at the same time because they cannot be overwritten
- The CGLB binary distribution needs to be placed under the CLASspath
With these two aspects in mind, there are two ways to force the CGLIB agent on:
<! -- one -->
<aop:config proxy-target-class="true">.</aop:config>
<! -- two -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
Copy the code
The relevantCGLIB
Agent, the elder brother spoke very thoroughly, suggested that we can go to understand ~Cglib and its basic use
expose-proxy
Sometimes the internal self-invocation of the target object will not be able to implement the enhancement in the aspect.
For example, both methods have the transaction annotation @transactional but the transaction type is different:
public interface TestService {
void a(a);
void b(a);
}
public class TestServiceImpl implements TestService {
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void a(a) {
this.b();
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b(a) {
System.out.println("Hello world"); }}Copy the code
Where this refers to the target object, the this.b() method will not perform the slice of the B transaction, that is, will not perform transaction enhancement.
To solve this problem, the a() and B () methods can be enhanced simultaneously with expose-proxy:
<! -- one -->
<aop:config expose-proxy="true">.</aop:config>
<! -- two -->
<aop:aspectj-autoproxy expose-proxy="true"/>
Copy the code
Register the component and notify
Emmmm, the internal logic of this method is as clear as its name, so I won’t go into details.
Creating an AOP proxy
Mainly around in front of the automatic proxy AnnotationAwareAspectJAutoProxyCreator registration process to explain that the next see agent is done automatically to complete the operation of AOP.
The following is AnnotationAwareAspectJAutoProxyCreator inheritance system:
In the upper right corner of the image, you can see that it implements the BeanPostProcessor interface, which, as mentioned in the previous article, is a post-processor that can be extended before and after bean instantiation. Reviewed the implements the interface of the two methods, postProcessBeforeInitialization didn’t do, return the object directly.
What we’re actually dealing with ispostProcessAfterInitialization
Methods,bean
After instantiation, the proxy enhancement is performed in this step, so take a look at this method:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if(bean ! =null) {
/ / assembly key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {// Encapsulate the specified bean if it is suitable to be proxied
returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// If it has already been processed
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// No enhancement is required
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
/ / whether the given bean class represents a class of infrastructure, infrastructure class should not be acting | | configuration specified beans do not need to agent
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create a proxy if there is an enhancement method
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if(specificInterceptors ! = DO_NOT_PROXY) {// The enhancement method is not null
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// Create the proxy
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;
}
Copy the code
To extract the core flow:
- Gets the enhancer method or enhancerWe just wrote that
@Before
、@After
Something like that, which is the enhancement method,AOP
Identify these enhancements first. - Proxy based on the obtained enhancementWhen enhancements are found, they need to be enhanced proxies, actually this
bean
It’s not exactly the old type anymore, it’s going to be the proxied type.
Gets the enhancer method or enhancer
The entry method is here:
protectedObject[] getAdvicesAndAdvisorsForBean( Class<? > beanClass, String beanName,@Nullable TargetSource targetSource) {
// Find a matching slice
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class
beanClass, String beanName) {
// Get classes declared as AspectJ annotations from beanFactory, and extract those classes from the enhancer
/ / delegated to subclass implementation org. Springframework. Aop. Aspectj. Autoproxy. AspectJAwareAdvisorAutoProxyCreator. ExtendAdvisors
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// Find the matching enhancer
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if(! eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); }return eligibleAdvisors;
}
Copy the code
Getting an enhancement method for a given bean consists of getting all the enhancements and finding all the enhancements that apply to the bean and applying them. This corresponds to the two methods findCandidateAdvisors and findAdvisorsThatCanApply. If no corresponding enhancer is found, DO_NOT_PROXY is returned, indicating that no enhancement is required.
Due to too much logic, so the next post code will not be too much, mainly to understand its general process, there is a need to follow the source code engineering notes to track the complete process ~ :
Find the corresponding enhancer, findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors(a) {
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder ! =null) {
/ / comment the actual call is 8.3 org. Springframework. Aop). Aspectj annotation. BeanFactoryAspectJAdvisorsBuilder. BuildAspectJAdvisors
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
Copy the code
Practical perspective, the key is this. This method aspectJAdvisorsBuilder. BuildAspectJAdvisors () this method looks simple, but the actual processing logic, depth of the code is very much also, so in order to avoid too much code, I list the main processes, And what are the key solutions
The main process is as follows:
- Fetching all beannames extracts beans previously registered in the beanFactory.
- Walk through the list of beans extracted in the previous step to find classes annotated with @AspectJ for further processing
- Proceed with the enhancer extraction of the @AspectJ annotated class extracted in the previous step
- Add the extracted results to the cache
Comments in the code can be queried, from [Comment 8.3] to [Comment 8.8 generating enhancers from pointcut information] is the processing logic of this method
Does does in the last step in this process, will identify to the point of contact information (PointCut) and enhanced method (Advice) encapsulation, concrete is carried out by the realization of the Advisor class InstantiationModelAwarePointcutAdvisorImpl unified encapsulation.
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// Simple assignment
this.declaredPointcut = declaredPointcut; .if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
// Initializes the enhancer
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); }}Copy the code
The first half of the package logic is simply assignment. The key is the method instantiateAdvice(this.declaredpointcut), in which the logic is different for different enhancements (Before/After/Around). In ReflectiveAspectJAdvisorFactory# getAdvice difference in method realized according to the different types of annotations encapsulate different enhancer.
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {...// Comment 8.7 encapsulates different enhancers for different annotation types
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
}
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:}}Copy the code
Finally, the pointcut method is parsed and encapsulated into Advisor, and the extracted results are added to the cache. If you are careful, you may notice that there are two other types of enhancers in addition to the regular enhancer: synchronous instantiation enhancer and introduction enhancer. ** Due to the use of less, so I see the source code of the two branch processing did not in-depth study, interested students please continue to in-depth study of the two enhancer ~
Get the matching enhancer findAdvisorsThatCanApply
In the previous process, all of the enhancers were resolved, but not all of the previously resolved enhancers are applicable to the bean currently being processed, so you need a method to pick out the appropriate enhancers.
protected List<Advisor> findAdvisorsThatCanApply(List
candidateAdvisors, Class
beanClass, String beanName)
{
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// Perform the filter enhancer in this step
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null); }}Copy the code
As you can see, the concrete implementation of filtration is a tool of class methods AopUtils. FindAdvisorsThatCanApply:
public static List<Advisor> findAdvisorsThatCanApply(List
candidateAdvisors, Class
clazz)
{
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
// Iterate over all enhancers
for (Advisor candidate : candidateAdvisors) {
// Handle the introduction enhancement first
if (candidate instanceofIntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); }}booleanhasIntroductions = ! eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {
// Skip it
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// Handle normal enhancer types
if(canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); }}return eligibleAdvisors;
}
Copy the code
In the canApply() method, if eligibleAdvisors are qualified, add them to eligibleAdvisors, and return a list of enhancers suitable for the bean.
To create the agent
After obtaining the enhancers for all the corresponding beans from the previous process, you can begin the creation of the proxy.
protected Object createProxy(Class
beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
// Copy to get related attributes in the current class
proxyFactory.copyFrom(this);
// Determine whether targetClass should be used instead of its interface proxy for a given bean
if(! proxyFactory.isProxyTargetClass()) {// Check the proxyTargetClass setting and the preserveTargetClass attribute
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// Add a proxy interfaceevaluateProxyInterfaces(beanClass, proxyFactory); }}// In this step, the interceptor is primarily encapsulated as an enhancer
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// Customize the proxy
customizeProxyFactory(proxyFactory);
// used to control whether the agent factory is allowed to change after it has been configured
// The default value is false and the configuration of the proxy is not allowed to be modified
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Generate proxy, delegate to ProxyFactory to handle.
return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code
For the creation and processing of proxy classes,Spring
Entrusted to theProxyFactory
To handle, the function posted above is mainly onProxyFactory
To prepare for the actual creation of the agent, the main process is as follows:
- Gets the attributes of the current class
- Adding a Proxy Interface
- Encapsulate the Advisor and add it to ProxyFactory
- Sets the class to proide
- Provide a custom function customizeProxyFactory to subclasses, which further encapsulates the ProxyFactory
- Get the agent
The key steps are the third and sixth, where the interceptor wrapper is performed in the third step. See [Annotation 8.9 Creating an AOP proxy for a given bean] and [Annotation 8.10 Wrapping an Interceptor as an Advisor] for the code flow.
Then, all the enhancers are wrapped, and the last step of the parsing is the creation and retrieval of the proxy.
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
Copy the code
Create proxy createAopProxy()
Locate the code to create the agent:
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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return newJdkDynamicAopProxy(config); }}Copy the code
As you can see from the code above, you use several key attributes to determine what type of AopProxy you are creating. One is JDK dynamic proxy, and the other is CGLIB dynamic proxy.
The proxy-target-class and targetClass attributes mentioned earlier determine which proxy should be created.
GetProxy getProxy()
[JDK dynamic proxy] and [CGLIB dynamic proxy]
At the same time, the implication of dynamic proxy is that abstract classes are not subclassed at compile time and the final object is generated at run time.
JDK dynamic proxy
JDK
Proxy is the default recommended proxy, usingProxy
+ InvocationHandler
.
This can be done by defining an interface, an implementation class, and a handler that inherits from InvocationHandler, and then overriding the invoke method in the handler to enhance the proxy object.
JdkDynamicAopProxy.java
public Object getProxy(@Nullable ClassLoader classLoader) {
// Comments 8.11 JDK Dynamic Proxy
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code
NewProxyInstance (classLoader, proxiedInterfaces, this), the third parameter is JdkDynamicAopProxy itself, And it implements the InvocationHandler interface, overloading the Invoke method.
org.springframework.aop.framework.JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Comment 8.12 JDK dynamic proxy overloaded invoke method
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true; } target = targetSource.getTarget(); Class<? > targetClass = (target ! =null ? target.getClass() : null);
// Get the interception chain for this method.
// Get the interceptor chain for this method
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check if we have any aspect logic. If we don't do this, we can fall back on the direct reflection invocation target and avoid creating the MethodInvocation.
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
/ / packages interceptors in ReflectiveMethodInvocation, easy to use proceed to perform the interceptor
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Execute interceptor chainretVal = invocation.proceed(); }...return retVal;
}
finally {
if(target ! =null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if(setProxyContext) { AopContext.setCurrentProxy(oldProxy); }}}Copy the code
createJDK
The main work in the proxy process is when an interceptor chain is created and usedReflectiveMethodInvocation
Class is encapsulated, and after encapsulation, its arguments are called one by oneproceed
Method used to achieve the pre-enhancement and post-enhancement of the target method.
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
public Object proceed(a) throws Throwable {
// Execute the pointcut method after executing all enhancers
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// Get the next interceptor to execute
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Dynamic matchingInterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<? > targetClass = (this.targetClass ! =null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Fail to match, skip interceptor, return directly
returnproceed(); }}else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}Copy the code
Refer to this method for specific code and comments. About JDK dynamic proxy, in-depth study can also be carried out separately, so it is recommended to see this information leopards take you to see the source code: JDK dynamic proxy, and learn
CGLIB dynamic proxy
CGLIB[Code Generation LIB] is a powerful high-performance Code Generation package. It is widely used in many AOP frameworks.
Once again, this brother willCGLIB
Agent, introduced in detailCGLIB
In what scenarios, and in what order code is processed by it,Cglib and its basic use.
Hopefully, by the end of this article, you can see how the CGLIB code generation package specifically enhances classes.
Proxy enhancement results
Through the previous series of steps, you parse tags, attributes, enhance methods, and finally get a CGLIB proxy from which to create beans
Take a look inside the bean that was last proxied:
As you can see from the figure, the final creation is decoratedbean
The inside obviously isCGGLIB
For the code generated by the agent, we implemented method enhancement without modifying the business code.
Static AOP
If there are dynamic proxies, there will also be static proxies.
When using static AOP, you use LTW (load-time Weaving), which refers to dynamically Weaving AspectJ facets as the virtual machine loads bytecode files.
AOP
Static agent mainly in the virtual machine is started by changing the way target byte code to complete the target object, it has higher efficiency compared with the dynamic proxy, because in the process of dynamic proxy invocation, also need a dynamically create the proxy class and proxy target steps, and the static agent is at startup is complete bytecode increase or decrease, When the system calls the target class again, it is no different from invoking the normal class, so it is relatively efficient.
For static AOP usage and learning, see this article: From Proxy Mechanisms to Spring AOP
conclusion
Dynamic AOP is simple to use, but there are two main points to summarize on how to implement it:
- The dynamic analysis
AOP
The label - create
AOP
The agent
But in theSpring
The underlying implementation logic is extremely complex, fromSpring
As you can see in the framework, this is good code design, the top level of entry as simple as possible, users can easily master the function, complex implementation logic is hidden.
It took me nearly a week to write this AOP learning summary. I read books first, and spent a night after work to explain the general process. The next night, I went through the code, and found that there were still doubts in some places, such as JDK and Cglib dynamic proxy.
Add the code comments, analyze what the dynamic proxy does at each step, and comb through it again with the knowledge of the post-processor BeanPostProcessor and custom tag parsing. Scattered, finally finishing finishing.
In the staticAOP
Knowledge point, according to my understanding, the deeper into the bottom of the system, its execution efficiency is higher, so reduce the dynamic creation of proxy class and proxy target object steps, static proxy speed will be improved. At the same time, the complexity of code writing will also increase after getting close to the bottom layer, so I am weighing high frequency usage scenarios (dynamic proxy), this study did not understand in detail, leave this hole, later have a chance to fill in ~
Due to limited personal skills, if there is any misunderstanding or mistake, please leave a comment, and I will correct it according to my friends’ suggestions
Gitee address https://gitee.com/vip-augus/spring-analysis-note.git
Making the address https://github.com/Vip-Augus/spring-analysis-note
The resources
-
Cglib and its basic use
-
Talk about cGLIb dynamic proxy
-
Spring-aop automatically creates proxies
-
Small leopard with you to see the source code: JDK dynamic proxy
-
From proxy mechanisms to Spring AOP
-
— Beijing: Posts and Telecommunications Press
Portal:
-
Spring source learning – environment preparation
-
(1) The infrastructure of the container
-
Spring source code learning (2) default tag parsing
-
Spring source code learning (3) custom tags
-
Spring source code learning (four) bean loading
-
Spring source code learning (5) loop dependency
-
Spring source code learning (six) extension function part 1
-
Spring source code learning (seven) extension features part 2
-
Spring source learning (eight) AOP use and implementation principle
-
Spring source code learning (9) Transaction Transaction
-
(10) Spring MVC