Java Spring 杂记 - BaseService

Java Spring 杂记 - BaseService

六月 26, 2019

项目中缓存服务CacheService继承的BaseService抽象类源码学习,其他服务层实现类在同包名下也有在使用(CacheService继承BaseService,其他不同模块相同包名的,继承CacheService)。


Golang、前端所没有的 …(回顾面向对象)

Java 抽象类

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。

由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。

父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。

在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。

BaseService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// @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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
}