What is AOP?
AOP (Aspect Oriented Programming) : Aspect Oriented Programming, unlike OOP whose key unit is class, its key unit is Aspect, which complements OOP by providing ways to change program structure. In plain English, we can enhance the functionality of a method without modifying its source code through precompilation or runtime dynamic proxies.
In practical development, the emergence of AOP facilitates the decoupling and extension between business requirements and system functions, such as logging, transaction, security, permissions and other system functions, greatly reducing the repeated code, as well as the maintenance of complex, improve the efficiency of development.
The essence of AOP’s solution is the proxy mechanism.
The preparatory work
Since you probably already know something about AOP proxies from parsing Spring source code or from actual development, we need to take a closer look at some of the basics before parsing the source code.
Basic concept
Aspect: Before introducing the Aspect, we first understand the JoinPoin in the Aspect. A join point represents the execution method of a class. An aspect is a cross section of join points that connect multiple classes (officially called modularity modularity across concerns of multiple classes). An Aspect is usually declared using the @aspect annotation.
Pointcut: A regular definition of all join point matches in an aspect, defined by multiple Pointcut expressions. Spring AOP supports the following Pointcut expressions:
-
Execution: Matches any join point at which a method is executed.
-
Within: Matches any join points executed by a method within the specified type.
-
This: Matches any join points of the interface of the type specified by the proxy implementation.
-
Target: Matches any join point of the target object implementing the interface of the specified type.
-
Args: Join points that match method execution of the specified type parameter.
-
@target: Matches any join point with the specified annotation that implements the specified type interface of the target object.
-
@args: Matches the join point of the method execution with the specified annotation for the specified type parameter.
-
@WITHIN: Matches any join point within the specified type for method execution with the specified annotation.
-
Annotation: Join points that match method execution of the specified annotation.
Execution and @annotation may be more commonly used in actual development. Please refer to the official documentation for the writing rules of different expressions.
Notifications: Interceptor methods around the matching pointcut. Spring includes several different types of notifications,
- Pre-notification: Interception performed by a method.
- Post-notification: Interception after a method executes, whether it returns normally or exits abnormally.
- Return notification: method intercepts after normal execution.
- Circular notification: Custom interception before and after method execution to manually control the method called.
- Exception notification: Interception of an abnormal exit during method execution.
Target object: an object notified by one or more facets, which is always a proxied object.
use
There are two ways to configure AOP in Spring:
- Xml-based configuration: Cumbersome configuration, centralized configuration of different requirements does not meet the single responsibility principle, and named pointcuts of declarations cannot be combined.
- @aspect-based annotations: Simple configuration, support for richer configuration combinations, and cell modularity. This is also a major use in development.
Proxy mechanism
There are two types of proxy mechanisms in Spring:
- JDK dynamic proxy: Built into the JDK, implemented through interception and reflection, the propped object must implement the interface.
- CGLIB dynamic proxy: an open source class definition library, using ASM bytecode generation method to generate proxy classes, default proxy does not implement the interface object, can not be final modified methods proxy; Spring can be set
@EnableAspectJAutoProxy(proxyTargetClass = true)
Enforces dynamic proxies using CGLIB.
Calling process
With some of the basics covered, we’ll examine the process from AOP initialization to actual invocation from the source code.
And the completion of AOP initialization is actually inserted in the IOC container initialization process, we have learned about the IOC container, including the dependency injection initialization process, and then look at the RELEVANT source AOP should be easier to start, at the same time this is the previous content of the supplement.
Here we mainly use and analyze the source code process in the form of annotations.
Let’s first write a simple test class to see how AOP is used and applied.
@Configuration
@EnableAspectJAutoProxy
public class Config {}@Component
public class AopA{
public void print(a){
System.out.println("AAAAAAAAAAAAAA"); }}@Aspect
@Component
public class AspectA {
@Pointcut("execution(* com.test.spring.entity.AopA.*(..) )"
public void point(a){}@Before("point()")
public void before(a){
System.out.println("before");
}
@After("point()")
public void after(a) throws Exception {
System.out.println("after");
}
@Around("point()")
public void around(JoinPoint joinPoint){
System.out.println("aroundBefore");
try {
((ProceedingJoinPoint)joinPoint).proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("aroundAfter");
}
@AfterReturning("point()")
public void afterReturn(a){
System.out.println("afterReturn");
}
@AfterThrowing("point()")
public void afterThrow(a){
System.out.println("afterThrow"); }}public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("com.test.spring"); AopA aopA = ac.getBean(AopA.class); aopA.print(); }}Copy the code
Run the code, and the console prints the following,
aroundBefore
before
AAAAAAAAAAAAAA
aroundAfter
after
afterReturn
Copy the code
If an exception is thrown during method execution, afterThrow is printed in the configured exception notification.
Enabling Automatic Proxy
Spring has a lot of rich and powerful functionality built in, but not everything is turned on by default. For example, we first have to tell the Spring container to enable the AOP automatic proxy so that it can scan, register, and apply the relevant annotation classes we configured. It is also easy to enable automatic proxy via annotations, using the @EnableAspectJAutoProxy annotation configured in the test class above.
Again, from 0 to 1, we’ll start with the @enableAspectJAutoProxy annotation.
Let’s take a look at the source code for this annotation,
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass(a) default false;
boolean exposeProxy(a) default false;
}
Copy the code
The class with an annotation @ Import ({AspectJAutoProxyRegistrar. Class}), And we know @ Import annotations represent AspectJAutoProxyRegistrar object can be configured @ EnableAspectJAutoProxy annotation class to load, That is to say, the IOC instantiation will instantiate AspectJAutoProxyRegistrar Config object.
Also here we can see that EnableAspectJAutoProxy has two configuration properties,
- ProxyTargetClass: the default is false. Spring automatically selects JDK or CGLIB dynamic proxies based on the implementation interface of the proxyTargetClass. When set to true, the proxy mechanism enforces the use of CGLIB dynamic proxies, but this results in no interception notification for final modified methods because it cannot be overridden.
- ExposeProxy: Whether to expose the current proxy object as
ThreadLocal
So that the target can access it. For example, methods a() and b() are blocked from being notified. By default, false means that method b() is not blocked from being notified when called from method A (), and true means that method b() is also blocked from being notified.
Let’s move on to what happens after the startup annotations are configured. The role of and AspectJAutoProxyRegistrar class. Then we will start with the initialization of the IOC container. First of all, our test classes complete the initialization of the IOC container based on annotations. In the previous IOC source analysis, we have already known that the first step is to call the scan() method to scan all Bean objects configured under the specified package path and encapsulate them into BeanDefinition objects stored in the beanDefinitionMap. The refresh() method is then called to load the Bean’s configuration resources, and the focus here is on two methods,
// Instantiate and invoke all registered BeanFactoryPostProcessor beans
invokeBeanFactoryPostProcessors(beanFactory);
// Register the BeanPost event handler
registerBeanPostProcessors(beanFactory);
Copy the code
We see first invokeBeanFactoryPostProcessors (), through comments probably know it’s work, that we take a look at the source code,
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(newContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }}Copy the code
We can see the real implementation is entrusted PostProcessorRegistrationDelegate invokeBeanFactoryPostProcessors () to complete,
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List
beanFactoryPostProcessors)
{
Set<String> processedBeans = new HashSet<>();
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else{ regularPostProcessors.add(postProcessor); }}// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true.false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true.false);
for (String ppName : postProcessorNames) {
if(! processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear();// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true.false);
for (String ppName : postProcessorNames) {
if(! processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate =true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true.false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else{ nonOrderedPostProcessorNames.add(ppName); }}// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
Copy the code
Look feel implementation logic is quite complex, in fact you look found many treatment is almost repeat code, first get postProcessorNames array, and then traverse the calls to the getBean () instantiate and added to the currentRegistryProcessors temporary collection, And then spread to the refs invokeBeanFactoryPostProcessors () calls, finally the clear () temporary collection. It is mainly to achieve PriorityOrdered, Ordered, and the rest of the BeanDefinitionRegistryPostProcessor handled separately. First will instantiate and call ConfigurationClassPostProcessor PriorityOrdered interface, it is used to guide the processing Configuration class (Configuration @ the Configuration class), such as configuring @ the Configuration, This is usually registered by default, but can also be declared manually using any other BeanFactoryPostProcessor, which registers the Bean definition declared in the configuration class before any other BeanFactoryPostProcessor executes. This is why we added the @Configuration annotation to the @EnableAspectJAutoProxy class.
In fact, the analysis to here, the next process may have a bright future, and then down the source code involved in more details, and continue to paste the source code will be more complicated and not easy to seize the main process, here is not a spread out, I will use the sequence diagram to show the process behind,
Finally is to through AspectJAutoProxyRegistrar registerBeanDefinitions () method AnnotationAwareAspectJAutoProxyCreator encapsulated into BeanDefinition and the object Register with the container and inject the proxyTargetClass and exposeProxy property values.
Register the post-processor
Why did we start just through the configuration class finally AnnotationAwareAspectJAutoProxyCreator registered into the container? In fact, if we look at the structure of its class diagram,
It implements the BeanPostProcessor interface, so let’s take a look at the source code,
public interface BeanPostProcessor {
// The Bean's pre-initialization callback
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// After the Bean's initialization callback
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
returnbean; }}Copy the code
The BeanPostProcessor defines the Bean’s initialization callback method and implements some custom logic after instantiation, configuration, and initialization. You can think of it as an interceptor for Bean objects, which you can implement if you want to extend the functionality of your Bean, modify its wrapper, etc. So AnnotationAwareAspectJAutoProxyCreator role is very clear, it is to after the Bean is initialized to do some custom processing, as to how the processing of fine behind us.
But here we’re just registering it in the container, we need to instantiate it and add it to the collection List
beanPostProcessors, So the above registerBeanPostProcessors () method is to complete the matter,
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
Copy the code
Equally true implementation or to PostProcessorRegistrationDelegate to complete,
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true.false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceofMergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); }}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else{ nonOrderedPostProcessorNames.add(ppName); }}// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
private static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors){
for(BeanPostProcessor postProcessor : postProcessors) { beanFactory.addBeanPostProcessor(postProcessor); }}@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
this.beanPostProcessors.remove(beanPostProcessor);
this.beanPostProcessors.add(beanPostProcessor);
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
this.hasInstantiationAwareBeanPostProcessors = true;
}
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
this.hasDestructionAwareBeanPostProcessors = true; }}Copy the code
We can see here is to realize PriorityOrdered BeanDefinitionRegistryPostProcessor, Ordered, and the rest BeanPostProcessor is added separately to the collection beanPostProcessors; And we know AnnotationAwareAspectJAutoProxyCreator is a subclass of Ordered, here we mainly look for orderedPostProcessorNames processing:
- Will first traversal orderedPostProcessorNames collection and get AnnotationAwareAspectJAutoProxyCreator;
- Then call the getBean() method to instantiate it;
- After calling registerBeanPostProcessors () will add to the collection of BeanPostProcessor AnnotationAwareAspectJAutoProxyCreator.
This is where the automatic proxy configuration is finally enabled, and we can also guess that the configured section class will be used to proxy the target Bean that matches the rule after it is initialized.
Parse @Aspect Aspect configuration
After I finish on these above, we know that the IOC container will invoke finishBeanFactoryInitialization next () method of container singleton Bean instantiation, namely dependency injection process.
So what happens next when you parse the configured aspect class and its defined pointcuts and notifications (that is, the AspectA class we configured)?
We continue to analysis, we know that before creating Bean invokes resolveBeforeInstantiation () method to apply analytical Bean configuration before and after the instantiation of the processor,
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if(! Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<? > targetType = determineTargetType(beanName, mbd);if(targetType ! =null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if(bean ! =null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean ! =null);
}
return bean;
}
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class
beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if(result ! =null) {
returnresult; }}}return null;
}
Copy the code
Also will traverse the collection before adding the BeanPostProcessor and calls the realization of the parent class is a subclass of InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation () method, We know from the class diagram above, before adding AnnotationAwareAspectAutoProxyCreator is a subclass of it, And here is really call AnnotationAwareAspectAutoProxyCreator superclass AbstractAutoProxyCreator method implementation,
@Override
public Object postProcessBeforeInstantiation(Class
beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);
if(! StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null; }}// if a custom TargetSource exists, create an agent
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if(targetSource ! =null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
Copy the code
So let’s take a look at the isInfrastructureClass() method,
protected boolean isInfrastructureClass(Class
beanClass) {
boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
Pointcut.class.isAssignableFrom(beanClass) ||
Advisor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
if (retVal && logger.isTraceEnabled()) {
logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
}
return retVal;
}
Copy the code
It determines whether the current Bean is an infrastructure class that cannot be proided, such as the implementation class of the Advice, PointCut, Advisor, and other interfaces; This is to filter the aspect configuration class AspectA in our test code and place it in the Map
,>
Also calls the shouldSkip () method to judge whether the current Bean should skip, there is relationship between the configuration @ the Aspect of the cut surface of the annotation class configuration parsing in advance, which is entrusted by the actual subclass AspectJAwareAdvisorAutoProxyCreator, As we look down,
@Override
protected boolean shouldSkip(Class
beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor) {
if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
return true; }}}return super.shouldSkip(beanClass, beanName);
}
Copy the code
The findCandidateAdvisors() method is called to obtain the set of notification Advisors, and then the Advisor is traversed to check whether the beanName of the class that configured the Pointcurt pointcut matches the beanName of the current Bean. Return true if it is consistent, otherwise call shouldSkip() to get the default false implementation.
Are we looking at key need findCandidateAdvisors () method, the actual call here is implemented in AnnotationAwareAspectJAutoProxyCreator,
@Override
protected List<Advisor> findCandidateAdvisors(a) {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder ! =null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
Copy the code
The discovery turns out to be an enhancement to the superclass method, which has an important effect: it retains the superclass’s notification to get the XML configuration file definition, but also adds the notification to get the annotation configuration. Since we are configuring it through annotations, we will look directly at the implementation of the buildAspectJAdvisors() method in aspectJAdvisorsBuilder. If you are interested in the implementation in findCandidateAdvisors(), You’ll notice that the code for both implementations is pretty much the same,
public List<Advisor> buildAspectJAdvisors(a) {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<>();
aspectNames = new LinkedList<>();
// Get all beanName registered in the container
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true.false);
for (String beanName : beanNames) {
if(! isEligibleBean(beanName)) {continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.Class<? > beanType =this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// Determine if the Bean has an @aspect annotation
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// Get a set of configured notification methods
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory)); }}}this.aspectBeanNames = aspectNames;
returnadvisors; }}}if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
// Cache notification methods
List<Advisor> advisors = new LinkedList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if(cachedAdvisors ! =null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory)); }}return advisors;
}
Copy the code
The implementation flow of this method is as follows:
- Get all beannames registered in the container;
- Iterate through all the beanName and find the section class that configured the @aspect annotation;
- Parse the notification method configured in the get aspect class;
- Caches the last notification method retrieved.
Method and the notice of the resolution is to ReflectiveAspectJAdvisorFactory getAdvisors () method is implemented,
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { Class<? > aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass);// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new LinkedList<>();
// Iterate over the @pointcut configuration
for (Method method : getAdvisorMethods(aspectClass)) {
// Get the notification method of the configuration slice
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if(advisor ! =null) { advisors.add(advisor); }}// If it's a per target aspect, emit the dummy instantiating aspect.
if(! advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor =new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if(advisor ! =null) { advisors.add(advisor); }}return advisors;
}
Copy the code
There are two important methods here: one is getAdvisorMethods(), which gets the @pointcut method configured in the section class,
private List<Method> getAdvisorMethods(Class
aspectClass) {
final List<Method> methods = new LinkedList<>();
ReflectionUtils.doWithMethods(aspectClass, method -> {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { methods.add(method); }}); Collections.sort(methods, METHOD_COMPARATOR);return methods;
}
Copy the code
One is getAdvisor(), which gets several notification methods (Before, Around, After, AfterReturning, AfterThrowing) for the @pointcut.
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// Encapsulate pointcut and notification method information
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class
candidateAspectClass) { AspectJAnnotation<? > aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {
return null;
}
// Encapsulate the object
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0].newClass<? > [0]);
// Get the pointcut expression
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory ! =null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
@Nullable
protected staticAspectJAnnotation<? > findAspectJAnnotationOnMethod(Method method) { Class<? >[] classesToLookFor =newClass<? >[] { Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};for(Class<? > c : classesToLookFor) { AspectJAnnotation<? > foundAnnotation = findAnnotation(method, (Class<Annotation>) c);if(foundAnnotation ! =null) {
returnfoundAnnotation; }}return null;
}
Copy the code
The main effect of these methods is to obtain the tangent point method and notice information encapsulated into InstantiationModelAwarePointcutAdvisorImpl object, this object defines the different notes at the same time inform the implementation of the strategy (interested can look at the source of the implementation class), respectively is:
- AspectJMethodBeforeAdvice
- AspectJAfterAdvice
- AspectJAfterReturningAdvice
- AspectJAfterThrowingAdvice
- AspectJAroundAdvice
It is up to them to handle the implementation when the corresponding notification proxy methods are called later.
Generating proxy objects
After parsing the section class above, the next step is definitely to filter out the target class based on the defined pointcut expression and replace it with the generated proxy object.
So let’s move on. We know that after the Bean is created, property injection is done and initializeBean() method is called to initialize the Bean. While initializeBean () method will be called applyBeanPostProcessorsAfterInitialization () method, add to apply before the collection of rear BeanPostProcessor processor initialization processing callback,
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
// Iterate through the BeanPostProcessor post-processor
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
Copy the code
Can see here will traverse the BeanPostProcessor implementation class, and call its implementation postProcessAfterInitialization () method, And here we focus on is adding AnnotationAwareAspectAutoProxyCreator before, but we look at its source, is not the method that actually concrete method in its superclass AbstractAutoProxyCreator,
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if(bean ! =null) {
// Get the cached beanName
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code
Use earlyProxyReferences to cache beanName. If you know how to resolve cyclic dependencies in dependency injection, you know what this code does: After the object Bean is created, the ObjectFactory of the object is placed in the singletonFactories cache to give the dependent Bean the reference to the object. And this is what the ObjectFactory encapsulates the Bean that is returned by calling getEarlyBeanReference(), which also iterates through the BeanPostProcessor and calls the getEarlyBeanReference() method, And the actual implementation that it calls is abstractautoXyCreator,
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
this.earlyProxyReferences.add(cacheKey);
}
return wrapIfNecessary(bean, beanName, cacheKey);
}
Copy the code
If the wrapIfNecessary() method is found to be the same, it will be called again, so the cache judgment is clearly used to prevent the wrapIfNecessary() method from being called repeatedly. And that’s what we’re trying to understand,
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && 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.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
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
Is found that the code is very familiar with, Bean instantiation before they are processed in advance and caching, we see getAdvicesAndAdvisorsForBean directly () method,
@Override
@Nullable
protectedObject[] getAdvicesAndAdvisorsForBean(Class<? > beanClass, String beanName,@Nullable TargetSource targetSource) {
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 cached notifications
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// Gets the notification that the Bean matches the pointcut definition
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if(! eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); }return eligibleAdvisors;
}
Copy the code
And easy to understand the role of, here is to get the cache before a collection of encapsulation InstantiationModelAwarePointcutAdvisorImpl class Advisor, matching the current Bean again meet the tangent point defined notification, the upper end of method, if the notification is empty, There is no need to proxy the current Bean, and the createProxy() method will be called to create a proxy object for the current Bean anyway.
Let’s look at the implementation of the createProxy() method,
protected Object createProxy(Class<? > beanClass,@Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// Determine the configured proxyTargetClass
if(! proxyFactory.isProxyTargetClass()) {// Determine whether the Bean should be proxied with its target class instead of its interface
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
If there is an interface, add the proxy interface; if there is no interface, set proxyTargetClass to trueevaluateProxyInterfaces(beanClass, proxyFactory); }}// Add notification interceptor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
@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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return newJdkDynamicAopProxy(config); }}Copy the code
The last part of the method is to create a proxy class by choosing different proxy mechanisms, mainly the JDK and CGLIB dynamic proxy mechanisms. The differences between the two mechanisms have been briefly described in the previous article. For more detailed differences and implementation of the two proxy mechanisms, there is a chance to write a separate article.
The generation of this proxy object is complete.
Calling the proxy method
After the IOC container is initialized, break the point and you’ll see that all references to propped objects are l like $Proxy27@2699. What about the flow when we call the notified methods?
The invoke() method of JdkDynamicAopProxy is invoked.
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// check whether eqauls() is used
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
// check whether it is a hashCode() method
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
// Determine if it is a proxy class
else if (method.getDeclaringClass() == DecoratingProxy.class) {
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// Determine if the Advised interface is a method defined in its parent interface
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get the target objecttarget = targetSource.getTarget(); Class<? > targetClass = (target ! =null ? target.getClass() : null);
// Get notification interceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// Direct reflection calls
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
/ / create the MethodInvocation
invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); } Class<? > returnType = method.getReturnType();if(retVal ! =null&& retVal == target && returnType ! = Object.class && returnType.isInstance(proxy) && ! RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { retVal = proxy; }else if (retVal == null&& returnType ! = Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if(target ! =null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if(setProxyContext) { AopContext.setCurrentProxy(oldProxy); }}}Copy the code
In the method, first get the proxy class inform interceptor in the chain, then call ReflectiveMethodInvocation proceed () method; Let’s look at the getInterceptorsAndDynamicInterceptionAdvice () method is how to convert the Advisor
@Nullable
public Object proceed(a) throws Throwable {
// When Interceptor is finished, joinPoint is executed
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// To dynamically match joinPoint
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
// Match the run-time parameters
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Call the next Interceptor
returnproceed(); }}else {
// Perform the current interception
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}Copy the code
Get here interceptorOrInterceptionAdvice is said before several different annotations to inform policy classes, respectively, will call them to realize the invoke () method (method of different strategies can go and see, you don’t post the source), The custom implementation within the notification method completes the configuration and invokeJoinpoint() method is invoked before and after completion, the essence of which is to invoke the target method in the proxied class directly through reflection.
This is the end of the full AOP implementation process in Spring.
To do one thing extremely well is talent!