1, the introduction of

If a non-transactional method a of Service is called from a Controller and then transactional method B is called from a, does the b transaction take effect?

 public void update() {
        updateActual();
        int a = 1 / 0;
    }
    @Transactional
    public void updateActual() {
        WithHoldInfoVO vo = new WithHoldInfoVO();
        vo.setId(18);
        vo.setStatus(5);
        withholdMapper.updateWithHoldInfo(vo);
        WithHoldInfoVO vo1 = new WithHoldInfoVO();
        vo1.setId(27);
        vo1.setStatus(5);
        withholdMapper.updateWithHoldInfo(vo1);
    }Copy the code

Like this, if the update method is called directly from the Controller layer, does the updateActual transaction take effect? If not, how can transactions be made effective?

2. What is agency?

An agent, simply, is a surrogate agent, a surrogate operator. In Java, there are generally two types, static proxy and dynamic proxy. Dynamic proxy is divided into CGLIB and JDK.


3. How to use it?

3.1 Static Proxy:

public class StaticProxy { public static void main(String[] args) { Singer singer = new Agent(new Star()); singer.sing(); * * *}} / agent, agent the singer, sing Will first receive money before Singer to sing a song, and then sing * / class Agent implements Singer {Star s; public Agent(Star s) { super(); this.s = s; } @Override public voidsing() {
        System.out.println("Collect money before the singer sings...."); s.sing(); } /** * implements Singer */ class Star implements Singer {@override public voidsing() {
        System.out.println("Star Singing~~~"); } /** * interface Singer {void sing(); }Copy the code

3.2 JDK Dynamic Proxy


@SuppressWarnings("restriction") public class JavaProxyTest { public static void main(String[] args) throws Exception { JavaProxyInterface javaProxyInterface = new ConcreteClass(); JavaProxyInterface newJavaProxyInterface = (JavaProxyInterface) Proxy.newProxyInstance( JavaProxyTest.class.getClassLoader(), new Class[] { JavaProxyInterface.class }, new MyInvocationHandler(javaProxyInterface)); // Here you can see that the class, as well as the proxied, executes aopMethod () before executing the method. One thing to note here is the difference between the oneDay () method and oneDayFinal (). OneDayFinal method aopMethod execution, oneDay aopMethod perform 1 times newJavaProxyInterface. GotoSchool (); newJavaProxyInterface.gotoWork(); newJavaProxyInterface.oneDayFinal(); newJavaProxyInterface.oneDay(); }} /** * an implementation of InvocationHandler, */ class MyInvocationHandler implements InvocationHandler {JavaProxyInterface javaProxy; public MyInvocationHandler(JavaProxyInterface javaProxy) { this.javaProxy = javaProxy; } private voidaopMethod() {
        System.out.println("before method"); Method. Invoke (javaProxy, args) is called. AopMethod () is also called, which is analogous to the angular before annotation in Spring. @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { aopMethod();returnmethod.invoke(javaProxy, args); }} /** */ JavaProxyInterface {void gotoSchool(); void gotoWork(); void oneDay(); void oneDayFinal(); } class ConcreteClass implements JavaProxyInterface {@override public void implements JavaProxyInterfacegotoSchool() {
        System.out.println("gotoSchool");
    }
    @Override
    public void gotoWork() {
        System.out.println("gotoWork");
    }
    @Override
    public void oneDay() {
        gotoSchool();
        gotoWork();
    }
    @Override
    public final void oneDayFinal() { gotoSchool(); gotoWork(); }}Copy the code

3.3 Cglib Dynamic Proxy

public class CglibProxyTest { public static void main(String[] args) throws Exception { CglibTestSon CglibTestSon = new CglibTestSon(); Enhancer enhancer = new Enhancer(); Callback s = new MthdInvoker(CglibTestSon); enhancer.setSuperclass(CglibTestSon.class); Callback callbacks[] = new Callback[] { s }; enhancer.setCallbacks(callbacks); CglibTestSon CglibTestSon2 = (CglibTestSon) enhancer.create(); CglibTestSon2.gotoHome(); CglibTestSon2.gotoSchool(); //// You can see here that the class, as well as the proxied, executes aopMethod () before executing the method. One thing to note here is the difference between the oneDay () method and onedayFinal (). Cglibtestson2.oneday (); cglibtestson2.oneday (); CglibTestSon2.onedayFinal(); Class CglibTestSon {class CglibTestSon {publicCglibTestSon() {
    }
    public void gotoHome() {
        System.out.println("============gotoHome============");
    }
    public void gotoSchool() {
        System.out.println("===========gotoSchool============");
    }
    public void oneday() {
        gotoHome();
        gotoSchool();
    }
    public final void onedayFinal() { gotoHome(); gotoSchool(); }} /** * Invoke (s, args);} /** * Invoke (s, args);} /** * Invoke (s, args); There's also an aopMethod(); */ class MthdInvoker implements MethodInterceptor { private CglibTestSon s; public MthdInvoker(CglibTestSon s) { this.s = s; } private voidaopMethod() {
        System.out.println("i am aopMethod");
    }
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        aopMethod();
        Object a = method.invoke(s, args);
        returna; }} Cglib requires maven configuration: <dependencies> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> </dependency> </dependencies>Copy the code

4, the difference between

The name of the note
Static agent Simple, proxy mode is the theoretical basis of dynamic proxy. Commonly used in proxy mode
JDK dynamic proxy

The mapper file of Mybatis is a proxy file. The mapper file of Mybatis is a proxy file.

Use reflection to do this. Dynamic bytecode generation technology is used.

Cglib dynamic proxy Classes can be proxyed directly, using bytecode technology, and final classes cannot be inherited. Dynamic bytecode generation technology is used.

5. How is the bottom layer implemented?

We all know that dynamic proxies generate bytecode using dynamic bytecode generation technology, so what is the difference between the generated bytecode? What about the generated bytecode? How to generate bytecode?

5.1 JDK underlying implementation

The bytecode is finally generated by this line,

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); // interfaces are the top level interfaces. // Interfaces are the top level interfaces. // Interfaces are the top level interfaces."D:/testProxy/Ddd.class");
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(bs);
fileOutputStream.flush();
fileOutputStream.close();Copy the code

NewProxyInstance (ClassLoader paramClassLoader, Class
[] paramArrayOfClass, paramInvocationHandler, paramInvocationHandler) First get his constructor with class, then use constructor reflection to get an instance of him.

The ones in red are the most complicated. However, the cglib implementation principle is basically the same, the only difference is that the new class file generation method and results are different.

5.2 Cglib underlying implementation

Directly on the final bytecode generation code. It is important to note that this statement must be implemented after cglib dynamic proxy code, otherwise there is no instance of DefaultGeneratorStrategy.

byte[] bs = DefaultGeneratorStrategy.INSTANCE.generate(enhancer);
FileOutputStream fileOutputStream = new FileOutputStream("D:/testProxy/Cc.class");
fileOutputStream.write(bs);
fileOutputStream.flush();
fileOutputStream.close();Copy the code

Tracking enhancer. The create (); This code looks at how the bytecode is generated and converted into a class. The process is basically the same as JDK, with the main difference being the difference in bytecode generation.

5.3 summary

In the process of generating code here, caching is used. The JDK uses weakReference reference, while Cglib uses WeakHashMap directly, basically similar.

6. What exactly does the generated bytecode look like?

6.1 additional

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package aaa;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibTestSon$$EnhancerByCGLIB$$3119e01d extends CglibTestSon implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$gotoHome$0$Method;
    private static final MethodProxy CGLIB$gotoHome$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$gotoSchoolThe $1$Method;
    private static final MethodProxy CGLIB$gotoSchoolThe $1$Proxy;
    private static final Method CGLIB$oneday$2$Method;
    private static final MethodProxy CGLIB$oneday$2$Proxy;
    private static final Method CGLIB$equals$3$Method;
    private static final MethodProxy CGLIB$equals$3$Proxy;
    private static final Method CGLIB$toString$4$Method;
    private static final MethodProxy CGLIB$toString$4$Proxy;
    private static final Method CGLIB$hashCodeA $5$Method;
    private static final MethodProxy CGLIB$hashCodeA $5$Proxy;
    private static final Method CGLIB$clone$6$Method;
    private static final MethodProxy CGLIB$clone$6$Proxy;

    static void CGLIB$STATICHOOK2() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.rrc.finance.CglibTestSon$$EnhancerByCGLIB$$3119e01d");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals"."(Ljava/lang/Object;) Z"."toString"."()Ljava/lang/String;"."hashCode"."()I"."clone"."()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$3$Method = var10000[0];
        CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;) Z"."equals"."CGLIB$equals$3");
        CGLIB$toString$4$Method = var10000[1];
        CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;"."toString"."CGLIB$toString$4");
        CGLIB$hashCodeA $5$Method = var10000[2];
        CGLIB$hashCodeA $5$Proxy = MethodProxy.create(var1, var0, "()I"."hashCode"."CGLIB$hashCodeA $5");
        CGLIB$clone$6$Method = var10000[3];
        CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;"."clone"."CGLIB$clone$6");
        var10000 = ReflectUtils.findMethods(new String[]{"gotoHome"."()V"."gotoSchool"."()V"."oneday"."()V"}, (var1 = Class.forName("com.rrc.finance.CglibTestSon")).getDeclaredMethods());
        CGLIB$gotoHome$0$Method = var10000[0];
        CGLIB$gotoHome$0$Proxy = MethodProxy.create(var1, var0, "()V"."gotoHome"."CGLIB$gotoHome$0");
        CGLIB$gotoSchoolThe $1$Method = var10000[1];
        CGLIB$gotoSchoolThe $1$Proxy = MethodProxy.create(var1, var0, "()V"."gotoSchool"."CGLIB$gotoSchoolThe $1");
        CGLIB$oneday$2$Method = var10000[2];
        CGLIB$oneday$2$Proxy = MethodProxy.create(var1, var0, "()V"."oneday"."CGLIB$oneday$2");
    }

    final void CGLIB$gotoHome$0() {
        super.gotoHome();
    }

    public final void gotoHome() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! = null) { var10000.intercept(this, CGLIB$gotoHome$0$Method, CGLIB$emptyArgs, CGLIB$gotoHome$0$Proxy);
        } else {
            super.gotoHome();
        }
    }

    final void CGLIB$gotoSchoolThe $1() {
        super.gotoSchool();
    }

    public final void gotoSchool() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! = null) { var10000.intercept(this, CGLIB$gotoSchoolThe $1$Method, CGLIB$emptyArgs, CGLIB$gotoSchoolThe $1$Proxy);
        } else {
            super.gotoSchool();
        }
    }

    final void CGLIB$oneday$2() {
        super.oneday();
    }

    public final void oneday() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! = null) { var10000.intercept(this, CGLIB$oneday$2$Method, CGLIB$emptyArgs, CGLIB$oneday$2$Proxy);
        } else {
            super.oneday();
        }
    }

    final boolean CGLIB$equals$3(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! = null) { Object var2 = var10000.intercept(this, CGLIB$equals$3$Method, new Object[]{var1}, CGLIB$equals$3$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

    final String CGLIB$toString$4() {
        return super.toString();
    }

    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        returnvar10000 ! = null ? (String)var10000.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy) : super.toString();
    }

    final int CGLIB$hashCodeA $5() {
        return super.hashCode();
    }

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! = null) { Object var1 = var10000.intercept(this, CGLIB$hashCodeA $5$Method, CGLIB$emptyArgs, CGLIB$hashCodeA $5$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }

    final Object CGLIB$clone$6() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        returnvar10000 ! = null ? var10000.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy) : super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case- 1311123629:if (var10000.equals("gotoHome()V")) {
                return CGLIB$gotoHome$0$Proxy;
            }
            break;
        case- 508378822:if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$6$Proxy;
            }
            break;
        case 800823646:
            if (var10000.equals("gotoSchool()V")) {
                return CGLIB$gotoSchoolThe $1$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;) Z")) {
                return CGLIB$equals$3$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$4$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCodeA $5$Proxy;
            }
            break;
        case 2071325759:
            if (var10000.equals("oneday()V")) {
                return CGLIB$oneday$2$Proxy; }}return null;
    }

    public CglibTestSon$$EnhancerByCGLIB$$3119e01d() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        CglibTestSon$$EnhancerByCGLIB$$3119e01d var1 = (CglibTestSon$$EnhancerByCGLIB$$3119e01d)var0;
        if(! var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (CGLIB$STATIC_CALLBACKS == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        CglibTestSon$$EnhancerByCGLIB$$3119e01d var10000 = new CglibTestSon$$EnhancerByCGLIB$$3119e01d(a); CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
        CglibTestSon$$EnhancerByCGLIB$$3119e01d var10000 = new CglibTestSon$$EnhancerByCGLIB$$3119e01d(a); CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        CglibTestSon$$EnhancerByCGLIB$$3119e01d var10000 = new CglibTestSon$$EnhancerByCGLIB$$3119e01d;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found");
        }
    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK2();
    }
}

Copy the code

6.2 the JDK

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.rrc.finance.JavaProxyInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class Ddd extends Proxy implements JavaProxyInterface {
    private static Method m1;
    private static Method m5;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m6;
    private static Method m0;

    public Ddd(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void gotoSchool() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void oneDay() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void oneDayFinal() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void gotoWork() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("gotoSchool");
            m3 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("oneDay");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("oneDayFinal");
            m6 = Class.forName("com.rrc.finance.JavaProxyInterface").getMethod("gotoWork");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }}}Copy the code


Cglib directly inherits the Factory interface, while the JDK implements its own top-level interface, inheriting the Proxy interface. It’s important to note that the JDK won’t be able to find its implementation by class because its implementation class is actually a Proxy class, not itself.

See the next section for more general meaning of this sentence.

7. What kind of proxy is used in Spring?

If a class has a top-level interface, the JDK dynamic proxy is used by default. If a class is directly a class, the Cglib dynamic proxy is used. Second, methods that do not need to be proxied, such as Aop, will not be proxied if they do not have the @Transactional annotation.

The problem is, how can we convince people if we only say the conclusion without any basis?

@Service
public class TestServiceImpl implements TestService {
    @Transactional
    public void updateActual() {}}Copy the code

In this case, we implement a Service, let it implement a top-level interface, and then we use it in the Controller

@Autowired

private TestServiceImpl testServiceImpl;

Annotation to inject, at this point you will find that the startup times is wrong.

The error is also obvious:

The bean 'testServiceImpl' could not be injected as a 'com.rrc.finance.service.apply.TestServiceImpl' because it is a JDK dynamic proxy that implements:
	com.rrc.finance.service.apply.TestServiceCopy the code

@autoWired private TestService testServiceImpl;

Can start normally.

Prove that the code generated by the dynamic proxy is a TestService but not a TestServiceImpl. You use the JDK’s dynamic proxy.

Remove the transaction annotations and remove the interface implementation here and try again.

8. Is that all?

Under The Spring architecture, most implementations use dynamic proxies, and we can learn from their excellent source code to write our own projects.

For example: Mybatis mapper agent, paging plug-in agent implementation. Spring Aop, transaction annotations, etc.