ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(四)Spring-AOP

2022-05-10 18:31:31  阅读:138  来源: 互联网

标签:Spring void AOP System import println org public


(四)Spring-AOP

一、AOP背景知识

1.1 相关概念

1 横切关注点:

跨越应用程序多个模块的方法或功能。

即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存,事务等等...

2 切面(ASPECT)

横切关注点,被模块化的特殊对象。即,它是一个类。比如,专注于完成日志的Log类

3 通知(Advice)

切面必须要完成的工作。即,它是类中的一个方法。比如,专注于完成日志的Log类的一个方法。

4 目标(Target)

被通知对象。

5 代理(Proxy)

向目标对象应用通知之后创建的对象。就是我们生成的代理类。

6 切入点(PointCut)

切面通知执行的“地点”的定义。说白了就是在哪个地方执行,如jdk动态代理中InvocationHandler的invoke()方法

7 连接点(JointPoint)

与切入点匹配的执行点。

1.2 Spring中5种Advice

在spring中,通过advice定义横切逻辑,spring中支持5种类型的advice

通知类型 连接点 实现接口
前置通知 方法前 org.springframework.aop.MethodBeforeAdvice
后置通知 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知 方法前后 org.aopalliance.intercept.MethodInterceptor
异常抛出通知 方法抛出异常 org.springframework.aop.ThrowsAdvice
引介通知 类中增强新的方法属性 org.springframework.aop.IntroductionIntercepter

二、使用Spring实现AOP

2.1 导入相关依赖或者包

使用AOP织入,需要导入一个依赖包

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

2.2 实现AOP的3个方式

2.2.1 方式一:使用spring的AOP接口

在application-context注册对象和aop切面以及切点,spring会自动帮拦截目标方法

1 定义目标(被代理)的接口
package com.happy.service;

public interface UserService {
    void add();
    void delete();
    void update();
    void query();

}

2 编写目标类,实现接口
package com.happy.service.impl;

import com.happy.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}

3 编写切面(类)和advice(方法)
package com.happy.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

//这个类是切面
public class BeforeLog implements MethodBeforeAdvice {

    //这个方法是通知
    //spring会在我们执行目标方法前自动调用
    //method为要执行的目标对象的方法
    //target标志目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass()+"的"+method.getName()+"开始执行前记录日志");
    }
}

package com.happy.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

//一个切面
public class AfterLog implements AfterReturningAdvice {


    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass()+"的"+method.getName()+"执行结束后记录日志");
        System.out.println("返回结果为->"+returnValue);
    }
}

4 注册目标类(被代理类)、切面以及切点

在spring的xml上下文中注册 以及切面、切点

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

    <!--注册bean-->
    <bean id="beforeLog" class="com.happy.log.BeforeLog"></bean>
    <bean id="afterLog" class="com.happy.log.AfterLog"></bean>

    <bean id="userService" class="com.happy.service.impl.UserServiceImpl"></bean>

    <!--方式一:使用原生的spring api接口-->
    <!--配置aop,需要导入aop的约束-->
    <aop:config>
        <!--需要一个切入点,即在哪里执行我们spring的方法-->
        <!--需要一个表达式expression:表达式。execution要执行的位置!表示会在这个地方执行-->
        <aop:pointcut id="pointcut" expression="execution(* com.happy.service.impl.UserServiceImpl.*(..) )"/>

        <!--执行环绕增加!-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"></aop:advisor>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>
    <context:annotation-config></context:annotation-config>
</beans>
5 执行目标方法
package com.happy.service;

import com.happy.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceTest {

    @Test
    public void test() {

        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();

        System.out.println("==============自己new对象=================");
        //必须用容器管理的bean才能执行advice方法
        UserServiceImpl userService2 = new UserServiceImpl();
        userService2.add();

    }
}

2.2.2 方式二:使用自定义切面

  • 可以自定义一个切面
  • 但没有方式一强大, 因为自定义切面的advice只是普通方法,拿不到method等上下文
1 定义目标(被代理)的接口

同方式一

2 编写目标类

同方式一

3 编写自定义切面Aspect
package com.happy.diyAspect;

public class MyAspect {

    public void before(){
        System.out.println("div myApsect============方法执行前");
    }

    public void after(){
        System.out.println("diy myApsect============方法执行后");
    }
}

4 注册目标类、切面以及切点

同方式一,也在xml中配置,当稍微配置不同

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

    <!--注册bean-->
    <bean id="beforeLog" class="com.happy.log.BeforeLog"></bean>
    <bean id="afterLog" class="com.happy.log.AfterLog"></bean>

    <bean id="userService" class="com.happy.service.impl.UserServiceImpl"></bean>

    <!--方式一:使用原生的spring api接口-->
    <!--配置aop,需要导入aop的约束-->
    <!--  <aop:config>
          &lt;!&ndash;需要一个切入点,即在哪里执行我们spring的方法&ndash;&gt;
          &lt;!&ndash;需要一个表达式expression:表达式。execution要执行的位置!表示会在这个地方执行&ndash;&gt;
          <aop:pointcut id="pointcut" expression="execution(* com.happy.service.impl.UserServiceImpl.*(..) )"/>

          &lt;!&ndash;执行环绕增加!&ndash;&gt;
          <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"></aop:advisor>
          <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
      </aop:config>-->


    <!--方式二:自定义aspect-->
    <bean id="myAspect" class="com.happy.diyAspect.MyAspect"></bean>
    <aop:config>
        <aop:aspect id="myAspect" ref="myAspect">
            <aop:pointcut id="pointcut" expression="execution(* com.happy.service.impl.UserServiceImpl.*(..) )"/>
            <aop:before method="before" pointcut-ref="pointcut"></aop:before>
            <aop:after method="after" pointcut-ref="pointcut"></aop:after>
        </aop:aspect>
    </aop:config>
    <context:annotation-config></context:annotation-config>
</beans>
5 执行目标方法
package com.happy.service;

import com.happy.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceTest {

    @Test
    public void test() {

        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();

        System.out.println("==============自己new对象=================");
        //必须用容器管理的bean才能执行advice方法
        UserServiceImpl userService2 = new UserServiceImpl();
        userService2.add();

    }
}

2.2.3 方式三:使用注解

1 编写注解模式的自定义切面
package com.happy.annotationAspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAnnotationAspect {

    @Before("execution(* com.happy.service.impl.UserServiceImpl.* (..))")
    public void before(){
        System.out.println("=="+this.getClass().getName()+"执行before方法");
    }

    @After("execution(* com.happy.service.impl.UserServiceImpl.* (..))")
    public void after(){
        System.out.println("=="+this.getClass().getName()+"执行after方法");
    }

    @Around("execution(* com.happy.service.impl.UserServiceImpl.* (..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("===="+this.getClass().getName()+"执行环绕around方法1====");
        System.out.println("signature->"+joinPoint.getSignature());
        Object proceed = joinPoint.proceed();
        System.out.println("===="+this.getClass().getName()+"执行环绕around方法2====");

    }
}

2 开启注解模式的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 https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="beforeLog" class="com.happy.log.BeforeLog"></bean>
    <bean id="afterLog" class="com.happy.log.AfterLog"></bean>

    <bean id="userService" class="com.happy.service.impl.UserServiceImpl"></bean>

    <!--开启注解支持-->
    <context:annotation-config></context:annotation-config>
    <context:component-scan base-package="com.happy"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
3 执行目标方法
4 spring支持的两种动态代理模式

proxy-target-class="false"-->jdk,默认!

proxy-target-class="true"-->cglib

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

区别在于cglib不要求被代理类实现接口。而jdk要求被代理类必须先定义核心业务接口,再实现核心业务接口。

标签:Spring,void,AOP,System,import,println,org,public
来源: https://www.cnblogs.com/happycarpediem/p/16254788.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有