https://coding.imooc.com/lesson/270.html#mid=18123
1 代码演练
1.1 代码演练1(静态代理之分库操作)
1 代码演练
1.1 代码演练1(静态代理之分库操作)
需求:
订单管理,模拟前置后置方法,模拟分库管理
重点:
重点看订单静态代理,动态数据源和分库操作上下文。
UML类图:
测试类:
package com.geely.design.pattern.structural.proxy; import com.geely.design.pattern.structural.proxy.staticProxy.OrderServiceStaticProxy; public class Test { public static void main(String [] args){ Order order = new Order(); order.setUserID(1); OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy(); orderServiceStaticProxy.saveOrder(order); order.setUserID(2); orderServiceStaticProxy.saveOrder(order); } }
订单类:
package com.geely.design.pattern.structural.proxy; /** * 建立订单实体类 */ public class Order { private Object orderInfo; //之所以选择integer类型,是为了方便OrderServiceStaticProxy静态代理类进行分库 private Integer userID; public Object getOrderInfo() { return orderInfo; } public void setOrderInfo(Object orderInfo) { this.orderInfo = orderInfo; } public Integer getUserID() { return userID; } public void setUserID(Integer userID) { this.userID = userID; } }
订单dao:
package com.geely.design.pattern.structural.proxy; public interface IOrderDao { int insertOrder(Order order); }
订单daoIMPL:
package com.geely.design.pattern.structural.proxy; public class OrderDaoImpl implements IOrderDao{ @Override public int insertOrder(Order order) { System.out.println("新增一条订单!"); return 1; } }
订单Service:
package com.geely.design.pattern.structural.proxy; public interface IOrderService { int saveOrder(Order order); }
订单ServiceIMPL:
package com.geely.design.pattern.structural.proxy; public class OrderServiceImpl implements IOrderService { private IOrderDao orderDao; @Override public int saveOrder(Order order) { //Spring会自己注入,这里我们直接new了 orderDao = new OrderDaoImpl(); System.out.println("Service层调用dao层添加Order"); return orderDao.insertOrder(order); } }
订单静态代理:
package com.geely.design.pattern.structural.proxy.staticProxy; import com.geely.design.pattern.structural.proxy.IOrderService; import com.geely.design.pattern.structural.proxy.Order; import com.geely.design.pattern.structural.proxy.OrderServiceImpl; import com.geely.design.pattern.structural.proxy.db.DataSourceContextHolder; public class OrderServiceStaticProxy { private IOrderService orderService; /** * 添加前置方法和后置方法 * @param order * @return */ public int saveOrder(Order order){ beforeMethod(); //spring中会注入,这里我new一下 orderService = new OrderServiceImpl(); /** * 这里添加分库方法,根据user取模,根据余数进行分库 */ int userID = order.getUserID(); int dbRouter = userID%2; //todo设置datasource,记住,dbType一定要和我们xml中配置的key相同 DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter)); System.out.println("静态代理分配到 【db"+dbRouter+"】数据库进行处理数据!"); int a = orderService.saveOrder(order); afterMethod(); return a; } /** * 这里参照spring aop的做法,增加了前置通知方法 方法的增强 */ private void beforeMethod(){ System.out.println("静态代理 前置方法"); } /** * 这里参照spring aop的做法,增加了后置通知方法 方法的增强 */ private void afterMethod(){ System.out.println("静态代理 后置方法"); } }
动态数据源:
package com.geely.design.pattern.structural.proxy.db; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 分库操作:该类为动态数据源类 */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDBType(); } /** * 如果是在spring中开发,ioc.xml中 */ // <bean id="db0" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> // <property name="driverClassName" value="${db.driverClassName}"/> // <property name="url" value="${db.url}"/> // <property name="username" value="${db.username}"/> // <property name="password" value="${db.password}"/> // <!-- 连接池启动时的初始值 --> // <property name="initialSize" value="${db.initialSize}"/> // <!-- 连接池的最大值 --> // <property name="maxActive" value="${db.maxActive}"/> // <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --> // <property name="maxIdle" value="${db.maxIdle}"/> // <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 --> // <property name="minIdle" value="${db.minIdle}"/> // <!-- 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制 --> // <property name="maxWait" value="${db.maxWait}"/> // <!--#给出一条简单的sql语句进行验证 --> // <!--<property name="validationQuery" value="select getdate()" />--> // <property name="defaultAutoCommit" value="${db.defaultAutoCommit}"/> // <!-- 回收被遗弃的(一般是忘了释放的)数据库连接到连接池中 --> // <!--<property name="removeAbandoned" value="true" />--> // <!-- 数据库连接过多长时间不用将被视为被遗弃而收回连接池中 --> // <!--<property name="removeAbandonedTimeout" value="120" />--> // <!-- #连接的超时时间,默认为半小时。 --> // <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"/> // // <!--# 失效检查线程运行时间间隔,要小于MySQL默认--> // <property name="timeBetweenEvictionRunsMillis" value="40000"/> // <!--# 检查连接是否有效--> // <property name="testWhileIdle" value="true"/> // <!--# 检查连接有效性的SQL语句--> // <property name="validationQuery" value="SELECT 1 FROM dual"/> // </bean> // <bean id="db1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> // <property name="driverClassName" value="${db.driverClassName}"/> // <property name="url" value="${db.url}"/> // <property name="username" value="${db.username}"/> // <property name="password" value="${db.password}"/> // <!-- 连接池启动时的初始值 --> // <property name="initialSize" value="${db.initialSize}"/> // <!-- 连接池的最大值 --> // <property name="maxActive" value="${db.maxActive}"/> // <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --> // <property name="maxIdle" value="${db.maxIdle}"/> // <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 --> // <property name="minIdle" value="${db.minIdle}"/> // <!-- 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制 --> // <property name="maxWait" value="${db.maxWait}"/> // <!--#给出一条简单的sql语句进行验证 --> // <!--<property name="validationQuery" value="select getdate()" />--> // <property name="defaultAutoCommit" value="${db.defaultAutoCommit}"/> // <!-- 回收被遗弃的(一般是忘了释放的)数据库连接到连接池中 --> // <!--<property name="removeAbandoned" value="true" />--> // <!-- 数据库连接过多长时间不用将被视为被遗弃而收回连接池中 --> // <!--<property name="removeAbandonedTimeout" value="120" />--> // <!-- #连接的超时时间,默认为半小时。 --> // <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"/> // // <!--# 失效检查线程运行时间间隔,要小于MySQL默认--> // <property name="timeBetweenEvictionRunsMillis" value="40000"/> // <!--# 检查连接是否有效--> // <property name="testWhileIdle" value="true"/> // <!--# 检查连接有效性的SQL语句--> // <property name="validationQuery" value="SELECT 1 FROM dual"/> // </bean> /** * 这个是在spring中的db的具体的配置 * * 如果我们不指定,默认的是db0beanId */ // <bean id="dataSource" class="com.geely.design.pattern.structural.proxy.db.DynamicDataSource"> // <property name="targetDataSources"> // <map key-type="java.lang.String"> // <entry value-ref="db0" key="db0"></entry> // <entry value-ref="db1" key="db1"></entry> // </map> // </property> // <property name="defaultTargetDataSource" ref="db0"></property> // </bean> // <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> // <property name="dataSource" ref="dataSource" /> // </bean> }
分库操作上下文:
package com.geely.design.pattern.structural.proxy.db; /** * 分库操作: * dbRouter上下文类 ,在执行dao层之前,如果我们设置了 setDBType设置了dbType为db1 或者 db0,dao层就会去连接对应的数据库。 * db0和db1就是Spring容器中我们配置的beanID */ public class DataSourceContextHolder { //该变量可以存放dataSource的beanName private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<String>(); public static void setDBType(String dbType){ CONTEXT_HOLDER.set(dbType); } public static void clearDBType(String dbType){ CONTEXT_HOLDER.remove(); } public static String getDBType(){ return (String) CONTEXT_HOLDER.get(); } }
打印结果:
Connected to the target VM, address: '127.0.0.1:8341', transport: 'socket' 静态代理 前置方法 静态代理分配到 【db1】数据库进行处理数据! Service层调用dao层添加Order 新增一条订单! 静态代理 后置方法 静态代理 前置方法 静态代理分配到 【db0】数据库进行处理数据! Service层调用dao层添加Order 新增一条订单! 静态代理 后置方法 Disconnected from the target VM, address: '127.0.0.1:8341', transport: 'socket' Process finished with exit code 0
诸葛