Aop基于xml和注解应用

news/2024/11/23 16:49:09/

基于 XML 的 AOP 开发

问题1:在通知方法中如何定义切入点表达式?

问题2:如何配置切面?

问题3:在配置类上如何开启AOP注解功能?

1.1 快速入门

①导入 AOP 相关坐标

②创建目标接口和目标类(内部有切点)

③创建切面类(内部有增强方法)

④将目标类和切面类的对象创建权交给 spring

⑤在 applicationContext.xml 中配置织入关系

⑥测试代码

1.1.1实现步骤

第一步 导入 AOP 相关坐标

<!--导入spring的context坐标,context依赖aop-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.5.RELEASE</version>
</dependency>
<!-- aspectj的织入 -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version>
</dependency>

第二步 创建目标接口和目标类(内部有切点)

/*目标接口*/
public interface TargetInterface {public void method();}@Component
public class Terget implements TargetInterface {@Overridepublic void method() {System.out.println("Target 正在运行中....");}
}

第三步 创建切面类(内部有增强方法)

public class MyAspect {//前置增强方法public void before(){System.out.println("前置代码增强.....");}
}

导入aop命名空间

第四步 在 applicationContext.xml 中配置织入关系

配置切点表达式和前置增强的织入关系

<aop:config><!--引用myAspect的Bean为切面对象--><aop:aspect ref="myAspect"><!--配置Target的method方法执行时要进行myAspect的before方法前置增强--><aop:before method="before" pointcut="execution(public void com.by.aop.Target.method())"></aop:before></aop:aspect>
</aop:config>
​
<!--优化代码-->
<aop:config><aop:aspect ref="myAspect"><aop:pointcut id="mypointcut" expression="execution(public void com.by.aop.Target.method())"/>
​
<aop:before method="before" pointcut-ref="mypointcut"></aop:before>
​</aop:aspect>
</aop:config>

第六步 测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {@Autowiredprivate TargetInterface target;@Testpublic void test1(){target.method();}
}

1.2 XML 配置 AOP 详解

1.2.1 切点表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 访问修饰符可以省略

  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意

  • 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类

  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

例如:

execution(public void com.by.aop.Target.method())   
execution(void com.by.aop.Target.*(..))
execution(* com.by.aop.*.*(..))
execution(* com.by..*.*(..))
execution(* *..*.*(..))

1.2.2.通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

通知执行顺序

1.2.3 切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。

<aop:config><!--引用myAspect的Bean为切面对象--><aop:aspect ref="myAspect"><aop:pointcut id="myPointcut" expression="execution(* com.by.aop.*.*(..))"/><aop:before method="before" pointcut-ref="myPointcut"></aop:before></aop:aspect>
</aop:config>

1.3 知识要点

  • aop织入的配置

<aop:config><aop:aspect ref=“切面类”><aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before></aop:aspect>
</aop:config>
  • 通知的类型:前置通知、后置通知、环绕通知、异常抛出通知、最终通知

  • 切点表达式的写法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

2.基于注解的 AOP 开发

2.1 AOP案例思路分析

  • 案例设定:测定接口执行效率

  • 简化设定:在接口执行前输出当前系统时间

  • 开发模式:XML or ==注解==

  • 思路分析:

    • 导入坐标(pom.xml)

    • 制作连接点方法(原始操作,dao接口与实现类)

    • 制作共性功能 (通知类与通知)

    • 定义切入点

    • 绑定切入点与通知关系(切面)

2.2 AOP入门案例实现

【第二步】定义dao接口与实现类

public interface BookDao {public void save();public void update();
}@Repository
public class BookDaoImpl implements BookDao {@Overridepublic void save() {System.out.println(System.currentTimeMillis());System.out.println("book dao save ...");}@Overridepublic void update() {System.out.println("book dao update ...");}
}

【第三步】定义通知类,制作通知方法

//通知类必须配置成Spring管理的bean
@Component
public class MyAdvice {public void method(){System.out.println(System.currentTimeMillis());}
}

【第四步】定义切入点表达式、配置切面(绑定切入点与通知关系)

//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {//设置切入点,@Pointcut注解要求配置在方法上方@Pointcut("execution(void com.by.dao.BookDao.save())")public void pt(){
​}//设置在切入点pt()的前面运行当前操作(前置通知)@Before("pt()")public void method(){System.out.println(System.currentTimeMillis());}
}

【第五步】在配置类中进行Spring注解包扫描和开启AOP功能

@Configuration
@ComponentScan("com.by")
//开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}

测试类和运行结果

public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);bookDao.update();}
}

3. 注解+配置文件结合案例

基于注解+配置的aop开发步骤:

①创建目标接口和目标类(内部有切点)

②创建切面类(内部有增强方法)

③将目标类和切面类的对象创建权交给 spring

④在切面类中使用注解配置织入关系

⑤在配置文件中开启组件扫描和 AOP 的自动代理

⑥测试

3.1 AOP入门案例实现

第一步 创建目标接口和目标类(内部有切点)

public interface TargetInterface {public void method();
}
​
public class Target implements TargetInterface {@Overridepublic void method() {System.out.println("Target running....");}
}

第二步 创建切面类(内部有增强方法)

public class MyAspect {//前置增强方法public void before(){System.out.println("前置代码增强.....");}
}

第三步 将目标类和切面类的对象创建权交给 spring

@Component("target")
public class Target implements TargetInterface {@Overridepublic void method() {System.out.println("Target running....");}
}
@Component("myAspect")
public class MyAspect {public void before(){System.out.println("前置代码增强.....");}
}

第四步 在切面类中使用注解配置织入关系

@Component("myAspect")
@Aspect
public class MyAspect {@Before("execution(* com.by.aop.*.*(..))")public void before(){System.out.println("前置代码增强.....");}
}

第五步 在配置文件中开启组件扫描和 AOP 的自动代理

<!--组件扫描-->
<context:component-scan base-package="com.by.aop"/>
​
<!--aop的自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

第六步 测试代码 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {@Autowiredprivate TargetInterface target;@Testpublic void test1(){target.method();}
}

4. AOP工作流程【理解】

什么是目标对象?什么是代理对象?

4.1 AOP工作流程

  1. Spring容器启动

  2. 读取所有切面配置中的切入点

  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点

    • 匹配失败,创建原始对象

    • 匹配成功,创建原始对象(目标对象)的代理对象

  4. 获取bean执行方法

    1. 获取的bean是原始对象时,调用方法并执行,完成操作

    2. 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

4.2 AOP核心概念

目标对象(Target):被代理的对象,也叫原始对象,该对象中的方法没有任何功能增强。

代理对象(Proxy):代理后生成的对象,由Spring帮我们创建代理对象。

4.3 在测试类中验证代理对象

public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);bookDao.update();//打印对象的类名System.out.println(bookDao.getClass());}
}

回顾切入点表达式

5.1 语法格式

  • 切入点:要进行增强的方法

  • 切入点表达式:要进行增强的方法的描述方式execution(void com.by.dao.BookDao.update())execution(void com.by.dao.impl.BookDaoImpl.update())

    • 描述方式一:执行com.by.dao包下的BookDao接口中的无参数update方法

    • 描述方式二:执行com.by.dao.impl包下的BookDaoImpl类中的无参数update方法

  • 切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)execution(public User com.by.service.UserService.findById(int))

    • 动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点

    • 访问修饰符:public,private等,可以省略

    • 返回值:写返回值类型

    • 包名:多级包使用点连接

    • 类/接口名:

    • 方法名:

    • 参数:直接写参数的类型,多个类型用逗号隔开

    • 异常名:方法定义中抛出指定异常,可以省略

5.2 通配符

目的:可以使用通配符描述切入点,快速描述。

  • :单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现

匹配com.by包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法

execution(public * com.by.*.UserService.find*(*))
  • .. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法

execution(public User com..UserService.findById(..))
  • +:专用于匹配子类类型

execution(* *..*Service+.*(..))

5.3 书写技巧

  • 所有代码按照标准规范开发,否则以下技巧全部失效

  • 描述切入点通==常描述接口==,而不描述实现类

  • 访问控制修饰符针对接口开发均采用public描述(==可省略访问控制修饰符描述==

  • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述

  • ==包名==书写==尽量不使用..匹配==,效率过低,常用*做单个包描述匹配,或精准匹配

  • ==接口名/类名==书写名称与模块相关的==采用*匹配==,例如UserService书写成*Service,绑定业务层接口名

  • ==方法名==书写以==动词==进行==精准匹配==,名词采用匹配,例如getById书写成getBy,selectAll书写成selectAll

  • 参数规则较为复杂,根据业务方法灵活调整

  • 通常==不使用异常==作为==匹配==规则


http://www.ppmy.cn/news/754336.html

相关文章

堆排序选择排序

选择排序 选择排序&#xff08;Selection sort&#xff09;是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&…

钉钉聊天对话框和截图经常发生白屏

环境&#xff1a; 7.0.30-rel6019102 Win10专业版 L盾加密环境 问题描述&#xff1a; 钉钉聊天对话框和截图经常发生白屏 解决方案&#xff1a; 1.【电脑端钉钉】- 左上角【头像】-【设置】-【高级】- 下拉【网络检测】- 点击【开始检测】 如果变红说明网络有问题&#x…

1213123

**1.9bk**1、传感器数据的获取是传感网应用开发的基础,也是认证考试中经常考核的考点,属于必须掌握的范围。在不同的题目中采集传感器的代码有些许差别,Keil项目和IAR项目也不一样,本节先介绍Keil项目,IAR项目请参见第三章对应章节。 采集传感器的值,代码如下: int main…

注意力机制[矩阵]

每一个输入的向量( Embedding后的向量)&#xff0c;均有q,k,v,三个东西。其中q由下图所生成 I矩阵有a1,a2,a3,a4组成&#xff0c;Wq为权重矩阵&#xff0c;将I与Wq相乘求得Q(q1,q2,q3,q4)。K和V与I同理均可求得。 将求得出来的K&#xff0c;转置为竖向量与Q相乘&#xff0c;就…