SpringAop

1.动态代理

  实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。

                    jdk动态代理要求目标类必须实现接口

  cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。

                           子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的

2.动态代理的作用:

   1)在目标类源代码不改变的情况下,增加功能。

         2)减少代码的重复

         3)专注业务逻辑代码

         4)解耦合,让你的业务功能和日志,事务非业务功能分离。

3.Aop:面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。

  Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,

  让开发人员用一种统一的方式,使用动态代理。

4. AOP(Aspect Orient Programming)面向切面编程

  Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。

          切面的特点: 一般都是非业务方法,独立使用的。

  Orient:面向, 对着。

  Programming:编程

  oop: 面向对象编程

  怎么理解面向切面编程 ?

   1)需要在分析项目功能时,找出切面。

   2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)

   3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

  术语:

   1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,

                        常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。

   2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法

   3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法

   4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象

   5)Advice:通知,通知表示切面功能执行的时间。

切面有三个关键的要素

   1)切面的功能代码,切面干什么

   2)切面的执行位置,使用Pointcut表示切面执行的位置

   3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

 5.aop的实现

   aop是一个规范,是动态的一个规范化,一个标准

         aop的技术实现框架:

         1.spring:spring在内部实现了aop规范,能做aop的工作。

                   spring主要在事务处理时使用aop。

                   我们项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。

         2.aspectj 一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。

               aspectJ框架实现aop有两种方式:

                    1.使用xml的配置文件 : 配置全局事务

                    2.使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。

 6.学习aspectj框架的使用。

   1)切面的执行时间, 这个执行时间在规范中叫做Advice(通知,增强)

            在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签

                  1)@Before

                  2)@AfterReturning

                  3)@Around

                  4)@AfterThrowing

                  5)@After

   2)表示切面执行的位置,使用的是切入点表达式。

                  com.service.impl

                  com.dyx.service.impl

                  cn.crm.dyx.service

                  execution(* *..service.*.*(..))

7.实际应用

使用aspect实现aop的基本步骤:

1.新建maven项目

2.加入依赖
        1 ) spring依赖

        2 ) aspectj依赖

        3 ) junit单元测试
3.创建目标类:接口和他的实现类。要做的是给类中的方法增加功能
4.创建切面类:普通类
        1)在类的上面加入@Aspect
        2)在类中定义方法,方法就是切面要执行的功能代码在方法的上面加入aspectj中的通知注解,例如@Before有需要指定切入点表达式execution()

5.创建spring的配置文件:声明对象,把对象交给容器统―管理声明对象你可以使用注解或者xml配置文件<bean>
        1)声明目标对象
        2)声明切面类对象
        3)声明aspectj框架中的自动代理生成器标签。
           自动代理生成器:用来完成代理对象的自动创建功能的。

6.创建测试类,从spring容器中获取目标对象(实际就是代理对象)。通过代理执行方法,实现aop的功能增强。
​​​​​​​前期准备:加入依赖

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>

1、在applicationContext.xml中声明bean

<?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-4.3.xsd

       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd"
       >

    <!--目标类-->
    <bean id="userDao" class="com.hxci.aop.dao.impl.UserDaoImpl"/>
    <bean id="studentDao" class="com.hxci.aop.dao.impl.StudentDaoImpl"/>
    <bean id="scoreDao" class="com.hxci.aop.dao.impl.ScoreDaoImpl"/>


    <!--切面类-->
    <bean id="myAspect" class="com.springmvc.aop.xml.bean.MyAspectImpl"/>

    <!--代理-->
    <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--指定代理实现的接口-->
        <property name="proxyInterfaces" value="com.hxci.aop.dao.UserDao"/>
        <!--指定目标对象,切入点-->
        <property name="target" ref="userDao"/>
        <!--指定切面,织入环绕通知-->
        <property name="interceptorNames" value="myAspect"/>
    </bean>

    <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
        创建代理对象是在内存中实现的,修改目标对象的内存中的结构。
        创建为代理对象所以目标对象就是被修改后的代理对象.

        aspectj-autoproxy :会把spring容器中的所有的目标对象,一次性都生成代理对象。
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>


</beans>

2、编写接口,并编写接口的实现类

1)接口

//用户接口
public interface UserDao {
    public void addUser();
    public void deleteUser();
}

//学生接口
public interface StudentDao {
    public void addStudent();
    public void deleteStudent();
}

//成绩接口
public interface ScoreDao {
    public void addScore();
    public void deleteScore();
}

2)接口实现类

//用户实现类
@Repository("userDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void addUser() {
        System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("=====用户管理模块,删除用户=====");
    }
}

//学生实现类
@Repository("studentDao")
public class StudentDaoImpl implements StudentDao {
    @Override
    public void addStudent() {
        System.out.println("添加学生");
    }

    @Override
    public void deleteStudent() {
        System.out.println("=====学生管理模块,删除学生=====");
    }
}

//成绩实现类
@Repository("scoreDao")
public class ScoreDaoImpl implements ScoreDao {
    @Override
    public void addScore() {
        System.out.println("添加成绩");
    }

    @Override
    public void deleteScore() {
        System.out.println("=====成绩管理模块,删除成绩=====");
    }
}

3、创建切面类

/**
 * @Aspect:是框架中的注解
 *  作用:表示当前类是切面类
 *  切面类:是用来给业务方法增加功能的类,
 */
@Aspect
public class MyAspect  {
    /**
     * 定义方法,方法是实现切面功能的。
     * 方法的定义要求;
     * 1.公共方法 public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法可以有参数,也可以没有参数。
     *   如果有参数,参数不是自定义的,有几个参数类型可以使用。
     */

    /**
     * Before:前置通知注解
     *  属性: value,是切入点表达式,表示切面的功能执行的位置。
     *  位置:在方法的上面
     * 特点:
     * 1.在目标方法之前先执行的
     * 2.不会改变目标方法的执行结果
     * 3.不会影响目标方法的执行。
     *
     * execution("访问修饰符 返回值类型 所在具体包.具体方法.(方法参数)")
     */

    /**
     * 指定通知方法中的参数:JoinPoint
     * Joinpoint:业务方法,要加入切面功能的业务方法
     *      作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参。
     *      如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
     *      这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
     */
    //@Before(value = "execution(public void com.hxci.aop.dao.impl.UserDaoImpl.deleteUser(..))")
    //@Before(value = "execution(void com.hxci.aop.dao.impl.UserDaoImpl.deleteUser(..))")
    //@Before(value = "execution(void *..hxci.aop.dao.impl.UserDaoImpl.deleteUser(..))")
    //@Before(value = "execution(* *..hxci.aop.dao.impl.UserDaoImpl.delete*(..))")
    @Before(value = "execution(* *..hxci.*..UserDaoImpl.delete*(..))")
    public void Bf(JoinPoint jp){
        System.out.println("前置通知,如执行权限检查,被织入增强处理的目标方法为:");
        System.out.println("方法定义:"+jp.getSignature());
        System.out.println("方法名称:"+jp.getSignature().getName());

        //jp.getArgs();  获取方法的实参,可以用for循环输出

    }


    /**
     * @AfterReturning
     *  属性:1.value切入点表达式
     *      2.returning自定义的变量,表示目标方法的返回值的。
     *       自定义变量名必须和洒知方法的形参名一样。
     *  位置:在方法定义的上面
     * 特点:
     *  1.在目标方法之后执行的。
     *  2能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
     *  3.可以修改这个返回值
     */
    @AfterReturning(value = "execution(* com.hxci.*..UserDaoImpl.deleteUser(..))")
    public void log() {
        //object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:记录日志--------");
    }


    /**
     * 环绕通知方法定义格式:
     *  1.public
     *  2.必须有一个返回值,推荐使用object
     *  3.方法名称自定义
     *  4.方法有参数,固定的参数ProceedingjoinPoint
     */
    /**
     * @Around:环绕通知
     *      属性: value切人点表达式
     *      位置:在方法的定义什么
     *  特点;
     *      1.它是功能最强的通知
     *      2.在目标方法的前和后都能增强功能。
     *      3.控制目标方法是否被调用执行
     *      4.修改原来的目标方法的执行结果。影响最后的调用结果
     *
     *      环绕通知,等同于jdk动态代理的,InvocationHandLer接口
     *
     *      参数: ProceedingJoinPoint 就等同于Method
     *      作用: 执行目标方法的
     *      返回值:就是目标方法的执行结果,可以被修改。
     */
    @Around(value = "execution(* com.hxci.*..UserDaoImpl.deleteUser(..))")
    public Object Ad(ProceedingJoinPoint pjp) throws Throwable {
        //实现环绕通知
        Object result = null;

        //1.目标方法调用
        System.out.println("环绕通知:在目标方法之前,如开启事务");
        result = pjp.proceed();  //method.invoke();
        System.out.println("环绕通知:在目标方法之后,提交事务");

        //2.在目标方法前/后,加功能

        //返回目标方法执行结果
        return result;
    }
}

4、测试类

public class TestXml {
    public static void main(String[] args) {
        String xmlPath = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
        UserDao userDao = (UserDao)ac.getBean("userDao");

        //jdk的动态代理
        System.out.println("AOP目标类是:"+userDao.getClass().getName());
        userDao.deleteUser();

    }
}

5、运行结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值