spring的AOP是什么

 一、什么是AOP

1. 简介

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

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

2. 作用及优势

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

3. 底层实现原理

spring提供的一种动态代理技术实现功能的增强

(1)JDK代理:基于接口的动态代理技术

 我们先来写一个简单的目标接口:

public interface TargetInterface {
    public void save();
}

我们再写一个目标对象,实现刚才那个目标接口:

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

最后我们实现一个动态代理方法使其功能增强,比如前置增强和后置增强。

public class ProxyTest {
    public static void main(String[] args) {
        //目标对象
        Target target = new Target();
        TargetInterface tagetInterface = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("前置增强...."); //前置增强
                Object invoke = method.invoke(target, args); //执行目标方法
                System.out.println("后置增强...."); //后置增强
                return invoke;
            }
        });
        //调用代理对象的方法
        tagetInterface.save();
    }
}

查看运行结果可以看到方法被增强了! 

(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();       
        //1、创建增强器
        Enhancer enhancer = new Enhancer();
        //2、设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3、设置回调
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("前置增强...."); 
                Object invoke = method.invoke(target, args); //执行目标
                System.out.println("后置增强...."); 
                return invoke; //返回值 就是动态生成的代理对象  基于cglib
            }
        });
        //4、创建代理对象
        Target proxy = (Target) enhancer.create();
        proxy.save();
    }
}

查看运行结果可以看到方法被同样增强了!

二、基于XML的AOP开发

1. 导入AOP相关坐标

找到 pom.xml,将下面两个坐标导入

(1)spring的context坐标,context依赖aop

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

(2)aspectj的织入

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

2. 创建类和方法

(1)目标接口

public interface TargetInterface {
    public void save();
}

(2)目标类(也就是切点)

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

(3) 切面类(也就是增强方法)

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

    public void afterReturning(){
        System.out.println("后置增强....");
    }
}

3. 面向切面编程 

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

找到 applicationContext.xml,写入以下代码:

<!--配置目标类-->
<bean id="target" class="com.itheima.aop.Target"></bean>
<!--配置切面类-->
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean>

(2)在 applicationContext.xml 中配置织入关系,

首先导入aop命名空间:

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

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

先写出大体的格式:

<aop:config>
    <aop:aspect ref=“切面类”>
        <aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>
    </aop:aspect>
</aop:config>

  注意这里的切点表达式的写法:        

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

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

通知类型

切点表达式的抽取:

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

比如下面的完整写法中,把切点命名为 myPointCut 并放入 <aop:aspect ... /> 标签中,直接再下面的前置通知或后置通知 pointcut-ref="myPointCut" 调用即可。

最后,完整写出配置切点表达式和前置增强的织入关系: 

    <!--配置织入-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <aop:pointcut id="myPointCut" expression="execution(* aop.*.*(..))"/>
            <!--切面:切点+通知-->
            <aop:before method="before" pointcut-ref="myPointCut"/>
            <!--<aop:after method="afterReturning" pointcut="execution(public void aop.Target.save())"/>-->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/>
        </aop:aspect>
    </aop:config>-->

4. 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.save();
    }
}

运行结果: 

 这样我们就实现了基于XML的AOP开发!不过我们一般使用更为方便的注解开发

三、基于注解的 AOP 开发

1. 导入AOP相关坐标

与XML开发完全相同

2. 创建类和方法

(1)创建目标接口和目标类(与XML开发相同)

// 目标接口
public interface TargetInterface {
    public void save();
}
// 目标类(也就是切点)
// @Component("target")
public class Target implements TargetInterface{
    @Override
    public void save() {
        System.out.println("save....");
    }
}

注意这里的切点类也可以使用下面说到的的 @Component() 注解,不加也行!

(3) 切面类

首先我们需要用到 @Component() @Pointcut() @Aspect() 注解

  • @Component():实现bean的注入
  • @Pointcut():抽取切面表达式
  • @Aspect():表明当前类是一个切面类
@Component("myAspect") 
@Aspect //标注切面类
public class MyAspect {
    @Pointcut("execution(* aop.*.*(..))") // 抽取切面表达式
    public void pointCut(){};
    @Before("MyAspect.pointCut()")
    public void before(){
        System.out.println("前置增强....");
    }
    @AfterReturning("MyAspect.pointCut()")
    public void afterReturning(){
        System.out.println("后置增强....");
    }
}

 然后通知方法的注解:

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

打开 applicationContext.xml 文件,将基于 xml 的AOP开发加注释,然后加上以下两行配置。

base-package 的值是需要扫描的切面和切点类所在的package包。

<!--组件扫描-->
<context:component-scan base-package="aop"/>
<!--aop自动代理-->
<aop:aspectj-autoproxy/>

4. 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.save();
    }
}

编写测试代码,可以看到也能实现方法增强。 

猜你喜欢

转载自blog.csdn.net/weixin_51418964/article/details/123714834