May 31, 2021 Article blog
This article comes from Python Geek Technology Author: Duck Blood Fans
Recently, I learned a little trick when I looked at my colleagues' code, which is very useful in some scenarios, and I'll share it with you here.
Spring
@Autowired
annotations, we should be no strangers, with
Spring's
affirmation can not be separated from this annotation, through this annotation can help us automatically inject the beans we want.
In addition to this basic feature,
@Autowired
has more powerful features, as well as injecting arrays of specified types, List/Set collections, and even Map objects.
For example, the current application has a payment interface
PayService
respectively, need to connect Alipay, WeChat payment, bank cards, so there are three different implementation
AliPayService
WechatPayservice
BankCardPayService
The relationship between the four classes is shown in the following image:
If at this point I need to get all
PayService
beans for the current system class, the old way we can only get them through
BeanFactory
or
ApplicationContex
t.
// 首先通过 getBeanNamesForType 获取 PayService 类型所有的 Bean
String[] names = ctx.getBeanNamesForType(PayService.class);
List anotherPayService = Lists.newArrayList();
for (String beanName : names) {
anotherPayService.add(ctx.getBean(beanName, PayService.class));
}
// 或者通过 getBeansOfType 获取所有 PayService 类型
Map beansOfType = ctx.getBeansOfType(PayService.class);
for (Map.Entry entry : beansOfType.entrySet()) {
anotherPayService.add(entry.getValue());
}
But now that we don't have to worry about it, we can inject
PayService
Bean array directly using
@Autowired
or
PayService
List/Set collection, or even
PayService
Map collection.
@Autowired
List payServices;
@Autowired
PayService[] payServicesArray;
Knowing this feature, it's easy when we need to implement a policy pattern with Spring.
There may be small partners do not know much about the policy model, it doesn't matter, this kind of powder introduces a business scenario, through this scenario to introduce the policy model.
Or the example above, our current system needs to connect WeChat payment, Alipay, and bank card payment.
When we receive this request, we first need to get the corresponding interface documentation, analysis of the commonality of the three.
Let's say we find here that the three patterns are similar, but some of them are different.
So we abstract a set of common
PayService
based on the commonality of the three,
public interface PayService {
PayResult epay(PayRequest request);
}
Then implement three implementation classes, each inheriting the interface.
So now the problem is, because there are three implementation classes, how to choose the specific implementation class?
In fact, this problem is very good to solve, the request parameters pass in a unique identity, and then we select the appropriate implementation class according to the identity.
Let's say we're asking for a
channelNo
field in the request class
PayRequest
which represents the unique identification of the corresponding payment channel, such as Alipay
is: 00000001,
WeChat pays
0000002,
and the card pays
0000003.
Then we need to map the unique identity one by one with the concrete implementation class, just as we can use
Map
store this mapping relationship.
We implement a
RouteService
and the code logic is as follows:
@Service
public class RouteService {
@Autowired
Map payServiceMap;
public PayResult epay(PayRequest payRequest) {
PayService payService = payServiceMap.get(payRequest.getChannelNo());
return payService.epay(payRequest);
}
}
We automatically inject all the related beans of
PayService
and then use a unique identity to find the implementation class.
RouteService
This allows us to mask differences in payment channels, and other service classes simply call
RouteService
However, this implementation is still a bit of a problem, because we uniquely identify as a string of numbers, if like we above directly use
@Autowired
injection
Map
which requires us to implement the
Bean
name of the class is
00000001
these.
But such a name is not very elegant, so that later students will be difficult to understand, not good maintenance.
So we need to make a transition, and we can do that.
First, let's modify the
PayService
interface to add a method through which each concrete implementation class returns its unique identity.
public interface PayService {
PayResult epay(PayRequest request);
String channel();
}
Specifically, the code of an Alipay implementation class is specified, and other implementation class implementations are similar.
@Service("aliPayService")
public class AliPayService implements PayService {
@Override
public PayResult epay(PayRequest request) {
// 业务逻辑
return new PayResult();
}
@Override
public String channel() {
return "00000001";
}
}
Finally, let's revamp
RouteService
with the following logic:
@Service
public class RouteService {
@Autowired
Set payServiceSet;
Map payServiceMap;
public PayResult epay(PayRequest payRequest) {
PayService payService = payServiceMap.get(payRequest.getChannelNo());
return payService.epay(payRequest);
}
@PostConstruct
public void init() {
for (PayService payService : payServiceSet) {
payServiceMap = new HashMap();
payServiceMap.put(payService.channel(), payService);
}
}
}
The code above first automatically injects a collection of
PayService
and then we turn it into a
Map
so that the internal storage happens to be the only identity that maps to the implementation class.
That's
W3Cschool编程狮
said about
implementing policy patterns with Spring could have been as simple as that!
Related to the introduction, I hope to help you.