The problem background

A long, long time ago, when I was a bronze (and still a bronze) interviewee, the interviewer asked me how to get classes, methodological notes.

I used annotations at the time, and I answered them off the top of my head, using isAnnotationPresent to determine if it was annotated, getAnnotation to get the annotation object, and then get the value in the annotation.

The rough code looks like this:

Class<? > clz = bean.getClass(); Method[] methods = clz.getMethods();for (Method method : methods) {
    if(method.isAnnotationPresent(Encrypt.class)) { String uri = method.getAnnotation(Encrypt.class).value(); }}Copy the code

Just when I was smug, the interviewer went after me again. So when reading the annotations, is there any situation where the way you just said can’t be judged and read successfully?

This I once masked, still can not read the situation? Had not met before, then I firmly answer interview officer, impossible read less than, interview officer smiled…………

In my encryption framework Monkey-api-encrypt (github.com/yinjihuan/m…) Support annotation identification encryption and decryption function, in fact, by reading annotations, into the OPERATION of uri. At the beginning, the annotation was read in the same way as above. When the Controller in our program was cut into by AOP, the annotation could not be read. This is the problem to be shared today.

Under normal circumstances, our class is com cxytiandi. Eureka_client. Controller. ArticleController this form, if use the AOP, So it becomes com. Cxytiandi. Eureka_client. Controller. ArticleController? EnhancerBySpringCGLIB? 3323DD1e here it is.

Solution 1

In this case, the Method is also proxied, so the annotation on the Method is not available. Now that we know why, the simplest and quickest solution is to truncate the extra content and retrieve the Method from the unproxied Class object. This will get the annotation on Method.

Class<? > clz = bean.getClass(); String fullName = clz.getName();if (fullName.contains("EnhancerBySpringCGLIB") || fullName.contains("?")) {
	fullName = fullName.substring(0, fullName.indexOf("?"));
	try {
		clz = Class.forName(fullName);
	} catch (ClassNotFoundException e) {
		throw new RuntimeException(e);
	}
}
Method[] methods = clz.getMethods();
for (Method method : methods) {
    if(method.isAnnotationPresent(Encrypt.class)) { String uri = method.getAnnotation(Encrypt.class).value(); }}Copy the code

Solution 2

Although the problem is solved, it still feels not elegant enough. Is there a better way? Annotations can be read using AnnotationUtils provided in Spring.

Encrypt encrypt = AnnotationUtils.findAnnotation(method, Encrypt.class);
if(encrypt ! = null) { String uri = encrypt.value(); }Copy the code

AnnotationUtils. FindAnnotation () what is the principle? Why does it get annotations on proxied methods?

If you want to know the principle, you can only look at the source code, source code, not posted out, posted a little bit of the key on the line

An AnnotationCacheKey is built, fetched from the local cache, and returned if it exists, meaning it will be cached as soon as it reads:

AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
A result = (A) findAnnotationCache.get(cacheKey);
Copy the code

The next step is to check whether the bridge method is available, return it if not, get the bridge annotation if it is available, and get it from the interface if not available.

Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);
if (result == null) {
	result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
}
Copy the code

I won’t continue, but the key code is actually this:

clazz = clazz.getSuperclass();
Copy the code

Since the CGLIB proxy dynamically generates a subclass for the target class, we need to get the original class and use getSuperclass directly, which is the same as the first method, but the first method looks a bit silly…..

I recommend AnnotationUtils, which encapsulates a lot of logic, takes into account a lot of scenarios, don’t reinvent the wheel.