入门Spring框架的AOP

什么是AOP的技术

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
AOP是一种编程范式,最早由AOP联盟组织提出的一套规范.
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,
提高程序的可重用性,同时提高了开发的效率,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查)

AOP的底层实现

Srping框架的AOP技术底层也是采用的代理技术,代理的方式提供了两种
基于JDK的动态代理:
必须是面向接口的,只有实现了具体接口的类才能生成代理对象
基于CGLIB动态代理
对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式,Spring的核心包已经集成了cglib的包
具体采用哪种是由Spring来决定的,如果代理的类实现了接口则使用JDK动态代理完成AOP, 如果没有实现接口则采用CGLIB动态代理完成AOP

AOP基于XML方式的实现

步骤一:引入maven依赖

tip:这里并没有使用SpringBoot

<dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>
        <!-- StringUtils-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.5.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.7.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

步骤二:定义目标类和切面类

//定义目标类,对其方法进行日志处理
public class UserDao {
		public void saveUser() {
      		  System.out.println("保存用户成功...");
  		  }
}
//切面类
public class MyAspectXml {
    // 定义通知
    public void log() {
        System.out.println("记录日志...");
    }
}

步骤三:配置spring的核心配置类

<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd">
	 <!-- 注册目标类 -->
    <bean id="userDao" class="springaop.UserDao"></bean>
    <!--切面类-->
    <bean id="myAspectXml" class="springaop.MyAspectXml"></bean>
	 
	 <!-- 完成aop的配置 -->
    <aop:config>
        <!-- 引入切面类 -->
        <aop:aspect ref="myAspectXml">
            <!--
                aop:after :定义通知类型,常用的有5种通知类型,后面会介绍
                method:切面类的方法
                pointcut:切入点的表达式,用于指定要切入的目标类的方法, execution()固定写法,里面的表达式可以有简写方式
            -->
            <aop:after method="log" pointcut="execution(public void springaop.UserDao.saveUser())"/>
        </aop:aspect>
    </aop:config>
    
</beans>

测试

@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TestUserDao {
    @Autowired
    private UserDao userDao; // 通过ioc注入值(name的值对应的是xml配置中bean的id属性)

    /*
    这里如果打印了
       切面类MyAspectXml里面的
       System.out.println("记录日志...");则测试Aop成功
     */
    @Test
    public void test1() {
        userDao.saveUser();
    }
}

测试通过
这样打印,则表示测试成功

5种AOP通知类型

  • 前置通知
    在目标类的方法执行之前执行。
    配置文件信息:<aop:before />

  • 最终通知
    在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
    在配置文件中编写具体的配置:<aop:after />

  • 后置通知
    方法正常执行后的通知。
    在配置文件中编写具体的配置:<aop:after-returning />

  • 异常抛出通知
    在抛出异常后通知
    在配置文件中编写具体的配置:<aop:after-throwing />

  • 环绕通知
    方法的执行前后执行。
    在配置文件中编写具体的配置:<aop:around />
    需要手动使用ProceedingJoinPoint对来让目标对象的方法执行

切入点表达式

完整表达式为:execution([权限修饰符] 返回值类型 包名.类名.方法名(参数))
简写规则如下:
1.权限修饰符非必填,如果目标方法是private的,则必填
2.返回值类型必填,根据你的方法来编写返回值。但是可以使用代替。
3. com.
.saveUser 可以匹配到 com.UserDao.saveUser
*…*则表示任意的包的结构 如: .saveUser() 也可以匹配到 com.UserDao.saveUser
4.类名必填,也可以使用 * 号代替任何,同样有类似的写法:Dao表示以Dao结尾的类名
5.方法必填,也可以使用 * 号代替任何,或者以save
表示以save开头的方法名
6.参数非必填,如果是一个参数可以使用 *号代替,如果想代表任意参数使用 …

AOP基于注解方式的实现

简单来说:就是省去了在xml中配置aop:config,通过注解来实现,同时需要在核心配置来进行开启Aop注解化

步骤一:引入maven依赖(这里和上面的依赖一样)

步骤二:定义目标类和切面类

//定义目标类,对其方法进行日志处理
public class UserDao {
		public void saveUser() {
      		  System.out.println("保存用户成功2...");
  		  }
}

//切面类(与XML不同的是,这里要加上@Aspect标签)
@Aspect
public class MyAspectXml {
    /**
     * 定义通知方法,常用的有
     * @Before:前置通知
     * @AfterReturing:后置通知
     * @Around:环绕通知
     * @After:最终通知
     * @AfterThrowing:异常抛出通知
     *
     * value:切入点表达式(这里也可以使用通配符)
     */
     @Before(value = "execution(public void springaop.userdao.updateUser())")
    public void log() {
        System.out.println("记录日志2...");
    }
}

步骤三:配置spring的核心配置类

<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd">
	
	<!-- 开启注解化的方式配置切面类 -->
	<aop:aspectj-autoproxy/>
	
	 <!-- 注册目标类 -->
    <bean id="userDao" class="springaop.UserDao"></bean>
    <!--切面类-->
    <bean id="myAspectXml2" class="springaop.MyAspectXml2"></bean>
	 
	 </beans>

测试

@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TestUserDao {
    @Autowired
    private UserDao userDao; // 通过ioc注入值(name的值对应的是xml配置中bean的id属性)

    /*
    这里如果打印了
       切面类MyAspectXml2里面的
       System.out.println("记录日志...");则测试Aop成功
     */
    @Test
    public void test1() {
        userDao.saveUser();
    }
}

测试通过
这样打印,则表示测试成功

配置通用的切入点

当切面类多个通知方法都对应同一个切入点时,你会发现你需要重复写很多个相同的切入点表达式,例如:

@Aspect
public class MyAspectXml2 {
    
    @Before(value = "execution(public void springaop.UserDao.updateUser())")
    public void log() {
        System.out.println("前置通知...");
    }

    @Before(value = "execution(public void springaop.UserDao.updateUser())")
    public void log1() {
        System.out.println("后置通知...");
    }

    @Around(value = "execution(public void springaop.UserDao.updateUser())")
    public void log2(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("环绕通知1...");
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕通知2...");
    }

}

这样很麻烦,接下来通过 定义通用的切入点,来更好的处理

@Aspect
public class MyAspectXml2 {
    /**
     * 定义通用的切入点
     */
    @Pointcut(value = "execution(public void springaop.UserDao.updateUser())")
    public void method() {}

    //value不再是切入点表达式了,而是定义好的通用的切入点
    @Before(value = "MyAspectXml2.method()")
    public void log() {
        System.out.println("前置通知...");
    }

    @Before(value = "MyAspectXml2.method()")
    public void log1() {
        System.out.println("后置通知...");
    }

    @Around(value = "MyAspectXml2.method()")
    public void log2(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("环绕通知1...");
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕通知2...");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值