天天看点

SSM框架---Spring的动态代理AOP(四)

目录

一、Spring的AOP概述

二、Spring AOP快速入门(全xml配置)

1)Schema-based实现步骤

2)AspectJ实现步骤(推荐)

三、Spring AOP的注解方式(半xml和半注解,基于 Aspect)

四、Spring的AOP全注解(理解)

五、动态代理的简单理解

一、Spring的AOP概述

1、AOP: 面向切面编程思想

2、本质理解: 将一些共性的内容进行抽取,在需要用到的地方,以动态代理的方式进行插入。

3、好处:在不修改源码的基础上,还能对源码进行前后的增强。高扩展性。

4、底层实现的技术: 动态代理。spring就是把动态代理进行层层封装 诞生出了aop思想。

动态代理的实现有两种:JDK动态代理 和 CGLIB动态代理

    ①JDK动态代理 --不用导包,jdk提供好了

        proxy

         条件: 目标类必须得有接口

    ②CGLIB动态代理---第三方 单用它就必须导包(导包cglib.jar和asm.jar--字节码解析工具包。但在spring里面使用不需单独导包,因为spring-core.jar里面已经整合)

        enhance

         条件: 只要有一个目标类即可增强

    ③spring如何均衡这两种:

         如果目标类有接口 会默认使用jdk的动态代理

         如果目标类没有接口 会默认使用CGLIB的动态代理

SSM框架---Spring的动态代理AOP(四)
SSM框架---Spring的动态代理AOP(四)

aop是oop的延续,用于实现oop不太适合实现的代码,比如:日志。

ps:对象增强的手段:

①继承   如:httpServletRequest request就不能被继承; 因为是tomcat帮忙创建出来的,不知道父类是谁

    缺点: 需要知道要继承的父类是谁    

②装饰者模式

    缺点: 需要有接口  save()   100个方法

             这个接口除了要增强的方法以外,剩余的方法也都得实现

③动态代理

    jdk的动态代理  save()   100个方法

          需要有接口 可以指定只增强这个接口下的某个方法

    cglib的动态代理

         优点:不需要有接口 也可以指定增强方法

         缺点:代码写的较为复杂

5、常用概念

  • 1.PointCut 切点/切入点:被增强的方法。例如:save()
  • 2.advice 通知/增强,增强代码。包括:前置通知@Before; 后置通知@AfterReturning; 异常通知@AfterThrowing等
  • 3.Aspect(切面): 是切点pointcut和通知advice的总称。切面=切点+通知/增强

    一个线是一个特殊的面。

    一个切点和一个通知,组成一个特殊的面。

  • 4. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.简单来说,把切面添加到原有功能的过程。是一个动作。
  • 5.Joinpoint 连接点:可以被增强的方法。例如:impl类里的所有方法都可以叫做连接点
  • 6.target:目标类,需要被代理的类。例如:UserService

6、AOP的应用:

  •  权限拦截
  •  日志的输出
  •  性能的检测
  •  事务管理

二、Spring AOP快速入门(全xml配置)

spring提供了两种AOP的实现方式:

1)Schema-based

      ①每个通知都需要实现接口或类(如:前置通知需实现MethodBeforeAdvice接口一样)

      ② 配置 spring 配置文件时在<aop:config>配置

2)AspectJ(推荐)

     ①每个通知不需要实现接口或类

     ②配置 spring 配置文件是在<aop:config>的子标签<aop:aspect>中配置

1)Schema-based实现步骤

1、导包

aopalliance.jar(AOP联盟,即AOP的一套规范(接口) )

aspectj.jar(第三方--实现了AOP的一套规范)

Spring-aop.jar(实现了AOP的一套规范)

spring-aspects.jar (spring整合aspectj)

SSM框架---Spring的动态代理AOP(四)

2、新建通知类

     2.1 前置通知,实现MethodBeforeAdvice接口

            arg0: 切点方法对象 + Method 对象

            arg1: 切点方法参数

            arg2: 切点在哪个对象中

SSM框架---Spring的动态代理AOP(四)

    2.2 后置通知,实现AfterReturningAdvice接口

           arg0: 切点方法返回值  ---比前置对象多一个参数

           arg1: 切点方法对象

           arg2: 切点方法参数

           arg3: 切点方法所在类的对象

SSM框架---Spring的动态代理AOP(四)

    2.3 异常通知,实现ThrowsAdvice接口

           API不提供重写方法,我们必须自己写方法,且方法名必须叫 afterThrowing

           方法有两种实现,参数必须是 1 个或 4 个

           异常类型要与切点报的异常类型一致

SSM框架---Spring的动态代理AOP(四)

    2.4 环绕通知,实现MethodInterceptor接口

           环绕通知 = 前置通知 + 后置通知

           有了环绕通知就不需要再写前置通知和后置通知了

SSM框架---Spring的动态代理AOP(四)

3、配置 spring 配置文件

           3.1 引入 aop 命名空间

           3.2 确定目标类 (目标类中有切入点 ---要被增强的方法) 

           3.3 确定切面类 (里面有通知/增强 ----增强的那段代码方法)

           3.4 配置织入过程 (将增强方法和被增强方法进行结合)

                 <aop:pointcut id="pointcut" expression="execution(* com.ly.spring.service.impl.*.*(..))" />

                   表达式:第一个*是返回值       第二个*是包名/子包名    第三个*是类名        第四个*是方法名

                        *   : 通配符,代表可以匹配任意方法名,任意类名,任意一级包名

                        ..  :代表匹配任意方法参数

<?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">
        
    <!-- 目标类 (有切入点 有被增强的方法demo1) -->
    <bean id="personService" class="com.ly.spring.service.PersonService"></bean>
    <bean id="userService" class="com.ly.spring.service.impl.UserServiceImpl"></bean>
    
    <!-- 配置通知类对象,在切面中引入 -->
    <bean id="mybefore" class="com.ly.spring.advice.MyBeforeAdvice"></bean>
    <bean id="myafterReturning" class="com.ly.spring.advice.MyAfterReturningAdvice"></bean>
    <bean id="myaround" class="com.ly.spring.advice.MyAround"></bean>
    <bean id="mythrows" class="com.ly.spring.advice.MyThrowsAdvice"></bean>
    
    <!-- 配置切面 -->
    <aop:config>
   		<!-- 配置切点 -->
   		<aop:pointcut expression="execution(* com.ly.spring.service.PersonService.*(..))" id="pointcut1"/>
   		<aop:pointcut expression="execution(* com.ly.spring.service.impl.UserServiceImpl.*(..))" id="pointcut2"/>
   		
   		<!--  <aop:pointcut expression="execution(void com.ly.spring.service.PersonService.save())" id="pointcut1"/>
   		<aop:pointcut expression="execution(void com.ly.spring.service.PersonService.delete())" id="pointcut2"/>
   		<aop:pointcut expression="execution(* com.ly.spring.service.PersonService.up*(..))" id="pointcut3"/>
   		<aop:pointcut expression="execution(* com.ly.spring.service.*.find(..))" id="pointcut4"/>
   		<aop:pointcut expression="execution(* com.ly.spring.service.PersonService.*(..))" id="pointcut5"/>
   		<aop:pointcut expression="execution(* com.ly.spring.service.impl.UserServiceImpl.*(..))" id="pointcut6"/> -->
   		
   		<!-- 通知 -->
   		<aop:advisor advice-ref="mybefore" pointcut-ref="pointcut1"/>           <!-- 前置通知  -->
   		<aop:advisor advice-ref="myafterReturning" pointcut-ref="pointcut1"/>   <!-- 后置通知 -->
   		<aop:advisor advice-ref="myaround" pointcut-ref="pointcut1"/>           <!-- 环绕通知 = 前置通知 + 后置通知 -->
   		<aop:advisor advice-ref="mythrows" pointcut-ref="pointcut1"/>           <!-- 异常通知 -->
   		                                                                        <!-- 最终通知,暂未定义 -->
   		
   		<aop:advisor advice-ref="myaround" pointcut-ref="pointcut2"/>           <!-- 环绕通知 = 前置通知 + 后置通知 -->
    </aop:config>
        
 </beans>
           

4、测试

SSM框架---Spring的动态代理AOP(四)

2)AspectJ实现步骤(推荐)

1、导包

aopalliance.jar(AOP联盟,即AOP的一套规范(接口) )

aspectj.jar(第三方--实现了AOP的一套规范)

Spring-aop.jar(实现了AOP的一套规范)

spring-aspects.jar (spring整合aspectj)

SSM框架---Spring的动态代理AOP(四)

2、新建通知类,不用实现接口,类中方法名任意

SSM框架---Spring的动态代理AOP(四)

3、配置 spring 配置文件

           1) 确定目标类 (目标类中有切入点 ---要被增强的方法) 

           2 )确定切面类 (里面有通知/增强 ----增强的那段代码方法)

           3) 配置织入过程 (将增强方法和被增强方法进行结合)

           4)细节:①在<aop:config>的子标签<aop:aspect>中配置

                   ② <aop:pointcut id="pointcut" expression="execution(* com.ly.spring.service.impl.*.*(..))" />

                   表达式:第一个*是返回值       第二个*是包名/子包名    第三个*是类名        第四个*是方法名

                        *   : 通配符,代表可以匹配任意方法名,任意类名,任意一级包名

                        ..  :代表匹配任意方法参数

           5)通知类型:

                前置通知 @Before                <aop:before method="" pointcut-ref=""/>

                后置通知 @AfterReturning

                环绕通知 @Around               <aop:around method="" pointcut-ref=""/>

                异常通知 @AfterThrowing    <aop:after-throwing method="" pointcut-ref=""/>

                最终通知 @After

<?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">
    
     <!-- 目标类 (有切入点 有被增强的方法save) -->
    <bean id="personService" class="com.ly.spring.service.PersonService"></bean>
    <bean id="userService" class="com.ly.spring.service.impl.UserServiceImpl"></bean>
    
    <!-- 切面类(有通知/增强  有增强方法) -->
    <bean id="myAspect" class="com.ly.spring.aspectj.MyAspect"></bean>
    
    <!-- 配置织入(增强方法和被增强方法集成在一起) -->
    <aop:config>
    	<aop:aspect ref="myAspect">
    		<!-- 定义切入点 -->
    		<aop:pointcut expression="execution(* com.ly.spring.service.PersonService.*(..))" id="pointcut1"/>
   		<aop:pointcut expression="execution(* com.ly.spring.service.impl.UserServiceImpl.*(..))" id="pointcut2"/>
    		<!--  <aop:pointcut expression="execution(void com.ly.spring.service.PersonService.save())" id="pointcut1"/>
    		<aop:pointcut expression="execution(void com.ly.spring.service.PersonService.delete())" id="pointcut2"/>
    		<aop:pointcut expression="execution(* com.ly.spring.service.PersonService.up*(..))" id="pointcut3"/>
    		<aop:pointcut expression="execution(* com.ly.spring.service.*.find(..))" id="pointcut4"/> -->
    		
    		<!-- 原始方式 -->
    		<!-- <aop:before method="beforeMethod" pointcut="execution(void com.ly.spring.domain.Person.save())"/>
    		<aop:after-returning method="aftereturningMethod" pointcut="execution(void com.ly.spring.domain.Person.save())"/> -->
    		
    		<!--便捷方式 -->
    		<aop:before method="beforeMethod" pointcut-ref="pointcut1"/>      				<!-- 前置通知 -->
    		<aop:after-returning method="afterReturningMethod" pointcut-ref="pointcut1"/>   <!-- 后置通知 -->
    		<aop:around method="aroundMethod" pointcut-ref="pointcut1"/> 					<!-- 环绕通知 = 前置通知 + 后置通知 -->
    		<aop:after-throwing method="throwingMethod" pointcut-ref="pointcut1"/> 			<!-- 异常通知 -->
    		<aop:after method="afterMethod" pointcut-ref="pointcut1"/> 						<!-- 最终通知 -->
    		
    		<aop:around method="aroundMethod" pointcut-ref="pointcut2"/> 					<!-- 环绕通知 -->
    	</aop:aspect>
    </aop:config>
        
 </beans>
           

4、测试

SSM框架---Spring的动态代理AOP(四)

三、Spring AOP的注解方式(半xml和半注解,基于 Aspect)

1、导包

aopalliance.jar(AOP联盟,即AOP的一套规范(接口) )

aspectj.jar(第三方--实现了AOP的一套规范)

Spring-aop.jar(实现了AOP的一套规范)

spring-aspects.jar (spring整合aspectj)

SSM框架---Spring的动态代理AOP(四)

2、spring配置文件

          2.1 开启注解扫描器

          2.2 开启注解的动态代理方式,缺省值false:使用jdk动态代理

<?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.ly.spring"></context:component-scan>
   		
   	<!-- 开启注解的动态代理方式  为了识别@Before @AfterReturning -->
    	<!-- proxy-target-class:  false:使用jdk动态代理(默认)   true:使用cglib动态代理    -->
    	<!-- <aop:aspectj-autoproxy></aop:aspectj-autoproxy>   这样写也可,使用默认    -->
   	<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
   		 
   
 </beans>
           

3、给切面类添加注解

         @Component("bean的id")   ---定义在类,被Spring管理

         @Aspect       ----切面类,相当于<aop:aspect ref=""/>表示通知方法在当前类中

         @Before(" ") 前置通知、@After最终通知  等五种类型                          

SSM框架---Spring的动态代理AOP(四)
SSM框架---Spring的动态代理AOP(四)

4、给目标类添加注解

         @Service("bean的id")/@Component("bean的id")   ---定义在类,被Spring管理

SSM框架---Spring的动态代理AOP(四)

5、测试

SSM框架---Spring的动态代理AOP(四)

四、Spring的AOP全注解(理解)

在半注解半xml的基础上:

1、添加注解类,删除applicationContext.xml配置文件

SSM框架---Spring的动态代理AOP(四)

2、测试

SSM框架---Spring的动态代理AOP(四)

五、动态代理的简单理解

代理模式是23中设计模式之一。又分为静态代理和动态代理。

1、代理设计模式

     1.1 真实对象(老总)

     1.2 代理对象(秘书)

     1.3 抽象对象(抽象功能)

2、代理设计模式优点:

        2.1 保护真实对象.    ---真实对象是 老总,一切都先经过秘书处理,不让老总的信息过多泄露

        2.2 让真实对象职责更明确.  ---老总只管做决定这种打算,预约时间这种小事交给秘书

        2.3 扩展

3、静态代理设计模式的缺点:

        3.1 代理类中每个功能都需要重写

        3.2 当代理功能比较多时,代理类中方法需要写很多

4、为了解决静态代理频繁重写代理功能的缺点,产生了动态代理。可分为:

        4.1 JDK 提供的

        4.2 cglib 动态代理,第三方

5、JDK动态代理

        代理类需实现InvocationHandler接口;真实类要实现接口

        5.1 优点:jdk 自带,不需要额外导入 jar

        5.2 缺点:

                 5.2.1 真实对象必须实现接口

                 5.2.2 利用反射机制invoke.效率不高

SSM框架---Spring的动态代理AOP(四)

6、cglib 动态代理

        代理类需实现MethodInterceptor接口;真实类不需要实现接口

        6.1 cglib 优点:

                6.1.1 基于字节码,生成真实对象的子类,运行效率高于 JDK 动态代理.

                6.1.2 真实类不需要实现接口

        6.2 cglib 缺点:

                6.2.1 需要额外导入 jar包  cglib.jar和asm.jar (字节码解析工具包,因为cglib是通过字节码来实现的)

                         如果cglib用在spring框架里面就不需要导包了,因为spring本身的jar包已经整合了cglib

        6.3 使用 spring aop 时,需在applicationContext.xml中添加  <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>    true 使用 cglib; false 使用 jdk(默认值)

否则,容易抛出异常。 Proxy 和真实对象转换异常,如下图:

SSM框架---Spring的动态代理AOP(四)
SSM框架---Spring的动态代理AOP(四)

继续阅读