Follow up with the article “Writing Spring– AOP faceted Programming (3)” to continue the update

Added: Extended Advisor

Advisor. Java under the Advisor package has been mentioned in the last article, that is, a notifier and an Advisor implemented based on AspectJ syntax. Since notifiers can be divided into many different types, we need to define different notifiers through different implementation methods when we extend them.

Now look at these two different design approaches

The second more than the first layer of abstraction, dropped out of the same properties method and reduces the redundant code, the pointcut must be generated by an abstract class AbstractPointcutAdvisor subclass to generate different pointcut, is the production of AspectJ AspectJ, Of the regular Reg that generates the regular.

I. Realization of Weaving

What is weaving to accomplish?

Adds user-provided enhancements to the specified methodCopy the code

② When to weave?

Create a bean instance and enhance it after the bean is initializedCopy the code

③ How do YOU determine that beans need enhancement

The bean classes and methods are traversed to match the user-specified facetsCopy the code

④ How to weave

The proxy server!Copy the code

2. Weaving design

Let’s review the AOP workflow at this point

User --> configure and register facets we --> Weave in process: Initialize bean-- determine if enhancement is required? Proxy enhancement -- Return instance: Return instanceCopy the code

① Where do users go to register the section?

② Judge the match, weave into the logic to write where

③ Do we need to add a layer of processing to the bean creation process at this point?

Remember the DefaultBeanFactory we implemented earlier? In its doGetBean method, just add an enhancement logic before initializing the BeanCopy the code

In the future, we will add more different processing to the bean creation process. If implemented directly in BeanFactory, the BeanFactory code will explode, and it is not easy to extend. How do we handle this?

At this point, let’s revisit the Bean production process described three weeks ago in writing Spring- IOC Container (1)

1. Create Bean definition --① 2. Register Bean definition ②-- ③ 3. In each of the four nodes, a variety of processing logic can be added before and after each process. In order to make us more flexible, we do not need to modify the code after designing the BeanFactory. We need to add extension points (using **① to ⑥** flags, for example **①** means before the definition of the registered bean,**②** means after the definition of the registered bean) and registration mechanism to each nodeCopy the code

At this point we need to think about observer mode (listener mode), which requires six extension points, namely six observers


Apply the observer pattern to our AOP weaving

At this time, it is not recommended to define only one interface to complete the monitoring of all extension points, because their purposes are different and six functions need to be realized at once. At this time, we define a listening interface BeanPostProcessor to monitor the process before and after Bean initialization. The specific process is as follows:

① Listener pattern implementation AOP flow chart

② Code implementation

1.BeanPostProcessor.java

So, by default, we’re doing nothing, we’re defining the default method, and we’re just making it easy for us to select only one extension point, so we’re just going to select only after initialization, right

public interface BeanPostProcessor { default Object postProcessBeforeInitialization(Object bean,String beanName) throws Throwable{ return bean; } default Object postProcessAfterInitialization(Object bean,String beanName) throws Throwable{ return bean; }}Copy the code

2.AdvisorRegistry.java

Provides registration and retrieval of advisors

public interface AdvisorRegistry {
    public void registAdvisor(Advisor ad);
    public List<Advisor> getAdvisors();
}
Copy the code

3.BeanFactory.java

After creating the instance, we need to put it into the Bean factory. We also need to add the method to register the listener in beanFactory.java

public interface BeanFactory { Object getBean(String name) throws Throwable; / / a new registration method void registerBeanPostProcessor (BeanPostProcessor BeanPostProcessor); }Copy the code

4.DefaultBeanFactory.java

Here is the BeanFactory default implementation, registerBeanPostProcessor implementation are provided

(1) Define a set

private List<BeanPostProcessor> beanPostProcessors = Collections.synchronizedList(new ArrayList<>());
Copy the code

(2) Register methods (just to add to the thread synchronization set beanPostProcessors)

@Override public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) { this.beanPostProcessors.add(beanPostProcessor); if (beanPostProcessor instanceof BeanFactoryAware){ ((BeanFactoryAware) beanPostProcessor).setBeanFactory(this); }}Copy the code

5. Aware and BeanFactoryAware

How to use the observer mode and also have to provide the wake up function

(1) Add processing before and after Bean initialization (doGetBean method)

/ / application bean initialization before the process instance = this. ApplyPostProcessBeforeInitialization (instance, beanName); // execute the init method this.doinit (bd, instance); / / application bean after initialization process instance = this. ApplyPostProcessAfterInitialization (instance, beanName);Copy the code

(2) applyPostProcessBeforeInitialization () and applyPostProcessAfterInitialization () method is through all of your register in the processor, and then call them one by one, You can extend as much functionality as you want with this specification

/ / application bean initialization processing before private Object applyPostProcessBeforeInitialization (Object bean, String beanName) throws Throwable { for (BeanPostProcessor bpp : this.beanPostProcessors) { bean = bpp.postProcessBeforeInitialization(bean, beanName); } return bean; } / / application bean after initialization processing private Object applyPostProcessAfterInitialization (Object bean, String beanName) throws Throwable { for (BeanPostProcessor bpp : this.beanPostProcessors) { bean = bpp.postProcessAfterInitialization(bean, beanName); } return bean; }Copy the code

6. AdvisorAutoProxyCreator implementation

AOP applyPostProcessAfterInitialization woven into function must be in this implementation

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {

}
Copy the code

③ Implement weaving: determine whether the bean needs to be enhanced

Recall the process: Weave into the process: Initialize the bean- decide if it needs to be enhanced? Proxy enhancement — Return instance: Return instance

1. How do I determine whether a bean instance should be enhanced?

1. Get the bean's class and all methods (by reflecting getMethods() and getDeclareMethods())) 2. Boolean matchClass(Class<? > targetClass); boolean matchMethod(Method method,Class<? > targetClass); The matchMethod() is compared with the method obtained by reflection aboveCopy the code

2. Do we need to confirm that getMethods() and getDeclareMethods() can get all methods?

If you read the source code, you'll see that getMethods() returns all public methods and cannot retrieve private and protected methods. When we look at this, we'll use the built-in methods in Spring. When we look at this, when we look at this, we'll use the built-in methods in SpringCopy the code

3. Return to AdvisorAutoProxyCreator. In Java postProcessAfterInitialization () method

List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName); // Step 2: Create proxy objects for proxy enhancement if necessary. Return the enhanced object. if (CollectionUtils.isNotEmpty(matchAdvisors)) { bean = this.createProxy(bean, beanName, matchAdvisors); }Copy the code

4. Write the getMatchedAdvisors() method mentioned in (3)

private List<Advisor> getMatchedAdvisors(Object bean, String beanName) { if (CollectionUtils.isEmpty(advisors)) { return null; } // Get the Class, all the methods Class<? > beanClass = bean.getClass(); List<Method> allMethods = this.getAllMethodForClass(beanClass); List<Advisor> matchAdvisors = new ArrayList<>(); // Walk through the Advisor to find a matching for (Advisor AD: this.advisors) { if (ad instanceof PointcutAdvisor) { if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) { matchAdvisors.add(ad); } } } return matchAdvisors; }Copy the code

5. Get getAllMethodForClass() for all methods in (4)

In maven, we need to use the ClassUtils and ReflectionUtils provided by Spring. Ps: You can add the dependency of SpringBoot directly to Maven without importing the package.

The logic for creating a collection, ClassUtils used to own traversal methods getAllInterfacesForClassAsSet () to iterate through all interfaces, after put beanClass itself also together in this collection, after to combine all the methods in the interface in a loop traverse

private List<Method> getAllMethodForClass(Class<? > beanClass) { List<Method> allMethods = new LinkedList<>(); Set<Class<? >> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass)); classes.add(beanClass); for (Class<? > clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method m : methods) { allMethods.add(m); } } return allMethods; }Copy the code

6. Match all methods of the Advisor and bean in getMatchedAdvisors

private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<? > beanClass, List<Method> methods) { Pointcut p = pa.getPointcut(); // If (!) {// If (!) {// If (!) {// If (!) {// If (!) {// If (!) {// If (! p.matchClass(beanClass)) { return false; } for (Method Method: methods) {if (p.m. Method(Method, beanClass)) {return true; } } return false; }Copy the code

7. PostProcessAfterInitialization () method

Now that we have matching methods, we are ready to perform proxy enhancement on them

@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName); // Enhance as needed and return the enhanced object. if (CollectionUtils.isNotEmpty(matchAdvisors)) { bean = this.createProxy(bean, beanName, matchAdvisors); } return bean; }Copy the code

8. Create a proxy object

private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Throwable {// Use AopProxyFactory to select and create proxy objects. return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory) .getProxy(); }Copy the code

④ Implementation of agent enhancement

This section will cover a lot of ground, so it is recommended to clear your head and understand the logic first, because our main starting point is to be able to understand the source code in the future rather than actually implementing a full Spring on our own

1. What is the logic of proxy enhancement?

Proxies can be divided into JDK dynamic proxies (enhanced by invoke method) and Cglib dynamic proxies (enhanced by Intercept method). You can return the corresponding proxy object after judging the generation method of proxyCopy the code

2. JDK dynamic proxy and Cglib dynamic proxy abstraction

We know that to implement JDK dynamic proxy, we need to implement the InvocationHandler interface, to implement cglib dynamic proxy, we need to implement MethodInteceptor interface, we can abstract the two, extract an AopProxy interface, which provides methods to obtain proxy objects

public interface AopProxy {
    Object getProxy();
    Object getProxy(ClassLoader classLoader);
}
Copy the code

3. Is JDK invoke the same logic as Cglib Intercept?

Advice is used to enhance the current method. Find the Advice in the Advisor to enhance the current method. To generate a proxy object, the JDK needs the following data: the interface to implement, the target object, the matching Advisors, and possibly the data required by the BeanFactory cglib: Class to inherit, interface to implement, construct parameter type, construct parameter, target object, matching Advisor and BeanFactory why do we need to construct parameters and construct parameter types, because if the extended target class does not provide a no-parameter constructor, then its subclass must call the parent class's parameterized constructorCopy the code

4. Design of JdkDynamicAopProxy and CglibDynamicAopProxy

AopProxyUtils

CglibDynamicAopProxy and JdkDynamicAopProxy do the same thing. So you can extract the same implementation as a generic method applyAdvices()

We can simply define a utility class, AopProxyUtils, to store such generic methods

public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> matchAdvisors,Object Proxy, BeanFactory BeanFactory) throws Throwable {// What to do here? / / 1, access to the current method enhanced the advice of the List < Object > advices. = AopProxyUtils getShouldApplyAdvices (target. GetClass (), the method, matchAdvisors, beanFactory); If (CollectionUtils. IsEmpty (advices)) {return method.invoke(target, args); } else {// the AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args) advices); return chain.invoke(); } } public static List<Object> getShouldApplyAdvices(Class<? > beanClass, Method method, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable { if (CollectionUtils.isEmpty(matchAdvisors)) { return null; } List<Object> advices = new ArrayList<>(); for (Advisor ad : matchAdvisors) { if (ad instanceof PointcutAdvisor) { if (((PointcutAdvisor) ad).getPointcut().matchMethod(method, beanClass)) { advices.add(beanFactory.getBean(ad.getAdviceBeanName())); } } } return advices; }Copy the code

For example, in the following code, we use I as the index. In the invoke() method, we need to check whether I is smaller than the size of ADVICES, and if it is smaller, the enhancement still hasn’t been implemented. The judgment logic is simple. If the enhancement is a front-facing enhancement, it can be implemented directly,

Invoke (invokeMethod, null, this), Referring to the invoke() parameter in MethodSurroudAdvice, at this point we need to understand Invoke () throws Throwable public Object Invoke () throws Throwable. Null means no parameters, and this means itself

Invoke –Object returnValue = this.invoke(); And then enhance it

If I <advices. Size () is not available, then return method.invoke(target, args);

public class AopAdviceChainInvocation { private static Method invokeMethod; static { try { invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null); } catch (NoSuchMethodException | SecurityException e) { e.printStackTrace(); } } private Object proxy; private Object target; private Method method; private Object[] args; private List<Object> advices; public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> advices) { super(); this.proxy = proxy; this.target = target; this.method = method; this.args = args; this.advices = advices; } private int I = 0; public Object invoke() throws Throwable { if (i < this.advices.size()) { Object advice = this.advices.get(i++); If (advice instanceof MethodBeforeAdvice) {// Perform pre-enhancer ((MethodBeforeAdvice) advice). Before (method, args, target); } else if (advice Instanceof MethodSurroudAdvice) {// Perform surround enhancement and exception handling enhancement. Return ((MethodSurroudAdvice) advice). Invoke (invokeMethod, null, this); } else if (advice instanceof AfterReturningAdvice) {returnValue = this.invoke(); ((AfterReturningAdvice) advice).afterReturning(returnValue, method, args, target); return returnValue; } return this.invoke(); } else { return method.invoke(target, args); }}}Copy the code
JdkDynamicAopProxy and CglibDynamicAopProxy code implementation

The JDK:

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class); private String beanName; private Object target; private List<Advisor> matchAdvisors; private BeanFactory beanFactory; public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) { super(); this.beanName = beanName; this.target = target; this.matchAdvisors = matchAdvisors; this.beanFactory = beanFactory; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); } @Override public Object getProxy() { return this.getProxy(target.getClass().getClassLoader()); } @override public Object getProxy(ClassLoader ClassLoader) {if (logger.isdebugenabled ()) {logger.debug(" for "+ target + "Create an agent." ); } return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this); }}Copy the code

Additional:

public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor { private static final Log logger = LogFactory.getLog(CglibDynamicAopProxy.class); private static Enhancer enhancer = new Enhancer(); private String beanName; private Object target; private List<Advisor> matchAdvisors; private BeanFactory beanFactory; public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) { super(); this.beanName = beanName; this.target = target; this.matchAdvisors = matchAdvisors; this.beanFactory = beanFactory; } @Override public Object getProxy() { return this.getProxy(target.getClass().getClassLoader()); } @override public Object getProxy(ClassLoader ClassLoader) {if (logger.isdebugenabled ()) {logger.debug(" for "+ target + "Create the Cglib proxy." ); } Class<? > superClass = this.target.getClass(); enhancer.setSuperclass(superClass); enhancer.setInterfaces(this.getClass().getInterfaces()); enhancer.setCallback(this); Constructor<? > constructor = null; try { constructor = superClass.getConstructor(new Class<? > [] {}); } catch (NoSuchMethodException | SecurityException e) { } if (constructor ! = null) { return enhancer.create(); } else { BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName); return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues()); } } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); }}Copy the code

The two dynamic proxies use their own API, we just need to understand our logic, note that cglib dynamic proxies must pass the construct parameter type and the construct parameter, we have a new problem

How do I pass the data acquired when creating the bean instance to the initialized AOP

When created, it is enhanced by defaultBeanFactory and used by AdvisorAutoXyCreator, Again in CglibDynamicAopProxy we consider putting parameters in the bean definition and using ThreadLocal to hold parameter values, since BeanDefinition is multiple instances of the same class. Especially when prototype was shared, we needed to ensure that each creation process used data from a different threadCopy the code

We’ve added two methods to BeanDefinition: because we already have a BeanDefiniion cache (public Constructor getConstructor(); Public void setConstructor(Constructor);) , parameter types that we can get from them

List<? > getConstructorArgumentValues(); public Object[] getConstructorArgumentRealValues();Copy the code

We can better understand this by using the getProxy method in CglibDynamicAopProxy

else {
    BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
    return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
}
Copy the code

Here we use getConstructor() to get the constructor and then call getParameterTypes() to get the type of the construction parameter

⑤ How to use AopProxy in Creator

Apply the factory pattern to hand the selected logic to the factory

public interface AopProxyFactory { AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable; / * * * * * to get the default AopProxyFactory instance @ return AopProxyFactory * / static AopProxyFactory getDefaultAopProxyFactory () {return (AopProxyFactory) new DefaultAopProxyFactory(); }}Copy the code

After we implemented, a default factory here made a judgement is to use the JDK or additional, we omit the judgment and the direct use of additional, the inside of the spring source judgment also is such, but the source code also exist some mechanism to judge, here we are no longer too much expounded, in the future will unfold the source reading

public class DefaultAopProxyFactory implements AopProxyFactory{ @Override public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory BeanFactory) throws Throwable {// Use JDK dynamic proxy or cglib? if (shouldUseJDKDynamicProxy(bean, beanName)) { return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory); } else { return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory); }} private Boolean shouldUseJDKDynamicProxy(Object bean, String beanName) { JDK if you have an implementation interface, cglib if you don't? return false; }}Copy the code

The next chapter is still about Spring configuration, followed by source code

The test code is relatively more, I will not paste it out, AOP this code is a lot, and more messy (I feel so), need to spend more time to sort out the summary, if necessary, after will once again a more detailed point summary, some of the process to tell a clear point. Any questions or suggestions can be put forward in the comment area for me to improve. Mutual encouragement, thank you.