首页 > 技术文章 > spring 实现 AOP

m1573 2021-10-19 08:59 原文

AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
在不改变原有的逻辑的基础上,增加一些额外的功能。代理也是这个功能,读写分离也能用aop来做。

Aop在Spring中的作用
提供声明式事务,允许用户自定义切面

使用AOP,需要导入依赖包:

<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>

        <!--spring aop + aspectj-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>

UserService 接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}

然后写一个接口实现类

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 select() {
        System.out.println("查询了一个用户");
    }
}

在此之外还需要其他的功能逻辑等,如打印时间、打印日志等等。这里开始就需要AOP替“UserServiceImpl”完成!解耦!写了两个log类。因为一个是切入点前执行、一个是切入点之后执行,所以实现对应接口。
横切关注点,这里是打印执行方法:

public class Log implements MethodBeforeAdvice {
    @Override
    //method:要执行的目标对象的方法
    //args:参数
    public void before(Method method, Object[] args, Object target) throws Throwable {

        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
}

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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--注册bean-->
    <bean id="userService" class="com.kangdamu.service.UserServiceImpl"/>
    <bean id="log" class="com.kangdamu.log.Log"/>
    <bean id="afterLog" class="com.kangdamu.log.AfterLog"/>


<!--使用原生Spring API接口-->

<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--切入点 expression表达式:execution(要执行的位置* ***)-->

 <aop:pointcut id="pointcut" expression="execution(* com.kangdamu.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加 aop:advisor;需要执行的类:advice-ref;切入到哪个方法pointcut-ref -->
   <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
  <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
 </aop:config>



<!-- 方式二:自定义类-->

    <bean id="diy" class="com.kangdamu.diy.DiyPointCut"/>

<!--  自定义切面,ref要引用的类-->

    <aop:config>

        <aop:aspect ref="diy">   <!-- 自定义切面,ref要引用的类-->
<!--  切入点-->
            <aop:pointcut id="point" expression="execution(* com.kangdamu.service.UserServiceImpl.*(..))"/>
<!--  通知-->
            <aop:before method="before" pointcut-ref="point"/> 

            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

测试类

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");

        userService.add();
    }
}

方式二:

实现类

public class DiyPointCut {
    public void before(){
        System.out.println("=========方法执行前===========");
    }
    public void after(){
        System.out.println("=========方法执行后===========");
    }
}

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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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
        http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--注册bean-->
    <bean id="userService" class="com.kangdamu.service.UserServiceImpl"/>
    <bean id="log" class="com.kangdamu.log.Log"/>
    <bean id="afterLog" class="com.kangdamu.log.AfterLog"/>


<!-- 方式二:自定义类-->

    <bean id="diy" class="com.kangdamu.diy.DiyPointCut"/>

    <aop:config>
<!-- aop:aspect 自定义切面,ref要引用的类-->
        <aop:aspect ref="diy">   
<!--  aop:pointcut 切入点;-->
            <aop:pointcut id="point" expression="execution(* com.kangdamu.service.UserServiceImpl.*(..))"/>
<!--  通知,method写自己定义的方法名; pointcut-ref在哪个切入点执行-->
            <aop:before method="before" pointcut-ref="point"/> 

            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

测试类

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");

        userService.add();
    }
}

方式三:通过注解实现AOP

//使用注解实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
    @Before("execution(* com.kangdamu.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("=====方法执行前=====");
    }
    @After("execution(* com.kangdamu.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("=====方法执行后=====");
    }
    //在环绕增强中,可以给定一个参数,代表要获取处理切入的点
    @Around("execution(* com.kangdamu.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("=====环绕前=====");
        //执行方法
        Object proceed = jp.proceed();
        System.out.println("=====环绕后=====");
    }
}

在xml文件中配置

<bean id="annotationPointCut" class="com.kangdamu.diy.AnnotationPointCut"/>
<!--    开启注解支持 jdk(默认proxy-target-class="false")-->
<aop:aspectj-autoproxy proxy-target-class="false"/>

推荐阅读