Spring+SpringMVC+MyBatis-教程、笔记-8

base黑马,视频链接:https://www.bilibili.com/video/BV1WZ4y1P7Bp?t=94.4
此系列文章可以当做视频的配套笔记,也可以当做自学SSM框架的教程。

aop简介-什么是aop

image.png
image.png

java代码要想运行,会经过编译运行阶段。

AOP是运行期间执行的一种技术。
AOP技术的底层是通过动态代理技术来完成的。
动态代理,在不修改源码的情况下,对目标方法进行相应的增强。
动态代理的作用:完成程序功能之间的松耦合。
动态代理它的作用可以等同于AOP的作用,Spring不用自己再去写动态代理(内部进行了封装)。

比如,我有A功能,也有B功能,我想让A功能和B功能结合到一起,我不把A功能和B功能的代码写到一起,因为写到一起,A和B功能就耦合了。我可以通过AOP技术,让A和B功能在运行期间去动态的进行结合,但是A和B在代码这个层次角度上,它没有结合到一起。
这个就是松耦合。

比如现在有A、B、C、D四个功能,B、C、D都想具有A功能,最终会通过配置的方式让B、C、D都具备A功能。从而提高程序的可重用性。

aop简介-aop的作用及其优势

image.png

目前只要想完成松耦合一般就是通过配置的方式。

image.png
AOP就是完成这样一个动态结合的技术。面向切面编程。🤔
什么是切面?

功能–save(user) 和 目标–日志控制,结合到一起的方法称之为一个切面。(这样解释可以但不是100%的正确)

aop简介-aop的底层实现

image.png
image.png

  • JDK代理:缺点是目标对象必须得有接口
  • cglib代理:第三方动态代理小工具,不需要接口是基于父类的

image.png
image.png

  • cglib技术是为目标对象动态的生成一个子对象,这样子对象和父对象的方法就一致了。子对象的方法、功能比父对象的功能要强大。
  • 上面的示意图表面上像继承,但是底层的实现代码并不是继承。是动态的生成一个对象,这个对象是基于目标对象的。
  • 调用的时候,调用代理对象,代理对象内部再调用目标对象,只不过在调用目标对象之前或者之后进行一些其他功能代码的介入,从而对目标对象进行增强。

aop简介-基于jdk的动态代理

需要一个接口,这个接口得有一个实现,这个实现就是目标对象,最终对目标对象中的某个方法进行增强。要增强的方法最终可以写到动态代理内部,写一个打印就可以。
接口:TargetInterface

package com.lyh.proxy.jdk;

public interface TargetInterface {
    public void save();
}

目标类:Target

package com.lyh.proxy.jdk;

public class Target implements TargetInterface{
    public void save() {
        System.out.println("save running......");
    }
}

增强类:Advice

package com.lyh.proxy.jdk;

//单独的建一个增强的方法
public class Advice {
    public void before(){
        System.out.println("前置增强......");
    }
    public void afterReturning(){
        System.out.println("后置增强......");
    }
}

测试类:ProxyTest

package com.lyh.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        //这个不用导入任何的包
        //本身属于jdk内部的一部分
        //反射包
        //得先写一个目标对象
        final Target target = new Target();
        
        //增强对象
        Advice advice = new Advice();
        //返回值 就是动态生成的代理对象
        //动态生成的代理对象 是 接口的实现
        TargetInterface proxy= (TargetInterface)Proxy.newProxyInstance(
            //字节码对象    类加载器
            target.getClass().getClassLoader(),//目标对象的类加载器
            
            //因为很有可能目标对象不是一个接口,因为java是单继承多实现
            //可能会有多个接口,多个接口的字节码对象我都要
            target.getClass().getInterfaces(),

            new InvocationHandler() {
                //调用代理对象的任何方法 实质执行的都是invoke方法
                //代表动态代理的invoke  代理对象  要执行目标方法的字节码对象  传递的参数
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //这段代码是aop底层的实现,这些代码不用自己去写,要完成解耦合aop会给你配置
                    //配置完之后,内部动态的生成这些代码,不用自己管
                    //前置增强
                    advice.before();
                    //代表反射时,反射的invoke
                    //传递的参数传递两个,第一个代表执行谁的,第二个代表实际参数
                    Object invoke = method.invoke(target, args);//执行目标方法
                    //后置增强
                    advice.afterReturning();
                    return invoke; //没有返回值,返回的是null
                }
            }

        );
        //调用代理对象的方法
        //接口有什么方法,代理对象就有什么方法
        proxy.save();

    }
}

aop简介-基于cglib的动态代理

cglib是第三方的,需要导入一些东西,但是现在不用再导入一些东西了,因为现在的Spring中都已经集成了cglib的一些东西了。image.png

创建目标对象:Target

package com.lyh.proxy.cglib;

public class Target{
    public void save() {
        System.out.println("==save running......");
    }
}

创建增强对象:Advice

package com.lyh.proxy.cglib;

//单独地建一个增强的方法
public class Advice {
    public void before(){
        System.out.println("===前置增强......");
    }
    public void afterReturning(){
        System.out.println("==后置增强......");
    }
}

代理测试:ProxyTest

package com.lyh.proxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class ProxyTest {
    public static void main(String[] args) {
        //目标对象
        final Target target = new Target();
        //增强对象
        Advice advice = new Advice();

        //返回值 就是动态生成的代理对象 基于cglib,cglib是基于父的
        //围绕父产生一个子,这个子就是一个代理对象
        //下面的代码目前来说是不用掌握
        //步骤:
        //1、创建增强器(这个增强器是cglib提供的)
        Enhancer enhancer = new Enhancer();
        //2、设置父类(父类 就是目标)
        enhancer.setSuperclass(Target.class);
        //3、设置回调    内部的参数需要一个Callback的接口
        //在这里可以给一个Callback接口,可以new一个匿名内部类
        //一般在设置的时候不会new Callback,会new Callback下面的子接口MethodInterceptor
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //intercept方法 相当于内部的 invoke方法
                advice.before();//执行前置
                Object invoke = method.invoke(target, args);//执行目标(还是用反射)
                advice.afterReturning();//执行后置
                return invoke;
            }
        });
        //4、创建代理对象
        //Object o = enhancer.create();
        //Object就是代理对象,但是不能用Object去接,用Target接
        //因为现在的代理对象是基于父的,代理对象就是Target的子,所以可以用它的父亲接
        Target poxy = (Target)enhancer.create();

        //调用代理对象的save方法,就相当于是调用目标对象的save方法,并且对目标方法进行了增强
        poxy.save();


    }
}

效果:
image.png

aop简介-aop相关概念

image.png

目标对象:要被增强的对象,目标对象内部的方法就是要被增强的目标方法。

image.png
image.png

就可以等同于方法,所谓的连接点就是被拦截到的方法。可以被增强的方法叫做连接点。

image.png

切入点简称“切点”,正真配置被增强的连接点称之为切入点

连接点的范围大,切入点是其中的一部分

image.png

通知/增强,是方法

image.png

目标方法+增强,就是**切面,**术语说就是:切点+增强

image.png

Weaving(织入)是**动词,将切点与增强结合的过程就是织入,**通过配置进行体现。

aop简介-aop开发明确的事项和知识要点

Spring在设计的时候,是把增强的方法设计到切面类内部了。image.png
image.png
image.png
如果目标对象有接口就使用JDK动态代理,如果没有接口就使用cglib动态代理。
image.png

xml方式实现aop-快速入门

image.png

AOP是一种思想,Spring框架对AOP进行了实现。aspectj(小框架)也对AOP进行了实现,并且aspectj在AOP配置方面的易用性要比Spring好一些。Spring本身也是一个轻量级框架,但在AOP配置方面,没有aspectj轻。Spring不排斥任何优秀的框架,在后期的Spring官方文档中,主张使用aspectj进行aop的配置,与此同时Spring把aspectj融入到了本身的内部。
Spring想要完成AOP的配置,可以用Spring原生的AOP进行配置,也可以使用Spring集成第三方aspectj的方式进行配置,目前Spring主张使用第三方aspectj这种方式进行配置。

创建接口:TargetInterface

package com.lyh.proxy.aop;

public interface TargetInterface {
    public void save();
}

创建接口的实现类:Target

package com.lyh.proxy.aop;

public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running......");
    }
}

切面类:MyAspect

package com.lyh.proxy.aop;

public class MyAspect {
    public void before(){
        System.out.println("前置增强......");
    }

}

pom.xml文件:导入相应的jar包

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.itheima</groupId>
  <artifactId>itheima_spring_exception</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>itheima_spring_exception Maven Webapp</name>
  <!--  FIXME change it to the project's website  -->
  <url>http://www.example.com</url>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.4</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.0.5.RELEASE</version>
    </dependency>
  </dependencies>
</project>

配置applicationContext.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: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 http://www.springframework.org/schema/context/spring-context.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
  ">
  <!--从Spring角度 目标对象和切面对象没有什么区别。-->
  <!--目标对象-->
  <bean id="target" class="com.lyh.proxy.aop.Target"></bean>

  <!--切面对象  这个普通的bean 经过配置 就会变成一个切面-->
  <bean id="myAspect" class="com.lyh.proxy.aop.MyAspect"></bean>

  <!--配置织入:告诉spring框架,那些方法(切点,目标方法)需要进行那些增强(前置、后置......)-->
  <!--首先配置aop的命名空间-->
  <aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
      <!--切面:切点+通知-->
      <!--myAspect这个里面,那些方法是前置增强   对pointcut这里的方法进行增强-->
      <aop:before method="before" pointcut="execution(public void com.lyh.proxy.aop.Target.save())"/>
      <!--当去访问save()时,save()方法要进行before(前置)增强-->
      <!--增强功能的逻辑代码和目标是解耦合的-->
    </aop:aspect>
  </aop:config>

</beans>

创建测试类:AopTest

package com.lyh.test;

import com.lyh.proxy.aop.Target;
import com.lyh.proxy.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

	@RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AopTest {
        
        @Autowired
        private TargetInterface target;

        @Test
        public void test1(){
            target.save();
        }
        
    }

效果:
image.png

xml方式实现aop-切点表达式的写法

image.png
更改后的切点表达式:

<aop:config>
  <!--声明切面-->
  <aop:aspect ref="myAspect">
    <aop:before method="before" pointcut="execution(* com.lyh.proxy.aop.*.*(..))"/>
  </aop:aspect>
</aop:config>

效果:
image.pngimage.png

xml方式实现aop-通知的种类

接口:TargetInterface

package com.lyh.proxy.aop;

public interface TargetInterface {
    public void save();
}

目标对象:Target

package com.lyh.proxy.aop;

public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running......");
        //著名的自杀式错误
        int i = 1/0;
    }
}

切面类:MyAspect

package com.lyh.proxy.aop;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect {
    public void before(){
        System.out.println("前置增强......");
    }

    public void afterReturning(){
        System.out.println("后置增强......");
    }

    //Proceeding JoinPoint:正在执行的 连接点===切点(目标方法)
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前通知......");
        //切点方法
        Object proceed = pjp.proceed(); //我的理解中间这个相当于是占位符
        System.out.println("环绕后通知......");
        return proceed;
    }

    //    得让目标有点异常
    public void afterThrowing(){
        System.out.println("异常抛出增强......");
    }

    //最终增强:不管抛不抛出异常,最终增强一定会执行
    public void after(){
        System.out.println("最终增强......");
    }

}

配置文件:applicationContext.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: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 http://www.springframework.org/schema/context/spring-context.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
  ">
  <!--从Spring角度 目标对象和切面对象没有什么区别。-->
  <!--目标对象-->
  <bean id="target" class="com.lyh.proxy.aop.Target"></bean>

  <!--切面对象  这个普通的bean 经过配置 就变成了一个切面-->
  <bean id="myAspect" class="com.lyh.proxy.aop.MyAspect"></bean>

  <!--配置织入:告诉spring框架,那些方法(切点,目标方法)需要进行那些增强(前置、后置......)-->
  <!--首先配置aop的命名空间-->
  <aop:config>
    <!--声明切面-->
    <!--切面:切点+通知-->
    <aop:aspect ref="myAspect">
      <!--增强的功能逻辑代码和目标是解耦合的-->
      <aop:before method="before" pointcut="execution(* com.lyh.proxy.aop.*.*(..))"/>
      <aop:after-returning method="afterReturning" pointcut="execution(* com.lyh.proxy.aop.*.*(..))"/>
      <aop:around method="around" pointcut="execution(* com.lyh.proxy.aop.*.*(..))"/>
      <aop:after-throwing method="afterThrowing" pointcut="execution(* com.lyh.proxy.aop.*.*(..))"/>
      <aop:after method="after" pointcut="execution(* com.lyh.proxy.aop.*.*(..))"/>
    </aop:aspect>
  </aop:config>

</beans>

效果:
image.png

xml方式实现aop-切点表达式的抽取

image.png
applicationContext.xml相关配置片段:

<aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
        <!--抽取切点表达式-->
        <aop:pointcut id="myPointcut" expression="execution(* com.lyh.proxy.aop.*.*(..))"/>
        <aop:around method="around" pointcut-ref="myPointcut"/>
        <aop:after method="after" pointcut-ref="myPointcut"/>
    </aop:aspect>
</aop:config>

效果:
image.png

xml方式实现aop-知识要点

image.png

注解方式实现aop-快速入门

image.png
接口:TargetInterface

package com.lyh.proxy.anno;
public interface TargetInterface {
    public void save();
}

目标类:Target

package com.lyh.proxy.anno;

import org.springframework.stereotype.Component;

	@Component("target")
    public class Target implements TargetInterface {
        public void save() {
            System.out.println("save running......");
        }
    }

切面类:MyAspect

package com.lyh.proxy.anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

	@Component("myAspect")
    @Aspect //标注当前MyAspect是一个切面类
    public class MyAspect {
        
        @Before(value = "execution(* com.lyh.proxy.anno.*.*(..))")
        public void before(){
            System.out.println("前置增强......");
        }
        
    }

配置文件:applicationContext-anno.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: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 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.lyh.proxy.anno"/>

    <!--    AOP自动代理 把这句话加上才会去 识别@Before这个注解-->
    <aop:aspectj-autoproxy/>

    </beans>

效果:
image.png

注解方式实现aop-注解通知种类和切点表达式抽取

注解通知种类:
image.png
MyAspect类

package com.lyh.proxy.anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

	@Component("myAspect")
    @Aspect //标注当前MyAspect是一个切面类
    public class MyAspect {

        //Proceeding JoinPoint:正在执行的 连接点===切点(目标方法)
        @Around("execution(* com.lyh.proxy.anno.*.*(..))")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("环绕前通知......");
            //切点方法
            Object proceed = pjp.proceed(); //我的理解中间这个相当于是占位符
            System.out.println("环绕后通知......");
            return proceed;
        }

        //最终增强:不管抛不抛出异常,最终增强一定会执行
        @After("execution(* com.lyh.proxy.anno.*.*(..))")
        public void after(){
            System.out.println("最终增强......");
        }

    }

测试类:AopAnnoTest

package com.lyh.test;

import com.lyh.proxy.anno.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

	@RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext-anno.xml")
    public class AopAnnoTest {
        
        @Autowired
        private TargetInterface target;

        @Test
        public void test1(){
            target.save();
        }
    }

效果:image.png

切点表达式抽取:
image.png
MyAspect类

package com.lyh.proxy.anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

	@Component("myAspect")
    @Aspect //标注当前MyAspect是一个切面类
    public class MyAspect {
       
        //抽取引入方式一:
        @Around("pointcut()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("环绕前通知......");
            //切点方法
            Object proceed = pjp.proceed(); //我的理解中间这个相当于是占位符
            System.out.println("环绕后通知......");
            return proceed;
        }
        
    	//抽取引入方式二:
        @After("MyAspect.pointcut()")
        public void after(){
            System.out.println("最终增强......");
        }

        //定义一个空实现,不用在方法内写东西,因为注解必须需要一个宿主
        @Pointcut("execution(* com.lyh.proxy.anno.*.*(..))")
        public void pointcut(){}

    }

测试类省去,与上面的相同
效果:
image.png

注解方式实现aop-知识要点

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值