注解

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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值