博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA入门[14]-Spring MVC AOP
阅读量:7090 次
发布时间:2019-06-28

本文共 6096 字,大约阅读时间需要 20 分钟。

一、基本概念

1.AOP简介

DI能够让相互协作的软件组件保持松散耦合;而面向切面编程(aspect-oriented programming,AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题

常见场景:日志、安全、事物、缓存

2.AOP用到的一些术语

项目中每个模块的核心功能都是为特定业务领域提供服务,但是这些模块都需要类似的辅助功能,例如安全和事务管理,这时候需要引入AOP的概念。

通知定义了切面是什么以及何时使用, Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能;
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  • 返回通知(After-returning):在目标方法成功执行之后调用通知;
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

连接点(join potint)是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为

切点(poincut)的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点

 

二、准备service模块

1.service bean

1
2
3
4
5
6
7
8
9
10
11
public 
class 
CategoryService1 {
    
public 
void 
add(
int 
id) {
        
System.out.println(
"CategoryService1.add()"
);
    
}
}
 
public 
class 
CategoryService2{
    
public 
void 
add(
int 
id) {
        
System.out.println(
"CategoryService2.add()"
);
    
}
}

2.配置bean

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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"
xmlns:tx=
"http://www.springframework.org/schema/tx"
xsi:schemaLocation="http:
//www.springframework.org/schema/beans
        
http:
//www.springframework.org/schema/beans/spring-beans-4.2.xsd
        
http:
//www.springframework.org/schema/aop
        
http:
//www.springframework.org/schema/aop/spring-aop-4.2.xsd">
 
<bean id=
"categoryServiceImpl" 
class
=
"service.CategoryService1"
></bean>
<bean id=
"CategoryServiceImpl2" 
class
=
"service.CategoryService2"
></bean>
</beans>

3.单元测试

1
2
3
4
5
6
7
8
9
10
@Test
public 
void 
test(){
    
ApplicationContext context=
new 
ClassPathXmlApplicationContext(
"aop.xml"
);
 
    
CategoryService1 service1=context.getBean(CategoryService1.
class
);
    
service1.add(
1
);
 
    
CategoryService2 service2=context.getBean(CategoryService2.
class
);
    
service2.add(
2
);
}

运行结果:

CategoryService1.add()

CategoryService2.add()

 

三、XML方式声明AOP

Spring所创建的通知都是用标准的Java类编写的, 定义通知所应用的切点通常会使用注解或在Spring配置文件里采用XML来编写,这两种语法对于Java开发者来说都是相当熟悉的。

注意Spring只支持方法级别的连接点。

切入点表达式

execution指示器是我们在编写切点定义时最主要使用的指示器

 

Demo

我们要实现的一个简单示例是:在service方法调用前和调用后打印日志“write log”。

1
2
3
4
5
public 
class 
LogHandler {
    
public 
void 
log(){
        
System.out.println(
"write log."
);
    
}
}

aop.xml添加配置:

1
2
3
4
5
6
7
8
<bean id=
"logHandler" 
class
=
"pointcut.LogHandler"
></bean>
    
<aop:config>
        
<aop:aspect id=
"log" 
ref=
"logHandler"
>
            
<aop:pointcut id=
"addLog" 
expression=
"execution(* service.*.*(..))"
></aop:pointcut>
            
<aop:before method=
"log" 
pointcut-ref=
"addLog"
></aop:before>
            
<aop:after method=
"log" 
pointcut-ref=
"addLog"
></aop:after>
        
</aop:aspect>
    
</aop:config> 

单元测试:

1
2
3
4
5
6
7
8
9
10
public 
class 
AopTests {
    
@Test
    
public 
void 
test() {
        
ApplicationContext context = 
new 
ClassPathXmlApplicationContext(
"aop.xml"
);
        
CategoryService1 service1 = context.getBean(CategoryService1.
class
);
        
service1.add(
1
);
        
CategoryService2 service2 = context.getBean(CategoryService2.
class
);
        
service2.add(
2
);
    
}
}

运行报错:

org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException

原来是忘了pom依赖:

1
2
3
4
5
<dependency>
    
<groupId>org.springframework</groupId>
    
<artifactId>spring-aspects</artifactId>
    
<version>${spring.version}</version>
</dependency> 

运行结果:

write log.

CategoryService1.add()

write log.

write log.

CategoryService2.add()

write log.

 
完整的pom.xml

 

四、aop:around

通过使用环绕通知,可以实现前置通知和后置通知所实现的功能,而且只需要在一个方法中实现。

1
2
3
4
5
6
7
8
9
10
11
public 
class 
LogTimeHandler {
    
public 
void 
log(ProceedingJoinPoint jp) 
throws 
Throwable {
        
try 
{
            
System.out.println(
"1.before log "
+
new 
Date().getTime());
//记录开始时间
            
jp.proceed();
            
System.out.println(
"2.after log "
+
new 
Date().getTime());
//记录结束时间
        
}
catch 
(Exception e){
            
System.out.println(
"log fail "
);
        
}
    
}
}

  

在aop1.xml中配置aop:round通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd"
>
 
    
<bean id=
"categoryService" 
class
=
"service.CategoryService1"
></bean>
    
<bean id=
"logHanlder" 
class
=
"pointcut.LogTimeHandler"
></bean>
    
<aop:config>
        
<aop:aspect id=
"log" 
ref=
"logHanlder"
>
            
<aop:pointcut id=
"addlog" 
expression=
"execution(* service.*.*(..))"
></aop:pointcut>
            
<aop:around method=
"log" 
pointcut-ref=
"addlog"
></aop:around>
        
</aop:aspect>
    
</aop:config>
</beans>

  

单元测试:

1
2
3
4
5
6
7
8
public 
class 
AopTest1 {
    
@Test
    
public 
void 
test(){
        
ApplicationContext context=
new 
ClassPathXmlApplicationContext(
"aop1.xml"
);
        
CategoryService1 service1=context.getBean(CategoryService1.
class
);
        
service1.add(
1
);
    
}
}

运行结果:

1
2
3
1
.before log 
1489990832246
CategoryService1.add()
2
.after log 
1489990832263

  

五、注解方式创建AOP

定义切面需要给类添加@Aspect注解。然后需要给方法添加注解来声明通知方法,各通知类型对应的注解:

  • @After 通知方法会在目标方法返回或抛出异常后
  • @AfterReturning 通知方法会在目标方法返回后调用
  • @AfterThrowing 通知方法会在目标方法抛出异常后调用
  • @Around 通知方法会将目标方法封装起来
  • @Before 通知方法会在目标方法调用之前执行
1
2
3
4
5
6
7
8
9
@Component
@Aspect
public 
class 
LogHelper3 {
 
    
@Before
(
"execution(* service.*.*(..))"
)
    
public 
void 
logStart(){
        
System.out.println(
"log start "
+
new 
Date().getTime());
    
}
}

然后定义JavaConfig类,注意需要给类添加@EnableAspectJAutoProxy注解启用自动代理功能。

1
2
3
4
5
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
(basePackageClasses = {service.CategoryService3.
class
,pointcut.LogHelper3.
class
})
public 
class 
BeanConfig {
}

  单元测试:

1
2
3
4
5
6
7
8
9
10
11
12
@RunWith
(SpringJUnit4ClassRunner.
class
)
@ContextConfiguration
(classes = BeanConfig.
class
)
public 
class 
AopTest3 {
 
    
@Autowired
    
CategoryService3 service;
 
    
@Test
    
public 
void 
testConfigAop(){
        
service.add(
100
);
    
}
}

运行结果:

1
2
log start 
1489990977264
add category id=
100

  

结尾:

参考:《spring实战》

源码下载:https://github.com/cathychen00/learnjava/tree/master/DemoAOP

    本文转自 陈敬(Cathy) 博客园博客,原文链接:http://www.cnblogs.com/janes/p/6873732.html,如需转载请自行联系原作者

你可能感兴趣的文章
mysql并发插入重复数据问题的解决思路
查看>>
MySQL 5.7.x修改root默认密码(CentOS下)
查看>>
Linux下动态加载SO文件
查看>>
Mysql创建、删除用户
查看>>
我的友情链接
查看>>
MySQL-MySQL常用命令
查看>>
Linux iptraf 网络监控
查看>>
Swift::8::枚举
查看>>
ZABBIX web端 显示 server 运行状态 不
查看>>
Aspose.Words使用教程之在文档中找到并替换文本
查看>>
ORACLE官方文档如何学习
查看>>
google guava包集合类HashMultiset的基本用法
查看>>
linux 进程和线程
查看>>
网工考试!
查看>>
Spring Boot 项目脚本(启动、停止、重启、状态)
查看>>
专访路彦雄:理解语言其实还是很难的
查看>>
Windows 2003服务器群集(负载均衡)简介
查看>>
日程日历示例
查看>>
linux下解决对外udp***
查看>>
fluuter 浮动 和 触摸
查看>>