Spring (4)——AOP

22 篇文章 0 订阅

目录

一、Spring的AOP简介

1.1 什么是AOP

1.2 AOP的作用及其优势

1.3 AOP的底层实现

1.4 AOP的动态代理技术

 1.4.1 JDK的动态代理

1.4.2 cglib的动态代理

1.5 AOP相关概念

1.6 AOP开发明确的事项

二、基于XML的AOP开发

2.1 快速入门

2.2 XML配置AOP详解

2.2.1 切点表达式的写法

2.2.2 通知的类型

2.2.3 切点表达式的抽取

2.3 知识要点

三、基于注解的AOP开发

3.1 快速入门

3.2 注解配置AOP详解

3.2.1 注解通知的类型

3.2.2 切点表达式的抽取

3.3 知识要点


一、Spring的AOP简介

1.1 什么是AOP

AOPAspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

1.2 AOP的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,并且便于维护

1.3 AOP的底层实现

实际上,AOP的底层是通过Spring提供的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强

1.4 AOP的动态代理技术

常用的动态代理技术

  • JDK代理:基于接口的动态代理技术
  • cglib代理:基于父类的动态代理技术

 1.4.1 JDK的动态代理

① 目标类接口

public interface TargetInterface {
    public void save();
}

② 目标类

public class Target implements TargetInterface{
    @Override
    public void save() {
        System.out.println("save running.....");
    }
}

③ 动态代理代码

public class ProxyTest {

    public static void main(String[] args) {
        //创建目标对象
        final Target target = new Target();
        //获取增强对象
        Advice advice = new Advice();

        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //目标对象加载器
                target.getClass().getInterfaces(), //目标对象相同的接口字节码对象数组
                new InvocationHandler() {
                    @Override
                    //调用代理对象的任何方法,实质执行的都是invoke方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前置增强
                        advice.before();
                        method.invoke(target, args);//执行目标方法
                        //后置增强
                        advice.afterReturning();
                        return null;
                    }
                }
        );
        //调用代理对象的方法
        proxy.save();
    }
}

1.4.2 cglib的动态代理

① 目标类

public class Target {
    public void save() {
        System.out.println("save running.....");
    }
}

② 动态代理代码

public class ProxyTest {

    public static void main(String[] args) {
        //创建目标对象
        final Target target = new Target();
        //获取增强对象
        Advice advice = new Advice();

        //返回值就是动态生成的代理对象,基于cglib
        //1. 创建增强器
        Enhancer enhancer = new Enhancer();
        //2. 设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3. 设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                advice.before();//执行前置
                Object invoke = method.invoke(target, args);//执行目标
                advice.afterReturning();//执行后置
                return invoke;
            }
        });
        //4. 创建代理对象
        Target proxy = (Target) enhancer.create();

        proxy.save();

    }
}

1.5 AOP相关概念

Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装好偶我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强

AOP相关术语:

  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个类被AOP织入增强后,就产生了一个结果代理类
  • Joinpoint(连接点):连接点是指那些被拦截到的点(可以被增强的方法叫连接点)。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点
  • Pointcut(切入点):切入点是指要对哪些Joinpoint 进行拦截的定义(真正被增强的连接点叫切入点)
  • Advice(通知/ 增强):通知是指拦截到连接点之后所要做的事情就是通知(增强方法)
  • Aspect(切面):是切入点和通知(引介)的结合
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程(切点和增强结合的过程叫织入)。spring采用动态代理织入,而 AspectJ采用编译期织入和类装载期织入

1.6 AOP开发明确的事项

1. 需要编写的内容

  • 编写核心业务代码(目标类和目标方法)
  • 编写切面类,切面类中由通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

2. AOP技术实现的内容

        Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑执行

3. AOP底层使用哪些代理方式

        在Spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式

二、基于XML的AOP开发

2.1 快速入门

基于XML的aop开发步骤:

①导入AOP相关坐标

②创建目标接口和目标类(内部有切点)

③创建切面类(内部有增强方法)

④在目标类和切面类的对象创建权交给spring

⑤在 applicationContext.xml 中配置织入关系

⑥测试代码

代码实现

① 导入 AOP 相关坐标

<!--导入spring的context坐标,context依赖aop-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<!-- aspectj的织入 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>

② 创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void save();
}
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running.....");
    }
}

③ 创建切面类(内部有增强方法)

public class MyAspect {
    public void before(){
        System.out.println("前置增强.....");
    }
}

④ 将目标类和切面类的对象创建权交给 spring

    <!--目标对象-->
    <bean id="target" class="com.learn.aop.Target"/>
    <!--切面对象-->
    <bean id="myAspect" class="com.learn.aop.MyAspect"/>

⑤ 在 applicationContext.xml 中配置织入关系

     导入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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

     配置切点表达式和前置增强的织入关系

    <!--配置织入:告诉Spring框架哪些方法(切点)需要进行哪些增强(前置、后置...)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切面:切点+通知-->
<!--            <aop:before method="before" pointcut="execution(public void com.learn.aop.Target.save())"/>-->
            <aop:before method="before" pointcut="execution( * com.learn.aop.*.*(..))"/>
        </aop:aspect>
    </aop:config>

⑥ 测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {

    @Autowired
    private TargetInterface target;

    @Test
    public void test1(){
        target.save();
    }
}

2.2 XML配置AOP详解

2.2.1 切点表达式的写法

表达式语法:

excution ( [修饰符] ) 返回值类型 包名.类名.方法名 (参数) )

  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号*  代表任意
  • 包名与类名之间一个点 . 代表当前报下的类,两个点 .. 表示当前包及其子包下的类
  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

2.2.2 通知的类型

<aop: 通知类型 method= "切面类中方法名" pointcut= "切点表达式"></aop: 通知类型>

名称标签说明
前置通知<aop:before>用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知<aop:after-returning>用于配置后置通知。指定增强的方法在切入点方法之后执行
环绕通知<aop:around>用于配置环绕通知。指定增强的方法在切入点方法之前和之后都 执行
异常抛出通知<aop:throwing>用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知<aop:after>用于配置最终通知。无论增强方式执行是否有异常都会执行

2.2.3 切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式

<aop:config>
    <!--引用myAspect的Bean为切面对象-->
    <aop:aspect ref="myAspect">
    <aop:pointcut id="myPointcut" expression="execution( * com.learn.aop.*.*(..))"/>
    <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
    </aop:aspect>
</aop:config>

2.3 知识要点

三、基于注解的AOP开发

3.1 快速入门

基于注解的aop开发步骤:

①创建目标接口和目标类(内部有切点)

②创建切面类(内部有增强方法)

③将目标类和切面类的对象创建权交给Spring

④在切面类中使用注解配置织入关系

⑤在配置文件中开启组件扫描和AOP的自动代理

⑥测试

代码实现

① 创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void save();
}
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running.....");
    }
}

② 创建切面类(内部有增强方法)

public class MyAspect {
    //前置增强方法
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

③ 将目标类和切面类的对象创建权交给 spring

@Component("target")
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running.....");
    }
}

@Component("myAspect")
public class MyAspect {
    public void before(){
        System.out.println("前置代码增强.....");
    }
}

④ 在切面类中使用注解配置织入关系

@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {

    //配置前置增强
    @Before("execution( * com.learn.anno.*.*(..))")
    public void before(){
        System.out.println("前置增强.....");
    }

⑤ 在配置文件中开启组件扫描和 AOP 的自动代理

    <!--组件扫描-->
    <context:component-scan base-package="com.learn.anno"/>

    <!--自动代理-->
    <aop:aspectj-autoproxy/>

⑥ 测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {

    @Autowired
    private TargetInterface target;

    @Test
    public void test1(){
        target.save();
    }
}

3.2 注解配置AOP详解

3.2.1 注解通知的类型

通知的配置语法:@通知注解("切点表达式")

名称标签说明
前置通知@Before用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知@AfterReturning用于配置后置通知。指定增强的方法在切入点方法之后执行
环绕通知@Around用于配置环绕通知。指定增强的方法在切入点方法之前和之后都 执行
异常抛出通知@AfterThrowing用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知@After用于配置最终通知。无论增强方式执行是否有异常都会执行

3.2.2 切点表达式的抽取

同 xml 配置 aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut 注解定义切点表达式,然后在在增强注解中进行引用。具体如下:

@@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("MyAspect.myPoint()")
    public void before(){
        System.out.println("前置代码增强.....");
    }
    @Pointcut("execution(* com.learn.aop.*.*(..))")
    public void myPoint(){}
}

3.3 知识要点

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了面向切面编程(AOP)的支持。AOP是一种编程思想,它可以在不改变原有代码的情况下,通过在程序运行时动态地将代码“织入”到现有代码中,从而实现对原有代码的增强。 Spring AOP提供了基于注解的AOP实现,使得开发者可以通过注解的方式来定义切面、切点和通知等相关内容,从而简化了AOP的使用。 下面是一个基于注解的AOP实现的例子: 1. 定义切面类 ```java @Aspect @Component public class LogAspect { @Pointcut("@annotation(Log)") public void logPointcut() {} @Before("logPointcut()") public void beforeLog(JoinPoint joinPoint) { // 前置通知 System.out.println("执行方法:" + joinPoint.getSignature().getName()); } @AfterReturning("logPointcut()") public void afterLog(JoinPoint joinPoint) { // 后置通知 System.out.println("方法执行完成:" + joinPoint.getSignature().getName()); } @AfterThrowing(pointcut = "logPointcut()", throwing = "ex") public void afterThrowingLog(JoinPoint joinPoint, Exception ex) { // 异常通知 System.out.println("方法执行异常:" + joinPoint.getSignature().getName() + ",异常信息:" + ex.getMessage()); } } ``` 2. 定义业务逻辑类 ```java @Service public class UserService { @Log public void addUser(User user) { // 添加用户 System.out.println("添加用户:" + user.getName()); } @Log public void deleteUser(String userId) { // 删除用户 System.out.println("删除用户:" + userId); throw new RuntimeException("删除用户异常"); } } ``` 3. 在配置文件中开启AOP ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> ``` 在这个例子中,我们定义了一个切面类LogAspect,其中通过@Aspect注解定义了一个切面,通过@Pointcut注解定义了一个切点,通过@Before、@AfterReturning和@AfterThrowing注解分别定义了前置通知、后置通知和异常通知。 在业务逻辑类中,我们通过@Log注解标注了需要增强的方法。 最后,在配置文件中,我们通过<aop:aspectj-autoproxy/>开启了AOP功能,并通过<context:component-scan>扫描了指定包下的所有组件。 这样,当我们调用UserService中的方法时,就会触发LogAspect中定义的通知,从而实现对原有代码的增强。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值