Spring的AOP面向切面编程

Spring的AOP面向切面编程

1、什么是AOP

AOP(Aspect Oriented Programming)面向切面编程,主张将程序中的相同业务逻辑进行横向隔离,并将重复的业务逻辑抽取到一个独立的模块中,实现提升程序可复用性和开发效率的目的。

用通俗一点话来说就是:把一个具体的操作看作一根竹,然后面向切面就是把竹子的中间切开,然后对这个切面进行操作,就是面向切面编程。假如说如果没有这个切面,那么对这个竹子不会有任何影响,有了这个切面,只会让这个竹子锦上添花。总结来就是,好的AOP应该是:如果去掉这个AOP不会对这个原来的程序造成任何影响,有了AOP会让这个程序更好。目的就是为了降低耦合。

2、Spring AOP的开发方法

Spring AOP的开发方法有两种:

  • 基于XML配置文件开发Spring AOP

    配置切面,然后在XML配置切点和切面

  • 基于注解开发Spring AOP(常用于SpringBoot)

    配置切面,并且在切面里直接加上注解配置切点和切面以及通知

3、基于XML开发SpringAOP

  • 创建Maven项目,导入依赖:

    <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>5.2.6.RELEASE</version>
        </dependency>
          <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjweaver</artifactId>
              <version>1.9.2</version>
          </dependency>
      </dependencies>
    
  • Service业务(里面的方法就是需要被切的操作)

    UserService.java接口

    public interface UserService {
        //查询用户
        void selectUser();
        //新增用户
        void insertUser();
        //删除用户
        void deleteUser();
        //修改用户
        void updateUser();
    }
    

    UserServiceImpl.java

    public class UserServiceImpl implements UserService{
        @Override
        public void selectUser() {
            System.out.println("执行了selectUser方法");
        }
    
        @Override
        public void insertUser() {
            System.out.println("执行了insertUser方法");
        }
    
        @Override
        public void deleteUser() {
            System.out.println("执行了deleteUser方法");
        }
    
        @Override
        public void updateUser() {
            System.out.println("执行了updateUser方法");
        }
    }
    

    UserServiceImpl是UserService的实现类,这里面实际应该是具体的操作,这里为了方便演示就打印就好了。

  • Aspect切面(就是对切面进行的操作)

    Aspect.java

    public class Aspect {
        //before前置通知
        public void before(){
            System.out.println("before前置通知");
        }
        //after后置通知
        public void after(){
            System.out.println("after后置通知");
        }
        //around环绕通知
        public Object around(ProceedingJoinPoint point)throws Throwable{
            System.out.println("around环绕通知之前——————");
            Object object = point.proceed();
            System.out.println("around环绕通知之后——————");
            return object;
        }
        //after-returning返回通知
        public void afterReturning(){
            System.out.println("after-returning返回通知");
        }
        //after-throwing异常通知
        public void afterThrowing(){
            System.out.println("after-throwing异常通知");
        }
    }
    
  • 在XML文件里进行相关配置

    spring-dao.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
           http://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 name="userService" class="cn.wqk.service.UserServiceImpl"/>
        <bean name="aspect" class="cn.wqk.aop.Aspect"/>
        <!--配置aop-->
        <aop:config>
            <!--切点pointcut-->
            <aop:pointcut id="pointcut" expression="execution(* cn.wqk.service.UserServiceImpl.*(..))"/>
            <!--切面aspect-->
            <aop:aspect id="aspect" ref="aspect">
                <!--前置通知-->
                <aop:before method="before" pointcut-ref="pointcut"/>
                <!--后置通知-->
                <aop:after method="after" pointcut-ref="pointcut"/>
                <!--环绕通知-->
                <aop:around method="around" pointcut-ref="pointcut"/>
                <!--返回通知-->
                <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
                <!--异常通知-->
                <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    </beans>
    

把userService和aspect都丢进Spring容器里,然后再配置aop:

<aop:config>:配置aop相关

<aop:pointcut>配置切点,其中expression里是需要切入的方法

<aop:aspect>配置切面,其中ref取的的bean里的name为aspect的切面

<aop:before>配置切面里的前置通知,pointcut-ref里是取的是bean里的name为pointcut的切点等。

​ applicationContext.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"
       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">
    <import resource="classpath:spring-aop.xml"/>
</beans>

把spring-aop导入到applicationContext容器里,让spring-aop专注于aop,利用程序后期更好的扩展。

  • 测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"classpath:applicationContext.xml"})
    public class TestAOP {
        @Autowired
        UserService userService;
        @Test
        public void test(){
            //调用UserService里的selectUser()方法
            userService.selectUser();
        }
    }
    

    测试结果:

    在这里插入图片描述

    我们可以得出结论:

    通知执行顺序
    前置通知在切点方法执行之前执行的
    后置通知在切点方法执行之后执行的(出现异常就不会调用)
    环绕通知在进入方法的时候执行和方法返回结果后执行
    返回通知在方法执行成功,并且返回返回值的时候执行
    异常通知在程序发生异常的时候执行,并且后面的通知也不执行

4、基于注解开发SpringAOP

UserService和UserServiceImpl依旧是上面的,我们只需要重新配置Aspect类

Aspect.java:

@Aspect
public class AspectAdvice {
    //定义切点
    @Pointcut("execution(* cn.wqk.service.UserServiceImpl.*(..))")
    public void pointcut(){}

    //before前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("before前置通知");
    }

    //after后置通知
    @After("pointcut()")
    public void after(){
        System.out.println("after后置通知");
    }

    //around环绕通知
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point)throws Throwable{
        System.out.println("around环绕通知之前——————");
        Object object = point.proceed();
        System.out.println("around环绕通知之后——————");
        return object;
    }

    //after-returning返回通知
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("after-returning返回通知");
    }

    //after-throwing异常通知
    @AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("after-throwing异常通知");
    }
}

记得在这个类的头上加上@Aspect切面注解,表面这是一个切面类,每个通知类的值是定义的切点的方法名

注解名描述
@Aspect定义切面
@Pointcut定义切点
@Beforebefore前置通知
@Afterafter后置通知
@Aroundaround环绕通知
@AfterReturningafter-returning返回通知
@AfterThrowingafter-throwing异常通知

将spring-aop.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
       http://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 name="userService" class="cn.wqk.service.UserServiceImpl"/>
    <bean name="aspectAdvice" class="cn.wqk.aop.AspectAdvice"/>
    <!--开启@Aspect自动代理-->
    <aop:aspectj-autoproxy/>
</beans>

注意不要忘了把它import到applicationContext里

最后用相同的方法进行测试就好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值