框架学习 三 Spring 、03、aop(maven,mybatis,spring,springmvc)

1.什么是AOP?

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

aop 就是面向切面编程

aop 解决的问题就是将共同重复的代码就行抽离(日志,事务,鉴权)

什么是切面?切面就是一个类,类里面包含类某一类具体业务(日志,事务,鉴权),用这个类 去拦截/增强 你需要监控的方法或者业务

在这里插入图片描述

作用:

  • 1.将共同/重复代码抽取 提高开发i效率
  • 2.调高代码课维护性

切入点:就是切面需要拦截增强的 类的内部的方法

连节点:每一个类里 普通的方法都是 连接点

切面:就是封装某一种业务抽离(日志,事务。鉴权)

增强/通知:就是切面中的方法 对目标对象中的方法进行增强/拦截/通知

织入:是一个过程/动作,就是将切面切向要拦截那些类的方法的过程

目标对象:。。。

代理:。。。。。

在这里插入图片描述

官方:

Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。

Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

Advice(通知/增强处理):AOP框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法。

Target Object(目标对象):指所有被通知的对象,也被称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。

Aspect(切面):封装的用于横向插入系统功能(如事务、日志等)的类

Joinpoint(连接点):在程序执行过程中的某个阶段点

Pointcut(切入点):切面与程序流程的交叉点,即那些需要处理的连接点

2.aop 实现的原理

aop实现就是通过动态代理模式实现

动态代理:有两种

1、 jdk 动态代理 只能代理 目标对象有实现的接口

JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。

-2、 cglib 可以代理类 代理接口

JDK的动态代理用起来非常简单,但它是有局限性的,使用动态代理的对象必须实现一个或多个接口。
为了实现类的代理可以使用CGLIB代理
​ CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。

3.Aop的实现

实现两种

  • 基于自身的ProxyFactoryBean 实现aop功能
  • 借助于aspectj第三方aop框架实现

aspectj 也是一个优秀的aop框架内

4.基于ProxyFactoryBean实现Aop

1. ProxyFactoryBean

ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。

可以配置的属性
在这里插入图片描述
ProxyTargetClass (是否强制使用CGLIB来实现代理)
​ (true : 强制使用CGLIB来实现代理)
​ (false : 不强制使用CGLIB来实现代理,首选JDK来实现代理)(默认值
isOptimize (是否对生成代理策略进行优化)
​ (true : 进行优化,如果有接口就代理接口(使用JDK动态代理),没有接口代理类(CGLIB代理))
​ (false : 不进行优化) (默认值)

ProxyFactoryBean 创建代理对象工厂,继承FactoryBean

FactoryBean:作用是创建bean,容器加载时所有的对象都是由FactoryBean的子类创建

BeanFactory:是spring的核心容器之一,顶层接口,作用主要是让用户获取bean,判断bean 是否单例

实现

引入依赖

    <properties>
        <!--在当前pom 或者父类pom 中声明属性  -->
        <spirng.version>5.0.16.RELEASE</spirng.version>
    </properties>


    <dependencies>

        <!-- spring 核心包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spirng.version}</version>
        </dependency>

        <!-- 导入spring aop 依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spirng.version}</version>
        </dependency>

    </dependencies>

1.创建切面

/**
 * 创建一个切面
 *
 * 切面放置的就是 对目标对象方法增强业务 (日志 事务)
 */
public class MyAspect implements MethodInterceptor {

    // 反射执行的方法
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {



        System.out.println("MyInspect----"+methodInvocation.getMethod().getName()+"开始执行");
        // 让目标对象执行
        Object result =  methodInvocation.proceed();
        System.out.println("MyInspect--result:"+result);

        System.out.println("MyInspect----"+methodInvocation.getMethod().getName()+"结束执行");


        return result;
    }
}

2.在xml中织入切面和目标对象

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">




    <!-- 声明 一个bean  -->
    <bean id="studentDao" class="com.qfedu.dao.IStudentDaoImpl">

    </bean>

    <!-- 声明一个切面-->

    <bean id="myAspect"  class="com.qfedu.proxyfactorybean.MyAspect">

    </bean>

    <!--
      ProxyFactoryBean 作用就是创建 代理对象
      通过容器根据id  = proxyBean 就可以得到studentDao 的代理对象
    -->
    <bean id="proxyBean"  class="org.springframework.aop.framework.ProxyFactoryBean">

        <!--
          proxyTargetClass  创建代理对象  true 代表强制使用 cglib
                         false  接口使用jdk代理  类 使用cgLib
        -->
        <property name="proxyTargetClass" value="false"></property>
        <!--
            proxyInterfaces 代理那些接口 可以不写 ,有接口可以写也可以不写  没接口不写
        -->
        <property name="proxyInterfaces" value="com.qfedu.dao.IStudentDao"></property>

        <!--
            target 创建的代理对象 需要让目标对象执行相应方法  需要目标对象引入
         -->
        <property name="target" ref="studentDao"></property>

        <!--
            指定代理类的切面
            value="myAspect"
        -->
        <property name="interceptorNames" value="myAspect" ></property>
    </bean>

</beans>

3.测试

public class ProxyFactoryTest {

    public static void main(String[] args) {


        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("proxyfactoybean/bean.xml");

        // 得到代理对象
        IStudentDao iStudentDao = (IStudentDao) applicationContext.getBean("proxyBean");

        // 让代理对象执行
        iStudentDao.findStudentById(1000);
    }
}

在这里插入图片描述

总结:

为什么会有jdk和CGLIB两种选择?

1.jdk 只能代理类实现的接口的类,而没有实现接口类必须使用CGLIB
2.从性能来说:
CGLIB:创建慢,执行块(CGLIB 要生成对应的代理的代码,在创建代理对象)
JDK:创建快,执行慢(反射本身执行就比正常代码的慢)

5.基于AspectJ开发的实现

AspectJ本身就是一个优秀的aop框架,spring就像胶水一样把优秀的框架引入到自己的生态中

实现:

基于xml实现aop

基于注解实现aop

Spring通知的类型

通知就是切面中的方法

Spring按照通知在目标类方法的连接点位置,可以分为5种类型,具体如下:

  • org.springframework.aop.MethodBeforeAdvice(前置通知)
    在目标方法执行前实施增强,可以应用于权限管理等功能。
  • org.springframework.aop.AfterReturningAdvice(后置通知)
    在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除
    临时文件等功能。
  • org.aopalliance.intercept.MethodInterceptor(环绕通知)
    在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。
  • org.springframework.aop.ThrowsAdvice(异常抛出通知)
    在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
  • org.springframework.aop.IntroductionInterceptor(引介通知)
    在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。
  • 最终通知

基于xml实现aop

1.引入依赖

        <!--引入 aspectj依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>

2.创建切面

/**
 * 切面
 */
public class MyAspect {




    //对目标对象增强的方法

    /**
     * 前置通知
     * 在目标对象调用前 调用
     * @param joinPoint
     */
    public void myBefore(JoinPoint joinPoint){

//        joinPoint  连接点 在这里我们得到的是 切入点    他们都是方法
        // 获取目标对象
//        System.out.println("myBefore-target:"+joinPoint.getTarget());
        System.out.println("myBefore-被调用的方法名:"+joinPoint.getSignature().getName());

    }

    /**
     * 后置通知
     * 目标对象 能偶正常的执行完毕 并可以得到结果
     * @param joinPoint
     */
    public void myAfterReturning(JoinPoint joinPoint,Object result){

//        System.out.println("Returning-target:"+joinPoint.getTarget());
        System.out.println("myAfterReturning-被调用的方法名:"+joinPoint.getSignature().getName());

        System.out.println("myAfterReturning-result"+result);

    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @throws Throwable
     */
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        System.out.println("myAround-被调用的方法名:"+proceedingJoinPoint.getSignature().getName());

        // 让 目标对象执行 相应的方法 如果不执行代表  被拦截
        // 返回值 目标对象执行 的结果
        Object result =  proceedingJoinPoint.proceed();
        System.out.println("myAround-result:"+result);

        // 必须将 目标对象执行结果 返回给代理
        return result;
    }

    /**
     * 异常通知
     *
     * Throwable e 接收 目标对象产生的异常
     * @param joinPoint
     */
    public void  myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("myAfterThrowing-被调用的方法名:"+joinPoint.getSignature().getName());

        // 得到异常信息
        System.out.println("myAfterThrowing-异常信息:"+e.getMessage());
    }

    /**
     * 最终通知 相当于 finally 无论如何都会调用
     * @param joinPoint
     */
    public void myAfter(JoinPoint joinPoint){
        System.out.println("myAfter-被调用的方法名:"+joinPoint.getSignature().getName());

    }

}

3.在xml 将 切面和切点进行织入

?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 https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--声明切面-->
    <bean id="myAspect" class="com.qfedu.aspectj.xml.MyAspect">

    </bean>

    <!-- 目标对象-->
    <bean id="studentDao" class="com.qfedu.dao.IStudentDaoImpl">

    </bean>

    <!--

        基于aspectj xml 实现 所有织入实现功能都必须写在 aop:config
    -->
    <aop:config>
        <!-- 配置 切点  全局切点 可以被多个切面所应用
             切点 就是要拦截的方法

            expression="execution()  切点表达式 就是找到那些需要被拦截的方法
        -->
       <!-- <aop:pointcut id="myPoint" expression="execution(* com.qfedu.dao.*.*(..))"/>-->

        <!-- 配置切面
             切面就是一个类 里面的方法主要用于拦截/增强 目标对象

             ref="myAspect" 引用切面实例 进行织入 过程
        -->
        <aop:aspect id="aopAspect" ref="myAspect">
            <!--
                    局部切点  只能被 当前切面使用
                -->
            <aop:pointcut id="myPoint" expression="execution(* com.qfedu.dao.*.*(..))"/>

            <!--
                前置通知 和 切点绑定
            -->
            <aop:before method="myBefore" pointcut-ref="myPoint"></aop:before>

            <!-- 绑定 后置通知 和切点

             returning="result" 指定后置通知用那个参数 接收目标对象返回的结果
            -->
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPoint" returning="result"></aop:after-returning>

            <!-- 绑定 环绕通知 和切点-->
            <aop:around method="myAround" pointcut-ref="myPoint"></aop:around>

            <!--  绑定异常通知 和 切点-->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPoint" throwing="e"></aop:after-throwing>

            <!--绑定 最终通知 和切点-->
            <aop:after method="myAfter" pointcut-ref="myPoint"></aop:after>
        </aop:aspect>


    </aop:config>




4.测试

public class AspectjXmlTest {
    public static void main(String[] args) {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aspectj/xml/aspectj_bean.xml");

        // 得到代理对象
        IStudentDao iStudentDao = applicationContext.getBean(IStudentDao.class);

       Student student =  iStudentDao.findStudentById(1000);
        System.out.println("student:"+student);

    }
}

基于注解的实现Aop

1.创建

@Component//将切面加入容器
@Aspect//  声明当前类是一个切面 类
public class MyAspect {

    /**
     * 声明一个切点
     *
     */
    @Pointcut("execution(* com.qfedu.dao.*.*(..))") // 声明当前方法是切点 并传递切点表达式
    public void myPoint(){}



    // 通知  将通知和切点绑定

    //对目标对象增强的方法

    /**
     * 前置通知
     * 在目标对象调用前 调用
     * @param joinPoint
     */
    @Before("myPoint()")// 将当前的前置通知 和 切点绑定
    public void myBefore(JoinPoint joinPoint){

//        joinPoint  连接点 在这里我们得到的是 切入点    他们都是方法
        // 获取目标对象
//        System.out.println("myBefore-target:"+joinPoint.getTarget());
        System.out.println("myBefore-被调用的方法名:"+joinPoint.getSignature().getName());

    }

    /**
     * 后置通知
     * 目标对象 能偶正常的执行完毕 并可以得到结果
     * @param joinPoint
     */
    @AfterReturning(pointcut = "myPoint()",returning = "result")
    public void myAfterReturning(JoinPoint joinPoint,Object result){

//        System.out.println("Returning-target:"+joinPoint.getTarget());
        System.out.println("myAfterReturning-被调用的方法名:"+joinPoint.getSignature().getName());

        System.out.println("myAfterReturning-result"+result);

    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @throws Throwable
     */
    @Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        System.out.println("myAround-被调用的方法名:"+proceedingJoinPoint.getSignature().getName());

        // 让 目标对象执行 相应的方法 如果不执行代表  被拦截
        // 返回值 目标对象执行 的结果
        Object result =  proceedingJoinPoint.proceed();
        System.out.println("myAround-result:"+result);

        // 必须将 目标对象执行结果 返回给代理
        return result;
    }

    /**
     * 异常通知
     *
     * Throwable e 接收 目标对象产生的异常
     * @param joinPoint
     */
    @AfterThrowing(pointcut = "myPoint()",throwing = "e")
    public void  myAfterThrowing(JoinPoint joinPoint, Throwable e){
        System.out.println("myAfterThrowing-被调用的方法名:"+joinPoint.getSignature().getName());

        // 得到异常信息
        System.out.println("myAfterThrowing-异常信息:"+e.getMessage());
    }

    /**
     * 最终通知 相当于 finally 无论如何都会调用
     * @param joinPoint
     */
    @After("myPoint()")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("myAfter-被调用的方法名:"+joinPoint.getSignature().getName());

    }


}


2.开启注解功能

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!-- 让@Component 生效-->
    <context:component-scan base-package="com.qfedu"></context:component-scan>


    <!--
        开启注解 aspectj 功能 让 @Aspect 生效
    -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

3.测试

public class AspectjAnnotationTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aspectj/annotation/aspectj_annotation_bean.xml");

        // 得到代理对象
        IStudentDao iStudentDao = (IStudentDao) applicationContext.getBean(IStudentDao.class);

        Student student =  iStudentDao.findStudentById(1000);
        System.out.println("student:"+student);


    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值