顾乔芝士网

持续更新的前后端开发技术栈

JAVA一天学一点之Spring AOP_java spring aop

AOP:面向切面编程

OOP:面向对象编程

AOP是OOP的一个有效补充

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented

Programming,面向对象编程)的补充和完善。

面向切面是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编

程。常见的使用场景:

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging 调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence 持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务


面向切面编程的几个核心概念

概念

说明

IOC/DI

本质是就是Java反射+XML解析

AOP

本质上Java动态代理

切点

要添加代码的地方称作切点

切面

切点+通知

通知(增强)

向切点插入的代码称为通知Advice

连接点

切点的定义

面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等。若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清

AOP术语介绍

术语

说明

切面

(Aspect)

切面泛指交叉业务逻辑。比如事务处理、日志处理就可以理解为切面。常用的切面有通知与顾问。实际就是对主业务逻辑的一种增强(通知和切入点之间的关系)

织入

(Weaving)

织入是指将切面代码插入到目标对象的过程。

连接点

(Joinpoint)

连接点指切面可以织入的位置。(目标类中的所有的方法)

切入点

(Pointcut)

切入点指切面具体织入的位置

通知

(Advice)

通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。(被抽取的共性功能的代码逻辑)

顾问

(Advisor)

顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。 不仅指定了切入时间点,还可以指定具体的切入点

目标对象

(Target

Object)

有切入点方法的对象

代理对象

(Proxy

Object)

Spring代理目标对象也就是AOP代理对象

基于AspectJ的AOP

对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。然而,AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。在Spring中使用AOP开发时,一般使用AspectJ的实现方式

ActJ中的通知类型

通知类型

说明

前置通知

目标方法执行之前调用

后置通知

目标方法执行完成之后调用

环绕通知

目标方法执行前后都会调用方法,且能增强结果

异常处理通知

目标方法出现异常调用

最终通知

无论程序执行是否正常,该通知都会执行。类似于try…catch中finally代码块

AspectJ的切入点表达式

execution(

[modifiers-pattern] 访问权限类型

ret-type-pattern 返回值类型

[declaring-type-pattern] 全限定性类名

name-pattern(param-pattern) 方法名(参数名)

[throws-pattern] 抛出异常类型

)

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中明显就是方法的签名。注意,表达式中加 [ ] 的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号

符号

意义

*

0至多个字符

..

方法参数中表示任意多个参数,用在包名后表示当前包及其子包路径

+

用在类名后表示当前类及子类,用在接口后表示接口及实现类

举例:

基于注解的实现

前置通知

创建对应的接口和实现

创建对应的切面类

/**

* 公共接口

*/

public interface SomeService {

String doSome(String msg);

void say();

}

package com.bobo.aop.service.impl;

import com.bobo.aop.service.SomeService;

/**

* 目标对象

*/

public class SomeServiceImpl implements SomeService {

@Override

public String doSome(String msg) {

System.out.println("目标对象doSome执行了..." + msg);

return "Hello:" + msg;

}

@Override

public void say() {

System.out.println("目标对象say执行了..." );

}

}

/*** 切面类

* @Aspect 该注解标注的 Java类就是一个切面类

*/

@Aspect

public class MyAspect {

/**

* 前置通知

* 通过切入点表达式来使 通知和切入点关联

*/

@Before(value = "execution(* com.bobo.aop.service.*.*(..))")

public void beforeMethod(){

System.out.println("前置通知");

}

}

配置文件

<?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">

<!-- 注册目标类 -->

<bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"/>

<!-- 注册切面类 -->

<bean class="com.bobo.aop.aspect.MyAspect" id="myAspect"/>

<!-- 注册自动代理 -->

<aop:aspectj-autoproxy/>

</beans>

也可以使用Java配置类的方式

@Configuration

@EnableAspectJAutoProxy

public class JavaConfig {

@Bean

public SomeService someService(){

return new SomeServiceImpl();

}

@Bean

public MyAspect myAspect(){

return new MyAspect();

}

}

测试

public class AppStart {

public static void main(String[] args) {

ApplicationContext ac = new

ClassPathXmlApplicationContext("applicationContext.xml");

SomeService service = ac.getBean(SomeService.class);

System.out.println(service);

service.doSome("aaaa");

System.out.println("-------------");

service.say();

}

}

后置通知

在前面的基础上我们只需要在切面类中添加对应的后置通知即可

/**

* 切面类

* @Aspect 该注解标注的 Java类就是一个切面类

*/

@Aspect

public class MyAspect {

/**

* 前置通知

* 通过切入点表达式来使 通知和切入点关联

*/

//@Before(value = "execution(* com.bobo.aop.service.*.*(..))")

public void beforeMethod(){

System.out.println("前置通知");

}

/**

* 后置通知

*/

@AfterReturning(value = "execution(* com.bobo.aop.service.*.*

(..))",returning = "msg")

public void afterReturningMethod(Object msg){

System.out.println("后置通知..." + msg);

}

}

环绕通知

切面类中添加对应的环绕通知,通过切入点表达式和切入点关联

/**

* 环绕通知

* @return

*/

@Around(value = "execution(* com.bobo.aop.service.*.*(..))")

public Object aroundMethod(ProceedingJoinPoint pjp){

System.out.println("执行之前...");

// 执行目标对象的方法

Object proceed = null;

try {

proceed = pjp.proceed();

} catch (Throwable throwable) {

throwable.printStackTrace();

}

System.out.println("执行之后...");

return proceed;

}

异常通知

/**

* 异常通知

* @param ex

*/

@AfterThrowing(value = "execution(* com.bobo.aop.service.*.*(..))",throwing = "ex")

public void throwsMethod(Exception ex){

System.out.println("异常通知..." + ex);

}

最终通知

/**

* 最终通知

* 只要切入点方法执行了,那么最终通知一定会执行

* 我们可以用来做一些资源回收等操作

*/

@After(value = "execution(* com.bobo.aop.service.*.*(..))")

public void afterMethod(){

System.out.println("最终通知...");

}

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言