06 Spring_AOP

目录

一、SpringAOP_AOP简介

二、SpringAOP_术语

 AOP的相关术语:(前4个常用,后3个不常用)

三、SpringAOP_AOP入门 

四、SpringAOP_五种通知类型

 AspectJ实现AOP共有五种通知类型:

测试五种通知类型

五、SpringAOP_切点表达式

六、SpringAOP_多切面配置

七、SpringAOP_注解配置AOP

1.Spring可以使用注解代替配置文件配置切面:

2.如何为一个类下的所有方法统一配置切点:

3.配置类如何代替xml中AOP注解支持?

八、知识点整理:


一、SpringAOP_AOP简介

 AOP的全称是Aspect Oriented Programming,即面向切面编程。是实现功能统一维护的一种技术,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

作用:在不修改源码的基础上,对已有方法进行增强。
实现原理:动态代理技术。
优势:减少重复代码、提高开发效率、维护方便
应用场景:事务处理、日志管理、权限控制、异常处理等方面。

二、SpringAOP_术语

  AOP的相关术语:(前4个常用,后3个不常用)

名称说明
JoinPoint(连接点)指能被拦截到的点,在Spring中只有方法能被拦截
Pointcut(切点)指要对那些连接点进行拦截,即被增强的方法
Advice(通知)指拦截后要做的事情,即切点被拦截后执行的(后置、前置、、)方法
Aspect(切面)切点+通知称为切面
Target(目标)

被代理的对象

Proxy(代理)代理对象
Weaving(织入)生成代理对象的过程

以下一段配置文件的嵌套代码更好地理解上述的相关术语: 

<!--通知对象-->
        <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice"></bean>

        <!--配置AOP-->
        <aop:config >
                <!--配置切面-->
                <aop:aspect ref="myAspectJAdvice">
                        <!--配置切点-->
                        <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
                        <!--配置通知-->
                        <!--前置通知-->
<!--                        <aop:before method="myBefore" pointcut-ref="myPointcut"></aop:before>-->
                        <!--后置通知1-->
                        <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"></aop:after-returning>
                        <!--异常通知-->
<!--                        <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="ex"></aop:after-throwing>-->
                        <!--最终通知-->
<!--                        <aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after>-->
                        <!--环绕通知-->
<!--                        <aop:around method="myAround" pointcut-ref="myPointcut"></aop:around>-->
                </aop:aspect>
        </aop:config>

三、SpringAOP_AOP入门 

AspectJ是一个基于Java语言的AOP框架,在Spring框架中建议使用AspectJ实现AOP
接下来我们写一个 AOP 入门案例: dao 层的每个方法结束后都可以打印一条日志

1.引入依赖:

<dependencies>
        <!--spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.13</version>
        </dependency>

        <!--aspectj-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.编写连接点

@Repository("userDao")//注解方式将UserDao放入Spring容器中
public class UserDao {
    public void add(){
        System.out.println("添加方法");
    }

    public void delete(){
        System.out.println("删除方法");
    }

    public void update(){
        int i=1/0;
        System.out.println("修改方法");
    }
}

3.编写通知类

//通知类
public class MyAspectJAdvice {
    // 后置通知
    public void myAfterReturning() {
        System.out.println("打印日志...");
   }
}

4.配置文件中配置切面

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
       http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--   扫描包,为包下的所有类配置扫描包,扫描到该注解才能生效   -->
        <context:component-scan base-package="com.itbaizhan"></context:component-scan>

        <!--设置通知对象   id:对象名    class:通知类路径名-->
        <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice"></bean>

        <!--配置AOP-->
        <aop:config >
                <!--配置切面   ref:指定通知类是哪一个类-->
                <aop:aspect ref="myAspectJAdvice">
                        <!--配置切点  id:为切点起名字   expression:设置哪些方法为切点-->
                        <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
                        <!--配置通知-->
                        <!--后置通知1   method:设置哪个方法为后置通知   pointcut-ref:引入要切入的切点(将通知切入到要切入的切点中)-->
                        <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"></aop:after-returning>
                </aop:aspect>
</beans>

5.测试:

public class UserDaoTest {

    @Test
    public void testAdd(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.add();
    }

    @Test
    public void testDelete(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.delete();
    }

    @Test
    public void testUpdate(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.update();
    }

6.测试testAdd方法结果:

如上所示:我们可以看到,执行userDao.add()方法本来只会打印添加方法,可最后却打印出了MyAspectJAdvice类中的方法“打印日志”语句,说明AOP后置通知切入到切点中了。

也就表明,AOP可以在不修改源码的情况下对已有方法进行增强。

四、SpringAOP_五种通知类型

 AspectJ实现AOP共有五种通知类型:

通知类型描述
前置通知在方法(切点)执行前添加功能
后置通知在方法(切点)正常执行后添加功能
异常通知在方法抛出异常后添加功能
最终通知无论方法是否抛出异常,都会执行该通知,相当于finally
环绕通知在方法执行前后添加功能

测试五种通知类型

1.编写通知方法

//通知类
public class MyAspectAdvice {

//    后置通知
    
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("切点方法名"+joinPoint.getSignature().getName());
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("打印日志"+joinPoint.getSignature().getName()+"方法被执行了!");
    }

//    前置通知
    public void myBefore(){
        System.out.println("前置通知。。。");
    }

//    异常通知
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知。。。");
        System.err.println(ex.getMessage());//err:打印红色字体
    }

//    最终通知
    public void myAfter(){
        System.out.println("最终通知");
    }

//    环绕通知
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
        System.out.println("环绕前");
        Object obj = proceedingJoinPoint.proceed();//执行方法
        System.out.println("环绕后");
        return obj;
    }
}

2.配置文件bean.xml中配置切面

<!--设置通知对象   id:对象名    class:通知类路径名-->
        <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice"></bean>

        <!--配置AOP-->
        <aop:config >
                <!--配置切面   ref:指定通知类是哪一个类-->
                <aop:aspect ref="myAspectJAdvice">
                        <!--配置切点  id:为切点起名字   expression:设置哪些方法为切点-->
                        <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
                        <!--配置通知-->
                        <!--前置通知-->
                        <aop:before method="myBefore" pointcut-ref="myPointcut"></aop:before>
                        <!--后置通知1   method:设置哪个方法为后置通知   pointcut-ref:引入要切入的切点(将通知切入到要切入的切点中)-->
                        <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"></aop:after-returning>
                        <!--异常通知   注意:要配置throwing:传入通知方法中的参数,将返回内容封装到参数中-->
                        <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="ex"></aop:after-throwing>
                        <!--最终通知-->
                        <aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after>
                        <!--环绕通知-->
                        <aop:around method="myAround" pointcut-ref="myPointcut"></aop:around>
                </aop:aspect>
        </aop:config>

3.测试方法

public class UserDaoTest {

    @Test
    public void testAdd(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.add();
    }

    @Test
    public void testDelete(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.delete();
    }

    @Test
    public void testUpdate(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.update();
    }

五、SpringAOP_切点表达式

使用AspectJ需要使用切点表达式配置切点位置,写法如下:
标准写法:访问修饰符 返回值 包名.类名.方法名(参数列表)
注意要点:
1.访问修饰符可以省略。
2.返回值使用 * 代表任意类型。
3.包名使用 * 表示任意包,多级包结构要写多个 * ,使用 *.. 表示任意包结构
4.类名和方法名都可以用 * 实现通配。
5.参数列表基本数据类型直接写类型,如:int、float
                 引用类型写 包名.类名      ,如:java.lang.String
                 * 表示匹配一个任意类型参数
                 .. 表示匹配任意类型任意个数的参数
6.全通配: * *..*.*(..)
 <!--设置通知对象   id:对象名    class:通知类路径名-->
        <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice"></bean>

        <!--配置AOP-->
        <aop:config >
                <!--配置切面   ref:指定通知类是哪一个类-->
                <aop:aspect ref="myAspectJAdvice">
                        <!--配置切点  id:为切点起名字   expression:设置哪些方法为切点-->
                        <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
                        <!--配置通知-->
                        <!--后置通知1   method:设置哪个方法为后置通知   pointcut-ref:引入要切入的切点(将通知切入到要切入的切点中)-->
                        <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"></aop:after-returning>
                </aop:aspect>

如上切点表达式中表示:com.itbaizhan.dao.UserDao类下任意返回值类型的方法的所有方法任意参数类型

int com.itbaizhan.dao.UserDao.*(..)表示只有返回值为int类型的方法可以被拦截

* com.*.*UserDao.*(int) 表示com包下的任意包下的UserDao类的任意返回值类型参数类型为int方法

* *..UserDao.*(..)表示任意包结果下的UserDao类下的所有任意类型任意参数的方法

六、SpringAOP_多切面配置

 我们可以为切点配置多个通知,形成多切面,比如希望dao层的每个方法结束后都可以打印日志并发送邮件:

1.编写发送邮件的通知类

//通知类2
public class MyAspectAdvice2 {
//    后置通知
    public void myAfterReturning2(){
        System.out.println("发送邮件");
    }
}

2.编写打印日志的通知类

public class MyAspectAdvice {

//    后置通知
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("打印日志。。");
    }
}

3.配置多切面

<!--设置通知对象   id:对象名    class:通知类路径名-->
        <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice"></bean>
        <bean id="myAspectJAdvice2" class="com.itbaizhan.advice.MyAspectAdvice2"></bean>

        <!--配置AOP-->
        <aop:config >
                <!--配置切面(第一个切面)   ref:指定通知类是哪一个类-->
                <aop:aspect ref="myAspectJAdvice">
                        <!--配置切点  id:为切点起名字   expression:设置哪些方法为切点-->
                        <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
                        <!--配置通知-->
                        <!--后置通知1   method:设置哪个方法为后置通知   pointcut-ref:引入要切入的切点(将通知切入到要切入的切点中)-->
                        <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"></aop:after-returning>
                </aop:aspect>
                <!--配置切面(第二个切面)   ref:指定通知类是哪一个类-->
                <aop:aspect ref="myAspectJAdvice2">
                        <!--配置切点-->
                        <aop:pointcut id="myPointcut2" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
                        <!--后置通知2-->
                        <aop:after-returning method="myAfterReturning2" pointcut-ref="myPointcut2"></aop:after-returning>
                </aop:aspect>
        </aop:config>

4.测试

    @Test
    public void testAdd(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.add();
    }

5.测试结果

七、SpringAOP_注解配置AOP

1.Spring可以使用注解代替配置文件配置切面:

1.在xml中开启AOP注解支持
<!--开启注解配置AOP-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2.在通知类上方加入注解 @Aspect

3.在通知方法上方加入注解@Before/@AfterReturning/@AfterThrowing/@After/@Around

@Aspect//注解方式配置通知需要在类上方标明@Aspect
@Component//把此类放入容器中
public class MyAspectAdvice {

//    后置通知
    @AfterReturning("execution(* com.itbaizhan.dao.UserDao.*(..))")
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("切点方法名"+joinPoint.getSignature().getName());
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("打印日志"+joinPoint.getSignature().getName()+"方法被执行了!");
    }

//    前置通知
    @Before("execution(* com.itbaizhan.dao.UserDao.*(..))")
    public void myBefore(){
        System.out.println("前置通知。。。");
    }

//    异常通知
    @AfterThrowing(value = "execution(* com.itbaizhan.dao.UserDao.*(..))",throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知。。。");
        System.err.println(ex.getMessage());//err:打印红色字体
    }

//    最终通知
    @After("execution(* com.itbaizhan.dao.UserDao.*(..))")
    public void myAfter(){
        System.out.println("最终通知");
    }

//    环绕通知
    @Around("execution(* com.itbaizhan.dao.UserDao.*(..))")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
        System.out.println("环绕前");
        Object obj = proceedingJoinPoint.proceed();//执行方法
        System.out.println("环绕后");
        return obj;
    }
}

4.测试方法:

     @Test
    public void testAdd2(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.add();
    }

2.如何为一个类下的所有方法统一配置切点:

1.在通知类中添加方法配置切点

2.在通知方法上使用定义好的切点

@Aspect//注解方式配置通知需要在类上方标明@Aspect
@Component//把此类放入容器中
public class MyAspectAdvice {

//    切点方法
    @Pointcut("execution(* com.itbaizhan.dao.UserDao.*(..))")
    public void pointCut(){
    }

//    后置通知
    @AfterReturning("pointCut()")
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("切点方法名"+joinPoint.getSignature().getName());
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("打印日志"+joinPoint.getSignature().getName()+"方法被执行了!");
    }

//    前置通知
    @Before("pointCut()")
    public void myBefore(){
        System.out.println("前置通知。。。");
    }

//    异常通知
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知。。。");
        System.err.println(ex.getMessage());//err:打印红色字体
    }
}

3.配置类如何代替xmlAOP注解支持?

<!--开启注解配置AOP-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在配置类上方添加@EnableAspectJAutoProxy即可
@Configuration//标明此类为配置类
@ComponentScan("com.itbaizhan")//包扫描
@EnableAspectJAutoProxy//用注解的方式开启配置类代替xml文件配置AOP
public class SpringConfig {
}

测试方法:

    @Test
    public void testAdd3(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserDao userDao = (UserDao)ac.getBean("userDao");
        userDao.add();
    }

八、知识点整理:

1.AOP的实现原理是“动态代理技术”

2.AOP的作用是“在不修改源代码的情况下对已有方法进项增强”

3.在AOP中,切面指“切点+通知”

4.在AOP中,通知指“切点被拦截后执行的方法”

5.AOP五种通知类型“前置通知”、“后置通知”、“异常通知”、“最终通知”、“环绕通知”

6.AOP切点表达式的标准写法是“访问修饰符 返回值类型 包名.类名.方法名(参数类型)”

7.使用AspectJ实现AOP时,使用<aop:aspect>标签配置切面

8.在xml中开启AOP注解支持的标签为:“<aop:aspectj-autoproxy>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值