In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly explains "how to define the implementation of java annotations". The content of the explanation in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn how to define the implementation of java annotations.
Strategy mode
It mainly defines the unified behavior (interface or abstract class) and implements the processing logic under different policies (corresponding to the implementation class). The client chooses the appropriate processing class when using it, using factory or other means.
Annotation implementation
The purpose of this article is to talk about the way to implement the strategy pattern with annotations, as well as some points for attention.
Needless to say, take the most common order processing as an example. First, define an order entity class:
@ Data
Public class Order {
/ * *
* Source of order
, /
Private String source
/ * *
* method of payment
, /
Private String payMethod
/ * *
* order number
, /
Private String code
/ * *
* order amount
, /
Private BigDecimal amount
/ /... Some other fields
}
If different logical processing is required for orders from different sources (PC, mobile). There is usually a class like OrderService in the project, as follows, there is a piece of if-else logic in it, which is designed to do different processing according to the source of the order.
@ Service
Public class OrderService {
Public void orderService (Order order) {
If (order.getSource () .equals ("pc")) {
/ / Logic for processing orders on the pc side
} else if (order.getSource () .equals ("mobile")) {
/ / Logic for processing mobile orders
} else {
/ / other logic
}
}
}
The strategy pattern is to kill a piece of if-else above and make the code look elegant and classy.
Let's start killing this if-else now. Let's first take a look at the structure:
1. First define an OrderHandler interface that specifies how to process the order.
Public interface OrderHandler {
Void handle (Order order)
}
two。 Define an OrderHandlerType annotation to indicate what source of order a class is used to process.
@ Target (ElementType.TYPE)
@ Retention (RetentionPolicy.RUNTIME)
@ Documented
@ Service
Public @ interface OrderHandlerType {
String source ()
}
3. The next step is to implement the order processing handler on the PC side and the mobile side, plus the OrderHandlerType annotations that we define.
@ OrderHandlerType (source = "mobile")
Public class MobileOrderHandler implements OrderHandler {
@ Override
Public void handle (Order order) {
System.out.println ("processing Mobile orders")
}
}
@ OrderHandlerType (source = "pc")
Public class PCOrderHandler implements OrderHandler {
@ Override
Public void handle (Order order) {
System.out.println ("processing PC side orders")
}
}
4. Once the above is ready, the handler for various order processing is injected into the spring container, and in the OrderService.orderService method, the policy (order source) is used to decide which OrderHandler is selected to process the order. We can do this:
@ Service
Public class OrderService {
Private Map orderHandleMap
@ Autowired
Public void setOrderHandleMap (List orderHandlers) {
/ / inject various types of order processing classes
OrderHandleMap = orderHandlers.stream () .collect (
Collectors.toMap (orderHandler-> AnnotationUtils.findAnnotation (orderHandler.getClass (), OrderHandlerType.class). Source ()
V-> v, (v1, v2)-> v1))
}
Public void orderService (Order order) {
/ /... Some pre-processing
/ / determine the corresponding handler through the order source
OrderHandler orderHandler = orderHandleMap.get (order.getSource ())
OrderHandler.handle (order)
/ /... Some post-processing
}
}
In OrderService, an orderHandleMap is maintained, whose key is the order source and value is the corresponding order processor Handler. Initialize orderHandleMap with @ Autowired (here is a lambda expression, which is not difficult to take a closer look at). In this way, a piece of if-else in OrderService.orderService is gone and replaced by just two lines of code. That is, first get the corresponding OrderHandler from the orderHandleMap according to the order source, and then execute the OrderHandler.handle method.
The advantage of this approach is that no matter how the business develops and the types of order sources increase in the future, the core logic of OrderService will not change. We only need to implement the OrderHandler of the new sources, and everyone in the team can develop the corresponding OrderHandler of the order sources they are responsible for, without interference with each other.
At this point, it seems to have finished the method of implementing strategic patterns through annotations and killing if-else. Is that the end? No, the real point starts now.
Now looking back at the Map of orderHandleMap, its key is the order source. What if we want to use the two attributes of order source and order payment method to decide which OrderHandler to use? For example, the order of Alipay on PC is one kind of processing logic (PCAliPayOrderHandler), while that of WeChat Pay on PC is another processing logic (PCWeChatOrderHandler), corresponding to mobile Alipay (MobileAliPayOrderHandler) and mobile WeChat Pay (MobileWeChatOrderHandler).
What should we store in our key at this time? some people may say, can't I just deposit the string composed of order source and order payment method? Yes, but if the business logic changes again (bowing to pm), PC Alipay and WeChat Pay are the same processing logic, while mobile Alipay and WeChat Pay are different processing logic, then the two classes PCAliPayOrderHandler and PCWeChatOrderHandler are the same set of code logic. We killed if-else, but made two copies of the same code, which is intolerable as an obsessive-compulsive programmer. How do you kill these two classes with the same logic?
First of all, we can review and comment on what it is. I wonder if you have noticed that the syntax for defining annotations, that is, @ interface, is only one more @ than the syntax for defining interfaces. If you look at jdk, you can find such an interface Annotation, as follows
/ * *
* The common interface extended by all annotation types. Note that an
* interface that manually extends this one does not define
* an annotation type. Also note that this interface does not itself
* define an annotation type.
*
* More information about annotation types can be found in section 9.6 of
* The Java ™Language Specification.
*
* The {@ link java.lang.reflect.AnnotatedElement} interface discusses
* compatibility concerns when evolving an annotation type from being
* non-repeatable to being repeatable.
*
* @ author Josh Bloch
* @ since 1.5
, /
Public interface Annotation {
/ / … Omit
}
It was made clear at the beginning, The common interface extended by all annotation types. To make it very clear, in fact, note that it is just an interface, yes, it is just an interface, @ interface is just a grammar sugar. So, since the annotation is an interface, there must be a corresponding implementation class, so where does the implementation class come from? In the above, we only defined OrderHandlerType annotations and did nothing else. At this point, we have to mention dynamic agents. Jdk must have done something for us behind our backs.
To track the JDK dynamic proxy classes generated by JVM while it is running. We can set the VM startup parameters as follows:
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
This parameter saves the generated JDK dynamic proxy class locally. By the way, if we want to track the proxy class generated by cglib, that is, the corresponding bytecode file, we can set the parameters:
System.setProperty (DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "saved path")
After adding parameters to start the project again, we can see that there are many more class files in our project (only part of it is intercepted here. The project started by the author generated a total of 97 dynamic proxy classes. Since my project is a springboot environment, I have generated many proxy classes corresponding to annotations such as ConditionalOnMissingBean, Configuration, Autowired, etc.):
The next step is to find out what we care about, that is, the proxy class generated by jdk for our custom OrderHandlerType annotations. Because all the dynamic proxy classes generated by jdk inherit Proxy, while java inherits only, we only need to find the class that implements OrderHandlerType. Walking through these class files, we find that $Proxy63.class implements the OrderHandlerType annotations we defined:
Public final class $Proxy63 extends Proxy implements OrderHandlerType {
Private static Method m1
Private static Method m2
Private static Method m4
Private static Method m3
Private static Method m0
Public $Proxy63 (InvocationHandler var1) throws {
Super (var1)
}
/ / … Omit
}
As we know, the core of the implementation of jdk dynamic proxy is:
That is, the InvocationHandler object of this constructor. So what is the concrete implementation of the annotated InvocationHandler? It is not difficult to find that:
The core attribute in this class is the memberValues, and the values we assign to the annotation properties when using annotations are stored in this map. The implementation of various methods in the proxy class actually calls the invoke method in AnnotationInvocationHandler.
Well, now we know that annotations are an interface and implement the various methods defined in it through dynamic proxies. So back to our OrderService, why not set the type of key to OrderHandlerType? Like this. Private Map orderHandleMap
In this way, no matter how the factors that determine the order processor orderhandler change, we can respond to changes in the same way (this is what we seek for high scalability and flexibility of the code). When the key of our map becomes OrderHandlerType, the logic of injection and acquisition will be changed accordingly, and the place of injection will be changed, as follows:
Public class OrderService {
Private Map orderHandleMap
@ Autowired
Public void setOrderHandleMap (List orderHandlers) {
/ / inject various types of order processing classes
OrderHandleMap = orderHandlers.stream () .collect (
Collectors.toMap (orderHandler-> AnnotationUtils.findAnnotation (orderHandler.getClass (), OrderHandlerType.class)
V-> v, (v1, v2)-> v1))
}
/ /... Omit
}
Then how to achieve the logic of acquisition? How do we get the corresponding OrderHandler in orderHandleMap according to the source and payment method of order? The problem becomes how to correlate the source and payment method of order with OrderHandlerType comments.
Remember that the annotation just mentioned is an interface, since it is an interface, we can implement a class by ourselves, so we associate the source and payment method of order with the OrderHandlerType annotation. Just do it, and now we have such a class.
Public class OrderHandlerTypeImpl implements OrderHandlerType {
Private String source
Private String payMethod
OrderHandlerTypeImpl (String source, String payMethod) {
This.source = source
This.payMethod = payMethod
}
@ Override
Public String source () {
Return source
}
@ Override
Public String payMethod () {
Return payMethod
}
@ Override
Public Class
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.