Java Spring 杂记 - BaseService

1,804 阅读3分钟

项目中缓存服务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);
}