MelonBlog

写Spring项目时,Service一定该要写接口吗?

前言

在开发java项目时,我们肯定是按照行业的范式来组织项目结构,一般都包含这些元素:

controller
service
serviceImpl
mapper
xml

那如果你会思考,为什么这里的service层,要写分成接口和实现类。

为了拓展

你当然可以说实现接口是为了更好的拓展,可是,在绝大部分项目里,你的service接口一般只会有一个实现类,所以这个理由不成立,否则这就是属于过度设计。

Spring AOP

如果你说是因为Spring AOP的原因,算是考虑到了核心的问题,因为Spring默认是通过JDK动态代理实现的Bean增强,包括我们常用的事务异步 等功能,都依赖aop的增强,但是JDK动态代理,需要被代理的类实现接口,如果你的service类没实现接口,那么这个service类是没办法实现JDK动态代理的,所以这个理由算是合理,但是不完全对,因为Spring如果发现你的service类没有实现接口,它会自动帮你通过Cglib来实现代理类,Cglib是通过继承来实现的代理,所以不需要你一定要实现接口。

Srping动态选择代理方式的源码

@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}
	/**
	 * Determine whether the supplied {@link AdvisedSupport} has only the
	 * {@link org.springframework.aop.SpringProxy} interface specified
	 * (or no proxy interfaces specified at all).
	 */
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}
}

根据Spring的源码,我们可以发现这里有2个判断

// 判断1:
config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)
// 判断2
targetClass.isInterface() || Proxy.isProxyClass(targetClass)

如果目标类是一个接口或者已经是一个代理类了,那么就通过使用JdkDynamicAopProxy 来实现代理,否则使用Cglib代理

private方法没办法被aop增强

我们确实没必要为每一个service类都写一个接口,但是在不用接口的时候,我们还是需要将业务逻辑通过 public 方法来封装,因为不管是JDK动态代理还是Cglib动态代理,都没办法代理私有方法