[Spring] 02 AOP 面向切面编程


prtyaa
prtyaa 2023-12-27 15:22:31 66865
分类专栏: 资讯

在学习Spring-AOP之前,我们学习一下一个设计模式--代理模式

为什么学?

因为AOP就是基于代理模式中的动态代理来的---增加了代码的扩展性 使代码之间解耦

代理模式:

代理模式的三个要素

A、抽象的类或者接口 ---完成一件怎样的事情

B、被代理对象---事情操作具体内容

C、代理对象----帮助我们完成事情的同是可以增加其他的功能

举个例子?

房屋租赁

A、抽象的类或者接口--租房子

B、被代理对象---房东

C、代理对象---中介

代理模式的好处

静态代理模式:

1.定义抽象类或接口

2.被代理对象 实现接口

3.代理对象 实现接口

4.调用代理对象,实现功能

5.实现结果

JDK代理模式:

接口

/**
 * 定于需要完成的事情 -- 租房
 */
public interface LetRoom {

    public void tenement();
}

被代理对象

/**
 * 房东
 */
public class Landlord implements LetRoom{
    @Override
    public void tenement() {
        System.out.println("--我要出租江景房5000元--");
    }
}

代理对象

/**
 * 在这个类中书写动态产生代理对象的方法
 */
public class MyInv implements InvocationHandler {

   private LetRoom lr;

    public void setLr(LetRoom lr) {
        this.lr = lr;
    }

    /**
     * 执行该方法就会产生动态代理对象
     * @return
     */
    public Object getProxy(){
        /**
         * 参数1 保证类型是ClassLoader
         * 参数2 接口的Class数组
         * 参数3 参数类型是InvocationHandler即可
         * 运行后会产生中介
         */
        Object o = Proxy.newProxyInstance(MyInv.class.getClassLoader(), new Class[]{LetRoom.class}, this);
        return o;
    }

    /**
     * 这个方法类似于书写中介添加的方法
     * @param proxy  代理对象
     * @param method 调用方法
     * @param args   参数 null
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-- 推荐费用50元 --");

        //使用反射调用方法  指定房东的方法租房
        Object invoke = method.invoke(lr, args);

        System.out.println("-- 管理费500元 --");

        return invoke;
    }
}

测试类

package com.lin.proxy2;

public class Test {

    public static void main(String[] args) {
        MyInv myInv = new MyInv();

        //设置具体的房东 设置被代理对象
        myInv.setLr(new Landlord());

        //产生中介代理对象
        LetRoom proxy = (LetRoom) myInv.getProxy();

        proxy.tenement();

    }
}

CGLIB代理模式-需要导包

使用cglib动态代理需要引入两个jar包cglib.jar, asm.jar

 

产生的代理对象和真实对象之间的关系: 父子类型关系.

代理对象是真实对象的子对象.

导包

被代理对象

/**
 * 房东-被代理对象
 */
public class Landlord  {

    public void tenement() {
        System.out.println("--我要出租江景房5000元--");
    }
}

设置动态代理

package com.lin.proxy3;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Methodinv implements MethodInterceptor {

    //产生代理类对象
    public Object getProxy(){
        Enhancer en = new Enhancer();
        //设置被代理对象 - 类似于接口
        en.setSuperclass(Landlord.class);
        //回调
        en.setCallback(this);
        //使整个设置生效
        Object o = en.create();
        //动态代理的中介
        return o;
    }

    /**
     *
     * @param o  被代理对象
     * @param method  被代理对象的方法
     * @param objects 参数
     * @param methodProxy 代理对象中方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("-- 推荐费用50元 --");

        //调用正真房东的租房
        Object o1 = methodProxy.invokeSuper(o, objects);

        System.out.println("-- 管理费500元 --");


        return o1;
    }
}

测试类

package com.lin.proxy3;

public class Test {
    public static void main(String[] args) {
        Methodinv methodinv = new Methodinv();
        Landlord proxy = (Landlord) methodinv.getProxy();
        proxy.tenement();
    }
}

 

代理模式的缺点

 


Spring-AOP

通过学习之前的内容,我们知道了IOC的作用是帮助我们创建对象,而接下来学习的AOP就是提升代码的扩展性并且对代码进行解耦

Aop的概念

Aop:Aspect Oriented Programming

中文含义:面向切面编程

没有AOP时, 代码的执行流程:

使用AOP后, 代码的执行流程

Aop实现的方式

A、Schema base

B、AspectJ

遇到问题

我们使用传统方式进行项目开发的时候,我们书写好的功能模块后期的扩展比较的麻烦

解决问题

使用AOP面向切面进行功能扩展

解决步骤

A、确定切点---需要扩展的方法

B、增加通知---

C、织入成切面

具体操作


Schema base 实现AOP

前置通知配置

创建需要扩展的原始类

package com.lin.pojo;

public class User {
    public void a(){
        System.out.println("a");
    }

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

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

创建拓展类实现MethodBeforeAdvice 实现前置通知方法

配置切面applicationContext.xml

注意切点写法 * 空格 方法的全限定路径

* com.lin.pojo.User.a(..)

完整代码

    <!--创建前置通知对象-->
    <bean id="beforeAdvice" class="com.lin.advice.BeforeAdvice"></bean>

    <!--织入切面-->
    <aop:config>
        <!--配置切点-->
       <aop:pointcut id="pt1" expression="execution(* com.lin.pojo.User.a())"/>
        <!--配置通知-->
        <aop:advisor advice-ref="beforeAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

测试类

package com.lin.test;

import com.lin.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //解析xml
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) app.getBean("user");
        user.a();
    }
}

后置通知配置

后置通知仅实现接口与前置接口不同(AfterReturningAdvice),其余写法基本一致

环绕通知配置

环绕通知仅实现接口与前置接口不同(MethodInterceptor),其余写法基本与前置一致

异常通知配置

异常通知仅实现接口与前置接口不同(ThrowsAdvice),其余写法基本与前置一致

通知的优先级

切面配置


AspectJ 实现AOP

在使用的schema Baes 实现的方式,发现了每一个通知都需要实现对应的接口每一个接口中就是一个方法,这样的书写方式比较的麻烦的,我们想所有的方法都在一个类中书写就比较方便了。

解决方式

Aspect J方式实现

缺点

我们发现 Aspect J的方式虽然可以把所有的通知都结合到一起,书写方便,但是获得切点中的参数和切点所在的类的时候比较的繁琐

所以需要在使用的时候考虑清楚

实现方式

需要扩展的原始类

package com.lin.pojo;

public class User {
    public void a(String a){
        int b = 5/0;
        System.out.println("a");
    }

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

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

创建定义拓展

package com.lin.advice;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * @author Lin
 */
public class AspectJAdvice {

    /**
     * 前置通知方法
     */
    public void beforeAd(){
        System.out.println("前置通知");
    }

    /**
     * 后置通知方法
     */
    public void afterAd(){
        System.out.println("后置通知");
    }

    /**
     * 环绕通知方法
     */
    public Object aroundAd(ProceedingJoinPoint point) throws Throwable {
        System.out.println("环绕通知-前");

        //放行
        Object proceed = point.proceed();

        System.out.println("环绕通知-后");

        return proceed;
    }

    /**
     * 异常通知方法
     */
    public void throwAd(){
        System.out.println("异常通知");
    }

}

配置applicationContext.xml

 <!--配置通知类的对象-->
    <bean id="aps" class="com.lin.advice.AspectJAdvice"></bean>
    
    <!--配置AspectJAdvice-->
    <aop:config>
        <aop:aspect ref="aps">
            <aop:pointcut id="pt" expression="execution(* com.lin.pojo.User.b(..))"/>
            <!--配置前置通知-->
            <aop:before method="beforeAd" pointcut-ref="pt"></aop:before>
            <!--配置后置通知 无论切点是否有异常,都会执行 但after-returning只适用于没有异常才会执行-->
            <aop:after method="afterAd" pointcut-ref="pt"></aop:after>
            <!--配置环绕通知-->
            <aop:around method="aroundAd" pointcut-ref="pt"></aop:around>
            <!--配置异常通知-->
            <aop:after-throwing method="throwAd" pointcut-ref="pt"></aop:after-throwing>
        </aop:aspect>
    </aop:config>

两种方式的使用场景,具体用哪一种,就看情况用

A、schema base :如果我们需要使用切点中的参数或者切点所在的类对象的时候

B、aspect J:就是简单的给切点增加通知的时候使用这个方式比较简单

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=31416
赞同 0
评论 0 条
prtyaaL1
粉丝 1 发表 2554 + 关注 私信
上周热门
银河麒麟添加网络打印机时,出现“client-error-not-possible”错误提示  1489
银河麒麟打印带有图像的文档时出错  1407
银河麒麟添加打印机时,出现“server-error-internal-error”  1196
统信操作系统各版本介绍  1118
统信桌面专业版【如何查询系统安装时间】  1116
统信桌面专业版【全盘安装UOS系统】介绍  1071
麒麟系统也能完整体验微信啦!  1029
统信【启动盘制作工具】使用介绍  674
统信桌面专业版【一个U盘做多个系统启动盘】的方法  618
信刻全自动档案蓝光光盘检测一体机  529
本周热议
我的信创开放社区兼职赚钱历程 40
今天你签到了吗? 27
信创开放社区邀请他人注册的具体步骤如下 15
如何玩转信创开放社区—从小白进阶到专家 15
方德桌面操作系统 14
我有15积分有什么用? 13
用抖音玩法闯信创开放社区——用平台宣传企业产品服务 13
如何让你先人一步获得悬赏问题信息?(创作者必看) 12
2024中国信创产业发展大会暨中国信息科技创新与应用博览会 9
中央国家机关政府采购中心:应当将CPU、操作系统符合安全可靠测评要求纳入采购需求 8

添加我为好友,拉您入交流群!

请使用微信扫一扫!