Spring自定义@Autowired注解+抽象工厂扩展切换数据库实现

在实际项目开发中,经常有换数据库的需求,这里将介绍怎样在原数据库基础上扩展一种数据库实现,并实现数据库切换

在此之前,如果你还不太了解抽象工厂设计模式,可以先了解其实现: 抽象工厂模式_行者Beck的博客-CSDN博客

1. 在配置文件中定义数据库模式配置:   db.mode=db1

2.  定义抽象工厂,实现不同db的具体工厂,在具体工厂里生成不同db的Dao接口实现   (这里使用context.getBean()是为了防止DaoImpl中使用@Autowired注入的类为null)

/**
 * @author beck.yang
 * @date 2022/7/11 10:49
 * @description 抽象工厂
 */
public interface AbstractDBFactory {
	<T extends Dao> T createDao();
}
/**
 * @author beck.yang
 * @date 2022/7/11 10:51
 * @description
 */
public class DB1Factory implements AbstractDBFactory {
	private static ApplicationContext context;

	@Override
	public <T extends Dao> T createDao() {
		return (T) context.getBean(DB1DaoImpl.class);
	}
}
/**
 * @author beck.yang
 * @date 2022/7/11 10:51
 * @description
 */
public class DB2Factory implements AbstractDBFactory{
	private static ApplicationContext context;

	@Override
	public <T extends Dao> T createDao() {
		return (T) context.getBean(DB2DaoImpl.class);
	}
}

3. 原有的Dao接口及DB1DaoImpl实现类,我们需要扩展DB2DaoImpl,实现Dao接口

/**
 * @author beck.yang
 * @date 2022/7/11 10:50
 * @description
 */
public interface Dao {
	void create();
	void delete();
}
/**
 * @author beck.yang
 * @date 2022/7/11 10:53
 * @description
 */
public class DB1DaoImpl implements Dao{
	@Override
	public void create() {

	}

	@Override
	public void delete() {

	}
}
/**
 * @author beck.yang
 * @date 2022/7/11 10:53
 * @description
 */
public class DB2DaoImpl implements Dao{
	@Override
	public void create() {

	}

	@Override
	public void delete() {

	}
}

4. 定义FactoryUtil,获取db.mode配置的数据库模式,这里使用了@Value静态注入


/**
 * @author beck.yang
 * @date 2022/7/11 10:56
 * @description
 */
@Slf4j
public class FactoryUtil {
	public static String dbMode = "db1";

	@Setter
	@Value("${db.mode}")
	public void setDbMode(String dbMode) {
		com.cloudwise.cmdb.core.factory.FactoryUtil.dbMode = dbMode;
	}

	public static String getDbMode() {
		return dbMode;
	}

	public static DatabaseFactory getFactory() {
		if ("db1".equals(getDbMode())) {
			return new ArangoFactory();
		} else if ("db2".equals(getDbMode())) {
			return new Neo4jFactory();
		} else {
			throw new RuntimeException("The conf of ${db.mode} isn't support");
		}
	}

	public static Object of(Class<?> clazz) {
		try {
			DatabaseFactory factory = getFactory();
			Method[] methods = factory.getClass().getDeclaredMethods();
			for (Method method : methods) {
				Class<?> returnType = method.getReturnType();
				if (returnType.equals(clazz)) {
					method.setAccessible(true);
					return method.invoke(factory);
				}
			}
		} catch (Exception e) {
			log.error("FactoryUtil build dao error, ", e);
		}
		return null;
	}
}

5. 定义@DaoAutowired注解,实现Dao层注入的封装

/**
 * @author beck.yang
 * @date 2022/7/7 15:00
 * @description
 */
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DaoAutowired {
}

4. 定义@DaoAutowired注解的处理类,这里实现了BeanPostProcessor(实现ApplicationContextAware是为了避免调用具体工厂类时context还未被装载)

/**
 * @author beck.yang
 * @date 2022/7/7 15:05
 * @description
 */
@Slf4j
public class DaoAutowiredProcessor implements ApplicationContextAware, BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object obj, String beanName) throws BeansException {
		Field[] declaredFields = obj.getClass().getDeclaredFields();
		for (Field declaredField : declaredFields) {
			DaoAutowired dbAutowired = declaredField.getAnnotation(DaoAutowired.class);
			if (dbAutowired != null) {
				Class<?> type = declaredField.getType();
				Object bean = FactoryUtil.of(type);
				declaredField.setAccessible(true);
				try {
					declaredField.set(obj, bean);
				} catch (IllegalAccessException e) {
					log.error("@DaoAutowired error, ", e);
				}
			}
		}
		return obj;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		DB1Factory.context = applicationContext;
		DB2Factory.context = applicationContext;
	}
}

5.  最后一步,也是不可或缺的一步,一定要在META-INF下创建spring.factories文件并增加注解处理类的配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.easytuop.factory.DaoAutowiredProcessor

至此,就可以像使用@Autowired一样使用@DaoAutowired注入Dao了,并且注入时并不关注是具体哪种数据库的实现!!!

	@DaoAutowired
	private Dao dao;

6. 实现时发现,有更简单的方式可以达到相同目的,那就是使用@ConditionalOnProperty注解,使用该注解可以根据配置文件project.database.model配置的数据库模式装载需要的Dao层实现类,使用时直接 @Autowired Dao dao; 

/**
 * @author beck.yang
 * @date 2022/7/11 10:50
 * @description
 */
@Repository
@ConditionalOnProperty(prefix = "project.database", value = "mode", havingValue = "db1")
public class DB1DaoImpl implements Dao{
	@Override
	public void create() {

	}

	@Override
	public void delete() {

	}
}
/**
 * @author beck.yang
 * @date 2022/7/11 10:53
 * @description
 */
@Repository
@ConditionalOnProperty(prefix = "project.database", value = "mode", havingValue = "db2")
public class DB2DaoImpl implements Dao{
	@Override
	public void create() {

	}

	@Override
	public void delete() {

	}
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值