ssm合集(spring)---003

3 AOP 面向切面编程
AOP 简介
AOP Aspect Orient Programming ),面向切面编程。面向切面编程是从动态角度考虑程
序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理: JDK 的动态代理 ,与 CGLIB
的动态代理
AOP Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态
代理实现程序功能的统一维护的一种技术。 AOP Spring 框架中的一个重要内容。利用 AOP
可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程
序的可重用性,同时提高了开发的效率。
面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到
主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、
事务、日志、缓存等。
若不使用 AOP ,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,
会使主业务逻辑变的混杂不清。
例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事
务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占
比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大
大干扰了主业务逻辑 --- 转账。
面向切面编程对有什么好处?
1. 减少重复;
2. 专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
使用 AOP 减少重复代码,专注业务实现:

AOP 编程术语 ( 掌握 )
1 ) 切面( Aspect
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面
是通知( Advice )。实际就是对主业务逻辑的一种增强。
2 ) 连接点( JoinPoint
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
3 ) 切入点( Pointcut
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不
能被增强的。
4 ) 目标对象( Target
目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。 上 例 中 的
StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,
不被增强,也就无所谓目标不目标了。
5 ) 通知( Advice
通知表示切面的执行时间, Advice 也叫增强。上例中的 MyInvocationHandler 就可以理
解为是一种通知。换个角度来说, 通知定义了增强代码切入到目标代码的时间点 ,是目标方
法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时间
AspectJ AOP 的实现 ( 掌握 )
对于 AOP 这种编程思想,很多框架都进行了实现。 Spring 就是其中之一,可以完成面向
切面编程。然而, AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,
而且还支持注解式开发。所以, Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框
架中。
Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
AspectJ 简介
AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。
官网地址: http://www.eclipse.org/aspectj/
AspetJ Eclipse 的开源项目,官网介绍如下:
a seamless aspect-oriented extension to the Javatm programming language
(一种基于 Java 平台 的面向切面编程的语言)
Java platform compatible (兼容 Java 平台,可以无缝扩展)
easy to learn and use (易学易用)
AspectJ 的通知类型 ( 理解 )
AspectJ 中常用的通知有五种类型:
1 )前置通知
2 )后置通知
3 )环绕通知
4 )异常通知
5 )最终通知
AspectJ 的切入点表达式 ( 掌握 )
AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:
execution(modifiers-pattern? ret-type-pattern 
declaring-type-pattern?name-pattern(param-pattern)
 throws-pattern?)
解释:
modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名 ( 参数类型和参数个数 )
throws-pattern 抛出异常类型
?表示可选的部分
以上表达式共 4 个部分。
execution( 访问权限 方法返回值 方法声明 ( 参数 ) 异常类型 )
切入点表达式要匹配的对象就是目标方法的方法名。所以, execution 表达式中明显就
是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可
以使用以下符号:

举例:
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“ set” 开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“ ..” 出现在类名中时,后
面必须跟“ * ”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
AspectJ 的开发环境 ( 掌握 )
1 maven 依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.5.RELEASE</version>
</dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.5.RELEASE</version>
</dependency>
插件
<build>
<plugins>
 <plugin>
 <artifactId>maven-compiler-plugin</artifactId>
 <version>3.1</version>
 <configuration>
 <source>1.8</source>
 <target>1.8</target>
 </configuration>
</plugin>
</plugins> 
</build>

2 ) 引入 AOP 约束
         在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签,
均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。
AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
AspectJ 基于注解的 AOP 实现 ( 掌握 )
AspectJ 提供了以注解方式对于 AOP 的实现。
1 ) 实现步骤
A Step1 :定义业务接口与实现类
package com.zsz.ba01;

public interface SomeService {
    void doSome(String name,Integer age);

}

package com.zsz.ba01;

//目标类
public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name,Integer age) {
//        给doSome()方法增加一个功能,在doSome()执行之前,输出方法的执行时间
        System.out.println("===目标方法doSome===");

    }
}

B Step2 :定义切面类
类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。
package com.zsz.ba01;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

/**
 * @Aspect:是aspectj框架中的注解。
 *
 *   作用:表示当前类是切面类。
 *   切面类是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *   位置:在类定义的上面
 */

@Aspect
public class MyAspect {


   

    /**
     * 指定通知方法中的参数 : JoinPoint
     * joinPoint:业务方法,要加入切面功能的业务方法
     *
     */
    @Before(value = "execution(public void com.zsz.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(JoinPoint jp){
        //获取方法的完整定义
        System.out.println("方法的签名(定义)="+jp.getSignature());
        System.out.println("方法的名称"+jp.getSignature().getName());

        //获取方法的实参
        Object[] args = jp.getArgs();
        for (Object arg : args){
            System.out.println("参数="+arg);
        }
        System.out.println("前置通知, 切面功能:在目标方法之前输出执行时间:"+new Date());
    }
}
C Step3 :声明目标对象切面类对象
<?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">


    <!--把对象交给spring容器,由spring容器统一创建,管理对象-->
    <!--声明目标对象-->
    <bean id="someService" class="com.zsz.ba01.SomeServiceImpl"></bean>

    <!--声明切面对象-->
    <bean id="myAspect" class="com.zsz.ba01.MyAspect"/>

    
</beans>

D Step4 :注册 AspectJ 的自动代理
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类 + 切面”的代理
对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj
自动代理生成器,其就会自动扫描到 @Aspect 注解,并按通知类型与切入点,将其织入,并
生成代理。
<!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象
        创建代理对象是在内存中实现的,修改内存对象的内存中的结构。

        aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生产代理对象。
     -->

    <aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy/> 的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的。
从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。
其工作原理是, <aop:aspectj-autoproxy/> 通过扫描找到 @Aspect 定义的切面类,再由切
面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
E Step5 :测试类中使用目标对象的 id
package com.zsz;

import com.zsz.ba01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest01 {
    @Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ac= new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService someService = (SomeService) ac.getBean("someService");
        //通过代理的对象执行方法,实现目标方法执行还时,增强了功能

        //someService:com.sun.proxy.$Proxy8
        System.out.println("someService:"+someService.getClass().getName());
        someService.doSome("赵书正",20);



    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值