时间:2017-2-4 21:12

——AspectJ简介

1、AspectJ是一个基于Java语言的AOP框架。
2、Spring2.0以后新增了对AspectJ切点表达式的支持。
3、@AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面。

4、新版本Spring框架,建议使用AspectJ方式来开发AOP。
5、使用AspectJ需要导入Spring AOP和AspectJ相关jar包:
    spring-aop-3.2.0.RELEASE.jar

    com.springsource.org.aopalliance-1.0.0.jar
 
    spring-aspects-3.2.0.RELEASE.jar
    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
 

——通过配置启用@AspectJ切面

<?xml version="1.0" encoding="UTF-8"?>
    <!-- 开启AspectJ自动代理-->
    <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
    <aop:aspectj-autoproxy />

</beans>
 

——在通知中通过value属性定义切点

1、通过execution函数,可以定义切点的方法切入
2、语法:
    execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
3、例如
    1)匹配所有类public方法:execution(public * *(..))
    2)匹配指定包下所有类方法:execution(* cn.itcast.dao.*(..)) 不包含子包
    3)execution(* cn.itcast.dao..*(..))  ..*表示包、子孙包下所有类
    4)匹配指定类所有方法:execution(* cn.itcast.service.UserService.*(..))
    5)匹配实现特定接口所有类方法,包含子类:execution(* cn.itcast.dao.GenericDAO+.*(..))
    6)匹配所有save开头的方法:execution(* save*(..))
 

——基于注解的开发

1、导包
    *   AspectJ依赖AOP环境。
        >    spring-aop-3.2.0.RELEASE.jar

        >   com.springsource.org.aopalliance-1.0.0.jar
 

*   导入AspectJ的jar包
        >   spring-aspects-3.2.0.RELEASE.jar
        >   com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

2、编写目标类
    UserDao
    提供增删改查方法。

3、使用AspectJ添加注解

    /**
     * 切面类
     * 切点 + 增强的结合
     * @author WYC
     *
     */
    // 用来定义切面类
    @Aspect
    public class MyAspect {
        // 对UserDao类的add()方法应用前置增强
        @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
        public void before(){
            System.out.println("前置增强");
        }
    }

4、创建applicationContext.xml
    1)引入AOP的约束
    2)打开标签:<aop:aspectj-autoproxy />
        表示自动生成代理,该标签底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理。


       <!-- 开启AspectJ自动代理 -->
        <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
        <aop:aspectj-autoproxy />
 
 
        <!-- 配置目标类 -->
        <bean id="userDao" class="com.wyc.spring3.demo1.UserDao" />
 
        <!-- 配置切面类(增强类) -->
        <bean id="myAspect" class="com.wyc.spring3.demo1.MyAspect" />

5、示例代码

UserDao目标类:

public class UserDao {
    public void add() {
        System.out.println("添加用户");
    }
 
    public void update() {
        System.out.println("修改用户");
    }
 
    public void delete() {
        System.out.println("删除用户");
    }
 
    public void find() {
        System.out.println("查询用户");
    }
}

----------------------------------------------------------------------------------------------------------------------------

MyAspect切面类:

/**
 * 切面类
 * 切点 + 增强的结合
 * @author WYC
 *
 */
// 用来定义切面类
@Aspect
public class MyAspect {
    // 对UserDao类的add()方法应用前置增强
    @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
    // JoinPoint表示切点方法的描述,打印结果为:execution(void com.wyc.spring3.demo1.UserDao.add())
    public void before(JoinPoint joinPoing){
        System.out.println("前置增强" + joinPoing);
    }
}

----------------------------------------------------------------------------------------------------------------------------

XML配置文件:

<?xml version="1.0" encoding="UTF-8"?>
    xsi:schemaLocation="
 
    <!-- 开启AspectJ自动代理 -->
    <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
    <aop:aspectj-autoproxy />
 
    <!-- 配置目标类 -->
    <bean id="userDao" class="com.wyc.spring3.demo1.UserDao" />
 
    <!-- 配置增强类 -->
    <bean id="myAspect" class="com.wyc.spring3.demo1.MyAspect" />
 
</beans>

----------------------------------------------------------------------------------------------------------------------------

测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
 
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
 
    @Test
    public void fun1(){
        userDao.add();
        userDao.delete();
        userDao.update();
        userDao.find();
    }
}

——AspectJ的通知类型

1、@Before 前置通知,相当于BeforeAdvice
    在目标方法之前执行,前置通知无法阻止目标对象执行。 
 
2、@AfterReturning 后置通知,相当于AfterReturningAdvice
    后置通知可以获得方法的返回值。 
 
3、@Around 环绕通知,相当于MethodInterceptor
    可以在方法执行前后执行增强方法,环绕通知可以阻止目标方法执行,可以得到目标方法的返回值。 
 
4、@AfterThrowing抛出通知,相当于ThrowAdvice
    当抛出异常时,会执行该通知。
 
5、@After 最终final通知,不管是否异常,该通知都会执行
 
6、@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)

示例代码:

UserDao目标类:

public class UserDao {
    public void add() {
        System.out.println("添加用户");
    }
 
    public int update() {
        System.out.println("修改用户");
        return 123;
    }
 
    public void delete() {
        System.out.println("删除用户");
    }
 
    public void find() {
        System.out.println("查询用户");
        throw new RuntimeException("哈哈哈");
    }
}

----------------------------------------------------------------------------------------------------------------------------

MyAspect切面类:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
 
/**
 * 切面类
 * 切点 + 增强的结合
 * @author WYC
 *
 */
// 用来定义切面类
@Aspect
public class MyAspect {
    // 对UserDao类的add()方法应用前置增强
    @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
    public void before(JoinPoint joinPoing){
        System.out.println("前置增强..." + joinPoing);
    }
 
 
    // 后置通知
    @AfterReturning(value="execution(* com.wyc.spring3.demo1.UserDao.update(..))", returning="returnVal")
    // 参数名必须和注解中的配置相同,returnVal会得到方法执行后的返回值
    public void afterReturing(Object returnVal){
        System.out.println("后置增强... " + returnVal);
    }
 
 
    // 环绕通知
    @Around(value = "execution(* com.wyc.spring3.demo1.UserDao.find(..))")
    // ProceedingJoinPoint,获得切点方法
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕前增强...");
 
        // 调用目标方法,该方法会返回目标方法的返回值
        Object obj = proceedingJoinPoint.proceed();
        System.out.println("环绕后增强...");
        return obj;
    }
 
 
    // 异常通知,throwing="e"表示获取一场对象,e表示异常对象的名称
    @AfterThrowing(value="execution(* com.wyc.spring3.demo1.UserDao.find())", throwing="e")
    public void afterThrowing(Throwable e){
        // 显示异常信息
        System.out.println("抛出异常... " + e.getMessage());
    }
 
 
    // 最终通知
    @After(value="execution(* com.wyc.spring3.demo1.UserDao.*())")
    public void after(){
        System.out.println("最终通知....");
    }

}

----------------------------------------------------------------------------------------------------------------------------

XML配置文件:

<?xml version="1.0" encoding="UTF-8"?>
    xsi:schemaLocation="
 
    <!-- 开启AspectJ自动代理 -->
    <!-- 底层基于AnnotationAwareAspectJAutoProxyCreator,如果存在注解,则自动生成代理 -->
    <aop:aspectj-autoproxy />
 
    <!-- 配置目标类 -->
    <bean id="userDao" class="com.wyc.spring3.demo1.UserDao" />
    <!-- 配置增强类 -->
    <bean id="myAspect" class="com.wyc.spring3.demo1.MyAspect" />
 
</beans>

---------------------------------------------------------------------------------------------------------------------------- 

测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
 
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
 
    @Test
    public void fun1(){
        userDao.add();
        userDao.delete();
        userDao.update();
        userDao.find();
    }
}

---------------------------------------------------------------------------------------------------------------------------- 

打印结果:

前置增强...execution(void com.wyc.spring3.demo1.UserDao.add())
添加用户
最终通知....
删除用户
最终通知....
修改用户
最终通知....
后置增强... 123
环绕前增强...
查询用户
最终通知....
抛出异常... 哈哈哈

——切点的注解

定义一个方法,添加注解表达式,然后在其他方法的注解中使用:类名.方法  即可。

示例代码:

/**
 * 切面类
 * 切点 + 增强的结合
 * @author WYC
 *
 */
// 用来定义切面类
@Aspect
public class MyAspect {
    // 对UserDao类的add()方法应用前置增强
    @Before(value = "execution(* com.wyc.spring3.demo1.UserDao.add())")
    public void before(JoinPoint joinPoing){
        System.out.println("前置增强..." + joinPoing);
    }
 
    // 后置通知
    @AfterReturning(value="execution(* com.wyc.spring3.demo1.UserDao.update(..))", returning="returnVal")
    // 参数名必须和注解中的配置相同
    public void afterReturing(Object returnVal){
        System.out.println("后置增强... " + returnVal);
    }
 
    // 环绕通知
    @Around(value = "MyAspect.myPointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕前增强...");
 
        // 调用目标方法
        Object obj = proceedingJoinPoint.proceed();
        System.out.println("环绕后增强...");
        return obj;
    }
 
    // 异常通知
    @AfterThrowing(value="MyAspect.myPointcut()", throwing="e")
    public void afterThrowing(Throwable e){
        // 显示异常信息
        System.out.println("抛出异常... " + e.getMessage());
    }
 
    // 最终通知
    @After("MyAspect.myPointcut()")
    public void after(){
        System.out.println("最终通知....");
    }
 
    /*
     * 定义一个通用的表达式
     */
    @Pointcut("execution(* com.wyc.spring3.demo1.UserDao.find())")
    private void myPointcut(){
 
    }
}

——Advisor和Aspect的区别

1、Advisor:Spring传统意义上的切面,支持一个切点和一个通知的组合。
2、Aspect:可以支持多个切点和多个通知的组合。 

——基于XML的开发

1、编写被增强的类:

    *   ProductDao

2、定义切面

3、配置applicationContext.xml
    1)前置通知:
        代码:
            public void before(){
                System.out.println("前置通知...");
            }
        配置:

            <!-- 定义AOP配置 -->
            <aop:config>
                <!-- 定义切点 -->
                <!-- 前置通知 -->
                <!-- 给指定表达式起名 -->
                <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.add(..))" id="beforePointcut"/>
 
                <!-- 定义切面 -->
                <aop:aspect ref="myAspectXML">
                    <!-- 给切面类的指定方法添加表达式 -->
                    <!-- 前置通知 -->
                    <aop:before method="before" pointcut-ref="beforePointcut"/>
                </aop:aspect>
            </aop:config>

2)后置通知
        代码:
            public void afterReturning(Object returnVal){

                System.out.println("后置增强... " + returnVal);
            }

配置:
            <!-- 定义AOP配置 -->

            <aop:config>
                <!-- 定义切点 -->
                <!-- 给指定表达式起名 -->
                <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.find(..))" id="afterReturning"/>

 
                <!-- 定义切面 -->
                <aop:aspect ref="myAspectXML">
                    <!-- 给切面类的指定方法添加表达式 -->
                    <!-- 后置通知 -->
                    <aop:after-returning method="afterReturning" pointcut-ref="afterReturning" returning="returnVal"/>
                </aop:aspect>
            </aop:config>

3)环绕通知
        代码:
            public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{

                System.out.println("环绕前增强...");
                Object result = proceedingJoinPoint.proceed();
                System.out.println(result);
                System.out.println("环绕后增强...");
                return result;
            }

配置:
            <!-- 定义AOP配置 -->

            <aop:config>
                <!-- 定义切点 -->
                <!-- 给指定表达式起名 -->
                <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.delete(..))" id="myPointcut"/>

 
                <!-- 定义切面 -->
                <aop:aspect ref="myAspectXML">
                    <!-- 给切面类的指定方法添加表达式 -->
                    <!-- 环绕通知 -->
                    <aop:around method="around" pointcut-ref="myPointcut"/>
                </aop:aspect>
            </aop:config>

4)异常通知
        代码:
            public void afterThrowing(Throwable e){

                System.out.println("异常通知..." + e.getMessage());
            }

配置:
            <!-- 定义AOP配置 -->

            <aop:config>
                <!-- 定义切点 -->
                <!-- 给指定表达式起名 -->
                <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.update(..))" id="afterThrowing"/>

 
                <!-- 定义切面 -->
                <aop:aspect ref="myAspectXML">
                    <!-- 异常通知 -->
                    <aop:after-throwing method="afterThrowing" pointcut-ref="afterThrowing" throwing="e"/>
                </aop:aspect>
            </aop:config>

5)最终通知
        代码:
            public void after(){

                System.out.println("最终通知...");
            }
 

配置:
            <aop:config>

                <!-- 定义切点 -->
                <!-- 给指定表达式起名 -->
                <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.update(..))" id="afterThrowing"/>

 
                <!-- 定义切面 -->
                <aop:aspect ref="myAspectXML">
                    <!-- 最终通知 -->
                    <aop:after method="after" pointcut-ref="afterThrowing"/>
                </aop:aspect>
            </aop:config>

4、示例代码

ProductDao目标类:

public class ProductDao {
    public void add() {
    System.out.println("添加商品");
}
 
public void update() {
    System.out.println("修改商品");
    // throw new RuntimeException("哈哈哈哈哈");
}
 
public Object delete() {
    System.out.println("删除商品");
    return "删除成功";
}
 
public int find() {
    System.out.println("查询商品");
    return 123;
}

------------------------------------------------------------------------------------------------------------------

MyAspectXML切面类:

import org.aspectj.lang.ProceedingJoinPoint;
 
/**
 * 切面类
 * 
 * @author WYC
 * 
 */
public class MyAspectXML {
    // 前置通知
    public void before() {
        System.out.println("前置增强...");
    }
 
    // 后置通知
    public void afterReturning(Object returnVal) {
        System.out.println("后置增强... " + returnVal);
    }
 
    // 环绕通知
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前增强...");
        Object result = proceedingJoinPoint.proceed();
        System.out.println(result);
        System.out.println("环绕后增强...");
        return result;
    }
 
    // 异常通知
    public void afterThrowing(Throwable e) {
        System.out.println("异常通知..." + e.getMessage());
    }
 
    // 最终通知
    public void after() {
        System.out.println("最终通知...");
    }
}

------------------------------------------------------------------------------------------------------------------

applicationContext.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
    xsi:schemaLocation="
 
    <!-- 定义目标类 -->
    <bean id="productDao" class="com.wyc.spring3.demo2.ProductDao" />
 
    <!-- 定义切面类 -->
    <bean id="myAspectXML" class="com.wyc.spring3.demo2.MyAspectXML" />
 
    <!-- 定义AOP配置 -->
    <aop:config>
        <!-- 定义切点 -->
        <!-- 前置通知 -->
        <!-- 给指定表达式起名 -->
        <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.add(..))" id="beforePointcut"/>
        <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.*(..))" id="afterReturning"/>
        <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.delete(..))" id="myPointcut"/>
        <aop:pointcut expression="execution(* com.wyc.spring3.demo2.ProductDao.update(..))" id="afterThrowing"/>
 
        <!-- 定义切面 -->
        <aop:aspect ref="myAspectXML">
            <!-- 给切面类的指定方法添加表达式 -->
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="beforePointcut"/>
 
            <!-- 后置通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="afterReturning" returning="returnVal"/>
 
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="myPointcut"/>
 
            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="afterThrowing" throwing="e"/>
 
            <!-- 最终通知 -->
            <aop:after method="after" pointcut-ref="afterThrowing"/>
        </aop:aspect>
    </aop:config>
 
</beans>

------------------------------------------------------------------------------------------------------------------

测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringDemo2 {
  @Autowired
  @Qualifier("productDao")
  private ProductDao productDao;
 
  @Test
  public void fun1() {
    productDao.add();
    productDao.update();
    productDao.delete();
    productDao.find();
  }
}

——总结

为了简化开发,引入了AspectJ的支持。

1、基于注解开发
    开启XML中支持AOP的配置。

    @Aspect:切面注解

    @Before:前置通知
    @Around:环绕通知
    @AfterReturing:后置通知
    @AfterThrowing:抛出异常通知
    @After:最终通知

    @Pointcut:切点注解
        可以简化开发,不需要每一个注解都写表达式。

2、基于XML开发
    1)引入AOP的名称空间。
    2)<aop-config>
            <!-- 定义切点 -->
            <aop:pointcut id="" expression="" />

            <!-- 定义切面,并引入切点 -->
            <aop:aspect ref="">
                <aop:brfore />
            </aop:aspect>
          </aop-config>

Spring之AspectJ的更多相关文章

  1. Spring AOP + AspectJ annotation example

    In this tutorial, we show you how to integrate AspectJ annotation with Spring AOP framework. In simp ...

  2. Spring AOP + AspectJ Annotation Example---reference

    In this tutorial, we show you how to integrate AspectJ annotation with Spring AOP framework. In simp ...

  3. Spring 使用AspectJ的三种方式

    Spring 使用AspectJ 的三种方式 一,使用JavaConfig 二,使用注解隐式配置 三,使用XML 配置 背景知识: 注意 使用AspectJ 的 时候 要导入相应的Jar 包 嗯 昨天 ...

  4. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  5. 学习 Spring (十七) Spring 对 AspectJ 的支持 (完结)

    Spring入门篇 学习笔记 @AspectJ 的风格类似纯 java 注解的普通 java 类 Spring 可以使用 AspectJ 来做切入点解析 AOP 的运行时仍旧是纯的 Spring AO ...

  6. 关于 Spring AOP (AspectJ) 该知晓的一切

    关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 本篇是年后第一篇博文,由于博主用了不少时间在构思这篇博文,加上 ...

  7. Spring学习(十八)----- Spring AOP+AspectJ注解实例

    我们将向你展示如何将AspectJ注解集成到Spring AOP框架.在这个Spring AOP+ AspectJ 示例中,让您轻松实现拦截方法. 常见AspectJ的注解: @Before – 方法 ...

  8. Spring结合AspectJ的研究

    本文阐述以下内容:1.AspectJ是什么及使用方式2.Spring AOP和AspectJ的区别3.Spring结合AspectJ的使用方法和原理4.Spring注解方式使用AspectJ遇到的问题 ...

  9. 10 Spring框架 AOP (三) Spring对AspectJ的整合

    上两节我们讲了Spring对AOP的实现,但是在我们的开发中我们不太使用Spring自身的对AOP的实现,而是使用AspectJ,AspectJ是一个面向切面的框架,它扩展了Java语言.Aspect ...

  10. Spring整合AspectJ的AOP

    学而时习之,不亦说乎!                              --<论语> 看这一篇之前最好先看前面关于AOP的两篇. http://www.cnblogs.com/z ...

随机推荐

  1. 【LeetCode】242. 有效的字母异位词

    242. 有效的字母异位词 知识点:字符串:哈希表 题目描述 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词. 注意:若 s 和 t 中每个字符出现的次数都相同,则称  ...

  2. 前端开发入门到进阶第一集【使用sublime快速编写Html和Css】

    1,安装sublime编辑器,下载地址:http://www.sublimetext.com/3 2,要使用sublime的插件机制必须安装package control:https://packag ...

  3. zookeeper与eureka比较

    一个分布式系统不可能同时满足C(一致性).A(可用性)和P(分区容错性) zookeeper确保cp 当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接d ...

  4. 开发工具IDE从入门到爱不释手(六)常用插件Git

    Git 环境准备 本地基本操作 本地文件关联git管理 文件提交git Ctrl+K:提交 关联远程仓库 先注册github账号 连接本地 git菜单 撤销提交 追加提交 Code Review

  5. C++第四十九篇 -- 将一个项目Copy到另一台电脑

    最近写项目,由于设备原因,需要将一台电脑的项目拷贝到另一台电脑上,在另一个电脑上继续编写.由于配置属性原因,最终还是在另一台电脑上创建了新项目,只是把这些cpp文件都复制过去了,然后重新部署属性. 背 ...

  6. 深入刨析tomcat 之---第6篇 how tomcat works 第5章 容器实现原理

    writedby 张艳涛

  7. 关于maven打包与jdk版本的一些关系

    最近让不同JAVA版本的容器maven打包折腾的不行,终于理出了一点头绪.在这里记录下备忘. 1. Maven与jdk版本的关系 先明确一个概念,关高版本JDK运行maven,是可以打出低版本的JAV ...

  8. javascript获取焦点对象ID

    document.activeElement 方法:if(document.activeElement.id="textbox1") { }

  9. Tomcat7+ 弱口令 && 后台getshell漏洞

    打开tomcat管理页面http://192.168.49.2:8080/manager/html,输入弱密码tomcat:tomcat,即可访问后台 先将jsp大马压缩为zip,再将zip后缀改名为 ...

  10. js 跨域请求失败

    注:错误返回:Failed to load http://xxxxxxxxxxx: No 'Access-Control-Allow-Origin' header is present on the ...