Spring-AOP

前导知识:动态代理    可参考上一节 https://www.cnblogs.com/wangid3/p/14159821.html

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

 

AOP相关术语:

1) Joinpoint:连接点:可以被拦截到的方法,能够被增强的方法,这些方法就可以称为是连接点,(被代理类的方法)

 

2)Pointcut:切入点 :真正被拦截的方法,真正被增加的方法:

 

3) Advice:通知 :增加的内容,通常都是封装成一个方法, 这个方法我们就称为通知

 

4)Introduction:引介
类层面的增加,给原有的类添加一些新的属性方法,在开发中通常都是给方法进行增加

5)Target:被增加的对象

 

6) Weaving:织入: 将通知应用到目标对象的过程
7) Proxy :代理对象
8) Aspect:切面 :多个通知和多个切入点的集合

 Spring中AOP的使用:

1.引入spring基本jar包

2.引入aop开发的相关jar包

3.配置文件中引入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: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">
</beans>

 

4.Spring测试
测试时,每次都需要获取工厂,通过spring-test,就不用每次获取
添加测试依赖包:

5.测试
1)在applicationContext.xml中加入:

<bean id="goodsDao" class="com.wangid3.demo1.GoodsDaoImpl"></bean>

 

我们看下现在GoodsDao实现类代码:

package com.wangid3.demo1;
public class GoodsDaoImpl implements GoodsDao {
    @Override
    public void save() {
        System.out.println("保存操作");
    }
    @Override
    public String update() {
        System.out.println("更新");
        return "update---res";
    }
    @Override
    public void delete() {
        System.out.println("删除操作");
    }
    @Override
    public void find() {
        System.out.println("find");
        /*int i = 1/0;*/
    }
}
import com.wangid3.demo1.GoodsDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Resource(name = "goodsDao")
    private GoodsDao goodsDao;
    @Test
    public void test() {
        this.goodsDao.save();
    }

}

 

 现在只是简单的把goodsDao注入进来,调用的仍然是goodsDaoImpl方法,下面我们才开始加入额外内容。

6.编写一个切面类

package com.wangid3.demo2;
import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class Myaspect {
    public void check(){
        System.out.println("权限校验");
    }
}

7.将切面交给spring

写进applicationContext.xml 交给Spring管理

<bean id="myaspect" class="com.wangid3.demo2.Myaspect"></bean>

8.配置AOP完成对目标产生代理

 下面的配置是在以后执行Spring注入的goodsDao后,如果执行的是切入点save(),则在那之前先执行myaspect类的check() (通知),动态代理生成的过程被Spring隐藏了

<bean id="goodsDao" class="com.wangid3.demo1.GoodsDaoImpl"></bean>
<bean id="myaspect" class="com.wangid3.demo2.Myaspect"></bean>
    <!--配置aop-->
    <aop:config>
        <!--切入点:给哪个方法增强-->
        <aop:pointcut id="savepoint" expression="execution(* com.wangid3.demo1.GoodsDaoImpl.save(..))"/>
        <!--切面=通知+切入点-->
        <!--配置切面:增强是功能是什么 -->
        <!--切面配置要素,实现通知的类,通知名称,切入点
        <aop:aspect ref="myaspect">
            <!--前置通知-->
            <aop:before method="check" pointcut-ref="savepoint" />
        </aop:aspect>
    </aop:config>        

 通知类型:

 

前置通知:在目标方法执行之前进行操作,比如刚才的check()


后置通知
在目标方法执行之后 进行操作,注意是after-returning

<aop:pointcut id="savepoint3" expression="execution(* com.wangid3.demo1.GoodsDaoImpl.delete(..))"/>
...
<aop:aspect ref="myaspect">
<aop:after-returning method="log" pointcut-ref="savepoint2" returning="res"/>
...
</aop:aspect>

 

环绕通知
在目标方法执行之前和之后进行操作

<aop:around method="around" pointcut-ref="savepoint3"/>

 

注意在通知实现类中的写法,最为特殊

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("开启事务");
        Object proceed = joinPoint.proceed();
        System.out.println("提交事务");
        return  proceed;
    }
//不去深入了,暂时知道写法就好了

异常抛出通知:在程序出现异常时进行操作

<aop:after-throwing method="exceptionM" pointcut-ref="savepoint4" throwing="ex"/>

最终通知:无论代码是否有异常,都会执行,注意这里才是 after

<aop:after method="after" pointcut-ref="savepoint4"/>

 

AOP切入点表达式(基于execution函数完成)

语法
【访问修饰符】 方法返回值 包名.类名.方法名(参数)
public com.myxq.demo2.GoodsDaoImpl.save(..)  参数为任意参数
* com.myxq.demo2.GoodsDaoImpl.save(..)  * 任意类型
* com.myxq.demo2.GoodsDaoImpl+.save(..)  + 当前类和子类
* com.myxq..*.*(..)  com.myxq包以及子包下面所有类的所有方法

AOP注解模式

流程和xml配置方式基本一致,记录下不同点

1.在配置文件中表明打开AOP注解

在applicationContext.xml中,加入到bean前面

<!--aop开启注解-->
    <aop:aspectj-autoproxy/>
    <bean id="goodsDao" class="com.wangid3.demo1.GoodsDaoImpl" ></bean>
    <bean id="goodsaspect" class="com.wangid3.demo1.GoodsDaoAspect"></bean>
    <bean id="userDao" class="com.wangid3.demo1.UserDao"></bean>

 

 2.在通知实现类上加上注解,表明切入点(切入点,通知都有了,那就叫通知实现类 切面类)

在下面的示例中,value后面可以直接不用刻意定义切入点的,比如 @Before(value = "execution(* com.myxq. dao.impl.GoodsDaolmpl.save(..))")

package com.wangid3.demo1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class GoodsDaoAspect {

    @Before(value = "GoodsDaoAspect.pointcut1() || GoodsDaoAspect.pointcut4() || GoodsDaoAspect.pointcut5()")
    public void log(){
        System.out.println("日志");
    }

    @AfterReturning(value = "GoodsDaoAspect.pointcut2()",returning = "res")
    public void afterreturning(Object res){
        System.out.println("后置通知---"+res);
    }

    @Around(value = "execution(* com.wangid3.demo1.GoodsDaoImpl.delete(..)))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("delete---之前");
        Object proceed = joinPoint.proceed();
        System.out.println("delete---之后");
        return proceed;

    }

    @Pointcut(value = "execution(* com.wangid3.demo1.GoodsDaoImpl.save(..))")
    private void pointcut1(){}

    @Pointcut(value = "execution(* com.wangid3.demo1.GoodsDaoImpl.update(..))")
    private void pointcut2(){}

    @Pointcut(value = "execution(* com.wangid3.demo1.GoodsDaoImpl.delete(..))")
    private void pointcut3(){}

    @Pointcut(value = "execution(* com.wangid3.demo1.GoodsDaoImpl.find(..))")
    private void pointcut4(){}

    @Pointcut(value = "execution(* com.wangid3.demo1.UserDao.save(..))")
    private void pointcut5(){}
}

 

posted @ 2020-12-22 09:53  wangid3  阅读(43)  评论(0)    收藏  举报