1、注解的简介
1.1、注解的作用
注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后某个时刻方便地使用这些数据(通过解析注解来使用这些数据),常见的作用有以下几种:
- 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等
- 跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量。也是
- 在编译时进行格式检查。如@Override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
1.2、元注解(注解的注解,包括@Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。)
1.2.1、@Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间,有以下的值
注解常量 | 存活时间 |
---|---|
RetentionPolicy.SOURCE | 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 |
RetentionPolicy.CLASS | 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。 |
RetentionPolicy.RUNTIME | 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。 |
1.2.2、@Documented
注解@Documented它的作用是能够将注解中的元素包含到 Javadoc 中去。
1.2.3、@Target
注解@Target 指定了注解运用的地方,限定了注解的使用范围,有以下的值
注解常量 | 使用范围 |
---|---|
ElementType.ANNOTATION_TYPE | 可以给一个注解进行注解 |
ElementType.CONSTRUCTOR | 可以给构造方法进行注解 |
ElementType.FIELD | 可以给属性进行注解 |
ElementType.LOCAL_VARIABLE | 可以给局部变量进行注解 |
ElementType.METHOD | 可以给方法进行注解 |
ElementType.PACKAGE | 可以给一个包进行注解 |
ElementType.PARAMETER | 可以给一个方法内的参数进行注解 |
ElementType.TYPE | 可以给一个类型进行注解,比如类、接口、枚举 |
1.2.4、@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Inherited @Retention(RetentionPolicy.RUNTIME) @interface Test {} @Test public class A {} public class B extends A {} |
注解 Test 被 @Inherited 修饰,然后超类A有注解Test,那么A的子类也有Test注解(虽然在定义类B时没有用Test注解)
1.2.5、@Repeatable
Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的。下面的例子定义人员的角色,其中SuperMan是程序员又是产品经理,同时他还是个画家。
@interface Persons { Person[] value(); } @Repeatable(Persons.class) @interface Person{ String role default ""; } @Person(role="artist") @Person(role="coder") @Person(role="PM") public class SuperMan{ } |
2、数据库Salve的实现
2.1、定义Salve注解
/** * The interface Data log. * 读从库标示 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface Slave { } |
2.2、定义动态切换数据库实现类DynamicDataSource(继承AbstractRoutingDataSource类)
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return ThreadDataSource.getDataSource(); } } |
并且在配置中配置主数据库和从数据库
<!-- 主库数据源 --> <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${jdbc_url}"/> <property name="username" value="${jdbc_user}" /> <property name="password" value="${jdbc_password}"/> <property name="driverClassName" value="${jdbc_driverClassName}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${jdbc_initialSize}"/> <property name="minIdle" value="${jdbc_minIdle}"/> <property name="maxActive" value="${jdbc_maxActive}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${jdbc_maxWait}"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${jdbc_timeBetweenEvictionRunsMillis}"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${jdbc_minEvictableIdleTimeMillis}"/> <property name="testWhileIdle" value="${jdbc_testWhileIdle}"/> <property name="testOnBorrow" value="${jdbc_testOnBorrow}"/> <property name="validationQuery" value="${jdbc_validationQuery}"/> <!-- 如果需要支持utf8mb4编码的话打开 --> <property name="connectionInitSqls" value="${jdbc_connectionInitSqls}"/> <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 --> <property name="filters" value="stat" /> </bean> <!-- 从库数据源 --> <bean id="salveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${slave_jdbc_url}"/> <property name="username" value="${slave_jdbc_user}"/> <property name="password" value="${slave_jdbc_password}"/> <property name="driverClassName" value="${jdbc_driverClassName}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${jdbc_initialSize}"/> <property name="minIdle" value="${jdbc_minIdle}"/> <property name="maxActive" value="${jdbc_maxActive}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${jdbc_maxWait}"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${jdbc_timeBetweenEvictionRunsMillis}"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${jdbc_minEvictableIdleTimeMillis}"/> <property name="testWhileIdle" value="${jdbc_testWhileIdle}"/> <property name="testOnBorrow" value="${jdbc_testOnBorrow}"/> <property name="validationQuery" value="${jdbc_validationQuery}"/> <!-- 如果需要支持utf8mb4编码的话打开 --> <property name="connectionInitSqls" value="${jdbc_connectionInitSqls}"/> <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 --> <property name="filters" value="stat" /> </bean> <!--动态切换数据库--> <bean id="dataSource" class="com.mwlife.mwvendor.config.DynamicDataSource" lazy-init="true"> <property name="targetDataSources"> <map key-type="java.lang.String" value-type="javax.sql.DataSource"> <!-- master --> <entry key="master" value-ref="masterDataSource" /> <!-- slave --> <entry key="slave" value-ref="salveDataSource" /> </map> </property> <!-- 设置默认的目标数据源为读 --> <property name="defaultTargetDataSource" ref="masterDataSource" /> </bean> |
并且ThreadDataSource类采用ThreadLocal实现线程间隔离
/** * The type Handle data source. * 记录当前请求 数据源选择 */ public class ThreadDataSource { // 数据源名称线程池 private static final ThreadLocal<String> holder = new ThreadLocal<>(); public static void putDataSource(String datasource) { Assert.notNull(datasource, "datasource cannot be null"); holder.set(datasource); } public static String getDataSource() { return holder.get(); } public static void clear() { holder.remove(); } } |
其中方法putDataSource决定采用主从哪个数据库的方法在DataSourceChooseAspect类中调用
/** * The type Data source aspect. * Description */ @Order(1) @Aspect @Service public class DataSourceChooseAspect { public static final String SLAVE_SOURCE = "slave"; public static final String MASTER_SOURCE = "master"; @Pointcut("execution(* com.mwlife.mwvendor.service.*.*(..))") public void aspect() {} /** * 前置通知,选择数据源 */ @Before("aspect()") public void before(JoinPoint point) { try { // 方法对象 Method m = ReflectUtil.getMethod(point); // Slave Slave slave = AnnotationUtils.findAnnotation(m, Slave.class); if (slave != null){ ThreadDataSource.putDataSource(SLAVE_SOURCE); } }catch (Exception e){ ThreadDataSource.putDataSource(MASTER_SOURCE); } } @After("aspect()") public void after(JoinPoint point) { ThreadDataSource.clear(); } } |