国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Spring AOP 深入剖析

2019-11-15 00:21:01
字體:
供稿:網(wǎng)友
SPRing AOP 深入剖析

AOP是Spring提供的關(guān)鍵特性之一。AOP即面向切面編程,是OOP編程的有效補(bǔ)充。使用AOP技術(shù),可以將一些系統(tǒng)性相關(guān)的編程工作,獨(dú)立提取出來,獨(dú)立實(shí)現(xiàn),然后通過切面切入進(jìn)系統(tǒng)。從而避免了在業(yè)務(wù)邏輯的代碼中混入很多的系統(tǒng)相關(guān)的邏輯——比如權(quán)限管理,事物管理,日志記錄等等。這些系統(tǒng)性的編程工作都可以獨(dú)立編碼實(shí)現(xiàn),然后通過AOP技術(shù)切入進(jìn)系統(tǒng)即可。從而達(dá)到了將不同的關(guān)注點(diǎn)分離出來的效果。本文深入剖析Spring的AOP的原理。

1. AOP相關(guān)的概念

1)aspect:切面,切入系統(tǒng)的一個(gè)切面。比如事務(wù)管理是一個(gè)切面,權(quán)限管理也是一個(gè)切面;

2)Join point:連接點(diǎn),也就是可以進(jìn)行橫向切入的位置;

3)Advice:通知,切面在某個(gè)連接點(diǎn)執(zhí)行的操作(分為:Before advice,After returning advice,After throwing advice,After (finally) advice,Around advice);

4)Pointcut:切點(diǎn),符合切點(diǎn)表達(dá)式的連接點(diǎn),也就是真正被切入的地方;

2. AOP 的實(shí)現(xiàn)原理

AOP分為靜態(tài)AOP和動(dòng)態(tài)AOP。靜態(tài)AOP是指AspectJ實(shí)現(xiàn)的AOP,他是將切面代碼直接編譯到java類文件中。動(dòng)態(tài)AOP是指將切面代碼進(jìn)行動(dòng)態(tài)織入實(shí)現(xiàn)的AOP。Spring的AOP為動(dòng)態(tài)AOP,實(shí)現(xiàn)的技術(shù)為:JDK提供的動(dòng)態(tài)代理技術(shù) CGLIB(動(dòng)態(tài)字節(jié)碼增強(qiáng)技術(shù))。盡管實(shí)現(xiàn)技術(shù)不一樣,但都是基于代理模式,都是生成一個(gè)代理對象。

1) JDK動(dòng)態(tài)代理

主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。JDK動(dòng)態(tài)代理要求被代理實(shí)現(xiàn)一個(gè)接口,只有接口中的方法才能夠被代理。其方法是將被代理對象注入到一個(gè)中間對象,而中間對象實(shí)現(xiàn)InvocationHandler接口,在實(shí)現(xiàn)該接口時(shí),可以在 被代理對象調(diào)用它的方法時(shí),在調(diào)用的前后插入一些代碼。而 Proxy.newProxyInstance() 能夠利用中間對象來生產(chǎn)代理對象。插入的代碼就是切面代碼。所以使用JDK動(dòng)態(tài)代理可以實(shí)現(xiàn)AOP。我們看個(gè)例子:

被代理對象實(shí)現(xiàn)的接口,只有接口中的方法才能夠被代理:

public interface UserService {    public void addUser(User user);    public User getUser(int id);}

被代理對象:

public class UserServiceImpl implements UserService {    public void addUser(User user) {        System.out.println("add user into database.");    }    public User getUser(int id) {        User user = new User();        user.setId(id);        System.out.println("getUser from database.");        return user;    }}

代理中間類:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class ProxyUtil implements InvocationHandler {    private Object target;    // 被代理的對象        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("do sth before....");        Object result =  method.invoke(target, args);        System.out.println("do sth after....");        return result;    }    ProxyUtil(Object target){        this.target = target;    }    public Object getTarget() {        return target;    }    public void setTarget(Object target) {        this.target = target;    }}

測試:

import java.lang.reflect.Proxy;import net.aazj.pojo.User;public class ProxyTest {    public static void main(String[] args){        Object proxyedObject = new UserServiceImpl();    // 被代理的對象        ProxyUtil proxyUtils = new ProxyUtil(proxyedObject);                // 生成代理對象,對被代理對象的這些接口進(jìn)行代理:UserServiceImpl.class.getInterfaces()        UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),                     UserServiceImpl.class.getInterfaces(), proxyUtils);        proxyObject.getUser(1);        proxyObject.addUser(new User());    }}

執(zhí)行結(jié)果:

do sth before....getUser from database.do sth after....do sth before....add user into database.do sth after....

我們看到在 UserService接口中的方法addUser 和 getUser方法的前面插入了我們自己的代碼。這就是JDK動(dòng)態(tài)代理實(shí)現(xiàn)AOP的原理。

我們看到該方式有一個(gè)要求,被代理的對象必須實(shí)現(xiàn)接口,而且只有接口中的方法才能被代理。

2)CGLIB(code generate libary)

字節(jié)碼生成技術(shù)實(shí)現(xiàn)AOP,其實(shí)就是繼承被代理對象,然后Override需要被代理的方法,在覆蓋該方法時(shí),自然是可以插入我們自己的代碼的。因?yàn)樾枰狾verride被代理對象的方法,所以自然CGLIB技術(shù)實(shí)現(xiàn)AOP時(shí),就必須要求需要被代理的方法不能是final方法,因?yàn)閒inal方法不能被子類覆蓋。我們使用CGLIB實(shí)現(xiàn)上面的例子:

package net.aazj.aop;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CGProxy implements MethodInterceptor{    private Object target;    // 被代理對象    public CGProxy(Object target){        this.target = target;    }    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {        System.out.println("do sth before....");        Object result = proxy.invokeSuper(arg0, arg2);        System.out.println("do sth after....");        return result;    }    public Object getProxyObject() {        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(this.target.getClass());    // 設(shè)置父類        // 設(shè)置回調(diào)        enhancer.setCallback(this);    // 在調(diào)用父類方法時(shí),回調(diào) this.intercept()        // 創(chuàng)建代理對象        return enhancer.create();    }}
public class CGProxyTest {    public static void main(String[] args){        Object proxyedObject = new UserServiceImpl();    // 被代理的對象        CGProxy cgProxy = new CGProxy(proxyedObject);        UserService proxyObject = (UserService) cgProxy.getProxyObject();        proxyObject.getUser(1);        proxyObject.addUser(new User());    }}

輸出結(jié)果:

do sth before....getUser from database.do sth after....do sth before....add user into database.do sth after....

我們看到達(dá)到了同樣的效果。它的原理是生成一個(gè)父類enhancer.setSuperclass(this.target.getClass())的子類enhancer.create(),然后對父類的方法進(jìn)行攔截enhancer.setCallback(this). 對父類的方法進(jìn)行覆蓋,所以父類方法不能是final的。

3)接下來我們看下spring實(shí)現(xiàn)AOP的相關(guān)

從上面的源碼我們可以看到:

            if (targetClass.isInterface()) {                return new JdkDynamicAopProxy(config);            }            return new ObjenesisCglibAopProxy(config);

如果被代理對象實(shí)現(xiàn)了接口,那么就使用JDK的動(dòng)態(tài)代理技術(shù),反之則使用CGLIB來實(shí)現(xiàn)AOP,所以Spring默認(rèn)是使用JDK的動(dòng)態(tài)代理技術(shù)實(shí)現(xiàn)AOP的。

JdkDynamicAopProxy的實(shí)現(xiàn)其實(shí)很簡單:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {        @Override    public Object getProxy(ClassLoader classLoader) {        if (logger.isDebugEnabled()) {            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());        }        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);    }

3. Spring AOP的配置

Spring中AOP的配置一般有兩種方法,一種是使用 <aop:config> 標(biāo)簽在xml中進(jìn)行配置,一種是使用注解以及@Aspect風(fēng)格的配置。

1)基于<aop:config>的AOP配置

下面是一個(gè)典型的事務(wù)AOP的配置:

    <tx:advice id="transactionAdvice" transaction-manager="transactionManager"?>        <tx:attributes >            <tx:method name="add*" propagation="REQUIRED" />            <tx:method name="append*" propagation="REQUIRED" />            <tx:method name="insert*" propagation="REQUIRED" />            <tx:method name="save*" propagation="REQUIRED" />            <tx:method name="update*" propagation="REQUIRED" />                <tx:method name="get*" propagation="SUPPORTS" />            <tx:method name="find*" propagation="SUPPORTS" />            <tx:method name="load*" propagation="SUPPORTS" />            <tx:method name="search*" propagation="SUPPORTS" />                <tx:method name="*" propagation="SUPPORTS" />        </tx:attributes>    </tx:advice>    <aop:config>        <aop:pointcut id="transactionPointcut" expression="execution(* net.aazj.service..*Impl.*(..))" />        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />    </aop:config>

再看一個(gè)例子:

    <bean id="aspectBean" class="net.aazj.aop.DataSourceInterceptor"/>    <aop:config>        <aop:aspect id="dataSourceAspect" ref="aspectBean">            <aop:pointcut id="dataSourcePoint" expression="execution(public * net.aazj.service..*.getUser(..))" />            <aop:pointcut expression="" id=""/>            <aop:before method="before" pointcut-ref="dataSourcePoint"/>            <aop:after method=""/>            <aop:around method=""/>        </aop:aspect>                <aop:aspect></aop:aspect>    </aop:config>

<aop:aspect> 配置一個(gè)切面;<aop:pointcut>配置一個(gè)切點(diǎn),基于切點(diǎn)表達(dá)式;<aop:before>,<aop:after>,<aop:around>是定義不同類型的advise. aspectBean 是切面的處理bean:

public class DataSourceInterceptor {    public void before(JoinPoint jp) {        DataSourceTypeManager.set(DataSources.SLAVE);    }}

2) 基于注解和@Aspect風(fēng)格的AOP配置

我們以事務(wù)配置為例:首先我們啟用基于注解的事務(wù)配置

    <!-- 使用annotation定義事務(wù) -->    <tx:annotation-driven transaction-manager="transactionManager" />

然后掃描Service包:

    <context:component-scan base-package="net.aazj.service,net.aazj.aop" />

最后在service上進(jìn)行注解:

@Service("userService")@Transactionalpublic class UserServiceImpl implements UserService{    @Autowired    private UserMapper userMapper;        @Transactional (readOnly=true)    public User getUser(int userId) {        System.out.println("in UserServiceImpl getUser");        System.out.println(DataSourceTypeManager.get());        return userMapper.getUser(userId);    }        public void addUser(String username){        userMapper.addUser(username);//        int i = 1/0;    // 測試事物的回滾    }        public void deleteUser(int id){        userMapper.deleteByPrimaryKey(id);//        int i = 1/0;    // 測試事物的回滾    }        @Transactional (rollbackFor = BaseBusinessException.class)    public void addAndDeleteUser(String username, int id) throws BaseBusinessException{        userMapper.addUser(username);        this.m1();        userMapper.deleteByPrimaryKey(id);    }        private void m1() throws BaseBusinessException {        throw new BaseBusinessException("xxx");    }    public int insertUser(User user) {        return this.userMapper.insert(user);    }}

搞定。這種事務(wù)配置方式,不需要我們書寫pointcut表達(dá)式,而是我們在需要事務(wù)的類上進(jìn)行注解。但是如果我們自己來寫切面的代碼時(shí),還是要寫pointcut表達(dá)式。下面看一個(gè)例子(自己寫切面邏輯):

首先去掃描 @Aspect 注解定義的 切面:

<context:component-scan base-package="net.aazj.aop" />

啟用@AspectJ風(fēng)格的注解:

<aop:aspectj-autoproxy />

這里有兩個(gè)屬性,<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>, proxy-target-class="true" 這個(gè)最好不要隨便使用,它是指定只能使用CGLIB代理,那么對于final方法時(shí)會(huì)拋出錯(cuò)誤,所以還是讓spring自己選擇是使用JDK動(dòng)態(tài)代理,還是CGLIB. expose-proxy="true"的作用后面會(huì)講到。

切面代碼:

import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Aspect    // for aop@Component // for auto scan@Order(0)  // execute before @Transactionalpublic class DataSourceInterceptor {    @Pointcut("execution(public * net.aazj.service..*.get*(..))")    public void dataSourceSlave(){};        @Before("dataSourceSlave()")    public void before(JoinPoint jp) {        DataSourceTypeManager.set(DataSources.SLAVE);    }}

我們使用到了 @Aspect 來定義一個(gè)切面;@Component是配合<context:component-scan/>,不然掃描不到;@Order定義了該切面切入的順序,因?yàn)樵谕粋€(gè)切點(diǎn),可能同時(shí)存在多個(gè)切面,那么在這多個(gè)切面之間就存在一個(gè)執(zhí)行順序的問題。該例子是一個(gè)切換數(shù)據(jù)源的切面,那么他應(yīng)該在 事務(wù)處理 切面之前執(zhí)行,所以我們使用 @Order(0) 來確保先切換數(shù)據(jù)源,然后加入事務(wù)處理。@Order的參數(shù)越小,優(yōu)先級越高,默認(rèn)的優(yōu)先級最低:

/** * Annotation that defines ordering. The value is optional, and represents order value * as defined in the {@link Ordered} interface. Lower values have higher priority. * The default value is {@code Ordered.LOWEST_PRECEDENCE}, indicating * lowest priority (losing to any other specified order value). */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})public @interface Order {    /**     * The order value. Default is {@link Ordered#LOWEST_PRECEDENCE}.     * @see Ordered#getOrder()     */    int value() default Ordered.LOWEST_PRECEDENCE;}

關(guān)于數(shù)據(jù)源的切換可以參加專門的博文:http://m.survivalescaperooms.com/digdeep/p/4512368.html

3)切點(diǎn)表達(dá)式(pointcut)

上面我們看到,無論是 <aop:config> 風(fēng)格的配置,還是 @Aspect 風(fēng)格的配置,切點(diǎn)表達(dá)式都是重點(diǎn)。都是我們必須掌握的。

1> pointcut語法形式(execution):

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

帶有 ? 號的部分是可選的,所以可以簡化成:ret-type-pattern name-pattern(param_pattern) 返回類型,方法名稱,參數(shù)三部分來匹配。

配置起來其實(shí)也很簡單: * 表示任意返回類型,任意方法名,任意一個(gè)參數(shù)類型; .. 連續(xù)兩個(gè)點(diǎn)表示0個(gè)或多個(gè)包路徑,還有0個(gè)或多個(gè)參數(shù)。就是這么簡單??聪吕樱?/p>

execution(* net.aazj.service..*.get*(..)) :表示net.aazj.service包或者子包下的以get開頭的方法,參數(shù)可以是0個(gè)或者多個(gè)(參數(shù)不限);

execution(* net.aazj.service.AccountService.*(..)): 表示AccountService接口下的任何方法,參數(shù)不限;

注意這里,將類名和包路徑是一起來處理的,并沒有進(jìn)行區(qū)分,因?yàn)轭惷彩前窂降囊徊糠帧?/p>

參數(shù)param-pattern部分比較復(fù)雜: () 表示沒有參數(shù),(..)參數(shù)不限,(*,String) 第一個(gè)參數(shù)不限類型,第二參數(shù)為String.

2> within() 語法:

within()只能指定(限定)包路徑(類名也可以看做是包路徑),表示某個(gè)包下或者子報(bào)下的所有方法:

within(net.aazj.service.*), within(net.aazj.service..*),within(net.aazj.service.UserServiceImpl.*)

3> this() 與 target():

this是指代理對象,target是指被代理對象(目標(biāo)對象)。所以 this() 和 target() 分別限定 代理對象的類型和被代理對象的類型:

this(net.aazj.service.UserService): 實(shí)現(xiàn)了UserService的代理對象(中的所有方法);

target(net.aazj.service.UserService): 被代理對象實(shí)現(xiàn)了UserService(中的所有方法);

4> args():

限定方法的參數(shù)的類型:

args(net.aazj.pojo.User): 參數(shù)為User類型的方法。

5> @target(), @within(), @annotation(), @args():

這些語法形式都是針對注解的,比如帶有某個(gè)注解的,帶有某個(gè)注解的方法,參數(shù)的類型帶有某個(gè)注解:

@within(org.springframework.transaction.annotation.Transactional)@target(org.springframework.transaction.annotation.Transactional)

兩者都是指被代理對象上有 @Transactional 注解的(類的所有方法),(兩者似乎沒有區(qū)別???)

@annotation(org.springframework.transaction.annotation.Transactional):方法 帶有@Transactional 注解的所有方法

@args(org.springframework.transaction.annotation.Transactional):參數(shù)的類型 帶有@Transactional 注解的所有方法

6> bean(): 指定某個(gè)bean的名稱

bean(userService): bean的id為 "userService" 的所有方法;

bean(*Service): bean的id為 "Service"字符串結(jié)尾的所有方法;

另外注意上面這些表達(dá)式是可以利用 ||, &&, ! 進(jìn)行自由組合的。比如:execution(public * net.aazj.service..*.getUser(..)) && args(Integer,..)

4. 向注解處理方法傳遞參數(shù)

有時(shí)我們在寫注解處理方法時(shí),需要訪問被攔截的方法的參數(shù)。此時(shí)我們可以使用 args() 來傳遞參數(shù),下面看一個(gè)例子:

@Aspect@Component // for auto scan//@Order(2)public class LogInterceptor {        @Pointcut("execution(public * net.aazj.service..*.getUser(..))")    public void myMethod(){};        @Before("myMethod()")    public void before() {        System.out.println("method start");    }         @After("myMethod()")    public void after() {        System.out.println("method after");    }         @AfterReturning("execution(public * net.aazj.mapper..*.*(..))")    public void AfterReturning() {        System.out.println("method AfterReturning");    }         @AfterThrowing("execution(public * net.aazj.mapper..*.*(..))")//  @Around("execution(public * net.aazj.mapper..*.*(..))")    public void AfterThrowing() {        System.out.println("method AfterThrowing");    }         @Around("execution(public * net.aazj.mapper..*.*(..))")    public Object Around(ProceedingJoinPoint jp) throws Throwable {        System.out.println("method Around");        SourceLocation sl = jp.getSourceLocation();        Object ret = jp.proceed();        System.out.println(jp.getTarget());        return ret;    }         @Before("execution(public * net.aazj.service..*.getUser(..)) && args(userId,..)")    public void before3(int userId) {        System.out.println("userId-----" + userId);    }          @Before("myMethod()")    public void before2(JoinPoint jp) {        Object[] args = jp.getArgs();        System.out.println("userId11111: " + (Integer)args[0]);        System.out.println(jp.getTarget());        System.out.println(jp.getThis());        System.out.println(jp.getSignature());        System.out.println("method start");    }    }

方法:

    @Before("execution(public * net.aazj.service..*.getUser(..)) && args(userId,..)")    public void before3(int userId) {        System.out.println("userId-----" + userId);    }

它會(huì)攔截 net.aazj.service 包下或者子包下的getUser方法,并且該方法的第一個(gè)參數(shù)必須是int型的,那么使用切點(diǎn)表達(dá)式args(userId,..)就可以使我們在切面中的處理方法before3中可以訪問這個(gè)參數(shù)。

before2方法也讓我們知道也可以通過 JoinPoint 參數(shù)來獲得被攔截方法的參數(shù)數(shù)組。JoinPoint 是每一個(gè)切面處理方法都具有的參數(shù),@Around類型的具有的參數(shù)類型為ProceedingJoinPoint。通過JoinPoint或者ProceedingJoinPoint參數(shù)可以訪問到被攔截對象的一些信息(參見上面的before2方法)。

5. Spring AOP的缺陷

因?yàn)镾pring AOP是基于動(dòng)態(tài)代理對象的,那么如果target中的方法不是被代理對象調(diào)用的,那么就不會(huì)織入切面代碼,看個(gè)例子:

@Service("userService")@Transactionalpublic class UserServiceImpl implements UserService{    @Autowired    private UserMapper userMapper;        @Transactional (readOnly=true)    public User getUser(int userId) {        return userMapper.getUser(userId);    }        public void addUser(String username){        getUser(2);        userMapper.addUser(username);    }

看到上面的 addUser() 方法中,我們調(diào)用了 getUser() 方法,而getUser() 方法是誰調(diào)用的呢?是UserServiceImpl的實(shí)例,不是代理對象,那么getUser()方法就不會(huì)被織入切面代碼。

切面代碼如下:

@Aspect@Componentpublic class AOPTest {    @Before("execution(public * net.aazj.service..*.getUser(..))")    public void m1(){        System.out.println("in m1...");    }        @Before("execution(public * net.aazj.service..*.addUser(..))")    public void m2(){        System.out.println("in m2...");    }}

執(zhí)行如下代碼:

public class Test {    public static void main(String[] args){                applicationContext context = new ClassPathXmlApplicationContext(                        new String[]{"config/spring-mvc.xml","config/applicationContext2.xml"});        UserService us = context.getBean("userService", UserService.class);        if(us != null){            us.addUser("aaa");

輸出結(jié)果如下:

in m2...

雖然 getUser()方法 被調(diào)用了,但是因?yàn)椴皇谴韺ο笳{(diào)用的,所以 AOPTest.m1() 方法并沒有執(zhí)行。這就是Spring aop的缺陷。解決方法如下:

首先: 將 <aop:aspectj-autoproxy /> 改為:

<aop:aspectj-autoproxy expose-proxy="true"/>

然后,修改UserServiceImpl中的 addUser() 方法:

@Service("userService")@Transactionalpublic class UserServiceImpl implements UserService{    @Autowired    private UserMapper userMapper;        @Transactional (readOnly=true)    public User getUser(int userId) {        return userMapper.getUser(userId);    }        public void addUser(String username){        ((UserService)AopContext.currentProxy()).getUser(2);        userMapper.addUser(username);    }
((UserService)AopContext.currentProxy()).getUser(2); 先獲得當(dāng)前的代理對象,然后在調(diào)用 getUser() 方法,就行了。expose-proxy="true" 表示將當(dāng)前代理對象暴露出去,不然 AopContext.currentProxy() 或得的是 null .修改之后的運(yùn)行結(jié)果:
in m2...in m1...


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 会理县| 柳林县| 浠水县| 广宁县| 金堂县| 五指山市| 阳西县| 秭归县| 呼和浩特市| 民勤县| 乌什县| 盱眙县| 齐河县| 广宗县| 十堰市| 公安县| 望都县| 泰和县| 文安县| 临邑县| 仙居县| 凌海市| 常山县| 登封市| 红河县| 五指山市| 广饶县| 巨野县| 北碚区| 临汾市| 昌平区| 富裕县| 祁阳县| 武隆县| 文昌市| 青岛市| 阿拉善盟| 大关县| 新干县| 石门县| 信丰县|