Five articles teach you to master spring 4

Posted by ashwood on Mon, 03 Jan 2022 01:38:07 +0100

Introduction of proxy pattern and AOP implementation of Spring

Agent model of AOP antecedents

Static proxy

Role analysis:

  • Abstract role: it is usually solved by interface or abstract class
  • Real role: the role represented
  • Agent role: acting as a real role. After acting as a real role, we usually do some related business processing
  • Client: the person who accesses the proxy object

Simulate static proxy

The existing service business is adding, deleting, modifying and querying

Service interface

package com.lwh.service;

public interface Service {
    public void add();
    public void delete();
    public void update();
    public void query();
}

service interface implementation class

package com.lwh.service;

public class ServiceImpl implements Service{

    @Override
    public void add() {
        System.out.println("Increase method");
    }

    @Override
    public void delete() {
        System.out.println("Delete method");
    }

    @Override
    public void update() {
        System.out.println("Modification method");
    }

    @Override
    public void query() {
        System.out.println("Query method");
    }
}

client

package com.lwh.service;

public class Client {
    public static void main(String[] args) {
        ServiceImpl service=new ServiceImpl();
        service.add();
    }
}

Now I need to implement other functions. I need to add some other services and print the log before each method execution, but I can't modify the code. In the company, modifying the code is taboo, so I need to use the agent mode

We only need to add a new proxy class, ProxyServletImpl

package com.lwh.service;

public class ProxyServletImpl implements Service{
    private ServiceImpl service;

    public ServiceImpl getService() {
        return service;
    }

    public void setService(ServiceImpl service) {
        this.service = service;
    }

    @Override
    public void add() {
        log("add");
        service.add();
    }

    @Override
    public void delete() {
        log("delete");
        service.delete();
    }

    @Override
    public void update() {
        log("update");
        service.update();
    }

    @Override
    public void query() {
        log("query");
        service.query();
    }
    public void log(String asg)
    {
        System.out.println("Used"+asg+"method");
    }
}

We didn't change the original code. We used a new object to implement our own methods in our proxy class and the original service logic to successfully implement the required functions. This is the advantage of the proxy.

Dynamic agent

  • Dynamic agents have the same role as static agents
  • The proxy class of dynamic proxy is generated dynamically, which is not written directly
  • Dynamic agents can be divided into two categories:
    • Interface based dynamic agent, such as JDK dynamic agent
    • Class based dynamic proxy, for example: cglib

Implementation of simulated dynamic agent based on AOP

Write dynamic proxy tool class

  • InvocationHandler interface is an interface implemented by the calling handler of proxy proxy instance. Each proxy instance has an associated calling handler; When a method is called by a proxy instance, the method call is encoded and dispatched to the invoke method of the calling handler.
  • The calling handler of each dynamic proxy class must implement the InvocationHandler interface, and the instance of each proxy class is associated with the dynamic proxy class calling handler that implements the interface. When we call a method through the dynamic proxy object, the call of this method will be forwarded to the invoke method that implements the InvocationHandler interface class

  • Proxy class is a class used to create a proxy object. It provides many methods, but the most commonly used method is newProxyInstance, which has the following parameters
    • loader: a classloader object that defines which classloader object loads the generated proxy class
    • Interfaces: an array of interface objects, which indicates what kind of interfaces we will provide to our proxy object. If we provide such an array of interface objects, we declare that the proxy class implements these interfaces, and the proxy class can call all the methods declared in the interface.
    • h: An InvocationHandler object indicates which InvocationHandler object will be associated with when the method is called by the dynamic proxy object, and finally called by it.
package com.lwh.demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyInvocationHandler implements InvocationHandler {
    //Real object represented
    private Object target;

    //For the sake of generality, set method is used to realize the injection of real objects
    public void setTarget(Object target) {
        this.target = target;
    }

    //Get proxy implementation class
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());//The name of the method passed in for execution
        Object result=method.invoke(target, args);
        return result;
    }
    //Other business logic added
    public void log(String msg){
        System.out.println("Yes"+msg+"method");
    }
}

Client client

package com.lwh.demo;

import com.lwh.service.Service;
import com.lwh.service.ServiceImpl;

public class Client {
    public static void main(String[] args) {
        ServiceImpl service=new ServiceImpl();
        ProxyInvocationHandler proxyInvocationHandler=new ProxyInvocationHandler();
        proxyInvocationHandler.setTarget(service);
        Service proxy = (Service) proxyInvocationHandler.getProxy();
        proxy.add();//output 	 The add method is executed
    }
}

We can understand that in the process of running the client, we pass in ServiceImpl and automatically generate the dynamic proxy class. The dynamic proxy class automatically implements the inheritance interface of the real class we pass in, so that we can use all the methods defined in the interface. When we want to use the dynamic proxy class, we will call the invoke method of proxyInvocationHandler, Execute the code logic we added to realize business expansion!

Dynamic agent benefits:

  • A dynamic proxy class represents an interface, which is generally a corresponding type of business (because what is passed in is a real object)
  • A dynamic proxy class can proxy multiple classes as long as it implements the same interface. Because it is a dynamically generated class, multiple classes can be generated.

AOP

What is AOP

AOP (Aspect Oriented Programming) means: aspect oriented programming, which realizes the unified maintenance of program functions through precompiled mode and runtime dynamic agent. AOP is the continuation of OOP, a hot spot in software development, an important content in Spring framework, and a derivative paradigm of functional programming. AOP can isolate each part of business logic, reduce the coupling between each part of business logic, improve the reusability of program, and improve the efficiency of development.

AOP implementation of Spring

First explain some official terms:

  • Crosscutting concerns: methods or functions that span multiple modules of an application, that is, those that have nothing to do with our business logic, but that we need to focus on are crosscutting concerns, such as logging, security, caching, transactions, and so on

  • Aspect: a special object whose crosscutting concerns are modularized. It is a class

  • Notification: the work that must be done by the aspect, that is, a method in the class

  • Target: notified object

  • Proxy: objects created after notification is applied to the target object

  • Entry point: the definition of the place where the aspect notification is executed

  • Join point: the execution point that matches the pointcut

Create a sub project and add maven:

<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>

Modify beans xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
</beans>

Method 1: use the API provided by Spring

  1. Write the Service interface first
package com.lwh.service;

public interface Service{
    public void add();
    public void delete();
    public void update();
    public void query();
}
  1. Then write the real object
package com.lwh.service;

public class ServiceImpl implements Service{

    @Override
    public void add() {
        System.out.println("The addition method is used");
    }

    @Override
    public void delete() {
        System.out.println("Deletion method used");
    }

    @Override
    public void update() {
        System.out.println("Modification method used");
    }

    @Override
    public void query() {
        System.out.println("Query method used");
    }
}
  1. Write Log class

You need to inherit the MethodBeforeAdvice interface before executing the method

package com.lwh.log;

import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {

    /**
     *After MethodBeforeAdvice is inherited, the logic written in the before method will be called before the real object is executed.
     * @param method:Method representing the target object to execute
     * @param objects: Represents a parameter
     * @param o: Represents the target object
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"of"+method.getName()+"Executed");
    }
}

If the operation is performed after the method is executed, and the operation returns a value, it needs to inherit the AfterReturningAdvice interface

package com.lwh.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    /**
     * AfterReturningAdvice It will be called after return, and it can get the return value of the real object.
     * @param returnValue
     * @param method
     * @param args
     * @param target
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Yes"+method.getName()+"The returned result is"+returnValue);
    }
}
  1. Register beans xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
    <bean id="userService" class="com.lwh.service.ServiceImpl"/>
    <bean id="blog" class="com.lwh.log.BeforeLog"/>
    <bean id="alog" class="com.lwh.log.AfterLog"/>

    <aop:config>
        <!--The entry point is where we need to execute,execution(Parameters to execute)-->
        <!--com.lwh.service.ServiceImpl This sentence represents ServiceImpl All methods under are proxied;*(..)Represents that any parameter can be passed in-->
        <aop:pointcut id="pointcut" expression="execution(* com.lwh.service.ServiceImpl.*(..))"/>
        <!--Execute surround increase,This is to cut the class configured below into the path configured above-->
        <aop:advisor advice-ref="alog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="blog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>
  1. Writing test classes
import com.lwh.service.Service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //The dynamic proxy is the interface. Just declare the interface class directly and accept it
        Service service=context.getBean("userService",Service.class);
        service.add();
        /**
         * Output:
         * com.lwh.service.ServiceImpl add of is executed
         * The addition method is used
         * add is executed, and the returned result is null
         */
    }
}

Mode 2: implement AOP (section definition) by customization

  1. Write custom cut
package com.lwh.log;

public class MyAspect {
    public void beforeMethod(){
        System.out.println("========Before method execution=========");
    }
    public void afterMethod(){
        System.out.println("========After method execution=========");
    }
}
  1. Modify beans xml
<bean id="myaspect" class="com.lwh.log.MyAspect"/>
<aop:config>
    <aop:aspect ref="myaspect">
        <!--breakthrough point-->
        <aop:pointcut id="point" expression="execution(* com.lwh.service.ServiceImpl.*(..))"/>
        <!--notice-->
        <aop:before method="beforeMethod" pointcut-ref="point"/>
        <aop:after method="afterMethod" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>
  1. Test class
import com.lwh.service.Service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //The dynamic proxy is the interface. Just declare the interface class directly and accept it
        Service service=context.getBean("userService",Service.class);
        service.add();
        /**
         * Output:
         * ========Before method execution=========
         * The addition method is used
         * ========After method execution=========
         */
    }
}

Mode 3: implement with annotation

  1. Add a method in the Service interface

    public String print();
    
  2. Add the implementation of real object class

    @Override
    public String print() {
        return "Print text";
    }
    
  3. Add proxy class annotationpointcut java

    1. Now annotate Aspect on the class name, indicating that this is an Aspect
    2. There are three notes for those with more land
      1. @Before means that before the method is executed, there are parameters inside to represent the pointcut. Like xml configuration, you need to set the value with the execution expression first
      2. @After means after the method is executed and the returned value, which can be understood as the last
      3. @Around stands for surround. Like others, you need to set a pointcut. You need to pass in a parameter proceed ingjoinpoint, which is automatically generated by Spring. There is a processed method in it to represent the return value after the method is executed. We use an Object to accept it and then print the results

    Think: what is the output order?

     package com.lwh.log;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect //Mark this class is a section
    public class AnnotationPointCut {
    
        @Before("execution(* com.lwh.service.ServiceImpl.*(..))")
        public void before(){
            System.out.println("===========Before method execution============");
        }
    
        @After("execution(* com.lwh.service.ServiceImpl.*(..))")
        public void after(){
            System.out.println("==========After method execution=============");
        }
    
        @Around("execution(* com.lwh.service.ServiceImpl.*(..))")
        public void around(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("Surround front");
            Object proceed = jp.proceed();
    
            System.out.println(proceed);
            System.out.println("After surround");
        }
    }
    
  4. Test class

      import com.lwh.service.Service;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            //The dynamic proxy is the interface. Just declare the interface class directly and accept it
            Service service=context.getBean("userService",Service.class);
            service.print();
            /**Output:
             * Surround front
             * ===========Before method execution============
             * Print text
             * After surround
             * ==========After method execution=============
             */
        }
    }
    

Topics: Java Spring Design Pattern AOP