SSM框架学习-4

SSM框架学习-4

  • 使用AspectJ实现AOP

注解方式
XML方式

1.AspectJ简介

  • AspectJ是一个基于Java语言的AOP框架
  • Spring2.0以后新增了对AspectJ切点表达式支持
  • @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
  • 新版本Spring框架,建议使用AspectJ方式来开发AOP
  • 使用AspectJ 需要导入SpringAOP和AspectJ相关jar包

-spring-aop-4.2.4.RELEASE.jar

  • com.springsource.org.aopalliance-1.0.0.jar
  • spring-aspects-4.2.4.RELEASE.jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

2.注解开发 :环境准备

applicationContext 配置如下:

<?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">
<!-- 开启AspectJ自动代理-->
<aop:aspectj-autoproxy />
</beans>

依赖添加:

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

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

    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>

    <dependency>
      <groupId>aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.5.4</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>

3.AspectJ提供不同的通知类型

  • @Before 前置通知,相当于BeforeAdvice
  • @AfterReturning 后置通知,相当于AfterReturningAdvice
  • @Around 环绕通知,相当于MethodInterceptor
  • @AfterThrowing异常抛出通知,相当于ThrowAdvice
  • @After 最终final通知,不管是否异常,该通知都会执行
  • @DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)

4.在通知中通过value属性定义切点

  • 通过execution函数,可以定义切点的方法切入
  • 语法:

execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)

  • 例如:
  • 匹配所有类public方法 execution(public * *(..))
  • 匹配指定包下所有类方法 execution(* com.imooc.dao.*(..)) 不包含子包
  • execution(* com.imooc.dao..*(..)) ..*表示包、子孙包下所有类
  • 匹配指定类所有方法 execution(* com.imooc.service.UserService.*(..))
  • 匹配实现特定接口所有类方法execution(* com.imooc.dao.GenericDAO+.*(..))
  • 匹配所有save开头的方法 execution(* save*(..))

在增强类前加上@Aspect,在增强方法前添加AspectJ提供不同的通知类型,value中使用execution定义切点的方法切入。

package com.snake_lvyonghao.demo1;

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

/**
 * 切面类
 */

@Aspect
public class MyAspectDao {

    @Before(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.save())(..))")
    public  void before(){
        System.out.println("before通知");
    }

}


实现类没啥好说的,随意放了几个方法

package com.snake_lvyonghao.demo1;

public class ProductDao {

    public void find(){
        System.out.println("find");
    }

    public void findAll(){
        System.out.println("findAll");
    }

    public void save(){
        System.out.println("save");
    }

    public void update(){
        System.out.println("update");
    }

    public void delete(){
        System.out.println("delete");
    }
}


配置文件在之前配置的基础上加上目标类的Bean和切面类的Bean就好

    <!--目标类-->
    <bean id="productDao" class="com.snake_lvyonghao.demo1.ProductDao"/>

    <!--定义切面-->
    <bean class="com.snake_lvyonghao.demo1.MyAspectDao" id="aspectDao"/>

  • ps可以在增强方法中传入JoinPoint对象,用来获得切点信息:(JionPoint joinpoint)
    @Before(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.save())(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("before通知" + joinPoint);
    }
    
  • 如果是后置通知可以在参数中添加returning属性,可以定义方法返回值,作为参数
    @AfterReturning(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.update())(..)",returning = "result")
    public void afterReturing(Object result){
        System.out.println("后置通知" + result);
    }
  • 使用环绕通知的时候方法必须有返回值,返回值就是执行目标方法,方法参数中要穿入ProceedingJoinPoint对象,并在函数体中调用proceed()获取执行目标方法,如果不调用ProceedingJoinPoint的proceed方法,那么目标方法就被拦截了。
    @Around(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.delete(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("环绕前通知");

        //执行目标方法
        Object obj = joinPoint.proceed();

        System.out.println("环绕后通知");

        return obj;
    }
  • 异常跑出通知,thowing属性可以捕获异常
    @AfterThrowing(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.find(..))",throwing = "e")
    public void afterThrowing(Throwable e){
        System.out.println("异常抛出通知" + e.getMessage());
    }

  • 最终通知,无论目标方法上是否有异常,最终通知总会运行
    @After(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.findAll(..))")
    public void After(){
        System.out.println("finally最终通知");
    }

5.通过@pointcut为切点命名

  • 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切 点,可以使用@Pointcut进行定义
  • 切点方法:private void无参数方法,方法名为切点名
  • 当通知多个切点时,可以使用||进行连接
  • 简单来说就是通过poincut确定一个或一组切点,以后在value属性当中只需要写如被@pointcut标记的方法名就好了
    @Pointcut(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.findAll(..))")
    private void myPoinCut1(){};

想用于findAll这个切点的,value填写myPointCut1即可


    @Pointcut(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.update())(..)")
    private void myPointCut1(){}

    @Pointcut(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.save())(..)")
    private void myPointCut2(){}

    @Pointcut(value = "execution(* com.snake_lvyonghao.demo1.ProductDao.delete())(..)")
    private void myPointCut3(){}

6.基于AspectJ的XML方式的AOP开发

相比于使用注释的开发方式,还有XML配置文件的开发方式,在这样的方法中,我们的增强类只需要写出增强的方法即可

    public void before(){
        System.out.println("XML方式的前置增强==============");
    }

    public void afterReturing(Object obj){
        System.out.println("XML方式的后置增强==============");
    }

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("环绕增强前=====================");
        Object obj = joinPoint.proceed();
        System.out.println("环绕后增强=====================");
        return obj;
    }

    public void afterThrowing(Throwable e){
        System.out.println("异常抛出通知===================" + e.getMessage());
    }

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

在XML的配置文件中不使用之前的aop自动代理方式,而是使用XML格式来告诉程序我的切点在哪,我要用什么样子的通知,同样先对目标类和切面进行Bean配置,在<aop:config>当中进行配置,其次配置切入点<aop:pointcut id="pointcut1" expression="execution(* com.snake_lvyonghao.demo2.CustomDao.save(..))"/>和之前在增强类中的配置是基本一致的,然后切点配置好后就要配置通知了在<aop:aspect ref="myAspectXML">中设置你的各种通知,ref内是你的增强类配置文件中的ID
eg:<aop:before method="before" pointcut-ref="pointcut1"/>
下面放上完整的配置文件,和增强类方法:

    <!--XML的配置方式完成AOP的开发-->
    <!--目标类-->
    <bean class="com.snake_lvyonghao.demo2.CustomDaoImpl" id="customDao"/>
    <!--定义切面-->
    <bean class="com.snake_lvyonghao.demo2.MyAspectXML" id="myAspectXML"/>

    <!--aop配置-->

    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pointcut1" expression="execution(* com.snake_lvyonghao.demo2.CustomDao.save(..))"/>
        <aop:pointcut id="pointcut2" expression="execution(* com.snake_lvyonghao.demo2.CustomDao.update(..))"/>
        <aop:pointcut id="pointcut3" expression="execution(* com.snake_lvyonghao.demo2.CustomDao.delete(..))"/>
        <aop:pointcut id="pointcut4" expression="execution(* com.snake_lvyonghao.demo2.CustomDao.findOne(..))"/>
        <aop:pointcut id="pointcut5" expression="execution(* com.snake_lvyonghao.demo2.CustomDao.findAll(..))"/>
        <!--配置切面类-->
        <aop:aspect ref="myAspectXML">
            <!--配置前置增强-->
            <aop:before method="before" pointcut-ref="pointcut1"/>
            <aop:after-returning method="afterReturing" pointcut-ref="pointcut2" returning="obj"/>
            <aop:around method="around" pointcut-ref="pointcut3"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/>
            <aop:before method="after" pointcut-ref="pointcut5" />
        </aop:aspect>
    </aop:config>

    public void before(){
        System.out.println("XML方式的前置增强==============");
    }

    public void afterReturing(Object obj){
        System.out.println("XML方式的后置增强==============");
    }

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("环绕增强前=====================");
        Object obj = joinPoint.proceed();
        System.out.println("环绕后增强=====================");
        return obj;
    }

    public void afterThrowing(Throwable e){
        System.out.println("异常抛出通知===================" + e.getMessage());
    }

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

SSM框架源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值