Annotation, reflection, dynamic proxy
The knowledge points involved are annotation, reflection, dynamic agent, AOP, Lombok, agent mode and ` builder mode```````````
Q: Have you ever used annotations in your work? Let's briefly introduce them.
A: Annotations are often used in daily development, including @ overridden, @ Deprecated provided by Java, @ Controller, @ Service, @ component, @ Configuration, @ Bean and other @ Data, @ Builder of Lombok provided by some frameworks, as well as their own annotations to realize logging and permission verification in combination with Spring's AOP. The function of annotation is to mark and process it according to the content of the mark. Java meta annotations are annotations that modify annotations. Commonly used are @ Retention and @ Target.
@Retention is used to set the declaration cycle of annotations in three ways
- RetentionPolicy.SOURCE: annotations are only retained in source files. When java files are compiled into class files, annotations are discarded; Ignored by compiler. For example, Java Native @ Override only performs checking operations. Lombok's @ Data
- RetentionPolicy.CLASS: annotations are retained in the class file, but are discarded when the jvm loads the class file. This is the default life cycle.
- RetentionPolicy.RUNTIME: the annotation is not only saved in the class file, but still exists after the jvm loads the class file. Writing business code is generally at the RUNTIME level, because we need to obtain meta annotation information at RUNTIME.
@The Target annotation is used to declare that the annotation can be used, including elementtype.type, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR and ElementType.LOCAL_VARIABLE, ElementType.ANNOTATION_TYPE, ElementType.PACKAGE, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE.
Reflection is generally used to obtain runtime annotations, and common API s are obtained from Package, Class, mthod and other classes
- public T getAnnotation(Class annotationClass)
Use AOP + custom annotation to realize custom permission verification
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface VerifyAuthority { String value() default ""; }
@Aspect @Component @Slf4j public class VerifyAuthorityAspect { @Pointcut(value = "@annotation(verifyAuthority)") public void pointcut(VerifyAuthority verifyAuthority) { } @Around(value = "pointcut(verifyAuthority)") public Object doAround(ProceedingJoinPoint joinPoint, VerifyAuthority verifyAuthority) throws Throwable { // Gets the value in the annotation String needResourcePermission = verifyAuthority.value(); // ... execute the corresponding business logic return joinPoint.proceed(); } }
Or use reflection to get annotations
@Around(value = "pointcut(verifyAuthority)") public Object doAround(ProceedingJoinPoint joinPoint, VerifyAuthority verifyAuthority) throws Throwable { // Get method signature MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); // Acquisition method Method method = methodSignature.getMethod(); // Get the annotation above the method VerifyAuthority verifyAuthority = method.getAnnotation(VerifyAuthority.class); // Gets the value in the annotation String needResourcePermission = verifyAuthority.value(); // ... execute the corresponding business logic return joinPoint.proceed(); }
Q: Tell me about your understanding of reflection
A: Reflection can obtain the information of classes and objects in the running state, construct objects and call object methods. It is often used when writing tool classes or frameworks. Common classes for reflection are
- Class
- Method
- Constructor
- Filed
There are three ways to get Class objects:
- Class.forName("xxx.Student"): get the class object through the full pathname of the class
- Student.class: through the class attribute of the class
- student.getClass(): through the getClass() function of the object.
Proxy mode:
Without changing the code of the original class (proxy class), the proxy class is introduced to add functions to the original class. Agents are divided into static agents and dynamic agents.
Static proxy implementation method:
-
The proxy class implements the same interface with the original class, and the original class method is called in the proxy class.
Disadvantages: the original class does not define an interface or the original class is not developed and maintained by us (using the third-party Class Library). We have no way to modify the original class and redefine an interface for it.
-
The external extension is realized by inheritance, and the proxy class inherits the original class, and then extends the additional functions.
Disadvantages: the proxy class needs to implement all the methods of the original class again, and each method adds similar code logic. If you want to add more than one additional function class, create a proxy class for each class
Dynamic proxy:
Do not write proxy classes for each original class in advance. When running, dynamically create the proxy class corresponding to the original class, and replace the original class with proxy class in the system. java has provided the syntax of dynamic proxy, and the underlying layer relies on reflection syntax.
public class UserControllerProxy { public Object createProxy(Object proxyObject) { DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(proxyObject); return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject.getClass().getInterfaces(), dynamicProxyHandler); } private class DynamicProxyHandler implements InvocationHandler { private Object proxiedObject; public DynamicProxyHandler(Object proxiedObject) { this.proxiedObject = proxiedObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ... other logic // ... // Execute the original logic Object result = method.invoke(proxiedObject, args); // ... other logic // ... return result; } } public static void main(String[] args) { UserControllerProxy userControllerProxy = new UserControllerProxy(); UserControllerInterface userControllerInterface = (UserControllerInterface) userControllerProxy.createProxy(new UserController()); } }
Q: So you're talking about the builder model
A: Usage scenario - check required parameters
If some properties are required when we create an object, to meet this requirement, we can set the required items through the constructor and the options through the set() method. However, the following three scenarios are the battlefield of Builder mode.
- If there are many required parameters and the required configuration is set in the constructor, the parameter list of the constructor will be very long. If the required items are also set through the set() method, there is no place for the logic of verifying the required items.
- There are dependencies between configuration items. For example, if the value is true, you must add a description.
- If you want to create an immutable object, you cannot modify the internal attribute value after the object is created.
In the build() method, verify it first, and then create the object.