<> preface

Agent mode no matter JDK,spring frame , Or is it almost everywhere in daily development , The following diagram describes the business scenario of agent mode , Students with some development experience should not be difficult to understand this figure ;

<> Why do I need an agent

1, Original function enhancement

for instance , When the existing class code can only meet some basic functions , These functions cannot meet the new requirements , But you can't change the previous code , At this time, you can consider using agents , Through proxy class , Extend the functions of the original class , The entry of client access is just to switch the target object to the proxy object ;

2, Reduce coupling

When programming , We need to follow a rule called “ Single responsibility ” Principle of , This principle requires that the function of each class should be as single as possible , Why single , Because only with a single function, the possibility of this class being changed will be minimal .

Now I suddenly received the demand , You need to add audit logs for transactional operations such as adding, deleting, and modifying existing classes , The first thing many people think of is to add logs to every place where they add, delete or change , If there are many such classes , This is a toss , A better way is , Using proxy classes , Without modifying the original class , Expand the log function (spring
aop This is the function );

<>java Commonly used proxy mode in

To sum up ,java Commonly used proxy implementations in mainly include :

* jdk Dynamic proxy ;
* cglib Static proxy
<> one ,JDK Dynamic proxy

JDK Dynamic proxy is based on interceptor and reflection , No third-party library support is required , It only needs JDK Environment is enough

* Must be achieved InvocationHandler Interface ;
* use Proxy.newProxyInstance Generate proxy object ;
* The proxy object must implement the interface ;
* Internal adoption asm Technology dynamically generates bytecode ;
Let's take a look at one jdk Simple case of dynamic agent , Understand its usage in depth
public class JdkProxyDemo { interface Foo{ void foo(); } static class Target
implements Foo{ @Override public void foo() { System.out.println(" target foo");
} } /** * jdk Proxy can only proxy for interfaces * Internal adoption asm Technology dynamically generates bytecode () * @param args */ public static
void main(String[] args) { Target target = new Target(); ClassLoader classLoader
= JdkProxyDemo.class.getClassLoader(); Foo proxy = (Foo)Proxy.newProxyInstance(
classLoader, new Class[]{Foo.class}, new InvocationHandler() { //proxy Proxy object itself
//method Method being executed // Method parameters @Override public Object invoke(Object proxy, Method
method, Object[] args) throws Throwable { System.out.println("before"); Object
result= method.invoke(target, args); // The proxy class returns the result of the target method execution System.out.println(
"after"); return result; } }); proxy.foo(); } }
Run this code , Observe the effect

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces
, InvocationHandler h) throws IllegalArgumentException
The code itself is simple , The most important thing is Proxy.newProxyInstance This method , It is important to understand these parameters , Explain the meanings of the following parameters respectively :

* classLoader, Class loader ;
* interfaces, Target object interface ;
* InvocationHandler, The processor that really completes the reflection agent of related methods ;
JDK Dynamic proxy , The key step is to InvocationHandler Execute the method reflection call of the target implementation class in

because JDK Dynamic proxy , The bottom layer adopts asm Dynamic generation of bytecode Technology , We can't pass debug Source code to learn , In order to deeply understand its principle , You might as well simulate the process of its implementation ;

To simulate this process , It probably needs the following preparations :

* Interface and interface implementation classes ;
* Customized invocationHandler;
* Customized Proxy object ( Implement the same interface as the target class );
1, Custom interface and Implementation
interface Foo{ void foo(); } static class Target implements Foo{ @Override
public void foo() { System.out.println(" target foo"); } }
2, Customized invocationHandler;

Through the above demo You can also see ,invocationHandler It's actually an interface , When actually implemented , Need to pass in to Proxy Object , Implemented through interface callback , Only reference is needed here JDK Medium invocationHandler, Define the method parameters ;
public interface MyInvocationHandler { Object invoke(Object proxy, Method
method, Object[] params); }
3, Customized Proxy object

The logic of this code is roughly as follows :

* Customize by construction invocationHandler afferent ;
* The method of getting the target interface through reflection ;
* Through customization invocationHandler Interface callback for , Complete the implementation of agent logic through reflection ; import java.lang.reflect.Method;
public class MyProxy implements SelfJdkProxy.Foo { private SelfJdkProxy.
MyInvocationHandler invocationHandler; public MyProxy(SelfJdkProxy.
MyInvocationHandler invocationHandler) { this.invocationHandler =
invocationHandler; } @Override public void foo() { try { Method foo =
SelfJdkProxy.Foo.class.getMethod("foo"); invocationHandler.invoke(this, foo, new
Object[0]); } catch (NoSuchMethodException e) { e.printStackTrace(); } }
@Override public int bar() { Method bar = null; Object result = null; try { bar
= SelfJdkProxy.Foo.class.getMethod("bar"); result = invocationHandler.invoke(
this, bar, new Object[0]); } catch (NoSuchMethodException e) { e.printStackTrace
(); } return (int) result; } }
Run the above code , Observation effect , Can see , The output effect is the same , Through this implementation , We simply simulated JDK Simple implementation of dynamic agent ;

<> two ,cglib Static proxy

JDK The implementation of dynamic proxy requires the implementation class to define business methods through interfaces , For classes without interfaces , How to implement dynamic proxy , This requires cglib Yes ;

cglib It adopts very low-level bytecode technology , Its principle is to create subclasses for a class through bytecode technology , And adopt the method interception technology in the subclass to intercept the calls of all parent methods , Homeopathic weaving into crosscutting logic .

Let's first look at a simple cglib Use cases

1, Original target method
/** * Original target method */ public class CglibTarget { public void save() { System.out.
println("save()"); } public void save(int i) { System.out.println("save(int)");
} public void save(long i) { System.out.println("save(long)"); } }
2, Agent implementation core logic
/** * cglib Proxy is created through parent-child inheritance * The proxy object itself exists as a subtype */ public class CglibProxyDemo {
static class Target { public void foo() { System.out.println(" target foo"); } }
public static void main(String[] args) { Target target = new Target(); Target
proxy= (Target)Enhancer.create(Target.class, new MethodInterceptor() { // o :
Proxy class object itself //method : Methods executed by proxy classes //args : Method execution parameters //methodProxy :
Method proxy 【 Take this parameter , You can avoid calling with method reflection , But reflection is not used internally 】 @Override public Object intercept(Object o
, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { /*
System.out.println("before"); Object result = method.invoke(target, args);
System.out.println("after"); return result;*/ //methodProxy use , Proxy with target
/*System.out.println("before"); Object result =
methodProxy.invoke(target,args); System.out.println("after"); return result;*/
//methodProxy use , Proxy with itself System.out.println("before"); Object result =
methodProxy.invokeSuper(o,args); System.out.println("after"); return result; } }
); proxy.foo(); } }

cglib Relative use of JDK For agents , Appear more flexible , It's also very convenient to use , The core is to call CglibProxy This method setMethodInterceptor, Implement the proxy logic in the callback interface ;

What needs to be explained here is , stay MethodInterceptor Interface callback method , It provides two ways to complete the implementation of agent logic ,

* method.invoke(target,args); Reflection based implementation ;
* methodProxy.invoke(target,args); Directly call the method of the target class ;
* methodProxy.invokeSuper(o,args); Directly call the method of the target class ;

What needs special attention is ,CGLib Created dynamic proxy object performance ratio JDK The dynamic proxy object created has much higher performance , but CGLib It takes more time to create proxy objects than JDK Much more , So for singleton objects , Because there is no need to create objects frequently , use CGLib appropriate , conversely , use JDK The way should be more appropriate . meanwhile , because CGLib Because it adopts the method of dynamically creating subclasses , about final method , Unable to proxy .

Similarly ,cglib The implementation of the underlying bytecode technology makes it difficult for us to pass debug To debug the source code , Let's simulate the above implementation process through code ;

Customize a class , simulation Enhancer realization

The logic of the whole code is described as follows

* injection MethodInterceptor , simulation cglib of create In method Enhancer;
* inherit CglibTarget, Namely cglib The proxy is completed by inheriting the target class ;
* Override methods in proxy classes , And passed methodInterceptor.intercept Complete interface callback ;
* The method in the target class , Inject as static code block , Avoid re instantiation every time you call ;
* custom saveSuper method , simulation MethodProxy Implementation process of ; /** * cglib The proxy of is based on inheritance , And override the parent method for enhancement */
public class CglibProxy extends CglibTarget { private MethodInterceptor
methodInterceptor; public void setMethodInterceptor(MethodInterceptor
methodInterceptor) { this.methodInterceptor = methodInterceptor; } static Method
save0; static Method save1; static Method save2; static MethodProxy save0Proxy;
static MethodProxy save1Proxy; static MethodProxy save2Proxy; static { try {
save0= CglibTarget.class.getMethod("save"); save1 = CglibTarget.class.getMethod(
"save", int.class); save2 = CglibTarget.class.getMethod("save", long.class);
save0Proxy= MethodProxy.create(CglibTarget.class, CglibProxy.class, "()V",
"save", "saveSuper"); save1Proxy = MethodProxy.create(CglibTarget.class,
CglibProxy.class, "(I)V", "save1", "saveSuper"); save2Proxy = MethodProxy.create
(CglibTarget.class, CglibProxy.class, "(J)V", "save2", "saveSuper"); } catch (
NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } }
public void saveSuper() { super.save(); } public void saveSuper(int i) { super.
save(i); } public void saveSuper(long i) { super.save(i); } @Override public
void save() { try { methodInterceptor.intercept(this, save0, new Object[0],
save0Proxy); } catch (Throwable throwable) { //throwable.printStackTrace();
throw new UndeclaredThrowableException(throwable); } } @Override public void
save(int i) { try { methodInterceptor.intercept(this, save1, new Object[]{i},
save1Proxy); } catch (Throwable throwable) { //throwable.printStackTrace();
throw new UndeclaredThrowableException(throwable); } } @Override public void
save(long i) { try { methodInterceptor.intercept(this, save2, new Object[]{i},
save2Proxy); } catch (Throwable throwable) { //throwable.printStackTrace();
throw new UndeclaredThrowableException(throwable); } } }
Writing test classes
public class CglibTest { public static void main(String[] args) { CglibProxy
proxy= new CglibProxy(); CglibTarget target = new CglibTarget(); proxy.
setMethodInterceptor(new MethodInterceptor() { @Override public Object intercept
(Object o, Method method, Object[] args, MethodProxy methodProxy) throws
Throwable { System.out.println("before..."); //return
method.invoke(target,args); //return methodProxy.invoke(target,args); return
methodProxy.invokeSuper(o,args); } }); proxy.save(); } }
Run the above code , Output results through the console , You can see that it is consistent with the above effect ;

This needs to be MethodProxy A little explanation , We can see from the above case ,cglib In the callback logic of , Not only through similar JDK Reflection implementation proxy for , You can also pass MethodProxy Provided invoke() perhaps invokeSuper() Mode implementation , Why ,cglib In some cases than JDK Efficient agency , Is to pass MethodProxy Two methods of , Will not walk reflection , Instead, it calls the methods of the target class directly ;

<> three ,spring Use of agents in

stay spring In the frame , A typical scenario using proxy mode is AOP The implementation of , The core points of agent logic are as follows :

* Default use JDK Dynamic proxy , In this way, all interface types can be represented ;
* If the target object does not implement any interfaces , Default to CGLIB agent ;
* Mandatory use CGLIB, appoint proxy-target-class = “true” perhaps
Annotation based @EnableAspectJAutoProxy(proxyTargetClass = true)
Below with AOP The function of is used as an entry , Let's feel the above three points AOP Implementation in agent of ,

Look at the following paragraph spring How the underlying simulation is implemented AOP Function code
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.
intercept.MethodInvocation; import org.springframework.aop.aspectj.
AspectJExpressionPointcut; import org.springframework.aop.framework.ProxyFactory
; import org.springframework.aop.support.DefaultPointcutAdvisor; public class
SpringAopTest { public static void main(String[] args) { //1, Define tangent point
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.
setExpression("execution(* foo())"); //2, Define notification MethodInterceptor advice = new
MethodInterceptor() { @Override public Object invoke(MethodInvocation
methodInvocation) throws Throwable { return methodInvocation.proceed(); } };
//3, Define cut plane DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,
advice); //4, Create proxy Target1 target1 = new Target1(); ProxyFactory proxyFactory =
new ProxyFactory(); proxyFactory.setTarget(target1); proxyFactory.addAdvisor(
advisor); F1 proxy = (F1)proxyFactory.getProxy(); // View the type of proxy class System.out.println(
proxy.getClass()); proxy.foo(); proxy.bar(); } interface F1 { void foo(); void
bar(); } static class Target1 implements F1 { @Override public void foo() {
System.out.println("Target1 foo() ..."); } @Override public void bar() { System.
out.println("Target1 bar() ..."); } } static class Target2 implements F1 {
@Override public void foo() { System.out.println("Target2 foo() ..."); }
@Override public void bar() { System.out.println("Target2 bar() ..."); } } }
The main logical ideas can be referred to the notes , Then run this code , Observe the console effect

Output results through the console , We can draw the following conclusions ,

* AOP The bottom layer of the technology adopts proxy implementation ;
* If the target class implements the interface ,spring By default, the JDK agent ;
* Can be modified by ProxyConfig Properties in proxyTargetClass Value to change the proxy method ;

According to this attribute , The following proxy methods will be generated

* proxyTargetClass = false; The target realizes the interface , use JDK Proxy implementation ;
* proxyTargetClass = false; The target does not implement the interface , use cglib realization ;
* proxyTargetClass = true; Always use cglib realization ;

About the above points , Let's observe the effect through the fine-tuning of the above code , Why does the above output cglib What about our agent ? Because this property is not set in the program , Therefore, it is judged that the interface is not implemented , So I left cglib agent ,

adjustment 1: Set the interface of proxy class

Run the above code again , This time it's JDK Dynamic proxy

adjustment 2: set up proxyTargetClass Attribute is true

Run the above code again , This time it's cglib agent

Technology
©2019-2020 Toolsou All rights reserved,
Solve in servlet The Chinese output in is a question mark C String function and character function in language MySQL management 35 A small coup optimization Java performance —— Concise article Seven sorting algorithms (java code ) use Ansible Batch deployment SSH Password free login to remote host according to excel generate create Build table SQL sentence Spring Source code series ( sixteen )Spring merge BeanDefinition Principle of Virtual machine installation Linux course What are the common exception classes ?