使用Adivisor配置增强处理,来实现数据库读写分离

时间:2022-06-29 05:06:18

一、先写一个demo来概述Adivisor的简单使用步骤

实现步骤:

1、通过MethodBeforeAdivice接口实现前置增强处理

 1 public class ServiceBeforeAdvisor implements MethodBeforeAdvice {
 2     private Logger logger = Logger.getLogger(ServiceBeforeAdvisor.class);
 3     @Override
 4     public void before(Method method, Object[] args, Object target)
 5             throws Throwable {
 6         logger.info("启动事务");
 7         logger.info("连接点对象:" target.getClass().getSimpleName());
 8         logger.info("连接点方法:" method.getName());
 9         logger.info("连接点方法参数:" args[0]);
10         
11     }
12     
13 }

2、使用<aop:advisor>标签织入增强处理

1 //注意:advisor要放在aspect前面
2     <bean id="userService" class="com.pb.service.UserService"></bean>
3      <bean id="serviceBeforeAdvisor" class="com.pb.aop.ServiceBeforeAdvisor"></bean>
4      <aop:config>
5          <aop:pointcut expression="execution(public * com.pb.service.*.*(..))" 
6                 id="servicePointcut"/>
7          <aop:advisor advice-ref="serviceBeforeAdvisor" pointcut-ref="servicePointcut"/>
8      </aop:config>

3、测试类型

1 public class Test {
2     public static void main(String[] args) {
3         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext
4                     ("applicationContext.xml");
5         UserService service =  (UserService)context.getBean("userService");
6         service.addUser(new User());
7     }
8 }

二、使用Adivisor来实现数据读写分离

实现步骤:

1、通过MethodBeforeAdivice接口实现前置增强处理

 1 public class DataBaseAdvice implements MethodBeforeAdvice {
 2     /**
 3      * 日志计数器
 4      */
 5     private final static Logger logger = LoggerFactory.getLogger(DataBaseAdvice.class);
 6     /**
 7      * 存储方法前缀
 8      */
 9     private List<String> methodKey;
10     /**
11      * 是否打印日志:true-打印,false-不打印
12      */
13     private boolean showLog = true;
14 
15     public void before(Method method, Object[] objects, Object o) throws Throwable {
16         boolean write = true;
17         String methodName = method.getName().toLowerCase();
18         //若方法前缀匹配成功,返回false
19         if (CollectionUtils.isNotEmpty(methodKey)) {
20             for (String key : methodKey) {
21                 if (methodName.startsWith(key.toLowerCase())) {
22                     write = false;
23                     break;
24                 }
25             }
26         }
27 
28         if (write) {
29             DbContextHolder.setDbType(DbContextHolder.DB_TYPE_RW);
30             if (showLog) {
31                 logger.info(method.getName() "连接主库");
32             }
33         }else {
34             DbContextHolder.setDbType(DbContextHolder.DB_TYPE_R);
35             if (showLog) {
36                 logger.info(method.getName() "连接从库");
37             }
38         }
39     }
40 
41     public List<String> getMethodKey() {
42         return methodKey;
43     }
44 
45     public void setMethodKey(List<String> methodKey) {
46         this.methodKey = methodKey;
47     }
48 
49     public boolean isShowLog() {
50         return showLog;
51     }
52 
53     public void setShowLog(boolean showLog) {
54         this.showLog = showLog;
55     }
56 }

切换主从库的持有类

 1 public class DbContextHolder {
 2 
 3     /**
 4      * 线程threadLocal
 5      */
 6     private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
 7     /**
 8      * 主库:执行读写操作
 9      */
10     public static String DB_TYPE_RW = "dataSourceMaster";
11     /**
12      * 从库:执行读库操作
13      */
14     public static String DB_TYPE_R = "dataSourceSlave";
15 
16     /**
17      * 默认是读写库
18      * @return
19      */
20     public static String getDbType() {
21         String db = contextHolder.get();
22         if (db == null) {
23             db = DB_TYPE_RW;
24         }
25         return db;
26     }
27 
28     /**
29      * 置本线程的dbType
30      * @param str
31      */
32     public static void setDbType(String str) {
33         contextHolder.set(str);
34     }
35 
36     /**
37      * @Title: clearDBType
38      * @Description: 清理连接类型
39      */
40     public static void clearDBType() {
41         contextHolder.remove();
42     }
43 }

2、使用<aop:advisor>标签织入增强处理

 1 <!-- 动态数据源 -->
 2     <bean id="dynamicDataSource" class="com.alibaba.health.dao.dynamic.DynamicDataSource">
 3         <!-- 通过key-value关联数据源 -->
 4         <property name="targetDataSources">
 5             <map>
 6                 <entry value-ref="dataSourceMaster" key="monitorDataSourceMaster"/>
 7                 <entry value-ref="dataSourceSlave" key="monitorDataSourceSlave"/>
 8             </map>
 9         </property>
10         <property name="defaultTargetDataSource" ref="dataSourceMaster"/>
11     </bean>
12 
13 <!-- 通知器的具体实现:配置主从库读写分离:以select、query、find、count开头的方法走从库 -->
14     <bean id="DataBaseAdvice" class="com.jd.yao.wj.dao.dynamic.DataBaseAdvice">
15         <property name="methodKey">
16             <list>
17                 <value>select</value>
18                 <value>query</value>
19                 <value>find</value>
20                 <value>count</value>
21             </list>
22         </property>
23         <property name="showLog" value="false"/>
24     </bean>
25 
26     <!-- 切面配置 -->
27     <aop:config>
28         <!-- 配置切点:第一个*表示匹配所有方法的返回值类型,.表示当前包下所有类的方法,..表示当前包下及此包下所有自爆中的所有类方法;
29              第二个*表示所有类名,第三个*表示所有方法名,(..)表示所有方法参数-->
30         <aop:pointcut expression="execution(* com.jd.yao.wj.dao..*.*(..))" id="cutPoint"/>
31         <!-- 定义通知器 -->
32         <aop:advisor advice-ref="DataBaseAdvice" pointcut-ref="cutPoint"/>
33     </aop:config>

此时就完成了主从数据库读写分离的代码和xml配置,尤其可见各种技术组件,在于是否能灵活运用。