【Spring】最核心的两大组件 IOC/AOP 细讲之AOP部分

Spring框架核心细讲 AOP部分



前言

此次是对于自己所学Spring中的一些重要知识的回顾与知新, 从浅入深,从使用Spring的好处到核心底层


一、什么是AOP

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

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

(3)使用登录例子说明 AOP

在这里插入图片描述

  • 如果在登录时,输入了用户名和密码错误,就会退回到l登录页面,
  • 如果成功,肯定也不可能直接说直接到功能页面,肯定是需要有个权限模块的方法进行判断,根据权限大小选择不同的主页面呈现
  • 此时的权限模块肯定不能以硬编码的形式呈现在代码里,那AOP就没有意义了此时,如果别的地方也要用到这个模块,则无法复用,而且会导致代码里会有很多冗余重复的代码。
  • 这就是AOP以及代理出现的原因,实现解耦以及重复代码抽取,以及复用

二.AOP(底层原理)

AOP底层使用了动态代理的方式来实现

2.1 JKD代理

我觉得直接上代码干脆
接口

// 接口
interface MyProxy {
    void eat(String food);

    void drink(String water);
}

被代理类

class MyClass1 implements MyProxy {
    @Override
    public void eat(String food) {
        System.out.println("吃 " + food);
    }

    @Override
    public void drink(String water) {
        System.out.println("喝 " + water);
    }
}

实现动态代理需要实现的接口,用来执行对应的方法

class Handler implements InvocationHandler {
    private Object obj;

    public Handler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodUtils methodUtils = new MethodUtils();

        methodUtils.method1();

        Object invoke = method.invoke(obj, args);

        methodUtils.method2();
        return invoke;
    }
}

测试类

public class ProxyTest {
    public static void main(String[] args) {
        Class[] interfaces = {MyProxy.class};
        MyClass1 myClass1 = new MyClass1();
        Handler handler = new Handler(myClass1);
        MyProxy my = (MyProxy) Proxy.newProxyInstance(MyClass1.class.getClassLoader(), interfaces, handler);

        my.eat("水果");
        my.drink("饮料");
    }
}

如果你看了代码还是一头雾水,那我只能说是正常的,这里算是有点比较难理解的

可以看下这篇,将的还是很详细的
对于这篇文章中获取代理对象的流程 代理类生成的是Class<?> 学过反射的应该都知道这是所有类型类的父类
在这里插入图片描述
这是文章中对于几个常见问题的解释

2.1.1 为什么需要创建一个接口来实现

代理类继承了Proxy类,其主要目的是为了传递InvocationHandler。这里补充一点,因为代理类已经继承了Proxy,java是不可以多继承的。

2.1.2 为什么代理类可以直接强转成接口

代理类实现了被代理的接口Foo,这也是为什么代理类可以直接强转成接口的原因。

2.1.3 为什么调用代理类中的方法时,总会分派到InvocationHandler中的invoke方法

有一个公开的构造函数,参数为指定的InvocationHandler,并将参数传递到父类Proxy中。
每一个实现的方法,都会调用InvocationHandler中的invoke方法,并将代理类本身、Method实例、入参三个参数进行传递。这也是为什么调用代理类中的方法时,总会分派到InvocationHandler中的invoke方法的原因。

2.2 CGLIB代理

这个代理与JDK代理不一样的是,jdk代理必须有接口实现才行,而且被代理类中需要加强的方法必须在类中声明才行,否则代理失败,
而该代理,则是创建子类的代理对象,增强类的方法
本文不多赘述
感兴趣可以看此篇

三、AOP术语

这里也是大家需要知道的,方便后续的理解

3.1 连接点

类中需要增强的方法 —> 初步决定需要的

3.2 切入点

实际被真正增强的方法 —> 最后决定增强的

3.3 通知(增强)

实际增强的逻辑部分 —> 增强的实现

3.4 通知

前置通知:before 增强方法执行前
后置通知:AfterReturning 增强方法执行返回值后执行
环绕通知:around 方法执行前后
异常通知:afterThrow 方法执行出现异常
最终通知:after 方法执行后通知

注意点:
增强方法除了异常 除了afterReturning不执行 都执行
around环绕通知需要添加 ProceedingJoinPoint proceedingJoinPoint形参 并执行增强方法 proceedingJoinPoint.proceed(); ------> 不执行 before和增强方法都不会有执行结果

3.4 切入点表达式

提取公共切入点表达式
切入点表达式

  1. 语法 execution(访问修饰符 返回值类型 类全路径.需要增强的方法(…)) ----> 可以使用通配符 * …代表形参
  2. 作用:知道具体对哪个类的哪个方法进行增强
    // 相同切入点抽取
@Pointcut(value = "execution(* com.worker.aopanno.User.add(..))")
public void pointdemo(){

}

3.5 切面

把通知应用到切入点的过程

四、SpringAop实现

注意:

  1. 需要在增强类上面添加@Aspect注解 生成代理对象
  2. 多个增强类对同一个方法增强 可以使用@Order(n) 设置增强类优先级 n越小优先级越高

4.0 需要增强的类

@Component
public class User {

    public void add() {
        System.out.println("add ......");
    }
}

4.1 半注解+半xml

代码实现(注解实现):

4.2 xml配置文件

<!--引入aop context命名空间--> 只要有xml配置都需要配置上


<!--这几行就是引入aop context命名空间-->
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.worker"></context:component-scan>

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

java代码

// 相同切入点抽取
@Pointcut(value = "execution(* com.worker.aopanno.User.add(..))")
public void pointdemo(){

}


// 前置通知 增强类作为通知方法上添加通知类型
@Before(value = "pointdemo()")
public void before(){
    System.out.println("before ......");
}

// 最终通知 方法执行后通知
@After(value = "pointdemo()")
public void after(){
    System.out.println("after ......");
}

// 后置通知/返回通知 方法执行返回值后执行 出了异常不执行 其它通知都执行
@AfterReturning(value = "pointdemo())")
public void afterReturning(){
    System.out.println("AfterReturning ......");
}

// 增强方法出了异常执行
@AfterThrowing(value = "pointdemo()")
public void afterThrowing(){
    System.out.println("AfterThrowing ......");
}

@Around(value = "pointdemo()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("around之前 ......");

    // 被增强的方法执行
    proceedingJoinPoint.proceed();

    System.out.println("around之后 ......");
}

4.3 完全xml配置文件实现


<!--创建对象-->
    <bean id="person" class="com.worker.aopxml.Person"></bean>
    <bean id="personProxy" class="com.worker.aopxml.PersonProxy"></bean>

    <!--配置文件aop增强-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* com.worker.aopxml.Person.add(..))"/>
        <!--切入面-->
        <aop:aspect ref="personProxy">
            <!--增强作用在具体方法上-->
            <aop:before method="before" pointcut-ref="p"></aop:before>
            <aop:after method="after" pointcut-ref="p"></aop:after>
            <aop:around method="around" pointcut-ref="p"></aop:around>
            <aop:after-throwing method="afterThrow" pointcut-ref="p"></aop:after-throwing>
            <aop:after-returning method="afterruning" pointcut-ref="p"></aop:after-returning>
        </aop:aspect>
    </aop:config>

代码实现

public void before(){
    System.out.println("before ......");
}

public void after(){
    System.out.println("after ......");
}

public void around(){
    System.out.println("around ......");
}

public void afterruning(){
    System.out.println("afterruning ......");
}

public void afterThrow(){
    System.out.println("afterThrow ......");
}

4.4 测试类


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

@Test
public void testAopXml(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("bean2.xml");
    Person person = context.getBean("person", Person.class);
    person.add();
}

4.5 完全注解开发实现

config配置类

@Configuration /* 让当前类作为Spring的配置类 */
@ComponentScan(basePackages = "com.worker") /* 开启注解扫描 */
@EnableAspectJAutoProxy(proxyTargetClass = true) /* 开启AspectJ生成代理对象  为true 是选择使用什么动态代理方式 cglib代理或jdk代理*/
public class MyConfig {
}

总结

以上就是今天要讲的内容,本文仅仅简单介绍了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值