Spring 06 -- proxy pattern and AOP

Posted by mlschutz on Wed, 09 Feb 2022 20:04:35 +0100

1, Agent mode

The bottom layer of spring AOP

Agent mode classification:

  • Static proxy
  • Dynamic agent

Benefits of agent mode:

  • It can make the operation of real roles more pure, without paying attention to some public businesses
  • The public business is handed over to the agent role, which realizes the division of business
  • When the public business is expanded, it is convenient for centralized management

Disadvantages:

  • A real role will produce an agent role, the amount of code will double, and the development efficiency will be low

1. Static agent

Abstract role: interface or abstract class

//Rent a house
public interface Rent {
    void rent();
}

Real role: the role represented

//landlord or landlady
public class HouseEast implements Rent{
    public void rent() {
        System.out.println("The landlord wants to rent the house");
    }
}

Proxy role: represents the real role. After proxy, there will be some additional operations

package com.shy.demo01;
public class Proxy implements Rent{
    private HouseEast houseEast;
    public Proxy(){}
    public Proxy(HouseEast houseEast){
        this.houseEast = houseEast;
    }
    public void rent() {
        lookHouse();
        houseEast.rent();
        money();
    }
    //House viewing
    public void lookHouse(){
        System.out.println("The agent will show you the house!");
    }
    //charge
    public void money(){
        System.out.println("Close 1 w!");
    }
}

Client: the person who accesses the proxy object

public class Client {
    @Test
    public void test(){
        //The landlord wants to rent the house
        HouseEast h = new HouseEast();
        //Agents and intermediaries help landlords rent houses. Intermediaries generally have ancillary operations
        Proxy proxy = new Proxy(h);
        //You don't have to face the landlord and rent a house directly from an intermediary
        proxy.rent();
    }
}

Once more:

UserService interface

package com.shy.demo02;
//Addition, deletion, modification and query of users
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}

We need to add and delete these real objects

package com.shy.demo02;
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("Added a user");
    }
    public void delete() {
        System.out.println("A user was deleted");
    }
    public void update() {
        System.out.println("Modified a user");
    }
    public void query() {
        System.out.println("Queried a user");
    }
}

Add a logging function and set the agent class

package com.shy.demo02;
public class UserServiceProxy implements UserService{
    private UserServiceImpl userService;
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
    public void add() {
        log("add");
        userService.add();
    }
    public void delete() {
        log("delete");
        userService.delete();
    }
    public void update() {
        log("update");
        userService.update();
    }
    public void query() {
        log("query");
        userService.query();
    }
    public void log(String msg){
        System.out.println("[debug]Used"+msg+"method");
    }
}

Test:

package com.shy.demo02;
import org.junit.Test;
public class Client {
    @Test
    public void test(){
        UserServiceImpl u = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(u);
        proxy.add();
    }
}

2, Dynamic agent

The bottom layer is reflection

Dynamic agents have the same role as static agents

The agent class of dynamic agent is generated dynamically, which is not written directly by us!

Dynamic agents are divided into two categories: interface based dynamic agents and class based dynamic agents

  • Interface based: JDK dynamic agent
  • Class based: cglib
  • java bytecode implementation: javassist

Two classes need to be understood: Proxy proxy, InvocationHandler and call handler

InvocationHandler

Benefits of dynamic agents

  • It can make the operation of real roles more pure, without paying attention to some public businesses
  • The public business is handed over to the agent role, which realizes the division of business
  • When the public business is expanded, it is convenient for centralized management
  • A dynamic agent class is an interface, which is generally the corresponding type of business
  • A dynamic proxy class can proxy multiple classes as long as it implements the same interface

Code implementation:

1. Abstract role

package com.shy.demo03;
//Rent a house
public interface Rent {
    void rent();
}

2. Real role

package com.shy.demo03;
//landlord or landlady
public class HouseEast implements Rent {
    public void rent() {
        System.out.println("The landlord wants to rent the house");
    }
}

3. Agent role

package com.shy.demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//Use this class to automatically generate proxy classes
public class ProxyInvocationHandler implements InvocationHandler {
    //Proxy interface
    private Rent rent;
    public void setRent(Rent rent) {
        this.rent = rent;
    }
    //Generated proxy class
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }
    //Process the proxy instance and return the result
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //The essence of dynamic agent is reflection
        c();
        Object result = method.invoke(rent, args);
        s();
        return result;
    }
    public void c(){
        System.out.println("see");
    }
    public void s(){
        System.out.println("money");
    }
}

4. Tenant

package com.shy.demo03;
import org.junit.Test;
public class Client {
    @Test
    public void test(){
        //Real role
        HouseEast houseEast = new HouseEast();
        //Agent role, not now
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //Handle the interface object we want to call by calling the program processing role
        pih.setRent(houseEast);
        Rent proxy = (Rent) pih.getProxy();//This proxy is dynamically generated
        proxy.rent();
    }
}

A dynamic agent generally represents a certain type of business. A dynamic agent can represent multiple classes, and the agent is the interface!

We can also write a general dynamic proxy implementation class! All proxy objects can be set to Object!

package com.shy.demo04;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//Use this class to automatically generate proxy classes
public class ProxyInvocationHandler implements InvocationHandler {
    //Proxy interface
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }
    //Generated proxy class
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
    //Process the proxy instance and return the result
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //The essence of dynamic agent is reflection
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }
    public void log(String msg){
        System.out.println("Yes"+msg+"method");
    }
}

Test:

package com.shy.demo04;
import com.shy.demo02.UserService;
import com.shy.demo02.UserServiceImpl;
import org.junit.Test;
public class Client {
    @Test
    public void test(){
        //Real role
        UserServiceImpl userService =  new UserServiceImpl();
        //delegable role
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(userService);//Sets the object to proxy
        //Dynamically generate proxy classes
        UserService proxy = (UserService) pih.getProxy();
        proxy.add();
    }
}

3, AOP

It is a technology to realize the unified maintenance of program functions through precompiled mode and dynamic agent during operation. AOP is the continuation of OOP, a hot spot in software development, an important content of Spring framework and a derivative paradigm of functional programming. AOP can isolate all parts of business logic, so as to reduce the coupling between all parts of business logic, improve the reusability of programs, and improve the efficiency of development.

Provide declarative transactions; Allows you to customize the cut plane

Crosscutting concerns: methods or functions that span multiple modules of an application. That is, the part that has nothing to do with our business logic, but we need to focus on is crosscutting concerns. Such as log, security, cache, transaction and so on

ASPECT: a special object whose crosscutting concerns are modularized. That is, it is a class.

Advice: work that must be completed in all aspects. That is, it is a method in a class.

Target: the notified object.

Proxy: an object created after notification is applied to the target object.

PointCut: the definition of the "place" where the aspect notification is executed.

JointPoint: the execution point that matches the pointcut.

Aop adds new functions without changing the original code

1. Use Spring to implement AOP

To use AOP weaving, you need to import a dependency package

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

Method 1: use the API interface of Spring

Method 2: use user-defined classes to implement AOP [mainly section definition]

Method 3: implement with annotation

Example: (three methods are put together here, with notes)

1. Write business interface and implementation class

package com.shy.service;
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
package com.shy.service;
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("Added a user");
    }
    public void delete() {
        System.out.println("A user was deleted");
    }
    public void update() {
        System.out.println("Modified a user");
    }
    public void query() {
        System.out.println("Queried a user");
    }
}

2. Write two enhancement classes, one pre enhancement and one post enhancement

Pre enhancement:

package com.shy.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class log implements MethodBeforeAdvice {
    //Method: the method of the target object to execute
    //args: parameter
    //Target: target object
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"of"+method.getName()+"Executed");
    }
}

Post enhancement:

package com.shy.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
    //returnValue: return value
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Yes"+method.getName()+"The return value result is:"+returnValue);
    }
}

3. The second method: a custom cut in class

package com.shy.diy;
public class DiyPointCut {
    public void before(){
        System.out.println("Before method execution");
    }
    public void after(){
        System.out.println("After method execution");
    }
}

4. The third way: write an enhanced class implemented by annotation

package com.shy.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//Implementing AOP with annotations
//Mark that this class is a facet
@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.shy.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("Before method execution");
    }
    @After("execution(* com.shy.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("After method execution");
    }
    //In surround enhancement, you can give a parameter to represent the entry point we want to obtain processing. Let's play
    @Around("execution(* com.shy.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pj) throws Throwable{
        System.out.println("Surround front");
        Object proceed = pj.proceed();
        System.out.println("After surround");
    }
}

5. In ApplicationContext Register in 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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--register bean-->
    <bean id="userService" class="com.shy.service.UserServiceImpl"/>
    <bean id="log" class="com.shy.log.log"/>
    <bean id="afterLog" class="com.shy.log.AfterLog"/>
    
    <!--Method 1: use native Spring API Interface-->
    <!--to configure aop Import required aop Constraints of-->
    <!--<aop:config>
        &lt;!&ndash;breakthrough point,expression:expression, expression(Where to execute * * * ...)&ndash;&gt;
        <aop:pointcut id="pointcut" expression="execution(* com.shy.service.UserServiceImpl.*(..))"/>
        &lt;!&ndash;Execute wrap&ndash;&gt;
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>-->

    <!--Method 2: use custom class to implement AOP-->
    <!--<bean id="diy" class="com.shy.diy.DiyPointCut"/>
    <aop:config>
        &lt;!&ndash;Custom section, ref:Referenced class&ndash;&gt;
        <aop:aspect ref="diy">
            &lt;!&ndash;breakthrough point&ndash;&gt;
            <aop:pointcut id="point" expression="execution(* com.shy.service.UserServiceImpl.*(..))"/>
            &lt;!&ndash;notice&ndash;&gt;
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>-->

    <!--Method 3: implement with annotation-->
    <bean id="annotationPointCut" class="com.shy.diy.AnnotationPointCut"/>
    <!--Enable annotation support-->
    <aop:aspectj-autoproxy/>
</beans>

6. Test

import com.shy.service.UserService;
import com.shy.service.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest1 {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //Dynamic proxy is the interface
        UserService userService = (UserService) context.getBean("userService");
        userService.delete();
    }
}

aop:aspectj-autoproxy:

Through the < aop: aspectJ AutoProxy / > declaration of the aop namespace, automatically create a proxy for those bean s in the spring container that configure the @ aspectJ aspect and weave it into the aspect. Of course, spring still uses AnnotationAwareAspectJAutoProxyCreator internally to create automatic proxy, but the specific implementation details have been hidden by < aop: aspectJ AutoProxy / >

< AOP: AspectJ AutoProxy / > has a proxy target class attribute, which is false by default, indicating that jdk dynamic proxy is used to weave enhancement. When it is configured as < AOP: AspectJ AutoProxy proxy target class = "true" / > it indicates that CGLib dynamic proxy technology is used to weave enhancement. However, even if proxy target class is set to false, if the target class does not declare an interface, spring will automatically use CGLib dynamic proxy.

Spring's Aop combines public business (logging, security, etc.) with domain business. When implementing domain business, public business will be added Realize the reuse of public business The domain business is more pure. The program ape focuses on the domain business, and its essence is dynamic agent

Topics: Java Spring