Spring(五)-AOP

一、AOP简介

1.1、什么是 AOP

1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

3)使用例子说明 AOP

image

二、AOP原理说明

2.1、JDK动态代理原理

1)创建接口,定义方法

package com.dianchou.spring.proxy;

public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}

2)创建接口实现类,实现方法

package com.dianchou.spring.proxy;

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("add方法执行了.....");
        return a+b;
    }

    @Override
    public String update(String id) {
        System.out.println("update方法执行了.....");
        return id;
    }
}

3)使用 Proxy 类创建接口代理对象

package com.dianchou.spring.proxy;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class JDKProxy {

    public static void main(String[] args) {

        //增强方法所在的类,这个类实现的接口,支持多个接口
        Class[] interfaces = {UserDao.class};
        //创建接口实现类代理对象
        //可以创建匿名内部类
        /**
         * Proxy.newProxyInstance方法有三个参数:
         * 第一参数,类加载器
         * 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
         * 第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
         */
//        Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                return null;
//            }
//        });
        UserDaoImpl userDao = new UserDaoImpl();
        //创建接口实现类代理对象
        UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int result = dao.add(1, 2);
        System.out.println("result:"+result);
    }
}

//创建代理对象代码,实现InvocationHandler接口
class UserDaoProxy implements InvocationHandler {

    //把创建的是谁的代理对象,把谁传递过来
    //有参数构造传递
    private Object obj;
    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    //增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("方法之前执行...."+method.getName()+" :传递的参数..."+ Arrays.toString(args));

        //被增强的方法执行
        Object res = method.invoke(obj, args);

        //方法之后
        System.out.println("方法之后执行...."+obj);
        return res;
    }
}

三、AOP相关术语

3.1、连接点

类里面那些方法可以被增强,这些方法叫做连接点

3.2、切入点

实际被真正增强的方法,叫做切入点

3.3、通知

实际被增强的逻辑部分叫做通知

通知的种类

  1. 前置通知
  2. 后置通知
  3. 环绕通知
  4. 异常通知
  5. 最终通知

3.4、切面

把通知应用到切入点过程

四、AOP操作

4.1、准备工作

1)AspectJ 介绍

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作

基于 AspectJ 实现 AOP 操作

(1)基于 xml 配置文件实现

(2)基于注解方式实现(使用)

2)引入AOP相关依赖

image

3)切入点表达式介绍

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )

举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.atguigu.dao.BookDao.add(..))

举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))

举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))

4.2、AOP操作(基于注解)

1)创建被增强的类,并定义方法

package com.dianchou.spring.aop;

import org.springframework.stereotype.Component;

/**
 * @author lawrence
 * @create 2020-07-08 14:24
 */

//被增强的类
@Component
public class User {
    public void add(){
//        int i = 10 / 0;
        System.out.println("User add ...");
    }
}

2)创建增强类(编写增强逻辑)

package com.dianchou.spring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author lawrence
 * @create 2020-07-08 14:25
 */

//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {

    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "execution(* com.dianchou.spring.aop.User.add())")
    public void before(){
        System.out.println("UserProxy before ...");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.dianchou.spring.aop.User.add())")
    public void afterReturning(){
        System.out.println("UserProxy afterReturning ...");
    }

    //最终通知
    @After(value = "execution(* com.dianchou.spring.aop.User.add())")
    public void after(){
        System.out.println("UserProxy after ...");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.dianchou.spring.aop.User.add())")
    public void afterThrowing(){
        System.out.println("UserProxy afterThrowing ...");
    }

    //环绕通知
    @Around(value = "execution(* com.dianchou.spring.aop.User.add())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("UserProxy around 之前...");
        proceedingJoinPoint.proceed();
        System.out.println("UserProxy around 之后...");
    }


}

3)配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.dianchou.spring.aop"></context:component-scan>

    <!-- 开启 Aspect 生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

4)测试

@Test
public void testAOP(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    User user = context.getBean("user", User.class);
    user.add();
}
image

当出现异常时:

image

4.3、相关细节

1)相同的切入点抽取

package com.dianchou.spring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author lawrence
 * @create 2020-07-08 14:25
 */

//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {

    @Pointcut(value = "execution(* com.dianchou.spring.aop.User.add())")
    public void pointdemo(){}

    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "pointdemo()")
    public void before(){
        System.out.println("UserProxy before ...");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "pointdemo()")
    public void afterReturning(){
        System.out.println("UserProxy afterReturning ...");
    }

    //最终通知
    @After(value = "pointdemo()")
    public void after(){
        System.out.println("UserProxy after ...");
    }

    //异常通知
    @AfterThrowing(value = "pointdemo()")
    public void afterThrowing(){
        System.out.println("UserProxy afterThrowing ...");
    }

    //环绕通知
    @Around(value = "pointdemo()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("UserProxy around 之前...");
        proceedingJoinPoint.proceed();
        System.out.println("UserProxy around 之后...");
    }


}

2)有多个增强类多同一个方法进行增强,设置增强类优先级

在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高

@Component
@Aspect
@Order(1)
public class PersonProxy

4.4、完全使用注解开发

1)创建配置类,不需要创建xml配置文件

@Configuration
@ComponentScan(basePackages = {"com.dianchou.spring.aop"})
@EnableAspectJAutoProxy
public class Config {
}

2)测试

@Test
public void testAOP2(){
    ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    User user = context.getBean("user", User.class);
    user.add();
}

4.5、AOP操作(基于配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--创建bean-->
    <bean id="user" class="com.dianchou.spring.xml.User"></bean>
    <bean id="userProxy" class="com.dianchou.spring.xml.UserProxy"></bean>

    <!--配置aop增强-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="p" expression="execution(* com.dianchou.spring.xml.User.add())"/>
        <!--配置切面-->
        <aop:aspect ref="userProxy">
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>
posted @ 2020-07-08 11:28  运维人在路上  阅读(197)  评论(0编辑  收藏  举报