Author: Xiao Fu Ge blog: https://bugstack.cn

Precipitation, share, grow, let oneself and others can harvest! 😄

One, foreword

Why is your code always on the pigsty?

🎙 how to do, know you in the Internet, do not know you in which big factory. You know you’re working overtime, you don’t know which product you’re arguing with. I know you’re slacking off. I don’t know when you’re going to fish. I know you’re moving bricks. I don’t know which pigsty you’re building.


When you’re working hard, day in and day out, day in, week out, month in, month out, you get the least growth and the least return. Save the most sweat and take the least money

You might start looking at the source code as soon as you get excited, but you don’t know what you’re going to do with it. Look at the design pattern, look at the time to understand, but change their own code and can not go down. In fact, on the one hand, the knowledge of their own stack is insufficient, on the other hand, their reserves of code is not enough. As a result, it is impossible to string together a series of pieces of knowledge, as you might see in HashMap, but you can’t think of the data hashing in the library and table components that also uses the perturbed function idea in HashMap and Poisson distribution validation. I can’t read how MyBatis solves the problem of just defining DAO interface and using configuration or annotation to CRUD database operation and look like JDK dynamic proxy, nor can I imagine how AOP is designed. So into the system learning, strengthen the integrity of technology stack knowledge, in order to better use these learned coding ability.

Second, the target

In this chapter we will move from the implementation of IOC to the development of content related to AOP(Aspect Oriented Programming). In the software industry, AOP stands for facet-oriented programming, which implements unified maintenance of program functions and functions through precompilation and dynamic proxies at run time. In fact, AOP is also a continuation of OOP, which is a very important content in the Spring framework. The use of AOP can isolate the various parts of the business logic, thus reducing the coupling degree of the business logic between the modules, improving the reusability of the code, and improving the development efficiency.

The core technical implementation of AOP is mainly the use of dynamic proxies, as you can replace an implementation class of an interface with a proxy class to handle the logic you need. Such as:

@Test public void test_proxy_class() { IUserService userService = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserService.class}, (proxy, method, "You're being represented!" ); String result = userService.queryUserInfo(); Println (" test result: "+ result); }

The implementation of a proxy class is pretty much familiar, so once you have a basic idea, you need to think about how to delegate methods, not proxy classes. And how do you delegate all methods in a class that conform to certain rules? If you can proxy all the methods of the class, you can act as a method blocker and add some custom processing to all the methods being proxied, such as printing logs, logging time, monitoring exceptions, and so on.

Three,

Before we can incorporate the entire AOP facet design into Spring, we need to address two issues: how to proxy methods that conform to the rules, and how to separate out the responsibilities of the class once we have done the proxy method case. The realization of these two functional points is designed and developed with the idea of section. If you’re not quite sure what AOP is, you can think of it as cutting leeks with a knife, which is always a bit slow to cut one by one, and then using your hand (proxy) to pinch the leeks into a handful, using different intercepting operations such as a kitchen knife or an axe. In fact, the program is the same, but the leek into a method, the knife into the intercepting method. The overall design structure is as follows:

  • Just as if you were using Spring AOP, only the methods that need to be intercepted are handled. After intercepting the method, perform your extension to the method.
  • Then we need to implement a Proxy that proxies methods, which use the method interceptor class to handle method callsMethodInterceptor#invokeInstead of using the Method Method directly in the invoke Methodmethod.invoke(targetObj, args)This is the overall difference in usage.
  • In addition to the above core function realization, also need to useorg.aspectj.weaver.tools.PointcutParserProcessing intercepting expressions"execution(* cn.bugstack.springframework.test.bean.IUserService.*(..) )"With method proxies and handling interceptions, we are ready to design a prototype of AOP.

Four, implementation,

1. Engineering structure

! [spring-12-02](https://bugstack.cn/assets/images/spring/spring-12-02.png)! [spring - 12-02] (https://bugstack.cn/assets/images/spring/spring-12-02.png) small - spring - step - 11 └ ─ ─ the SRC ├ ─ ─ the main │ └ ─ ─ Java │ └ ─ ─ cn. Bugstack. Springframework │ ├ ─ ─ aop │ │ ├ ─ ─ aspectj │ │ │ └ ─ ─ AspectJExpressionPointcut. Java │ │ ├ ─ ─ Framework │ │ ├─ Aopproxy.java │ │ ├─ Cglib2Aopproxy.java │ │ ├─ jdkdynamicaopproxy.java │ │ ├─ └ ├─ jdkdynamicaopproxy.java ReflectiveMethodInvocation. Java │ │ ├ ─ ─ AdvisedSupport. Java │ │ ├ ─ ─ ClassFilter. Java │ │ ├ ─ ─ MethodMatcher. Java │ │ ├ ─ ─ Pointcut.java │ │ ├─ Trash Exercises ─ Beans │ │ ├─ Trash Exercises ── Config │ │ ├─ Trash Exercises ── Trash Exercises AutowireCapableBeanFactory. Java │ │ │ │ ├ ─ ─ BeanDefinition. Java │ │ │ │ ├ ─ ─ spring BeanFactoryPostProcessor. Java │ │ │ │ ├ ─ ─ BeanPostProcessor. Java │ │ │ │ ├ ─ ─ BeanReference. Java │ │ │ │ ├ ─ ─ ConfigurableBeanFactory. Java │ │ │ │ └ ─ ─ SingletonBeanRegistry. Java │ │ │ ├ ─ ─ support │ │ │ │ ├ ─ ─ AbstractAutowireCapableBeanFactory. Java │ │ │ │ ├ ─ ─ AbstractBeanDefinitionReader. Java │ │ │ │ ├ ─ ─ AbstractBeanFactory. Java │ │ │ │ ├ ─ ─ BeanDefinitionReader. Java │ │ │ │ ├ ─ ─ BeanDefinitionRegistry. Java │ │ │ │ ├ ─ ─ CglibSubclassingInstantiationStrategy. Java │ │ │ │ ├ ─ ─ DefaultListableBeanFactory. Java │ │ │ │ ├ ─ ─ DefaultSingletonBeanRegistry. Java │ │ │ │ ├ ─ ─ DisposableBeanAdapter. Java │ │ │ │ ├ ─ ─ FactoryBeanRegistrySupport. Java │ │ │ │ ├ ─ ─ InstantiationStrategy. Java │ │ │ │ └ ─ ─ SimpleInstantiationStrategy. Java │ │ │ ├ ─ ─ support │ │ │ │ └ ─ ─ XmlBeanDefinitionReader. Java │ │ │ ├ ─ ─ Aware. Java │ │ │ ├ ─ ─ BeanClassLoaderAware. Java │ │ │ ├ ─ ─ the BeanFactory. Java │ │ │ ├ ─ ─ BeanFactoryAware. Java │ │ │ ├ ─ ─ BeanNameAware. Java │ │ │ ├ ─ ─ ConfigurableListableBeanFactory. Java │ │ │ ├ ─ ─ DisposableBean. Java │ │ │ ├ ─ ─ FactoryBean. Java │ │ │ ├ ─ ─ HierarchicalBeanFactory. Java │ │ │ ├ ─ ─ InitializingBean. Java │ │ │ └ ─ ─ ListableBeanFactory. Java │ │ ├ ─ ─ BeansException. Java │ └ ├── PropertyValue.java │ ├── PropertyValues.java │ ├── Context │ ├─ Event │ │ ├─ Bass Exercises - - Bass Exercises AbstractApplicationEventMulticaster. Java │ │ │ ├ ─ ─ ApplicationContextEvent. Java │ │ │ ├ ─ ─ ApplicationEventMulticaster. Java │ │ │ ├ ─ ─ ContextClosedEvent. Java │ │ │ ├ ─ ─ ContextRefreshedEvent. Java │ │ │ └ ─ ─ SimpleApplicationEventMulticaster. Java │ │ ├ ─ ─ support │ │ │ ├ ─ ─ AbstractApplicationContext. Java │ │ │ ├ ─ ─ AbstractRefreshableApplicationContext. Java │ │ │ ├ ─ ─ AbstractXmlApplicationContext. Java │ │ │ ├ ─ ─ ApplicationContextAwareProcessor. Java │ │ │ └ ─ ─ ClassPathXmlApplicationContext. Java │ │ ├ ─ ─ ApplicationContext. Java │ │ ├ ─ ─ ApplicationContextAware. Java │ │ ├ ─ ─ ApplicationEvent. Java │ │ ├ ─ ─ ApplicationEventPublisher. Java │ │ ├ ─ ─ ApplicationListener. Java │ │ └ ─ ─ ConfigurableApplicationContext. Java │ ├ ─ ─ core. IO │ │ ├ ─ ─ ClassPathResource. Java │ │ ├ ─ ─ DefaultResourceLoader. Java │ │ ├ ─ ─ FileSystemResource. Java │ │ ├ ─ ─ the Resource. The Java │ │ ├ ─ ─ ResourceLoader. Java │ │ └ ─ ─ UrlResource. Java │ └ ─ ─ utils │ └ ─ ─ ClassUtils. Java └ ─ ─ the test └ ─ ─ Java └ ─ ─ cn. Bugstack. Springframework. Test ├ ─ ─ bean │ ├ ─ ─ IUserService. Java │ ├ ─ ─ UserService. Java │ └ ─ ─ UserServiceInterceptor. Java └ ─ ─ ApiTest. Java

Project source code: public number “bugstack wormhole stack”, reply: Spring column, access to complete source code

AOP pointcut expressions and usage and dynamic proxy class relationships based on JDK and CGLIB, as shown in Figure 12-2

  • The whole class diagram is where the core logic of AOP is implemented. The top section is about matching the implementation of the method, and the following section is about proxying the method, starting with AOPProxy.
  • AspectJExpressionPointcut the core functions of relying mainly on aspectj Pointcut, ClassFilter components and processing, and MethodMatcher interface implementation, dedicated to handle classes and methods of matching filter operation.
  • AOPProxy is an abstract object of proxies, and its implementation is mainly based on JDK proxies and CGLIB proxies. In the previous section on object instantiation CglibSubclassingInstantiationStrategy, we also used provided additional functionality.

2. Agency method cases

Before implementing the core functions of AOP, let’s do a case study of the proxy method, which can summarize the core picture of the proxy method, so that we can better understand the subsequent implementation process of decoupling the various methods and designing the decoupling functions.

Unit testing

@test public void test_proxy_method() {@test public void test_proxy_method() {// Object targetObj = new UserService(); // IUserService Proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), New InvocationHandler () {/ / method matcher MethodMatcher MethodMatcher = new AspectJExpressionPointcut (" execution (* cn.bugstack.springframework.test.bean.IUserService.*(..) ) "); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (methodMatcher.matches(method, TargetObj.getClass ())) {// MethodInterceptor MethodInterceptor = Invocation -> {long start = System.currentTimeMillis(); try { return invocation.proceed(); } finally {System.out.println(" monitor -begin By AOP"); System.out.println(" Method Name: "+ Invocation.getMethod().getName()); System.out.println(" Method time: "+ (System.currentTimeMillis() -start) + "ms"); System.out.println(" monitor -end \r\n"); }}; / / reflection calls return methodInterceptor. Invoke (new ReflectiveMethodInvocation (targetObj, method, args)); } return method.invoke(targetObj, args); }}); String result = proxy.queryUserInfo(); Println (" test result: "+ result); }
  • First of all, the goal of the whole case is to give a UserService as the target object, and to intercept all the methods in the class to add monitoring information printing processing.
  • Invoke (Object Proxy, Method Method, Object[] args); invoke(Object Proxy, Method Method, Object[] args); It also uses the user’s own actions after intercepting the method. This is very similar to what we use with AOP, except that when you use AOP the framework provides better functionality, and this shows you all the core processes.

The test results

Process finished with exit code 0 monitor -begin By AOP method name: queryUserInfo method time: 86ms monitor -end
  • From the test results, we can see that we have intercepted and monitored the UserService# QueryUserInfo method. In fact, the AOP we implemented later is the result now, but we need to decouple this part of the test case into more extensible implementation of each module.

Dismantling case

  • Refer to screenshot 12-3 for the unpacking process. We need to unpack the proxy object, because it can be either a JDK implementation or a CGLIB processing.
  • The method matcher operation is already a separate implementation class, but we need to wrap the incoming target object, method matching, and intercepting methods in a uniform way to make it easier for external calls to pass in a pass-through.
  • And in the end it turns out thatReflectiveMethodInvocationThe use of it is currently already implementedMethodInvocationInterface. Parameter information includes: the object to call, the method to call, and the incoming arguments to call.

3. The pointcut expression

Defines the interface

cn.bugstack.springframework.aop.Pointcut

public interface Pointcut { /** * Return the ClassFilter for this pointcut. * @return the ClassFilter (never <code>null</code>) */ ClassFilter getClassFilter(); /** * Return the MethodMatcher for this pointcut. * @return the MethodMatcher (never <code>null</code>) */ MethodMatcher  getMethodMatcher(); }
  • A pointcut interface that defines two classes to get ClassFilter and MethodMatcher, both of which are provided by pointcut expressions.

cn.bugstack.springframework.aop.ClassFilter

public interface ClassFilter { /** * Should the pointcut apply to the given interface or target class? * @param clazz the candidate target class * @return whether the advice should apply to the given target class */ boolean  matches(Class<? > clazz); }
  • Defines class matching classes that are used to find the given interface and target class at pointcut.

cn.bugstack.springframework.aop.MethodMatcher

public interface MethodMatcher { /** * Perform static checking whether the given method matches. If this * @return whether or not this method matches statically */ boolean matches(Method method, Class<? > targetClass); }
  • Method matching to find the target class and method within the expression range. This is reflected in the above case:methodMatcher.matches(method, targetObj.getClass())

Implement the pointcut expression class

public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher { private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>(); static { SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); } private final PointcutExpression pointcutExpression; public AspectJExpressionPointcut(String expression) { PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITI VES, this.getClass().getClassLoader()); pointcutExpression = pointcutParser.parsePointcutExpression(expression); } @Override public boolean matches(Class<? > clazz) { return pointcutExpression.couldMatchJoinPointsInType(clazz); } @Override public boolean matches(Method method, Class<? > targetClass) { return pointcutExpression.matchesMethodExecution(method).alwaysMatches(); } @Override public ClassFilter getClassFilter() { return this; } @Override public MethodMatcher getMethodMatcher() { return this; }}
  • The Pointcut expression implements three interface definition methods, Pointcut, ClassFilter, and MethodMatcher, and this class is primarily used for expression validation methods provided by the AspectJ package.
  • Match matches:pointcutExpression.couldMatchJoinPointsInType(clazz),pointcutExpression.matchesMethodExecution(method).alwaysMatches()This part of the content can be tested separately.

Match the validation

@Test public void test_aop() throws NoSuchMethodException { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.UserService.*(..) ) "); Class<UserService> clazz = UserService.class; Method method = clazz.getDeclaredMethod("queryUserInfo"); System.out.println(pointcut.matches(clazz)); System.out.println(pointcut.matches(method, clazz)); // true, true}
  • A separate verification test is provided to see if the method you intercepted matches the corresponding object.

4. Packaging section notification information

cn.bugstack.springframework.aop.AdvisedSupport

Public class AdvisedSupport {private TargetSource targetSource; // private MethodInterceptor MethodInterceptor; Private MethodMatcher private MethodMatcher private MethodMatcher private MethodMatcher; / /... get/set }
  • AdvisedSupport is mainly used to wrap the properties of Proxy, intercept and match into a class for easy use in the Proxy implementation class.This is the same as packaging parameters in your business development
  • TargetSource is a target Object that provides the Object parameter property in the target Object class and gets the TargetClass targetClass information.
  • MethodInterceptor is a concrete method implementation class that implements the MethodInterceptor#invoke method.In the case of this article, we do method monitoring
  • MethodMatcher, it is a matching method of operation, the object is provided by AspectJExpressionPointcut service.

5. Proxy abstract implementation (JDK& CGlib)

Defines the interface

cn.bugstack.springframework.aop.framework

public interface AopProxy {

    Object getProxy();

}
  • Define a standard interface for getting the proxy class. Because the proxy can be implemented in either a JDK or a CGlib manner, defining interfaces makes it easier to manage the implementation classes.

cn.bugstack.springframework.aop.framework.JdkDynamicAopProxy

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { private final AdvisedSupport advised; public JdkDynamicAopProxy(AdvisedSupport advised) { this.advised = advised; } @Override public Object getProxy() { return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); } return method.invoke(advised.getTargetSource().getTarget(), args); }}
  • The proxy class based on the JDK implementation needs to implement the interface AOPProxy and InvocationHandler, so that the proxy object getProxy and the reflection calling method invoke can be handled separately.
  • The getProxy method is an operation that proxies an object. You need to provide arguments to ClassLoader, AdvisedSupport, and the current class, this, because this class provides the invoke method.
  • Invoke is a reflection call to MethodInterceptor.invoke, using the user-provided method interception implementation, after primarily processing the matched method.
  • There is also a ReflectiveMethodInvocation, it is one into the other packaging information, provides the reference object: the target object, method, the ginseng.

cn.bugstack.springframework.aop.framework.Cglib2AopProxy

public class Cglib2AopProxy implements AopProxy { private final AdvisedSupport advised; public Cglib2AopProxy(AdvisedSupport advised) { this.advised = advised; } @Override public Object getProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass()); enhancer.setInterfaces(advised.getTargetSource().getTargetClass()); enhancer.setCallback(new DynamicAdvisedInterceptor(advised)); return enhancer.create(); } private static class DynamicAdvisedInterceptor implements MethodInterceptor { @Override public Object intercept(Object  o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy); if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { return advised.getMethodInterceptor().invoke(methodInvocation); } return methodInvocation.proceed(); } } private static class CglibMethodInvocation extends ReflectiveMethodInvocation { @Override public Object proceed() throws Throwable { return this.methodProxy.invoke(this.target, this.arguments); }}}
  • Classes based on CGLIB that use the Enhancer proxy can handle the proxy object generation of the object at run time using the underlying ASM bytecode enhancement technique for the interface, so the proxee class does not need to implement any interface.
  • As for the extended user interception methods, they will be handled in Enhancer# SetCallback, the user’s own new interception handling. Here you can see DynamicAdvisedInterceptor# intercept do after matching the corresponding reflection.

Five, the test

1. Prepare beforehand

public class UserService implements IUserService { public String queryUserInfo() { try { Thread.sleep(new Random(1).nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } return "Xiao Fu Ge, 100001, Shenzhen "; } public String register(String userName) { try { Thread.sleep(new Random(1).nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } return "userName:" + userName + "success!" ; }}
  • There are two different methods provided in UserService, and you can add new classes to add tests. Later in our testing process, we will add our intercepting processing to these two methods, and the printing method will take time to execute.

2. Customize interception methods

public class UserServiceInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { long start = System.currentTimeMillis(); try { return invocation.proceed(); } finally {System.out.println(" monitor -begin By AOP"); System.out.println(" Method Name: "+ Invocation.getMethod()); System.out.println(" Method time: "+ (System.currentTimeMillis() -start) + "ms"); System.out.println(" monitor -end \r\n"); }}}
  • The user-defined interceptor method needs to implement the invoke method of the MethodInterceptor interface, which is used much like Spring AOP, wrapping the invocation.proceed() release and adding the monitoring information in finally.

Unit tests

@Test public void test_dynamic() {// IUserService UserService = new UserService(); // AdvisedSupport AdvisedSupport = new AdvisedSupport(); advisedSupport.setTargetSource(new TargetSource(userService)); advisedSupport.setMethodInterceptor(new UserServiceInterceptor()); advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..) ) ")); // IUserService proxy_jdk = (IUserService) new JDKDynamicAopProxy (AdvisedSupport).getProxy(); Println (" Test result: "+ proxy_jdk.queryUserInfo()); // IUserService proxy_cglib = (IUserService) new Cglib2AopProxy(AdvisedSupport).getProxy(); Println (" Test result: "+ proxy_cglib.register(" register ")); }
  • The whole case tested the core code of AOP before Spring was incorporated, including what the target object is, how to assemble the proxy information, and how to invoke the proxy object.
  • AdvisedSupport, which wraps the target object, the user-implemented interception method, and the method matching expression.
  • Then we call JDKDynamicaopProxy and Cglib2aopProxy, two different proxy classes, to see if they can intercept the method

The test results

Monitor -begin By AOP method name: Public abstract Java. Lang. String cn. Bugstack. Springframework. Test. Beans. IUserService. QueryUserInfo () method is time consuming: 86ms monitor - End test results: xiaogu, 100001, Shenzhen monitor - Begin By AOP Public Java. Lang. String cn. Bugstack. Springframework. Test. Beans. UserService. Register (Java. Lang. String) method is time consuming: 97ms monitor - End test results: registered user: HUA HUA SUCCESS! Process finished with exit code 0
  • As the AOP function definition, we can use such proxy mode, method matching and intercepting, in the corresponding target method, do the intercepting operation to monitor the printing of information.

Six, summarized

  • From the use of Proxy# NewProxyInstance and MethodInterceptor# Invoke in this paper to verify the core principles of facets and then decompose the functions into the Spring framework implementation, we can see that a seemingly complex technology often does not have much core content. But because of the need to decouple and wrap responsibilities in order to meet further extensions, the use of the pattern is designed in such a way that the invocation can be simplified and itself can be continuously expanded on demand.
  • The AOP implementation is not yet integrated with Spring, but is a concrete implementation of the faceted technology. You can learn how to handle proxy objects, filters, interceptors, and the differences between using CGlib and JDK proxies. This is used in all sorts of other scenarios that require less manual hard coding.Such as RPC, MyBatis, MQ, distributed tasks
  • The use of some of the core technologies is strongly related, and they do not exist in isolation. The process of connecting the whole technology stack in series requires a lot of learning, accumulation and laying from point to surface, so as to expand the learning of a knowledge point to a knowledge area and the construction of a knowledge system.

Seven, series of recommendations

  • Java Interface Manual (PDF), 417 pages, 115,000 words
  • You’ve been working for two or three years, and you don’t know what the architecture drawing is?
  • Code farming cloud service use learning, department environment, beginning mouth, domain name, SSL, take a blog!
  • Before graduation, I wrote 200,000 lines of code, which made me the face bully in the eyes of my classmates.
  • Thread.start(), how does it make threads start? -%E5%AE%83%E6%98%AF%E6%80%8E%E4%B9%88%E8%AE%A9%E7%BA%BF%E7%A8%8B%E5%90%AF%E5%8A%A8%E7%9A%84%E5%91%A2.html)