项目中缓存服务CacheService继承的BaseService抽象类源码学习,其他服务层实现类在同包名下也有在使用(CacheService继承BaseService,其他不同模块相同包名的,继承CacheService)。
Golang、前端所没有的 ...(回顾面向对象)
Java 抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
BaseService
import com.xxx.appserver.commons.support.JsonUtility;
import com.xxx.appserver.commons.support.SpringContextUtility;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
public abstract class BaseService<S> {
protected S proxy;
@Autowired
protected SpringContextUtility springContextUtility;
// 被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。
@PostConstruct
protected void post() {
// Class<?>它是个通配泛型,?可以代表任何类型
// 通过getInterfaces()获取接口信息(eg:moe.test.ITest)
Class<?>[] interfaces = this.getClass().getInterfaces();
if(null == interfaces || interfaces.length == 0) return;
proxy = (S) springContextUtility.getBean(interfaces[0]);
}
protected JsonNode getNodeList(String json) throws Exception {
JsonNode node = JsonUtility.readTree(json);
return node.get("list");
}
}
泛型类:在编译器,是无法知道S具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。(项目中一般传的是服务接口) 声明泛型proxy,通过post方法从Bean中获取到对应接口信息的接口实现,即在继承使用的类中可以通过proxy调用其他服务的方法,就是使用到SpringContextUtility,下面分析。
SpringContextUtility
// @Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean
// 和@Bean目的是一样的,都是注册bean到Spring容器中。
// @Component(@Controller、@Service、@Repository)通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
// 而@Bean注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
@Component
public class SpringContextUtility implements ApplicationContextAware {
@Getter
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext application) throws BeansException {
applicationContext = application;
}
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
创建个类实现ApplicationContextAware工具类,可以通过其它类引用它以操作spring容器及其中的Bean实例。
Spring容器会检测容器中的所有Bean,如果发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该Bean的setApplicationContextAware()方法,调用该方法时,会将容器本身作为参数传给该方法——该方法中的实现部分将Spring传入的参数(容器本身)赋给该类对象的applicationContext实例变量,因此接下来可以通过该applicationContext实例变量来访问容器本身。
使用
还是登陆方法吧
UserServiceImpl
@Slf4j
@Service
@CacheConfigList({
@CacheConfiguration(nameSpace = CacheKey.GAME_CATCH_USER, expTime = 172800)
})
public class UserServiceImpl extends CacheService<IUserService> implements IUserService {
@Override
public UserDto login(String code, String encryptedData, String iv) throws Exception {
...
UserDto user = proxy.queryUserByUid(wxUser.getOpenId());
...
//缓存用户
proxy.saveUserByUid(user);
log.info("用户登录:" + user);
return user;
}
}
可以看到这个实现类继承了CacheService,泛型传的是IUserService接口,通过proxy我们就可以调到接口实现的方法了,下面是IUserService接口
IUserService
public interface IUserService {
/***
* 登录
* @return
*/
UserDto login(String code, String encryptedData, String iv) throws Exception;
/**
*缓存用户数据
* @param userDto
*/
UserDto saveUserByUid(UserDto userDto);
/**
* 获取用户数据
* @param uid
* @return
*/
UserDto queryUserByUid(String uid);
}