Spring中AOP容器相关知识详解

什么是AOP

AOP意思是面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,降低各部分的耦合度,提高程序的可重用性,提高开发效率;
通俗来说,AOP指的是不改变源代码,在原有的基础之上可以加新的功能,这过程就叫AOP;

AOP底层原理和实现方式

有接口情况下,通过JDK动态代理

创建接口实现类的代理对象,增强类的方法,使用Proxy类里面的方法创建代理对象,使用newProxyInstance方法返回指定接口的实例,演示如下;
newProxyInstance(ClassLoader loader,类<?>[] interfaces ,InvocationHandler h)
ClassLoader loader : 类加载器;
类<?>[] interfaces : 增强方法所在的类,这个类实现的接口,支持多个接口;
InvocationHandler h : 需要实现这个接口,创建代理对象,写增强方法

public interface UserDao {

    public int add(int a,int b);
    public String update(String id);
}
public class UserDaoImpl implements UserDao {

    @Override
    public int add(int a,int b) {
        return a + b;
    }

    @Override
    public String update(String id) {
        return id;
    }
}
public class JDKProxy {

    public static void main(String[] args) {
        //创建接口实现类代理对象
        Class[] interfaces = {UserDao.class};
        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);
    }
}
//创建代理对象
class UserDaoProxy implements InvocationHandler{

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

    /*增强逻辑写在invoke中*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前执行的代码
        System.out.println("方法之前执行...");
        //被执行的方法也得执行
        Object res = method.invoke(obj, args);
        //方法之后执行的代码
        System.out.println("方法之后执行 ...");
        return res;
    }
}

在这里插入图片描述

没有接口情况下,通过CGLIB动态代理

创建当前类的子类的代理对象,增强类的方法;

AOP术语

  • 连接点

在一个类中,哪些方法可以被增强,这些方法就叫连接点;

  • 切入点

实际真正被增强的方法,被称为切入点;

  • 通知(增强)

实际增强的代码逻辑部分,就是通知;
通知有多种类型:
前置通知:被增强的方法前执行;
后置通知:被增强的方法后执行;
环绕通知:被增强的方法前后都执行;
异常通知:被增强的方法出现异常会执行;
最终通知:类似于finally,永远会执行;

  • 切面

是动作上的操作,把通知应用到切入点的过程,就叫切面;

AspectJ相关

AspectJ本身是一个单独的框架,它不是Spring框架的组成部分,但一般会把它和Spring一起使用,进行AOP操作;
实现方式有两种,一种基于配置文件,一种基于注解;

切入点表达式

execution(权限修饰符 返回类型 类全路径 方法名称 (参数列表))
举例:对com.example.dao.UserDao的add方法进行增强;
execution(* com.example.dao.UserDao.add(. .))
*表示所有权限修饰符
返回类型可以省略
参数列表可以用(. .)表示

案例

<?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.demo"/>
    <!--寻找有@Aspect注解的类,并生成代理对象-->
    <aop:aspectj-autoproxy/>

</beans>
@Component
public class User {
    public void add(){
        System.out.println("被增强的类执行了add ...");
    }
}
//增强类,用来增强User类
@Component
@Aspect   //生成代理对象
public class UserProxy {

    //前置通知
    //加上注解和切入点表达式
    @Before("execution(* com.demo.domain.User.add(..))")
    public void before(){
        System.out.println("前置执行 ..");
    }

    //后置通知
    @After("execution(* com.demo.domain.User.add(..))")
    public void after(){
        System.out.println("后置执行 ..");
    }

    //环绕通知
    @Around("execution(* com.demo.domain.User.add(..))")
    public void around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("环绕前通知 ..");
        //表示执行被增强的方法
        point.proceed();
        System.out.println("环绕后通知 ..");
    }

    //异常通知
    @AfterThrowing("execution(* com.demo.domain.User.add(..))")
    public void afterThrowing(){
        System.out.println("异常通知 ..");
    }

    //最终通知
    @AfterReturning("execution(* com.demo.domain.User.add(..))")
    public void afterReturning(){
        System.out.println("最终通知 ..");
    }
}
	//测试类
    @Test
    public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        User user = context.getBean("user", User.class);
        user.add();
    }

在这里插入图片描述

相同切入点表达式抽取

上面的案例中,切入点表达式都是相同的,现在对相同切入点表达式进行抽取;

    //抽取相同切入点表达式
    @Pointcut("execution(* com.demo.domain.User.add(..))")
    public void pointExpression(){}
    
    @Before("pointExpression()")
    public void before(){
        System.out.println("前置执行 ..");
    }

多个增强类情况下

多个增强类对同一个方法进行增强,可以设置他们的优先级;
使用@Order注解,再给一个值,值越小,优先级越高;

@Component
@Aspect
@Order(1) //设置优先级,值越小,优先级越高
public class AnotherPoxy {

    @Before("execution(* com.demo.domain.User.add(..))")
    public void before(){
        System.out.println("AnotherPoxy前置执行 ..");
    }
}
@Component
@Aspect
@Order(2)
public class UserProxy {

    @Before("execution(* com.demo.domain.User.add(..))")
    public void before(){
        System.out.println("UserProxy前置执行 ..");
    }
}

public class TestDemo {

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

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值