Can insist on others can not insist, to have others have not. Pay attention to programming avenue public number, let us stick to what we think in mind, grow up together!

 

Wrote a surprise interview series about redis years ago. In this series, I have compiled some interview questions to share with you, which will help students who want to change jobs in Jinsan Yinsi as well as ME. Let’s consolidate and surprise some interview questions often asked by interviewers, come on!!

Redis data type? Which scenarios are applicable?

The thread model of Redis Why is single thread efficiency so high?

[Interview shock] – Redis – Redis master copy? Sentinel mechanism?

Redis sentry principle and Persistence mechanism

Redis Cluster and frequently asked questions about cache usage and architecture design

What is AOP?

In the software industry, AOP for the abbreviation of Aspect Oriented Programming, meaning: section-oriented Programming, through pre-compilation and runtime dynamic proxy to achieve unified maintenance of program functions of a technology.

AOP is a continuation of OOP, a hot topic in software development, and an important content in Spring framework. It is a derivative paradigm of functional programming. Using AOP, each part of the business logic can be isolated, thus reducing the degree of coupling between each part of the business logic, improving the reusability of the program, and improving the efficiency of development.

Spring AOP is faceted oriented programming

Interface Call Time

Now that we have an interface that logs the interface time, what do we do? We usually get the system time at the start and end of the interface, and then subtract the interface time from the two. As follows, in line 20 we print out the interface time.

 1@RestController

2@Slf4j

3public class LoginController {

4    @Autowired

5    LoginService loginService;

6    @RequestMapping("/login/{id}")

7    public Map<String.Object> login(@PathVariable("id") Integer id){

8        long start = System.currentTimeMillis();

9        Map<String.Object> result = new HashMap<>();

10        result.put("status"."0");

11        result.put("msg" , "Failure");

12        if (loginService.login(id)) {

13            result.put("status"."1");

14            result.put("msg" , "Success");

15        }

16        long end = System.currentTimeMillis();

17        log.info("Take = > {} ms",end-start);

18        return result;

19    }

20}

Copy the code

Start the class:

1@SpringBootApplication

2public class SpringaopSbApplication {

3    public static void main(String[] args) {

4        SpringApplication.run(SpringaopSbApplication.class, args);

5    }

6}

Copy the code

But what if all interfaces logged elapsed time? Are we still doing it this way? Obviously not, this requires adding the same code to every interface, and if your boss says to remove it later, do you still have to delete it one by one? It’s unthinkable. So for this kind of demand, you can actually refine it. We think that counting the elapsed time of an interface is simply a matter of recording the elapsed time before and after the implementation of the interface and then printing it out, and then adding our extracted common code in such a place. This is like cross-cutting the original business code and adding common code where necessary to enhance the original business code. This is where AOP comes in.

Spring AOP application scenario – Interface time logging

Let’s take a look at using Spring AOP to meet this requirement.

PointCut () defines a set of join points. @pointcut defines a set of join points. AroundTimeCounter () is the function to add, and is modified by the @around annotation. Is a wrap notification (a type of Spring AOP notification), which is essentially the time spent logging before and after method execution, subtracting it, and printing it out.

 1@Aspect

2@Component

3@Slf4j

4public class TimeMoitor {

5    @Pointcut(value = "execution(* com.walking.springaopsb.controller.*.*(..) )")

6    public void pointCut(a){}

7

8    @Around(value = "com.walking.springaopsb.aop.TimeMoitor.pointCut()")

9    public Object aroundTimeCounter(ProceedingJoinPoint jpx){

10        long start = System.currentTimeMillis();

11        Object proceed = null;

12        try {

13             proceed = jpx.proceed();

14        } catch (Throwable throwable) {

15            throwable.printStackTrace();

16        }

17        long end = System.currentTimeMillis();

18        log.info("Take = > {} ms",end-start);

19        return proceed;

20    }

21}

Copy the code

And then in the LoginController#login method we can remove the code that takes time to print the log.

 1@RestController

2@Slf4j

3public class LoginController {

4    @Autowired

5    LoginService loginService;

6    @RequestMapping("/login/{id}")

7    public Map<String.Object> login(@PathVariable("id") Integer id){

8        Map<String.Object> result = new HashMap<>();

9        result.put("status"."0");

10        result.put("msg" , "Failure");

11        if (loginService.login(id)) {

12            result.put("status"."1");

13            result.put("msg" , "Success");

14        }

15        return result;

16    }

17}

Copy the code

For example, if there are other methods in LoginController, you can apply them as well. Console logging with Spring AOP:

The principles of Spring AOP

This is an application scenario for Spring AOP. So what is the principle of Spring AOP and what technology is used? It’s reflection + dynamic proxy. When does Spring AOP use JDK dynamic proxies or cglib? Which one is used by default?

Source code analysis

So let’s look at it through the source code. First we will start the class change, so we can debug the source code.

Start the class:

1@ComponentScan("com.walking.springaopsb.*")

2@EnableAspectJAutoProxy

3public class SpringaopSbApplication {

4    public static void main(String[] args) {

5        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringaopSbApplication.class);

6        LoginController loginController = (LoginController) applicationContext.getBean("loginController");

7        loginController.login(123);

8    }

9}

Copy the code

We changed the startup class a little bit, put the breakpoint on line 6, startup, go down a step, and look at the loginController variable.

We found that the proxy class is generated in cglib mode, which indicates that the proxy class is obtained from the IoC container. Is it generated when the IoC container is initialized or generated when it is acquired? We also follow the source code to have a look.

It is important to know whether we are now looking at the proxy class generated on line 5 or line 6. Look at line 6 getBean, enters this method org. Springframework. Context. Support. AbstractApplicationContext# getBean (Java. Lang. String).

And then we only see there is return, in entering the getBean (org. Springframework. Beans. Factory. Support. AbstractBeanFactory# getBean (Java. Lang. String)).

See doGetBean (org. Springframework. Beans. Factory. Support. AbstractBeanFactory# doGetBean) line 120 sharedInstance has become a proxy class

So we entered the org. Springframework. Beans. Factory. Support. DefaultSingletonBeanRegistry# getSingleton (Java. Lang. String) method, to run, And then I have a break point, Hit the org. Springframework. Beans. Factory. Support. DefaultSingletonBeanRegistry# getSingleton (Java. Lang. String, Boolean).

After through 88 rows, singletonObject became a proxy class, so the key is in this. SingletonObjects. Get (beanName); We can see that singletonObjects is a ConcurrentHashMap. The original IoC instance is in the ConcurrentHashMap. private final Map

singletonObjects = new ConcurrentHashMap(256); So here we know that the proxy class is not generated at getBean time, that is, not at line 6 of the startup class, that is, at line 5, when the IoC container is initialized. ConcurrentHashMap is a get, so there must be a put. Search and find, also in this class, an addSingleton method called in two places, One is in the org. Springframework. Beans. Factory. Support. DefaultSingletonBeanRegistry# registerSingleton calls, One is in the org. Springframework. Beans. Factory. Support. DefaultSingletonBeanRegistry# getSingleton (Java. Lang. String, org.springframework.beans.factory.ObjectFactory
)
,>

I’m just going to hit the breakpoint into these two methods, see which one it goes to, and I’m going to get rid of all the other breakpoints, of course, because spring has other instances of itself to fetch, and the IoC container has its own instances of Spring, so this breakpoint has to be conditional, if beanName is a loginController, That would be much more convenient. We’ll just keep line 5 because getSingleton is called in the getBean as well.

Run to start the class and found entered getSingleton method, but the Object singletonObject = this. SingletonObjects. Get (beanName); Null is returned, so keep going. We see that on line 127 we return the proxy class, and if we look at this line we don’t know which getObject is the implementation class, so we go to the bottom left corner of the method stack, and we look at the previous method of this method,

The second method, doGetBean, in the lower left corner, found that it passed an anonymous inner class, Adjustment in the anonymous inner class is org. Springframework. Beans. Factory. Support. AbstractBeanFactory# createBean so we finished the breakpoint, into this createBean breaking point, the same conditions. Break through 324 rows into a proxy class, or into the org. Springframework. Beans. Factory. Support. AbstractAutowireCapableBeanFactory# doCreateBean look, a breakpoint with same conditions

The breakpoint passes line 380 of the doCreateBean method to produce the proxy class, So the breakpoint is hit the org. Springframework. Beans. Factory. Support. AbstractAutowireCapableBeanFactory# initializeBean (Java. Lang. String, Java.lang.object, org. Springframework. Beans. Factory. Support. RootBeanDefinition) method, combined with same conditions, remove the other breakpoints, rerun.

By the time you get past line 1240, you’ve become a proxy class, So the breakpoint is hit the org. Springframework. Beans. Factory. Support. AbstractAutowireCapableBeanFactory# applyBeanPostProcessorsAfterInitia The lization method, again conditional, removes the other breakpoints and runs again.

We found that there is a cycle, the iteration is enclosing getBeanPostProcessors () as a result, we see what this is, is the List, below is the List of data

After several debug found when AnnotationAwareAspectJAutoProxyCreator BeanPostProcessor as the fourth element, the result becomes a proxy class. The key is in the processor. PostProcessAfterInitialization () this method, the breakpoint to play in.

Found no AnnotationAwareAspectJAutoProxyCreator the implementation class

Then look at the AnnotationAwareAspectJAutoProxyCreator parent class, Ctrl + Alt + Shift + U see AnnotationAwareAspectJAutoProxyCreator class diagram of the dependencies

Found AbstractAutoProxyCreator in last figure, and AnnotationAwareAspectJAutoProxyCreator without rewriting postProcessAfterInitialization method, So let’s look at the Abstracta toProxyCreator method.

Interrupt point found Object bean is not a proxy class, then see org. Springframework. Aop) framework. Autoproxy. AbstractAutoProxyCreator# wrapIfNecessary method. CreateProxy () is called in this method to create the proxy class.

Return proxyFactory.getProxy(getProxyClassLoader); Take a look at the getProxy method

So createAopProxy() returns an instance of type AopProxy. There are two implementation classes for creating CglibAopProxy and JdkDynamicAopProxy, as well as cglib and JDK dynamic proxies.

So which one to create is the key thing we’re going to look at today, so let’s go to the createAopProxy() method.

Again in org. Springframework. Aop. Framework. DefaultAopProxyFactory# createAopProxy method and have a look.

Config. IsOptimize () and config. IsProxyTargetClass () is false by default created here logincontroller config data is as follows

Then check if the targetClass is an interface. In this case, our LoginController is not an interface

So whether Spring AOP uses JDK dynamic proxies or cglib depends on whether it is an interface, and there is no default. Let’s change the LoginController to implement the interface

Debug starts, and the resulting proxy class is the JDK dynamic proxy.

Why do JDK dynamic proxies have to be interfaces?

LoginController (ILoginBaseController); LoginController (ILoginBaseController)

Org. Springframework. Aop. Framework. ProxyFactory# getProxy createAopProxy (Java. Lang. This) (.) getProxy is we solve the problem of the entrance, So let’s put a breakpoint in getProxy,

JdkDynamicAopProxy#getProxy(java.lang.classloader) method to add a breakpoint to the return statement

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);Copy the code

Then add a breakpoint on proxy.newProxyInstance, step by step, and line 719 is the key

in

Go to the proxyClassCache. Get method

In line 120, we see that the apply method is a BiFunction interface method, which has the following implementation class. If we put the mouse on subKeyFactory, we find that it is of type KeyFactory. If we go to debug, there is nothing we want

Then, further down, there is a while loop. After several debugging, I find that this loop is the key

We need to get into this get

There is a key line, line 230 in the figure below, that still has the apply method

As I said, it has the following implementation class

Know that valueFactory is of type ProxyClassFactory by looking at its type, and then enter the class. It is a static inner class of Proxy.

Line 639-643 is the key after many debuts, where line 639 is getting the bytecode, and then line 642 is calling defineClass0 (a native method) to create the instance.

 

As an interlude, the answer to why Java dynamic Proxy generates a Proxy class with a $Proxy in front of it can be found here.

 

 

So going back to the bytecode, we can’t read it, but we can decompile it so let’s take 639 lines and write a test class

 1public class Test {

2    public static void main(String[] args) throws Exception {

3        // Get the bytecode of ILoginBaseController

4        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy#MyLoginController".new Class[]{ILoginBaseController.class});

5        // Output to myLoginController.class

6        FileOutputStream fileOutputStream = new FileOutputStream(new File("MyLoginController.class"));

7        fileOutputStream.write(bytes);

8        fileOutputStream.flush();

9        fileOutputStream.close();

10    }

11}

Copy the code

 

We will see that the specified file is generated

Do you see why JDK dynamic proxies can only be interfaces? $Proxy#MyLoginController () {$Proxy#MyLoginController () {$Proxy#MyLoginController ();

 

conclusion

Spring AOP has no default proxy, either JDK dynamic proxy or Cglib dynamic proxy, and why Java dynamic proxy can only be interfaces. And we also have a look at the spring source code, although not very carefully, but through this look at the source code our understanding is more deepened, but also exercise the ability to read the source code.