Small knowledge, big challenge! This article is part of the “Programmer’s Essentials

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

I haven’t written an article for several days, so I’m going to write one today. I can see from the previous schedule that I haven’t written about Java dynamic proxy. This technology is rarely used and not particularly easy to understand

Dynamic proxy is divided into two parts, dynamic and proxy. Let’s talk about proxy mode first

1. Proxy mode

The proxy pattern is one of the most common design patterns and a common design pattern in development.

Simple to describe, the proxy pattern is isolated from the implementation class, did you want to give your girlfriend a birthday, for example, a star to sing the birthday song, your girlfriend’s idol is jay Chou, want to find for her birthday, jay Chou to sing, but you can not contact the jay Chou, even on social networking sites to contact, may not you, So you can contact Jay’s agent for communication, the agent is Jay’s agent.

Implementation process:

Define a singing interface that represents the business

public interface ISing {
   void sing(a);
}
Copy the code

Jay has singing business, and business outstanding, implementation interface

/** ** Jay Chou */
public class JayImp implements ISing {
   @Override
   public void sing(a) {
       System.out.println("say happy birthday to you girl friend"); }}Copy the code

The broker accepts business, and the broker’s constructor needs to be bound to the star

The agent receives the singing business, today it may be Jay Chou, tomorrow the agent may change the star, such as Jolin Tsai is also ok

/** * broker */
public class JayProxy implements ISing{
   ISing target;
 
   /** * When initializing, sign with star *@param target
    */
   public JayProxy(ISing target) {
       this.target = target;
  }
 
   @Override
   public void sing(a) { target.sing(); }}Copy the code

Contact agent to sing, jay Chou after singing, agent collect money, very happy

public class MoneyOwner {
   public static void main(String[] args) {
       JayImp jay = new JayImp();
       // Jay signed a contract with his agent. This step can be done internally
       JayProxy jayProxy = newJayProxy(jay); jayProxy.sing(); }}Copy the code

Check out the execution. Everybody’s happy. Your girlfriend’s happy.

The above set is the implementation of the proxy pattern,

But a proxy class can only delegate one kind, so it would be silly to create a proxy class for every service

Moreover, if the interface is changed, the agent class also needs to be changed, which is very inconvenient. Jay Chou is also involved in film making, variety shows and song writing

Well, the static agent to say also said, I believe that you should see here no what do not understand, below we formally begin today’s dinner, dynamic agent

Dynamic proxy

Dynamic proxy is a proxy method provided by Java. The core of this technology is to enhance the interface at runtime, generate class objects, and load them into the virtual machine. In simple terms, the virtual machine creates a class for you to implement your interface

Without further ado, let’s implement a dynamic proxy

The first step is to define the interface. The above code already has the ISing

The second step to achieve the interface, the above code has been implemented JayImp, also do not repeat the definition, this agent signed a singer, Lin Junjie, look at the implementation

package org.pdool.dynamic;
 
/** ** Lin Junjie */
public class JJImp implements ISing {
   @Override
   public void sing(a) {
       System.out.println("I am JJ! happy birthday to you"); }}Copy the code

Third, the agent can send the signed artist dynamically. Note that the agent needs to implement InvocationHandler so that all method calls are handled uniformly

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class JayAgentFactory implements InvocationHandler {
   Object target;
 
   public JayAgentFactory(Object target) {
       this.target = target;
  }
 
   // Generate the proxy class
   public ISing CreatProxyedObj(a) {
       return (ISing) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
  }
 
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       Object invoke = method.invoke(target, args);
       logAfter(invoke);
       return invoke;
  }
 
   public void logAfter(Object invoke) {
       System.out.println("The results" + invoke);
       System.out.println("Income ++"); }}Copy the code

Step 4: Receive the business

package org.pdool.dynamic;
 
import java.lang.reflect.Proxy;
 
public class Aain {
   public static void main(String[] args) {
       JayImp jayImp = new JayImp();
       ISing subjectProxy=(ISing) Proxy.newProxyInstance(jayImp.getClass().getClassLoader(), jayImp.getClass().getInterfaces(), newJayAgentFactory(jayImp)); subjectProxy.sing(); }}Copy the code

Summary: Dynamic proxy is the implementation provided by Java and requires the implementation class of InvocationHandler

1. Why can the editor prompt interface methods? Because the editor will be forced to prompt

The default constructor for the generated memory class requires the InvocationHandler argument

3. The core arguments for creating a proxy class are the class loader, interface, and InvocationHandler subclasses.

Class loaders ensure that they are in the same loader as the target class and can be called to prevent classes loaded by different loaders from being called

The interface is the interface you want to proxy

The InvocationHandler subclass is a forwarder that intercepts, processes and forwards all messages

3. Principle research

Implementation see, explore the principle, dynamic proxy is the most fundamental according to the interface to create a memory class, this step is how to achieve, we follow the source look

1. Clone the function information of the interface

2, find or generate the specified proxy class, if there is a cache, use the cache, not create

3. Get the constructor of the proxy class by reflection

Create a proxy object using the constructor and associate the object with InvocationHandler

/** parameter types of a proxy class constructor */ private static final Class<? >[] constructorParams = { InvocationHandler.class };Copy the code

Having seen the flow, let’s see what the proxy class actually looks like,

import sun.misc.ProxyGenerator; Public class Test {public static void main(String[] args) {// Enable saving code class attributes System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); ProxyGenerator.generateProxyClass("Xiangcai", JayImp.class.getInterfaces()); }}Copy the code

Execute the function above and you can see that xiangca.class is generated under the project path

Then take a look at xiangcai. Class and drag it to the editor

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import org.pdool.dynamic.ISing; public final class xiangcai extends Proxy implements ISing { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public xiangcai(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 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); }} // Look here!! Public final void sing() throws {try {super.h.invoke(this, m3, (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")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("org.pdool.dynamic.ISing").getMethod("sing"); 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

You can see that the sing interface is implemented and the invokeHandler method invoke is invoked. All right, the truth is out. Do you understand?

Some people will say, I understand the truth, but will not use ah, but did not see a good application scenario, so there is a period of time is not master these, we will be specific about the application scenario

4, application

In aspect programming (AOP), you need to intercept specific methods, and dynamic proxies are often chosen. Take a look at the spring-data-JPA implementation for a concrete example

Specific usage:

Use of database access in Spring

import com.tao.springboot.hibernate.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
 
public interface CustomerRepository extends JpaRepository<Customer, Long> {
 
}
Copy the code

As long as the implementation of the above interface can directly operate the database, is not very simple?

Here are a few questions for you to ponder:

1. What do two generics mean?

2. Where is the database connection? How is it injected?

3. How does an interface operate a database?

Answer to the first question:

Customer is the Entity corresponding to the table object.

Long is the primary key type of the table,

The second answer:

The database connection is automatically injected into the Spring container at spring startup, in the implementation class of JpaRepository

The third answer:

All interfaces generate proxy classes when Spring starts. The target class implements the SimpleJpaRepository class

Look at the class diagram

If you look at the definition of JpaRepository, these are all common methods

public interface JpaRepository<T.ID> extends PagingAndSortingRepository<T.ID>, QueryByExampleExecutor<T> {
   List<T> findAll(a);
 
   List<T> findAll(Sort var1);
 
   List<T> findAllById(Iterable<ID> var1);
 
   <S extends T> List<S> saveAll(Iterable<S> var1);
 
   void flush(a);
 
   <S extends T> S saveAndFlush(S var1);
 
   void deleteInBatch(Iterable<T> var1);
 
   void deleteAllInBatch(a);
 
   T getOne(ID var1);
 
   <S extends T> List<S> findAll(Example<S> var1);
 
   <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
Copy the code

Take a look at SimpleJpaRepository’s definition:

public class SimpleJpaRepository<T.ID> implements JpaRepositoryImplementation<T.ID> {
   private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";
   private finalJpaEntityInformation<T, ? > entityInformation;private final EntityManager em;// Look here!!
   private final PersistenceProvider provider;
   @Nullable
   private CrudMethodMetadata metadata;
   
   // The specific implementation method
      @Transactional
   public void delete(T entity) {
       Assert.notNull(entity, "The entity must not be null!");
       this.em.remove(this.em.contains(entity) ? entity : this.em.merge(entity));
  }
Copy the code

Something like the following code call:

Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), JpaRepository.class.getInterfaces(), new SimpleJpaRepository(());

Note: Just to express the meaning, the concrete implementation should not be like this

5, summary

With everything unraveled, here’s a summary:

Static proxy is the implementation of the proxy pattern, is for a specific interface implementation

Dynamic proxy is a method provided by the JDK that requires interfaces, as well as other implementations such as Cglib, Javassit, etc

Dynamic proxy is a class that generates a class file at runtime and then loads it automatically

Dynamic proxy is a technique based on reflection calls

The dynamic proxy generates classes to metaspace

6. Use it in frameworks

7. Decrypt the implementation of Spring Data JPA