spring-基础学习03

【第一章】AOP简介和入门案例

1 什么是AOP?

​ 面向切面编程,切面指定就是动态代理的方法,作用是在不改变业务层方法源代码的基础上对方法进行增强,底层使用的是动态代理技术,面向切面编程也可以理解成面向动态代理编程。

2 AOP专业术语

在这里插入图片描述

  • Target(目标对象):被代理的对象就是目标对象
  • Proxy(代理对象):被增强后的对象就是代理对象
  • Joinpoint(连接点):就是目标对象中所有被拦截到的方法
  • Pointcut(切入点):就是目标对象中被增强的方法
  • Advice(通知):执行目标方法之前或者之后调用的方法就是通知
  • Aspect(切面):通知方法和切入点方法结合所在的位置叫做切面
  • Weaving(织入):通知方法和切入点方法结合的过程,织入之后的结果就是切面

​ 连接点是所有被拦截到的方法,切入点是所有被增强的方法,连接点不一定是切入点,但是切入点一定是连接点。在执行目标对象方法之前或者之后要做的事叫做通知,通知中有增强的业务。将切入点和通知组织到一起叫织入,织入形成的结果就是切面。

3 入门案例【重点】

实现步骤
【第一步】导入相关依赖:spring-context、aspectjweaver
【第二步】定义通知类和目标对象(已完成)
【第三步】xml文件配置AOP
【第一步】导入相关依赖:spring-context、aspectjweaver
<!--spring核心依赖-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.1.5.RELEASE</version>
</dependency>
<!--切入点表达式依赖-->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.9.4</version>
</dependency>
【第二步】定义通知类和目标对象(已完成)
  • 通知类
//通知类,告诉spring在增强的前后需要做什么事
public class Advice {
  public void before(){
    //前置通知:开启事务
    System.out.println("前置通知:开启事务");
  }
  public void afterReturn(){
    //后置通知:提交事务
    System.out.println("后置通知:提交事务");
  }
  public void afterThrowable(){
    //异常通知:回滚事务
    System.out.println("异常通知:回滚事务");
  }
  public void after(){
    //最终通知:释放资源
    System.out.println("最终通知:释放资源");
  }
}
  • 目标类
public interface StudentService {
  //查询全部
  public abstract List<Student> findAll() throws IOException;

  //根据id查询
  public abstract Student findById(Integer id) throws IOException;

  //新增数据
  public abstract void insert(Student stu) throws IOException;

  //修改数据
  public abstract void update(Student stu) throws IOException;

  //删除数据
  public abstract void delete(Integer id) throws IOException;
}

public class StudentServiceImpl implements StudentService {
  @Override
  public List<Student> findAll() throws IOException {
    System.out.println("查询所有学生信息findAll...");
    return null;
  }
  @Override
  public Student findById(Integer id) throws IOException {
    System.out.println("根据id查询学生信息findById...");
    return null;
  }
  @Override
  public void insert(Student stu) throws IOException {
    //执行添加操作
    System.out.println("添加学生信息insert...");

  }
  @Override
  public void update(Student stu) throws IOException {
    System.out.println("修改学生信息update...");
  }
  @Override
  public void delete(Integer id) throws IOException {
    System.out.println("根据id删除学生信息delete...");
    int i=100/0;
  }
}
【第三步】xml文件配置AOP
<?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
">

  <!--1 配置目标对象:StudentServiceImpl-->
  <bean id="studentService" class="com.itheima.service.impl.StudentServiceImpl"/>
  <!--2 配置通知对象:Advice-->
  <bean id="advice" class="com.itheima.aop.Advice"/>
  <!--3 AOP配置切面,通知+切入点-->
  <aop:config>

    <!--3.1 配置切入点,此处配置在所有切面中都可以用-->
    <aop:pointcut id="pt" expression="execution( * com.itheima.service.impl.StudentServiceImpl.*(..))"/>

    <!--3.2 配置通知+切入点-->
    <aop:aspect ref="advice">
      <!--配置切入点,此处配置的只能在当前的切面中用-->
      <!--<aop:pointcut id="pt" expression="execution( * com.itheima.service.impl.StudentServiceImpl.*(..))"/>-->

      <!--配置前置通知,
        method="before"表示执行通知类的before方法
        pointcut="execution()" 切入点表达式,用于找到要代理的方法
        pointcut-ref="pt" 表示引用id="pt"的切入点表达式
      -->
      <aop:before method="before" pointcut-ref="pt"/>
      <!--后置通知,也叫后置返回通知-->
      <aop:after-returning method="afterReturn" pointcut-ref="pt"/>
      <!--异常通知,也叫后置异常通知-->
      <aop:after-throwing method="afterThrowable" pointcut-ref="pt"/>
      <!--最终通知-->
      <aop:after method="after" pointcut-ref="pt"/>
      
    </aop:aspect>
  </aop:config>
</beans>

【第二章】AOP的xml配置

1 切入点表达式的写法【重点】

execution( * com.itheima.service.impl.StudentServiceImpl.findAll(..) //较少
execution( * com.itheima.service.impl.StudentServiceImpl.*(..) //较少
execution( * com.itheima.service.StudentService.*(..) //StudentService中的所有方法会被代理,比较常用
execution( * com..StudentService.*(..) //StudentService中的所有方法会被代理,比较常用
execution( * com..service.*.*(..) //com包下的所有子包下的service包中的所有类的所有方法,比较常用

常见问题:如果使用AOP给接口/类配置了增强,那么只能用接口或父类接收代理对象。

'Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named ''studentService' 'is expected to be of type ''com.itheima.service.impl.StudentServiceImpl'' but was actually of type com.sun.proxy.$Proxy17 <-----以上是核心信息'
	'at org.springframework.beans.factory.support.DefaultListableBeanFactory.checkBeanNotOfRequiredType(DefaultListableBeanFactory.java:1672)
'	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1650)
'	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1213)
'	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
'	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
	... 27 more

2 通知类型【重点】

前置通知:在目标方法执行之前执行
后置通知/后置返回通知:在目标方法调用之后执行
异常通知/后置异常通知:在调用目标方法出现异常时执行
最终通知:调用目标方法不管是否出现异常,都会执行,相当于finally中的代码
----------以上四种通知无法阻止目标方法调用,目标方法是有Spring来控制------------------

----------环绕通知可以代替以上四种通知----------------
环绕通知:是Spring给我们提供的一种手动调用目标对象方法或者其他通知的通知方式

通知使用说明:要么单独使用环绕通知,要么组合其他四种通知一起使用,两种方案二选一。

3 在通知中获取目标方法的参数、返回值、异常信息(了解)

前置通知中获取目标方法的参数
		public void before(JoinPoint jp){
        //获取请求参数
        //doBefore(jp.getArgs());
        Object[] args = jp.getArgs();
        System.out.println("args = " + Arrays.toString(args));
        //前置通知:开启事务
        System.out.println("Advice前置通知:开启事务");
    }
    
后置通知中获取目标方法的结果/返回值
	<aop:after-returning method="afterReturn" pointcut-ref="pt" returning="result"/>
	public void afterReturn(Object result){ //result是结果
        //后置通知:提交事务
        System.out.println("Advice后置通知:提交事务   "+result);
    }
说明:后置通知方法的形参变量名要和配置中的returning="result"属性值一样

异常通知中获取异常信息
	<aop:after-throwing method="afterThrowable" pointcut-ref="pt" throwing="err"/>
	public void afterThrowable(Throwable err){
        //异常通知:回滚事务
        System.out.println("Advice异常通知:回滚事务 " +err.getMessage());
    }
  说明:异常通知方法的形参变量名要和配置中的throwing="err"属性值一样
  
环绕通知可以获取以上所有
			try {
            //前置增强
            //doBefore(pjp.getArgs());

            //手动调用目标方法
            //获取切入点方法的参数
            Object[] args = pjp.getArgs();   //参数
            //执行切入点方法,也就是执行目标方法
            value= pjp.proceed(args); //返回值就是目标方法的返回值,结果

            //后置增强
            //afterReturn();
        } catch (Throwable throwable) { //异常
            throwable.printStackTrace();
            //异常增强
            //afterThrowable();
        } finally {}

【第三章】AOP的注解配置【重点】

【第一步】开启spring的AOP注解扫描

  • xml文件配置
<!--开启spring的AOP注解扫描-->
<aop:aspectj-autoproxy/>
  • 纯注解配置
@Configuration
@ComponentScan("com.itheima")// <context:component-scan base-package="com.itheima"/>
@EnableAspectJAutoProxy // <aop:aspectj-autoproxy/>
public class SpringConfiguration {
}

【第二步】在通知类上使用@aspect表示这个类是一个切面类

@Component
@Aspect  //该类即是一个通知类也是一个切面类
public class Advice{
  ...
}

【第三步】定义切入点表达式方法

//定义切入点表达式的方法,方法体不需要写任何内容,方法名就是切入点的id
//<aop:pointcut id="pt" expression="execution( * com.itheima.service.StudentService.*(..))"/>
@Pointcut("execution( * com.itheima.service.StudentService.*(..))")
public void pt(){}

【第四步】在通知方法上加对应的注解

@Before("pt()")
public void before(){
  //前置通知:开启事务
  System.out.println("Advice前置通知:开启事务");
}
@AfterReturning("pt()")
public void afterReturn(){ //result是结果
  //后置通知:提交事务
  System.out.println("Advice后置通知:提交事务");
}
@AfterThrowing("pt()")
public void afterThrowable(){
  //异常通知:回滚事务
  System.out.println("Advice异常通知:回滚事务 ");
}
@After("pt()")
public void after(){
  //最终通知:释放资源
  System.out.println("Advice最终通知:释放资源");
}
----------------------------------------------
  @Around("pt()")
  public Object around(ProceedingJoinPoint pjp){
  //System.out.println("环绕通知执行了...");
  Object value=null;
  try {
    //前置增强
    before();

    //手动调用目标方法
    //获取切入点方法的参数
    Object[] args = pjp.getArgs();
    //执行切入点方法,也就是执行目标方法
    value= pjp.proceed(args); //返回值就是目标方法的返回值

    //后置增强
    afterReturn();
  } catch (Throwable throwable) {
    throwable.printStackTrace();
    //异常增强
    afterThrowable();
  } finally {
    //最终通知
    after();
  }
  return value;
}

注意:使用注解配置AOP,后置通知和异常通知会在最终通知之后调用,在spring-context的5.1.5版本中是这样的,在更高的版本中可能得到了解决,大家自己验证。但是我们可以使用环绕通知解决这个问题,推荐使用环绕通知。

【第四章】综合案例-业务层接口性能监控

【第五章】AOP底层原理

静态代理/装饰模式、jdk中的动态代理、cglib动态代理、织入时机。

Spring的AOP底层默认是采用jdk的动态代理,也可以通过配置使用cglib动态代理,配置如下:

  • 纯xml配置
<!--proxy-target-class="true" true表示使用cglib动态代理,默认值是false表示jdk动态代理-->
<aop:config proxy-target-class="true">
	...
</aop:config>        
  • 半xml半注解
<!--开启spring的AOP注解扫描-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
  • 纯注解配置
@Configuration
@ComponentScan("com.itheima")// <context:component-scan base-package="com.itheima"/>
@EnableAspectJAutoProxy(proxyTargetClass = true) // <aop:aspectj-autoproxy/>
public class SpringConfiguration {
}

今日总结

1 AOP : 面向切面编程,不改变业务层方法源代码的情况下对业务层方法进行增强,底层默认使用jdk的动态代理

AOP前提:导入依赖坐标:spring-context、aspectjweaver

xml配置:
	1 配置我们的业务层实现类
	<bean id="studentService" class=""></bean>
	2 配置我们的通知类
	<bean id="advice" class=""></bean>
	3 AOP配置(重要)
	<!--proxy-target-class="true" 表示使用cglib动态代理-->
	<aop:config proxy-target-class="true">
			<!--配置切面-->
    	<aop:aspect ref="advice">
        	<!--配置切入点表达式-->
        	<aop:pointcut id="pt" expression="execution( * com.itheima.service.StudentService.*(..))"/>
        	<!--配置不同的通知类型-->
        	<!--配置前置通知,
                method="before"表示执行通知类的before方法
                 pointcut="execution( * com.itheima.service.impl.StudentServiceImpl.findById(..))" 切入点表达式,用于找到要代理的方法
                 pointcut-ref="pt" 表示引用id="pt"的切入点表达式
              -->
            <aop:before method="before" pointcut-ref="pt"/>
            <!--后置通知,也叫后置返回通知-->
            <aop:after-returning method="afterReturn" pointcut-ref="pt" returning="result"/>
            <!--异常通知,也叫后置异常通知-->
            <aop:after-throwing method="afterThrowable" pointcut-ref="pt" throwing="err"/>
            <!--最终通知-->
            <aop:after method="after" pointcut-ref="pt"/>

            <!--环绕通知-->
            <!--<aop:around method="around" pointcut-ref="pt"/>-->
      </aop:aspect>
  </aop:config>

注解配置:
	第一步:开启Spring的AOP注解支持
			在xml文件中
			<!--proxy-target-class="true" 表示使用cglib动态代理-->
			<aop:aspectj-autoproxy proxy-target-class="true"/>
		  在配置类中:
			@EnableAspectJAutoProxy(proxyTargetClass = true) 
	第二步:在通知类上使用@Aspect表示该类是一个切面类
		@Component
    @Aspect  //该类即是一个通知类也是一个切面类
    public class Advice {}
	第三步:定义切入点表达式方法,在方法上使用@Pointcut注解指定切入点表达式
		@Pointcut("execution( * com.itheima.service.StudentService.*(..))")
    public void pt(){}
	第四步:在通知方法上加对应的注解表示不同的通知类型
	  @Before、@AfterReturning、@AfterThrowable、@After、@Around

	注意:注解的方式推荐使用环绕通知。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值