Spring——AOP实现的三种方式

Posted 我永远信仰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring——AOP实现的三种方式相关的知识,希望对你有一定的参考价值。

AOP实现

使用Spring实现Aop

需要导入一个依赖包

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

一、环境搭建

说明:UserService是一个接口,定义了增删改查、UserServiceImpl实现了这个接口。想要在这个基础上增加打印日志的功能,但不修改源码方法,使用Spring的AOP。这符合了OCP原则,对扩展开放,对修改关闭。

  • 用到Spring的aop,aop的底层原理是代理模式。

1、UserService 接口类

package com.yong.service;

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

2、UserServiceImpl 实现类

package com.yong.service;

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("执行了add方法");
    }

    public void update() {
        System.out.println("执行了update方法");
    }

    public void select() {
        System.out.println("执行了select方法");
    }

    public void delete() {
        System.out.println("执行了delete方法");
    }
}

现在我需要在每一个方法执行前后都打印日志,比如执行add方法,输出

======方法执行前=====
执行了add方法
======方法执行后=====

原来的做法是修改源码

public void add() {
	System.out.println("======方法执行前=====");
    System.out.println("执行了add方法");
    System.out.println("======方法执行后=====");
}

现在使用Spring的Aop实现。

介绍三种方法:

实现方式1

使用Spring 的API接口。

* 先创建好日志类

1.BeforeLog 前置增强日志类

package com.yong.log;

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

//前置增强,需要实现对应的MethodBeforeAdvice
//Method:要执行的目标对象的方法
//args:参数
//target:目标对象
public class BeforeLog implements MethodBeforeAdvice {

    public void before(Method method, Object[] args, Object target) throws Throwable {
		//System.out.println("======方法执行前=====");
                System.out.println("前置增强,"+o.getClass().getSimpleName()+"的"+method.getName()+"方法被执行了");

    }
}

2、AfterLog 后置增强日志类

package com.yong.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

//后置增强
//returnValue:返回值
public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
              System.out.println("后置增强,执行了"+method.getName()+"方法,返回值为"+returnValue);

    }
}

3、applicationContext.xml 配置文件

  • (1)注意命名空间也要加入aop

  • (2)注册bean

  • (3)配置切入点(在哪个地方执行)

  • 理解,execution()表达式(* * * * *)

    <aop:pointcut id="point" expression="execution(* 				com.yong.service.UserServiceImpl.*(..))"/>
    

    id:后面的切入方法需要

    com.yong.service.UserServiceImpl:类下的所有方法

    (…):方法下所有的参数

<?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">

    <!--注册bean-->
    <bean id="serviceImpl" class="com.yong.service.UserServiceImpl"/>
    <bean id="log" class="com.yong.log.AfterLog"/>
    <bean id="log" class="com.yong.log.BeforeLog"/>

    <!--使用aop
    方式一:原生Spring API接口-->
    <aop:config>
        <!--切入点 ,execution()表达式-->
        <aop:pointcut id="point" expression="execution(* 				com.yong.service.UserServiceImpl.*(..))"/>

        <!--执行环绕增强,将advice-ref方法插入到pointcut-ref-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="point"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="point"/>
    </aop:config>
    
</beans>

4、MyTest 测试类

  • 重点 动态代理代理的是一个接口,不是一个具体的实现类。所以context.getBean() 返回的对象是一个接口类UserService而不是UserServiceImpl
import com.yong.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService serviceImpl = (UserService) context.getBean("serviceImpl");
        serviceImpl.select();
    }
}

运行结果:
在这里插入图片描述

实现方式2(主要是切面定义)

  • 方法一有些繁琐,需要记住接口,每次也需要去实现一些接口。

  • 方法二,可以自定义切入点类,能很单纯的实现在方法执行的前后打印log这两件事情。

  • 比方法一使用简单,不过功能没有第一个强大,第一个能操作更多东西,但是这个一般也够用了。

  • 推荐这种方法。

1、自定义类 (非常清爽)

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

2、配置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"
       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">
       
    <!--注册bean-->
    <bean id="serviceImpl" class="com.yong.service.UserServiceImpl"/>

	<!--方式2:自定义类-->
    <bean id="diy" class="com.yong.service.DiyPointCut"/>
    <aop:config>
        <!--切面,ref引用类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.yong.service.UserServiceImpl.*(..))"/>
            <!--通知
                什么时候执行,执行什么方法,在哪里执行
            -->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

3、MyTest不变

运行结果:
在这里插入图片描述

实现方式3(使用注解)

1、AspectPointCut 切面类

  • @Aspect : 标注这个类为一个切面
  • @Before :表示切入位置,需要一个execution表达式表明切入点
package com.yong.service;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AspectPointCut {
    @Before("execution(* com.yong.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("======方法执行前=====");
    }
    
    @After("execution(* com.yong.service.UserServiceImpl.*(..))")
    public  void  after(){
        System.out.println("======方法执行后=====");
    }
}

2、配置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"
       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">

    <!--注册bean-->
    <bean id="serviceImpl" class="com.yong.service.UserServiceImpl"/>
	
    <!--方式三:注解-->
    <bean id="annotationPointcut" class="com.yong.service.AspectPointCut"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>


</beans>

3、MyTest不变

运行结果:

在这里插入图片描述

以上是关于Spring——AOP实现的三种方式的主要内容,如果未能解决你的问题,请参考以下文章

AOP的三种实现方式之一通过xml配置文件实现

AOP的三种实现方式之一通过xml配置文件实现

Spring-AOP的三种方式

Spring Bean定义的三种方式

Spring AOP 应用:三种配置及实现方式

Spring AOP 的使用和实现原理