spring面向切面编程(AOP)

谁说我不能喝 提交于 2020-02-19 04:04:29

AOP(Aspect Oriented Programing)面向切面编程

AOP是一种编程范式,隶属于软工范畴,指导开发 者如何组织程序结构

AOP弥补了OOP的不足,基于OOP基础之上进行 横向开发

AOP联盟

一、OOP范式编程

OOP规定程序开发以类为模型,一切围绕对象进行, OOP中完成某个任务首先构建模型,基于模型展开 业务

OOP主要应用于WEB开发,围绕OOP的主思想, 对开发过程进行了分层设计

二、AOP范式编程

AOP范式编程研究的不是层与层之间的关系,也不 是每层内部如何开发,AOP主要关注同一层面的各 个不同功能模块之间的共性功能。

AOP时代的到来,使开发模块化的思想进行了进一 步的提升,从刀耕火种的纯手工开发阶段,向半自 动化/自动化阶段进行了一个大的突破。IT研发朝着 “插拔式组件体系结构”又近了一步。

三、基本概念(重点)

1、连接点(Joinpoint)

2、切入点(Pointcut)

3、通知(Advice)

4、目标对象(Target Object)

5、AOP代理(AOP Proxy)

6、织入(Weaving)

7、切面(Aspect)

1、连接点(Joinpoint)

①、程序运行过程中,JVM负责程序运行。执行到某个 方法时,JVM能识别当前执行的是哪个方法。这些 定义在类中的方法,每个具有独立的功能,在AOP 中,将这些具有特定功能的方法称为连接点。

②、连接点指类中的方法

2、切入点(Pointcut)

①、 类中的方法具有很多,某些方法具有一些共同的流 程,例如数据库连接的获取与关闭,事务的开启与 提交,等等。将这些公共的特性抽取出来,抽取完 毕后,原始方法中就缺少了这些被抽取的代码。在 实际运行时,缺少这些代码是无法完成原始的功能 的。这些被抽取了公共功能的方法称为切入点。

②、切入点一定是连接点,连接点不一定是切入点

③、切入点指被抽取了共性功能的方法

3、通知(Advice)

①、切入点对应的方法的共性功能被抽取后,组成独立 代码逻辑,被封装在某个类的某个方法中,在被抽 取了共性功能的方法被执行时,这些代码逻辑还要 加入原始方法的执行,这些被抽取出来组成独立代 码逻辑的共性功能称为通知。

②、共性功能被抽取后,可能来自于切入点对应的方法 中的任何位置,因此通知不仅描述共性的代码逻辑, 还是描述被抽取时从何处抽取。例如切入点代码逻 辑的前面、中间还是后面,被抽取的代码在切入点 中的具体位置,称为通知类别。

4、目标对象(Target Object)

①、切入点对应的共性功能被抽取后创建独立的通知完 成共性功能,在程序运行时,动态的为类对象执行 切入点方法时动态加入被抽取的共性功能,此时需 要使用代理的模式完成。此时被代理创建的对象, 称为目标对象。

②、目标对象指包含切入点的类对象

5、AOP代理(AOP Proxy)

①、切入点所在的类对象执行切入点方法时,需要将原 始的共性功能(通知)加入,此时通过代理的形式创 建类对象,并完成共性功能(通知)的加入,上述过 程称为AOP代理。

②、AOP代理的意义是将被抽取共性功能的类对象创建 出,同时将共性功能(通知)加入,完成原始的完整 操作的执行过程。

6、切面(Aspect)

①、切面是一个设计概念,指切入点与通知的匹配模式, 换句话说指被抽取了共性功能的方法(切入点)与被 抽取了共性功能(通知)对应的绑定关系。

②、程序设计时,可以设置多个切面,用来描述切入点 与通知之间的关系。

7、织入(Weaving)

①、通过AOP代理,完成了切入点与通知的融合,并组 成了完整的代码逻辑,将通知加入到切入点对应位 置的动作称为织入。

②、织入是一个动态过程,不对应任何代码,可以理解 为动态的运行过程。

③、织入可以在三个阶段进行,编译时,类加载时,运 行时。Spring采用的是运行时织入。

四、AOP工作流程

1、 开发阶段(开发者完成) 将共性功能独立开发出来,制作成通知 将非共性功能开发到对应的目标对象类中,并制作成切入点 方法 在配置文件中,声明切入点与通知间的关系,即切面

2、运行阶段(AOP完成) JVM读取配置文件中的信息,监控切入点方法的执行 一旦监控到切入点方法被运行,使用代理机制,动态创建目 标对象的代理对象,根据通知类别,在代理对象的对应位置, 将通知对应的功能织入,完成完整的代码逻辑运行。

⑤、XML配置开发AOP

导入jar包

基本

Spring核心包(4个) 日志相关(2个) 集成JUnit(1个)

AOP相关

Spring进行AOP开发(1个)(3.2资源包)

spring-aop-3.2.0.RELEASE.jar

Spring整合AspectJ框架(3.2资源包)

spring-aspects-3.2.0.RELEASE.jar

AOP联盟规范(1个) (3.0.2依赖包)

com.springsource.org.aopalliance-1.0.0.jar

aspectJ支持(1个) (3.0.2依赖包)

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

六、AspectJ

1、AspectJ是一个面向切面的框架,它扩展了Java语 言。AspectJ定义了AOP语法,所以它有一个专门的 编译器用来生成遵守Java字节编码规范的Class文 件。

2、Spring2.0+提供对AspectJ的支持

3、早期的SpringAOP开发十分繁琐,Spring2.x推荐使 用AspectJ进行SpringAOP开发

七、AOP开发——

1、目标对象制作

制作目标对象类,制作连接点方法,并将其配置成 Spring所控制的Bean

2、AOP开发——通知制作

 制作通知对应的类,并制作出被抽取的通用功能, 将其封装到方法中,并将该类配置成Spring所控制 的Bean

3、AOP开发——开启AOP配置

在XML配置文件中,开启AOP命名空间

4、AOP开发——配置切面

在XML配置文件中使用aop命名空间进行AOP配置

5、AOP切入点表达式

AOP切入点表达式支持多种形式的定义规则

execution:匹配方法的执行(常用)

execution(public * *(..))

within:匹配包或子包中的方法(了解)

within(com.bdyc.aop..*)

this:匹配实现接口的代理对象中的方法(了解)

this(com.bdyc.aop.user.UserDAO)

target:匹配实现接口的目标对象中的方法(了解)

target(com.bdyc.aop.user.UserDAO) args:匹配

参数格式符合标准的方法(了解) args(int,int)

execution:

匹配方法的执行(常用)

格式:execution(方法格式表达式)

例一:匹配所有指定返回值类型的方法

void *(..) int *(..) double *(..)

例二:匹配指定包或子包中类的方法

*..*() com.bdyc.aop.user.dao.*.*(..) cn..dao.*.*(..)

例三:匹配指定类或接口的方法

* *..UserImpl.*(..) * *..*Impl.*(..) * *..*DAO.*(..)

例四:匹配指定方法名或部分匹配方法名的方法

*..*.add(..) *..*.*e*(..) *..*.get*(..)

例五:匹配所有指定参数个数或类型的方法

* *..*.*(int,int) * *..*.*(*,*) * *..*.*(*,..)

6、AOP切入点配置

AOP配置中,由于切入点使用量很大,Spring提供 了AOP切入点配置的引用格式

以下两种格式是等效的

格式一:直接配置切入点

7、AOP通知类型

 AOP的通知类型共5种

①、before:前置通知(应用:各种校验) 在方法执行前执行,如果其中抛出异常

②、after:后通知(应用:清理现场) 方法执行完毕后执行,无论方法中是否出现异常

③、afterReturning:返回后通知(应用:常规数据处理) 方法正常返回后执行,如果方法中抛出异常(没处理)无法执行

④、afterThrowing:抛出异常后通知(应用:包装异常信息) 方法抛出异常后执行,如果方法没有抛出异常,无法执行

⑤、around:环绕通知(应用:十分强大,可以做任何事情) 方法执行前后分别执行,可以阻止方法的执行

8、AOP通知类型配置

使用aop命名空间,配置5种通知类型

常用属性

pointcut:配置切入点表达式

pointcut-ref:配置切入点引用对象

method:配置切入点执行的通知方法

AOP通知类型对应的方法

注解开发AOP(@AspectJ支持)(重点)

@AspectJ提供使用注解开发SpringAOP
开启Spring对@AspectJ注解开发的支持

注解开发AOP

使用注解开发AOP要保障所有的相关类必须受到 Spring控制,因此在进行注解开发AOP之前,首先 将所有相关类配置成Bean

配置式 注解式

注解开发AOP——配置aspect

使用@Aspect将Bean设置为切面

使用注解配置@Aspect与下列效果等同,注解免去了配置 时指定对应Bean的环节

注解开发AOP——配置通知类型
通知类型通过注解的形式完成 使用@Before将指定方法设置为前置通知

注解开发AOP——切入点

只用注解形式开发公共切入点 使用@Pointcut配置切入点,@Pointcut声明在无返回值方 法上方,方法名即切入点名称,方法最好声明为private

注解开发AOP——各种通知类型

前置通知

@Before(value="execution(* *..*.*(..))")

 后置通知

@After(value="execution(* *..*.*(..))")

 抛出异常通知

@AfterThrowing(value="execution(* *..*.*(..))",throwing=“ex")

 返回后通知

@AfterReturning(value="execution(* *..*.*(..))",returning=“ret")

 环绕通知

@Around(value="execution(* *..*.*(..))")

 eg:

/**
 * 接口
 * @author Thinkpad
 *
 */
public interface UserServiceI {
    public void add();
    public void del();
    public void update();
    public void fnn();
}

/**
 * 业务类
 * @author Thinkpad
 *
 */
//目标对象
public class UserService implements UserServiceI{
    //连接点/切入点
    public void add() {
        //1、抽查公共模块到通知类中
        System.out.println("3、执行添加");
        
    }

    public void del() {
        // TODO Auto-generated method stub
        
    }

    public void update() {
        // TODO Auto-generated method stub
        
    }

    public void fnn() {
        // TODO Auto-generated method stub
        
    }

}

<!--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-3.0.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    ">
    <!-- 创建一个通知类对象:advice -->
    <bean id="myAdvice" class="com.wd.aop.Advice"></bean>
    <!-- AOP配置 -->
    <aop:config>
        <!-- 配置切面
            ref:使用通知类对象
         -->
         <aop:aspect ref="myAdvice">
             <!-- method:要切入的通知方法 -->
             <aop:before method="fn1" pointcut="execution(public void com.wd.aop.UserService.add())"/>
             <aop:after method="fn2" pointcut="execution(public void com.wd.aop.UserService.add())"/>
         </aop:aspect>
    </aop:config>
<!-- spring 内部默认支持代理是:
    JDKProxy:只能代理基于接口的对象
    CGLIBProxy:能代理任何对象
 -->
 <bean id="userService" class="com.wd.aop.UserService" ></bean>

</beans>

/**
 * 测试类
 * @author Thinkpad
 *
 */
public class Test {
    public static void main(String[] args){
        ApplicationContext ac =  new ClassPathXmlApplicationContext("applicationContext.xml");
        UserServiceI userService = (UserServiceI) ac.getBean("userService");//spring 内部默认支持代理是:JDKProxy:只能代理基于接口的对象    // CGLIBProxy:能代理任何对象
userService.add(); } }

 输出结果:

eg(cglib代理模式):

首先导入cglib.jar

/**
 * 业务类
 * @author Thinkpad
 *
 */
//目标对象
public class UserService{
    //连接点/切入点
    public void add() {
        //1、抽查公共模块到通知类中
        System.out.println("3、执行添加cglib代理");
        
    }

    public void del() {
        // TODO Auto-generated method stub
        
    }

    public void update() {
        // TODO Auto-generated method stub
        
    }

    public void fnn() {
        // TODO Auto-generated method stub
        
    }

}

<!--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-3.0.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    ">
    <!-- 创建一个通知类对象:advice -->
    <bean id="myAdvice" class="com.wd.aop.Advice"></bean>
    <!-- AOP配置 -->
    <aop:config>
        <!-- 配置切面
            ref:使用通知类对象
         -->
         <aop:aspect ref="myAdvice">
             <!-- method:要切入的通知方法 -->
             <aop:before method="fn1" pointcut="execution(public void com.wd.aop.UserService.add())"/>
             <aop:after method="fn2" pointcut="execution(public void com.wd.aop.UserService.add())"/>
         </aop:aspect>
    </aop:config>
<!-- spring 内部默认支持代理是:
    JDKProxy:只能代理基于接口的对象
    CGLIBProxy:能代理任何对象
 -->
 <bean id="userService" class="com.wd.aop.UserService" ></bean><!--开启cglib代理-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

/**
 * 测试类
 * @author Thinkpad
 *
 */
public class Test {
    public static void main(String[] args){
        ApplicationContext ac =  new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) ac.getBean("userService");//注:用类而不是接口
        userService.add();
    }
}

输出结果:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!