一、基本概念

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

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

<?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.单元测试

@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”。

public class LogHandler {
public void log(){
System.out.println("write log.");
}
}

aop.xml添加配置:

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

单元测试:

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依赖:

<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.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>DemoStore</groupId>
<artifactId>DemoAOP</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<spring.version>4.3.5.RELEASE</spring.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies> </project>

完整的pom.xml

四、aop:around

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

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通知

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

  

单元测试:

public class AopTest1 {
@Test
public void test(){
ApplicationContext context=new ClassPathXmlApplicationContext("aop1.xml");
CategoryService1 service1=context.getBean(CategoryService1.class);
service1.add(1);
}
}

运行结果:

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

  

五、注解方式创建AOP

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

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

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

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackageClasses = {service.CategoryService3.class,pointcut.LogHelper3.class})
public class BeanConfig {
}

  单元测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BeanConfig.class)
public class AopTest3 { @Autowired
CategoryService3 service; @Test
public void testConfigAop(){
service.add(100);
}
}

运行结果:

log start 1489990977264
add category id=100

  

结尾:

参考:《spring实战》

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

JAVA入门[14]-Spring MVC AOP的更多相关文章

  1. java企业架构 spring mvc +mybatis + KafKa+Flume+Zookeeper

    声明:该框架面向企业,是大型互联网分布式企业架构,后期会介绍linux上部署高可用集群项目. 项目基础功能截图(自提供了最小部分)      平台简介        Jeesz是一个分布式的框架,提供 ...

  2. JAVA入门[13]-Spring装配Bean

    一.概要 Sping装配bean主要有三种装配机制: 在XML中进行显式配置. 在Java中进行显式配置. 隐式的bean发现机制和自动装配. 原则: 建议尽可能地使用自动配置的机制,显式配置越少越好 ...

  3. 第63节:Java中的Spring MVC简介笔记

    前言 感谢! 承蒙关照~ Java中的Spring MVC简介笔记 MVC简介 Spring MVC 基本概念 Spring MVC 项目搭建 maven 使用Spring MVC进行开发 实现数据绑 ...

  4. Java方式配置Spring MVC

    概述 使用Java方式配置Spring MVC,以及回顾一下Spring MVC的各种用法. Spring MVC简述 关于Spring MVC的介绍网上有很多,这里就不再赘述了,只是要说一下,Spr ...

  5. 【Java】关于Spring MVC框架的总结

    SpringMVC是一种基于Java,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将Web层进行职责解耦.基于请求驱动指的就是使用请求-响应模型,框架的 ...

  6. 如何用Java类配置Spring MVC(不通过web.xml和XML方式)

    DispatcherServlet是Spring MVC的核心,按照传统方式, 需要把它配置到web.xml中. 我个人比较不喜欢XML配置方式, XML看起来太累, 冗长繁琐. 还好借助于Servl ...

  7. [Java] Maven 建立 Spring MVC 工程

    GIT: https://github.com/yangyxd/Maven.SpringMVC.Web 1. 建立 WebApp 工程 下一步: 下一步: 选择 maven-archetype-web ...

  8. Java框架之Spring MVC(二)

    一.Spring MVC 验证 JSR 303 是ajvaEE6 中的一项子规范 ,叫 Bean Validation 用于对javaBean中的字段进行校验. 官方的参考实现是: Hibernate ...

  9. Java框架之Spring MVC(一)

    一.Spring简介 Spring MVC是当前最优秀的 MVC 框架,自从Spring 2.5 版本发布后,由于支持注解配置,易用性有了大幅度的提高.Spring 3.0 更加完善,实现了对 Str ...

随机推荐

  1. 浅谈web移动端适配问题

    一.布局方案 目前在解决移动端页面适配问题方案选择上,目前用得比较多是百分比布局,弹性布局flex,rem布局,本文将重点跟大家探讨rem布局. 二.viewport 在介绍rem布局之前,首先跟大家 ...

  2. (原创)遗传算法C++实现

    本文没有对遗传算法的原理做过多的解释 基础知识可以参考下面的博客:http://blog.csdn.net/u010451580/article/details/51178225 本实验用到的变异用到 ...

  3. 状态压缩 - LeetCode #464 Can I Win

    动态规划是一种top-down求解模式,关键在于分解和求解子问题,然后根据子问题的解不断向上递推,得出最终解 因此dp涉及到保存每个计算过的子问题的解,这样当遇到同样的子问题时就不用继续向下求解而直接 ...

  4. phpcms v9 前台getshell脚本

    phpcms v9 前台getshell脚本 用法:python phpcmsv9getshell.py http://baidu.com # -*- coding:utf-8 -*- ''' --- ...

  5. 学习java的优势

    学习java之后,到企业的岗位 技术:java软件开发工程师(中初级):技术一般: 高级工程师:技术高等: 技术架构师:技术顶级: 管理:项目经理:产品经理: 质询:质询顾问:销售经理: 学会之后可以 ...

  6. head first python菜鸟学习笔记(第七章) ——web应用之为数据建模

    问题1. #意思是从athletelist.py中导入AthleteListfrom athletelist import AthleteList 源程序代码 import pickle from a ...

  7. 使用sklearn进行数据挖掘-房价预测(2)—划分测试集

    使用sklearn进行数据挖掘系列文章: 1.使用sklearn进行数据挖掘-房价预测(1) 2.使用sklearn进行数据挖掘-房价预测(2)-划分测试集 3.使用sklearn进行数据挖掘-房价预 ...

  8. 【javaFX学习】(一) 建一个简单的界面

    转载注明出处:http://www.cnblogs.com/lensener/p/7976953.html 用过swing都知道有多蛋疼,界面有多丑.自从用了javaFX,腰也不酸了,腿也不疼了. 废 ...

  9. AndroidStudio cannot resolve symbol 解决办法 清楚缓存

    <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=&qu ...

  10. 验证 Swarm 数据持久性 - 每天5分钟玩转 Docker 容器技术(104)

    上一节我们成功将 Rex-Ray Volume 挂载到了 Service.本节验证 Failover 时,数据不会丢失. Scale Up 增加一个副本: docker service update ...