目录

1、动态代理

1.1、jdk动态代理

1.2、cglib动态代理

1.3、动态代理的好处

2、什么是AOP

2.1、AOP常用术语

2.2、切面的构成

3、使用aspectJ框架实现AOP

3.1、aspectJ简介

声明实现类ServiceImpl

声明切面

3.3、@AfterReturning后置通知

切面类代码

3.4、@Around环绕通知(功能最强的通知)


1、动态代理

1.1、jdk动态代理

使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。 jdk动态代理要求目标类必须实现接口,关于细节本文就不赘述了。
要求:

1.2、cglib动态代理

第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。 要求目标类不能是final的,方法也不能是final的

1.3、动态代理的好处

2、什么是AOP

面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了, 让开发人员用一种统一的方式,使用动态代理实现。

2.1、AOP常用术语

2.2、切面的构成

切面就是要给别的方法进行增强的方法,一个切面有以下三个要素。

3、使用aspectJ框架实现AOP

3.1、aspectJ简介

aspectJ是一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。aspectJ框架实现aop有两种方式:

    <!--aspectJ依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

3.2 @Before前置通知
前置通知注解修饰的切面在连接点方法之前执行。下面通过一段代码体验一下。

public interface IService {
    void doSome(String name, int age);
}

声明实现类ServiceImpl

public class ServiceImpl implements IService {
    @Override
    public void doSome(String name, int age) {
        System.out.println("===doSome()===");
    }
}

声明切面

@Aspect
public class MyAspectJ {

    /**
     * 定义功能增强方法(方法就是切面)
     * 1、方法的必须为public
     * 2、方法无返回值
     * 3、方法名称自定义
     * 4、方法可以有参数,也可以没有参数
     * 5、方法的定义上方加入注解,表示切入点的执行时机

    @Before(value = "execution(public void com.mms.ba01.ServiceImpl.doSome(String,int))")
    public void beforeLog() {
        System.out.println("前置通知->系统当前时间:" + new Date());
    }
    */

    /*
        前置通知,带方法参数的切面
        切面方法有参数时要求参数是JoinPoint类型,参数名自定义,该参数就代表了连接点方法,即doSome方法
        使用该参数可以获取切入点表达式、切入点方法签名、目标对象等
     */
    @Before(value = "execution(* *..ServiceImpl.doSome(..))")
    public void beforeLog(JoinPoint jp) {
        System.out.println("连接点方法的方法签名="+jp.getSignature());
        System.out.println("连接点方法的方法名="+jp.getSignature().getName());
        //获取连接点方法参数
        Object[] args = jp.getArgs();
        for (Object arg : args) {
            System.out.println("arg="+arg);
        }
    }
}

测试

public class MyTest {
    //aop前置通知
    @Test
    public void test01() {
        String config = "ba01/applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //获取目标对象,此时的service就是spring生成的代理对象
        //注意返回值类型是接口类型,不能是实现类接口,否则报错
        IService service = (IService) ac.getBean("service");
        //使用代理对象执行方法
        service.doSome("张三",23);
    }
}

3.3、@AfterReturning后置通知

Student doStudent(Student student);
    @Override
    public Student doStudent(Student student) {
        return student;
    }

切面类代码

@Aspect
public class MyAspectJ {
    /**
     * @AfterReturning: 后置通知,在连接点方法执行之后执行后置通知方法
     * 方法定义格式:
     * 1、公共方法
     * 2、没有返回值
     * 3、方法名称自定义
     * 4、与前置通知一样,可以有JoinPoint类型参数,该参数表示连接点方法对象;还可以有一个
     *    Object类型参数,用于接收连接点方法的执行结果,注意该参数的参数名必须与切入点表达式
     *    的returning属性的属性值一致,表示将returning属性值赋给Object对象
     */
    /*@AfterReturning(value = "execution(* *..ServiceImpl.doOther(..))", returning = "obj")
        public void afterTransaction(JoinPoint jp, Object obj) {
        System.out.println("doOther方法的返回参数="+obj);
        System.out.println("事务已提交...");
        经过验证:在后置通知切面内不能改变连接点方法的返回值
    }*/

    @AfterReturning(value = "execution(* *..ServiceImpl.doStudent(..))", returning = "obj")
    public void afterTransaction(JoinPoint jp, Object obj) {
        System.out.println(obj);
        Student student = new Student();
        student.setName("李四");
        student.setAge(24);
        obj = student;
        System.out.println("===查看是否改变了连接点方法的返回值==="+obj);
        /*
            经过验证:在后置通知切面内不能改变连接点方法的返回值
         */
    }
}

3.4、@Around环绕通知(功能最强的通知)

环绕通知是功能最强的通知,它的本质就是jdk动态代理,他可以在连接点方法之前和之后都可以执行,最厉害的是他可以改变连接点方法的执行结果(返回结果)。还是拿上面的doStudent(Student student)方法来说明,经过验证前置通知和后置通知都不能改变doStudent(Student student)方法的返回值。下面看一下环绕通知是如何做的。

@Aspect
public class MyAspectJ {

    /*
        环绕通知:@Around(切入点表达式)
        1、环绕通知是最重要的一个通知,他表示在连接点方法的前或者后都可以执行,它的本质就是jdk动态代理的invoke
           方法的method参数
        2、定义格式
            a、public
            b、必须有返回值,类型为Object

     */
    @Around(value = "pointCut()")

    /*
        再次回忆一下jdk动态代理的invoke方法的定义
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        是不是感觉与下面的方法定义眼熟啊,没错,环绕通知切面的定义实质上就是jdk动态代理
     */
    public Object around(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("环绕通知在连接点方法之前执行了...");
        Object result = null;
        result = pj.proceed();
        Student student = new Student();
        student.setName("李四");
        student.setAge(24);
        //改变连接点方法返回值
        result = student;
        System.out.println("事务已提交...");
        return result;
    }

    /*
        使用pointcut管理切面表达式
        1、在一个切面类中,若多个切面的切面表达式均为同一个,每次都要写重复的代码,此时就可以使用pointcut来
          管理切面表达式了
        2、定义格式:
            公共public
            无返回值
            无参数
     */
    @Pointcut(value = "execution(* *.doStudent(..))")
    public void pointCut() {
        //空方法体
    }
}
03-01 02:12