ServiceLoader使用并结合工厂打造专属多态实例
有几个坑记录下META-INF下services文件夹下文件名要接口全路径名,里面内容是接口实现的全路径名
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Test3Application {
public static void main(String[] args) {
SpringApplication.run(Test3Application.class, args);
}
}
package com.test.bean;
public class Conetxt
{
private String captchaType;
public String getCaptchaType()
{
return captchaType;
}
public void setCaptchaType(String captchaType)
{
this.captchaType = captchaType;
}
}
package com.test.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.test.service.CaptchaService;
import com.test.service.impl.ServiceFactory;
@Configuration
public class ConfigurationSatrt
{
@Bean
@ConditionalOnMissingBean
public CaptchaService captchaService() {
CaptchaService s = ServiceFactory.getInstance();
return s;
}
}
package com.test.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.test.bean.Conetxt;
import com.test.service.CaptchaService;
@Controller
@RequestMapping("test")
public class TestController
{
@Autowired
private CaptchaService captchaService;
@RequestMapping("tt")
public String test()
{
Conetxt conetxt = new Conetxt();
conetxt.setCaptchaType("one");
return captchaService.get(conetxt);
}
}
package com.test.service;
import com.test.bean.Conetxt;
public interface CaptchaService
{
void init();
String get(Conetxt captchaVO);
String captchaType();
}
package com.test.service.impl;
import com.test.service.CaptchaService;
import com.test.util.CacheUtil;
public abstract class AbstractService implements CaptchaService
{
protected static String cacheType = "local";
@Override
public void init()
{
if (cacheType.equals("local")) {
CacheUtil.init(1000, 180);
}
}
}
package com.test.service.impl;
import org.apache.commons.lang.StringUtils;
import com.google.auto.service.AutoService;
import com.test.bean.Conetxt;
import com.test.service.CaptchaService;
//@AutoService(CaptchaService.class)
public class DefaultServiceImpl extends AbstractService
{
private CaptchaService getService(String captchaType){
return ServiceFactory.instances.get(captchaType);
}
@Override
public String get(Conetxt conetxtVO) {
if (conetxtVO == null) {
return "对象为空";
}
if (StringUtils.isEmpty(conetxtVO.getCaptchaType())) {
return "类型为空";
}
return getService(conetxtVO.getCaptchaType()).get(conetxtVO);
}
@Override
public String captchaType()
{
return "default";
}
}
package com.test.service.impl;
import com.google.auto.service.AutoService;
import com.test.bean.Conetxt;
import com.test.service.CaptchaService;
//@AutoService(CaptchaService.class)
public class OneServiceImpl extends AbstractService
{
@Override
public String get(Conetxt captchaVO)
{
System.err.println("业务逻辑one");
return "one";
}
@Override
public String captchaType()
{
return "one";
}
}
package com.test.service.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import com.test.service.CaptchaService;
public class ServiceFactory
{
public static CaptchaService getInstance() {
CaptchaService ret = instances.get("default");
if (ret == null) {
throw new RuntimeException();
}
ret.init();
return ret;
}
public volatile static Map<String, CaptchaService> instances = new HashMap();
static {
ServiceLoader<CaptchaService> services = ServiceLoader.load(CaptchaService.class);
for (CaptchaService item : services) {
instances.put(item.captchaType(), item);
}
}
}
package com.test.service.impl;
import com.google.auto.service.AutoService;
import com.test.bean.Conetxt;
import com.test.service.CaptchaService;
//@AutoService(CaptchaService.class)
public class TwoServiceImpl extends AbstractService
{
@Override
public String get(Conetxt captchaVO)
{
System.err.println("业务逻辑two");
return "two";
}
@Override
public String captchaType()
{
return "two";
}
}
package com.test.util;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
public class CacheUtil
{
private static final Map<String, Object> CACHE_MAP = new ConcurrentHashMap<String, Object>();
/**
* 缓存最大个数
*/
private static Integer CACHE_MAX_NUMBER = 1000;
/**
* 初始化
* @param cacheMaxNumber 缓存最大个数
* @param second 定时任务 秒执行清除过期缓存
*/
public static void init(int cacheMaxNumber, long second) {
CACHE_MAX_NUMBER = cacheMaxNumber;
if (second > 0L) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
refresh();
}
}, 0, second * 1000);
}
}
/**
* 缓存刷新,清除过期数据
*/
public static void refresh(){
for (String key : CACHE_MAP.keySet()) {
exists(key);
}
}
public static void set(String key, String value, long expiresInSeconds){
//设置阈值,达到即clear缓存
if (CACHE_MAP.size() > CACHE_MAX_NUMBER * 2) {
clear();
}
CACHE_MAP.put(key, value);
CACHE_MAP.put(key + "_HoldTime", System.currentTimeMillis() + expiresInSeconds*1000);//缓存失效时间
}
public static void delete(String key){
CACHE_MAP.remove(key);
CACHE_MAP.remove(key + "_HoldTime");
}
public static boolean exists(String key){
Long cacheHoldTime = (Long) CACHE_MAP.get(key + "_HoldTime");
if (cacheHoldTime == null || cacheHoldTime == 0L) {
return false;
}
if (cacheHoldTime < System.currentTimeMillis()) {
delete(key);
return false;
}
return true;
}
public static String get(String key){
if (exists(key)) {
return (String)CACHE_MAP.get(key);
}
return null;
}
/**
* 删除所有缓存
*/
public static void clear() {
CACHE_MAP.clear();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test</groupId>
<artifactId>test-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test-3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc7</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
理论上用谷歌com.google.auto.service包添加注解@AutoService也可以自动生成需要配置META-INF下的services文件,但是我使用注解编译后一直没有生成META-INF下的services文件,后续研究下这个注解
稍微修改下的第二个版本,不好地方是初始化失效了,另外不需要配置META-INF下的services文件但是需要在实现类上加上容器注解@Service
package com.test.service.impl;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import com.test.service.CaptchaService;
@Component
public class ServiceFactory implements BeanPostProcessor
{
public static CaptchaService getInstance() {
CaptchaService ret = instances.get("default");
if (ret == null) {
throw new RuntimeException();
}
return ret;
}
public volatile static Map<String, CaptchaService> instances = new HashMap();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
{
if (bean instanceof CaptchaService)
{
CaptchaService call = (CaptchaService) bean;
instances.put(call.captchaType(), call);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
return bean;
}
}
package com.test.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.test.bean.Conetxt;
import com.test.service.impl.ServiceFactory;
@Controller
@RequestMapping("test")
public class TestController
{
// @Autowired
// private CaptchaService captchaService;
@Autowired
private ServiceFactory ServiceFactory;
@RequestMapping("tt")
public String test()
{
Conetxt conetxt = new Conetxt();
conetxt.setCaptchaType("one");
System.err.println(ServiceFactory.instances.get(conetxt.getCaptchaType()));
// return captchaService.get(conetxt);
return null;
}
}