I’ll cover AOP’s implementation posture, including AspectJ and Spring, as well as my own understanding of AOP’s internal implementation.

preface

Before looking at this article, please first look at “[Spring foundation series 5] Spring AOP foundation (on)”, because the two articles are their own system, for the basic knowledge of Spring AOP, originally intended to write only one, because of the fear that we do not want to see a long article, it became the next two articles.

There are four main implementations of AOP in Java, and AspectJ’s XML-based declarative style was illustrated in detail in the last article, This article focuses on AspectJ’s annotation-based declarative, Spring-based JDK dynamic proxy and CGLib dynamic proxy.

This article is also the last one in the Spring series. It took nearly two weeks to summarize the results. If you want to learn Spring, you are advised to start from the first article:

  • 【Spring Basics series 1】 Assembly beans based on annotations
  • 【Spring Basics series 2】 Full Spring IOC Basics
  • 【Spring Basics series 3】 Common Notes on Spring
  • Annotated @Transactional with Spring Foundation Series 4
  • Spring AOP Basics (Part 1)
  • Spring AOP Basics (Part 2)

Spring AOP

Spring JDK dynamic proxy

JDK dynamic proxies are implemented through the java.lang.reflect.proxy class in the JDK. The following uses a specific example to demonstrate the use of JDK dynamic proxy, we first create a new interface, and give a specific implementation class:

public interface CustomerDao {
    public void add(a); / / add
    public void update(a); / / modify
    public void delete(a); / / delete
    public void find(a); / / query
}
Copy the code
public class CustomerDaoImpl implements CustomerDao {
    @Override
    public void add(a) {
        System.out.println("Add customer...");
    }
    @Override
    public void update(a) {
        System.out.println("Modify customer...");
    }
    @Override
    public void delete(a) {
        System.out.println("Delete customer...");
    }
    @Override
    public void find(a) {
        System.out.println("Modify customer..."); }}Copy the code

Create a new section class:

public class MyAspect {
    public void myBefore(a) {
        System.out.println("Before method execution");
    }
    public void myAfter(a) {
        System.out.println("After method execution"); }}Copy the code

Create a new factory class implemented through the proxy:

public class MyBeanFactory {
    public static CustomerDao getBean(a) {
        // Prepare the target class
        final CustomerDao customerDao = new CustomerDaoImpl();
        // Create the section class instance
        final MyAspect myAspect = new MyAspect();
        // Use proxy classes for enhancement
        return (CustomerDao) Proxy.newProxyInstance(
                / / MyBeanFactory. Class. GetClassLoader (), / / the can also
                CustomerDao.class.getClassLoader(),
                new Class[] { CustomerDao.class }, new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        myAspect.myBefore(); / / before
                        Object obj = method.invoke(customerDao, args);
                        myAspect.myAfter(); / / after enhancement
                        returnobj; }}); }}Copy the code

I use MyBeanFactory. Class. GetClassLoader (), found that also does not affect the function, study later free again.

Finally, a test example:

public class JDKProxyTest {
    @Test
    public void test(a) {
        // Get the specified content from the factory (equivalent to spring, but as a proxy object)
        CustomerDao customerDao = MyBeanFactory.getBean();
        // Execute method
        customerDao.add();
        customerDao.update();
        //customerDao.delete();
        //customerDao.find();}}/ / output:
// before the method is executed
// Add client...
// After the method is executed
// before the method is executed
// Modify customer...
// After the method is executed
Copy the code

You can see from the output that the enhanced code was successfully called before and after the target class’s methods, indicating that the JDK dynamic proxy has been implemented.

Spring CGLlB dynamic proxy

JDK dynamic proxies are very simple to use, but they have some limitations because JDK dynamic proxies must implement one or more interfaces. If you don’t want to implement interfaces, you can use CGLIB proxies.

CGLIB (Code Generation Library) is a high performance open source Code Generation package used by many AOP frameworks. The bottom layer is to convert bytecode and generate new classes by using a small and fast bytecode processing framework ASM (Java Bytecode Manipulation Framework). CGLIB therefore relies on ASM packages.

Let’s look at the implementation posture of the CGLlB dynamic proxy. First we define an implementation class:

public class GoodsDao {
    public void add(a) {
        System.out.println("Add goods...");
    }
    public void update(a) {
        System.out.println("Modify merchandise...");
    }
    public void delete(a) {
        System.out.println("Delete goods...");
    }
    public void find(a) {
        System.out.println("Modify merchandise..."); }}Copy the code

Create a new section class:

public class MyAspect {
    public void myBefore(a) {
        System.out.println("Before method execution");
    }
    public void myAfter(a) {
        System.out.println("After method execution"); }}Copy the code

Create a new factory by proxy class:

public class MyBeanFactory {
    public static GoodsDao getBean(a) {
        // Prepare the target class
        final GoodsDao goodsDao = new GoodsDao();
        // Create the section class instance
        final MyAspect myAspect = new MyAspect();
        // Generate proxy class, CGLIB at run time, generate the specified object subclass, enhanced
        Enhancer enhancer = new Enhancer();
        // Identify the classes that need to be enhanced
        enhancer.setSuperclass(goodsDao.getClass());
        // Add the callback function
        enhancer.setCallback(new MethodInterceptor() {
            // Invoke is the same as JDK invoke, and the first three arguments are the same as JDK invoke
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                myAspect.myBefore(); / / before
                Object obj = method.invoke(goodsDao, args); // Target method execution
                myAspect.myAfter(); / / after enhancement
                returnobj; }});// Create the proxy class
        GoodsDao goodsDaoProxy = (GoodsDao) enhancer.create();
        returngoodsDaoProxy; }}Copy the code

Finally, a test example:

public class JDKProxyTest {
    @Test
    public void test(a) {
        // Get the specified content from the factory (equivalent to spring, but as a proxy object)
        GoodsDao goodsDao = MyBeanFactory.getBean();
        // Execute method
        goodsDao.add();
        goodsDao.update();
        // goodsDao.delete();
        // goodsDao.find();}}/ / output:
// before the method is executed
// Add items...
// After the method is executed
// before the method is executed
// Modify the product...
// After the method is executed
Copy the code

As you can see from the output, the enhanced code is successfully called before and after the target class’s methods, indicating that manual proxies are also implemented using the CGLIB proxy.

Both are

Spring AOP Basics (part 1) Spring AOP Basics (Part 1)

Note: The JDK dynamic proxy is based on an interface, while the CGLIB dynamic proxy is based on a class. The above example shows the difference between the two.

Create an AOP proxy using ProxyFactoryBean

I have never encountered this approach in a project, just as an extension of knowledge.

Basic knowledge of

Now that I’ve covered two ways to use AOP manual proxies, here’s how Spring creates them. Spring to create a basic method is to use AOP agent org. Springframework. AOP) framework. ProxyFactoryBean, corresponding breakthrough point and inform the class provides a complete control ability, and can generate the specified content.

The common configurable properties in the ProxyFactoryBean class are shown in the table below:

Specific instance

CustomerDao and CustomerDaoImpl are the same as in the Spring JDK Dynamic Proxy example, except that we annotate @Component(” CustomerDao “). The main purpose is to reduce configuration.

@Component("customerDao")
public class CustomerDaoImpl implements CustomerDao {
  // The same method as the "Spring JDK dynamic Proxy" sample content
}
Copy the code

Then define a section class:

@Component
public class MyAspect implements MethodInterceptor {
    public Object invoke(MethodInvocation mi) throws Throwable {
        System.out.println("Before method execution");
        Object obj = mi.proceed();
        System.out.println("After method execution");
        returnobj; }}Copy the code

It is important to add the appropriate configuration to the applicationContext.xml file:

<context:component-scan base-package="com.java.spring.aop.xml"/ > <! Generate proxy object --> <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <! -- interface implemented by the proxy --> <property name="proxyInterfaces" value="com.java.spring.aop.xml.CustomerDao"/ > <! -- Target object of the proxy --> <property name="target" ref="customerDao"/ > <! -- Enhance target with notification --> <property name="interceptorNames" value="myAspect"/ > <! How to generate a proxy,true: the use of additional;falseUse JDK dynamic proxy --> <property name="proxyTargetClass" value="true" />
</bean>
Copy the code

A final look at the test case:

public class FactoryBeanTest {
    @Test
    public void test(a) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDaoProxy");
        customerDao.add();
        customerDao.update();
        // customerDao.delete();
        // customerDao.find();}}/ / output:
// before the method is executed
// Add client...
// After the method is executed
// before the method is executed
// Modify customer...
// After the method is executed
Copy the code

The difference between this and “Spring JDK Dynamic proxy” and “Spring CGLlB dynamic proxy” is that the first two are manual, and this is automatic, combined with “classification of advice” (see “Spring AOP Basics (part 1)”).

AspectJ AOP

AspectJ is based on XML

Refer directly to the examples in Spring AOP Basics (Part 1).

AspectJ is based on annotations

Basic knowledge of

In Spring, although AOP development can be achieved using XML configuration files, if all the relevant configuration is centralized in the configuration files, the XML configuration files will become bloated, which will make maintenance and upgrading difficult.

To that end, the AspectJ framework provides an alternative development approach to AOP development — annotation-based declarative. AspectJ allows you to define aspects, pointcuts, and enhanced processing using annotations, which the Spring framework recognizes and generates AOP proxies based on. An introduction to the Annotation Annotation is shown in the table below:

Specific instance

Define a concrete implementation class:

@Component("customerDao")
public class CustomerDaoImpl {
    public void add(a) throws Exception {
        System.out.println("Add customer...");
        //throw new Exception(" throw Exception test ");
    }
    public void update(a) {
        System.out.println("Modify customer...");
    }
    public void delete(a) {
        System.out.println("Delete customer...");
    }
    public void find(a) {
        System.out.println("Modify customer..."); }}Copy the code

Then define a section class:

@Aspect
@Component
public class MyAspect {
    / / is used to replace: < aop: pointcut expression = "execution (* com in Java. Spring. Aop. Customer. *. * (..) )" id="myPointCut"/>
    @Pointcut("execution(* com.java.spring.aop.customer.*.*(..) )"
    private void myPointCut(a) {}// Pre-notification
    @Before("myPointCut()")
    public void myBefore(JoinPoint joinPoint) {
        System.out.println("Pre-notification, method name: + joinPoint.getSignature().getName());
    }
    // post notification
    @AfterReturning(value = "myPointCut()")
    public void myAfterReturning(JoinPoint joinPoint) {
        System.out.println("Postnotification, method name:" + joinPoint.getSignature().getName());
    }

    // Wrap around the notification
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
            throws Throwable {
        System.out.println("Start around"); / /
        Object obj = proceedingJoinPoint.proceed(); // Execute the current target method
        System.out.println("Wrap end"); / / end
        return obj;
    }
    // Exception notification
    @AfterThrowing(value = "myPointCut()", throwing = "e")
    public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
        System.out.println("Exception notification, error.");
    }
    // Final notification
    @After("myPointCut()")
    public void myAfter(a) {
        System.out.println("Final notice"); }}Copy the code

As with “AspectJ based on XML”, you need to add the corresponding configuration to the applicationContext.xml file:

<context:component-scan base-package="com.java.spring.aop.customer" />
<context:component-scan base-package="com.java.spring.aop.annotation" />
<aop:aspectj-autoproxy proxy-target-class="true"/>
Copy the code

The first two lines are the package for automatic annotation injection, the third line is the need to turn on AOP, and here are the test cases:

public class AnnotationTest {
    @Test
    public void test(a) throws Exception {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // Get the instance from the Spring container
        CustomerDaoImpl customerDao = (CustomerDaoImpl) applicationContext.getBean("customerDao");
        // Execute methodcustomerDao.add(); }}/ / output:
// Wrap around start
// Pre-notification, method name :add
// Add client...
// Wrap around end
// Final notification
// After the notification, method name: add
Copy the code

Discussion on AOP implementation

The only difference is that the object of the proxy in the “Spring JDK dynamic proxy” is packaged as a factory. So factory mode + agent mode, feel no technical content.

But when we look at the “AspectJ based on XML” example, let’s review its test case:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerDaoImpl customerDao = (CustomerDaoImpl) applicationContext.getBean("customerDao");
// Execute method
customerDao.add();
Copy the code

Add (); customerDao (); customerDao (); I understand that the Bean customerDao is not the Bean customerDao itself, but the customerDao decorator class M, which modiates the customerDao method. How to modify it? Is through the section method to decorate!

This may sound a little confusing, but you can see the article “[Design Pattern Series 7] Decorator pattern”, I still directly steal the picture inside:

In this case, the entity class is Chicken, and the decorator is RoastFood. If there is no decorator, the call to cook() outputs “Chicken”, and with the RoastFood decorator, the call to cook() outputs “roast Chicken”, so the “roast” is the decoration of “Chicken”.

CustomerDaoImpl vs. Chicken, MyAspect vs. Roast, and finally decorator, decorate the method from MyAspect into CustomerDaoImpl. (Decorator mode only defines roast in the decorator class. You can wrap a separate class N with a bake method inside, and that class N can be compared to MyAspect.

Going back to our test case above, it should be easy to understand that Spring gets the CustomerDaoImpl class first, which is actually decorated through MyAspect, so instead of getting the CustomerDaoImpl class itself, Spring gets its decorator. Finally, this decorator is converted to CustomerDaoImpl because the CustomerDaoImpl class and decorator inherit the CustomerDao interface.

Summary: For Spirng AOP, the entity class obtained is not the entity class itself, but rather the decorator of the entity class. The decorator should have the entity class as a member variable of the decorator, and then enhanced by the method of section MyAspect, where the decorator pattern is actually used. This decorator is obtained through the factory encapsulation, and then the invocation of the entity class method in the decorator should be in the proxy mode, so Spirng AOP at least uses the decorator, proxy mode and factory mode, and then completes the whole function encapsulation.

This summary, is only one statement, may be a little unconstrained, after all, did not see AOP related source code and principle related articles, if not so to design AOP, that there are what other methods? At present, I have not thought of other ways, if there is a wrong understanding of the article, or just their own “wishful thinking”, welcome to point out to me!

Afterword.

This article will not write a summary, because about the basic knowledge of Spring AOP, I should speak more detailed, context is also very clear, write some hydrology.

I learned knowledge related to Spring for 10 days and summarized 6 articles. Although I was not in good condition for a few days in the middle, I still felt good about the overall learning progress. I plan to learn MyBatis in the next series. If I follow this schedule, MyBatis will be finished on June 20.

I have learned Java for some time, I also want to talk about my views on Java, I think Java is really a tool, I learned Java concurrent programming, Spring, feel that nothing about technology, even can say, learn things and technology completely irrelevant! The process of learning the Java language ecology is actually learning how to use Java’s set of tools, and then it’s gone. If there’s anything else, it’s probably the idea of programming, because Java’s tools use a lot of design patterns and encapsulate functionality that’s really generic. But this I actually only need to master a certain degree is enough, I still need to dig deep in technology.

Maybe some students will say, I have not learned enough, yes, I also just started to learn Java, when I have MyBatis, SpringCloud, Dubbo and so on all learned, that how can, estimated or a bunch of tools.

Having said that, since I’m going to learn Java, I still have to master these tools, but when I master them enough, I won’t spend any more time on them, and I’ll learn more about them and grow faster.

Welcome everyone to like a lot, more articles, please pay attention to the wechat public number “Lou Zai advanced road”, point attention, do not get lost ~~