时间: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. mybatis-6-动态sql

    动态sql简介&OGNL了解 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处 理器相似. MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作. if cho ...

  2. mybatis-3-核心配置文件

    全局配置文件:mybatis-config.xml 1.引入外部配置文件(properties) date.properties外部配置文件 driver = com.mysql.cj.jdbc.Dr ...

  3. PAT乙级:1088 三人行 (20分)

    PAT乙级:1088 三人行 (20分) 题干 子曰:"三人行,必有我师焉.择其善者而从之,其不善者而改之." 本题给定甲.乙.丙三个人的能力值关系为:甲的能力值确定是 2 位正整 ...

  4. ZYNQ FLASH+EMMC手动移植LINUX启动

    前言 虽可使用Petalinux进行移植,简单方便,但为了更清楚明白的了解整个流程,还是尝试了一波手动移植. 参考资料 ZYNQ Linux 移植:包含petalinux移植和手动移植debian9 ...

  5. 锁屏面试题百日百刷-java大厂八股文(day3)

    为了有针对性的准备面试,锁屏面试题百日百刷开始每日从各处收集的面经中选择几道经典面试题分享并给出答案供参考,答案中会做与题目相关的扩展,并且可能会抛出一定问题供思考.这些题目我会标注具体的公司.招聘类 ...

  6. npx的使用方法、场景

    目录 npx使用教程 npm与npx的概念 npx的使用场景(对比npm的一些优势) 使用场景1: 想用项目中已经安装好的某个包, 但是不能直接执行(因为没有全局安装, 涉及环境变量的问题) 使用场景 ...

  7. K8s之Prometheus监控

    目录 容器监控与报警 Prometheus prometheus简介 prometheus系统架构 prometheus 安装方式 容器方式安装prometheus operator部署 克隆项目 创 ...

  8. ZooKeeper(2181、2171) 未授权访问

    下载地址http://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/ tar -xzvf zookeeper-3.4.14.tar.gz cd ...

  9. 【进阶之路】Java的类型擦除式泛型

    Java选择的泛型类型叫做类型擦除式泛型.什么是类型擦除式泛型呢?就是Java语言中的泛型只存在于程序源码之中,在编译后的字节码文件里,则全部泛型都会被替换为原来的原始类型(Raw Type),并且会 ...

  10. 面试官疯狂问我:char和varchar的区别 怎么办?愣着干嘛?进来白嫖啊!

    MySQL的修仙之路,图文谈谈如何学MySQL.如何进阶!(已发布) 面前突击!33道数据库高频面试题,你值得拥有!(已发布) 大家常说的基数是什么?(已发布) 讲讲什么是慢查!如何监控?如何排查?( ...