When Spring looks for the injection point, there is a findBridgedMethod to find the bridging method? What is the bridging method?
Let's take a look at a scenario of generating bridge methods: when a subclass inherits (or implements) the generic methods of a parent class (or interface), the generic type is explicitly specified in the subclass, and the compiler will automatically generate bridge methods at compile time
Code case:
public interface IService<T> { void setHandler(T handler); } @Service public class AService implements IService<AHandler> { private Handler handler; @Autowired public void setHandler(AHandler handler) { this.handler = handler; } }
We use the tool provided with IDEA to view the bytecode of AService (view - > show bytecode)
① We found that there are two setHandler methods in the bytecode file. The second setHandler is preceded by a synthetic bridge, which is what we call a bridge method
② Both methods have @ Autowired annotation, which means there are two injection points. This is wrong, so Spring provides a BridgeMethodResolver to handle this situation
③ The bridge method calls the actual method
When the bridge method calls the actual method, it will perform forced type conversion. The byte code of the above bridge method is converted to:
@Autowired public void setHandler(Object handler) { setHandler((AHandler))handler); }
In other words, the bridging method is actually turning the Object strong and calling our actual method to see the following test code:
public class BridgeMethodTest { public static void main(String[] args) { IService service = new AService(); service.setHandler(new AHandler()); //The actual method is called service.setHandler(new Object()); //The bridge method is called. The bridge method will call the actual method after type coercion, so the conversion exception will be reported here. } }
Now that we know what a bridging method is, why do we need a bridging method? This requires us to understand generics and generic erasure.
generic paradigm
Why do I need generics
Let's start with a simple code:
public static void main(String[] args) { ArrayList al=new ArrayList(); al.add("Hello World"); al.add(1001); String str=(String)al.get(1); System.out.println(str); }
A collection can store any type of data, so when there is no generics in the early stage, when we traverse a collection, we often have to cast an Object object, but there is a risk of strong conversion failure, and this risk will be found when the program is running.
Therefore, the mechanism of generics is introduced in JDK1.5, through which the data types in the collection can be unified. With the help of generics, the compiler can find the problem of type mismatch for us during compilation.
Generic erase
JAVA's generic mechanism is basically implemented at the compiler level. In the process of generating bytecode, the generic information will be removed. We call this process generic erasure.
Let's take an example:
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ArrayList<Integer> arrayList3=new ArrayList<>(); arrayList3.add(1);//In this way, calling the add method can only store the Integer, because the instance of the generic type is Integer arrayList3.getClass().getMethod("add", Object.class).invoke(arrayList3, "asd"); for (int i=0;i<arrayList3.size();i++) { System.out.println(arrayList3.get(i)); } }
Here, we forcibly insert a string into a collection with the generic type of Integer through reflection. This is possible because when ArrayList is compiled into bytecode, the generic information has been erased.
Before erasure After erasure boolean add(E e) ==> boolean add(Object e)
After clarifying the generic erasure, let's take a look at why the bridging method is needed:
public interface IService<T> { void setHandler(T handler); } @Service public class AService implements IService<AHandler> { private Handler handler; @Autowired public void setHandler(AHandler handler) { this.handler = handler; } }
According to generic erasure, the code corresponding to IService bytecode should be as follows:
public interface IService { void setHandler(Object handler); }
Have you found the problem? Our original intention is that AService overrides the setHandler method of the parent class. After the generic is erased, it becomes the setHandler method of the overloaded parent class. Therefore, in order to be compatible with similar problems, the compiler will generate a bridging method for AService:
public class AService implements IService<AHandler> { private Handler handler; @Autowired public void setHandler(AHandler handler) { this.handler = handler; } // The bridge method generated by the compiler for us. Overrides the method of the parent class and calls the actual method @Autowired public void setHandler(Object handler) { setHandler((AHandler)handler); } }
reference resources:
What is bridge method in java_ Nonsense - CSDN blog_ Bridging method
Interview question series: after using Java generics for so many years, I only know its fur - learn architecture from Mic - blog Garden (cnblogs.com)