解析Spring第三天(面向切面AOP)
面向切面:AOP
在不修改源代码的基础上,对方法进行增强。AOP的底层原理就是代理技术(第一种:jdk的动态代理(编写程序必须要有接口)。第二种:cglib代理技术(生成类的子类)。如果编写的程序有借口,则spring框架会自动使用jdk的动态代理技术增强,)。
Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象)-- 代理的目标对象
Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程
Proxy(代理)-- 一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)-- 是切入点和通知的结合,以后自己来编写和配置的
创建一个普通的Maven项目工程引入坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency> <!-- AOP联盟 -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- Spring Aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
创建Spring的配置文件,引入具体的AOP的schema约束
<?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"> </beans>- 创建包结构,编写具体的接口和实现类
package cn.tx.demo2;
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("业务层:保存用户...");
}
} - 将目标类配置到Spring中
<bean id="userService" class="cn.tx.demo2.UserServiceImpl"/>
- 自定义切面类
package cn.tx.demo2;
/**
* 自定义切面类 = 切入点(表达式) + 通知(增强的代码)
*/
public class MyXmlAspect {
/**
* 通知
*/
public void log(){
// 发送手机短信
// 发送邮件/记录日志/事务管理
System.out.println("增强的方法执行了...");
}
} 在配置文件中定义切面类
<bean id="myXmlAspect" class="cn.tx.demo2.MyXmlAspect"/>
在配置文件中完成aop的配置
<!--配置AOP的增强-->
<aop:config>
<!--配置切面 = 切入点 + 通知组成-->
<aop:aspect ref="myXmlAspect">
<!--前置通知:UserServiceImpl的save方法执行前,会增强-->
<aop:before method="log" pointcut="execution(public void cn.tx.demo2.UserServiceImpl.save())" />
</aop:aspect>
</aop:config>- 对增强进行测试
package cn.tx.test;
import cn.tx.demo2.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_demo2.xml")
public class Demo2 {
@Autowired
private UserService userService;
/**
* 测试
*/
@Test
public void run1(){
userService.save();
}
} - 切入点的表达式格式:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
修饰符可以省略不写,不是必须要出现的。
返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
包名例如:com.tx.demo3.BookDaoImpl
- 首先com是不能省略不写的,但是可以使用 * 代替
- 中间的包名可以使用 * 号代替
- 如果想省略中间的包名可以使用 ..
类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
方法也可以使用 * 号代替
参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
- AOP的通知方式
<?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管理-->
<bean id="userService" class="cn.tx.demo1.UserServiceImpl" /> <!--=================编写AOP配置文件====================-->
<!--先配置切面类-->
<bean id="myXmlAspect" class="cn.tx.demo1.MyXmlAspect" /> <!--配置AOP的增强-->
<aop:config>
<!--正在配置切面,引入真正切面对象-->
<aop:aspect ref="myXmlAspect">
<!--配置的是前置通知:目标对象方法执行前,先增强。method="切面类中通知的方法" pointcut="切入点的表达式"-->
<!--
切入点的表达式
execution() 写法是固定的
public 可以省略不写的
方法返回值 void int String * 通用的写法
包名 * 通用的写法
类名 * 推荐的写法 *ServiceImpl 例如:UserServiceImpl DeptServiceImpl
方法名称 * 推荐写法:save*
方法参数列表 .. == Object... obj Object类型的可变参数
<aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
--> <!--
通知类型
前置通知:目标对象方法执行前,先增强。
<aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 最终通知:目标对象方法执行成功或者失败,都会增强。finally
<aop:after method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 后置通知:目标对象方法执行成功,才会增强。
<aop:after-returning method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 异常通知:目标对象方法执行失败,才会增强。
<aop:after-throwing method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> <aop:before method="begin" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
<aop:after-returning method="commit" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
<aop:after-throwing method="rollback" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
<aop:after method="close" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 环绕通知:自己决定增强的位置。使用了环绕通知,目标对象的方法默认没有执行的,需要自己手动执行目标对象方法。
--> <aop:around method="logAroud" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> </aop:aspect>
</aop:config> </beans>Spring的AOP技术-注解方式
- 同样创建一个普通的Maven工程,导入坐标,编写接口,同上
- 编写一个切面类,给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明
package cn.tx.demo3;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component // 把该类交给IOC去管理
@Aspect // 声明是切面类 == <aop:aspect ref="myXmlAspect">
public class MyAnnoAspect {
/**
* 通知的方法
*/
// @Before(value = "切入点的表达式")
@Before(value = "execution(public * cn.tx.demo3.OrderServiceImpl.save(..))")
public void log(){
System.out.println("增强了...");
}
}
配置文件中开启自动代理
<aop:aspectj-autoproxy/>
测试方法
package cn.tx.test;
import cn.tx.demo2.UserService;
import cn.tx.demo3.OrderService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_demo3.xml")
public class Demo3 {
@Autowired
private OrderService orderService;
/**
* 测试
*/
@Test
public void run1(){
orderService.save();
}
}@RunWith(SpringJUnit4ClassRunner.class) //在使用所有注释前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于spring测试环境
- @ContextConfiguration 用来指定加载的Spring配置文件位置,会加载默认配置文件。
@ContextConfiguration有两个常用的属性,locations、inheritLocations
locations:可以通过该属性手工指定spring配置文件所在的位置,可以指定一个或多个spring的配置文件,要使用【,】隔开。 例如:@ContextConfiguration(locations={“aa/aa.xml”,” aa/bb.xml”})
inheritLocations:是否要继承父测试用例中的Spring 配置文件,默认是true。
- 拓展注解解析:
- @DirtiesContext(classMode = ClassMode.AFTER_CLASS): //@DirtiesContext 在测试方法上出现这个注解时,表明底层Spring容器在该方法的执行中被“污染”,从而必须在方法执行结束后重新创建(无论该测试是否通过)。
- @TransactionConfiguration( transactionManager = "transactionManager" , defaultRollback = false)
@TransactionConfiguration为配置事物性测试定义了类级别的元数据。PlatformTransactionManager默认的实例叫transactionManager,如果需要的PlatformTransactionManager不 是“transactionManager”的话,那么可以显示配置驱动事物的PlatformTransactionManager的bean的名字。此外,可以将defaultRollback标志改为false,表示不回滚。通常, @TransactionConfiguration与@ContextConfiguration搭配使用。
@ContextConfiguration
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)】
通知类型注解
@Before -- 前置通知 @AfterReturing -- 后置通知 @Around -- 环绕通知(目标对象方法默认不执行的,需要手动执行) @After -- 最终通知 @AfterThrowing -- 异常抛出通知
纯注解的方式
package cn.tx.demo3;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration // 配置类
@ComponentScan(value = "cn.tx.demo3") // 扫描包
@EnableAspectJAutoProxy // 开启自动代理 == <aop:aspectj-autoproxy />
public class SpringConfig { }
解析Spring第三天(面向切面AOP)的更多相关文章
- Spring(三)面向切面编程(AOP)
在直系学长曾经的指导下,参考了直系学长的博客(https://www.cnblogs.com/WellHold/p/6655769.html)学习Spring的另一个核心概念--面向切片编程,即AOP ...
- Spring基础(二)_面向切面(AOP)
面向切面编程 面向切面编程[AOP,Aspect Oriented Programming]:通过预编译方式和运行期间动态代理实现程序功能的统一维护的技术.AOP 是 Spring 框架中的一个重要内 ...
- Spring框架系列(五)--面向切面AOP
背景: 当需要为多个不具有继承关系的对象引入一个公共行为,例如日志.权限验证.事务等功能时,如果使用OOP,需要为每个对象引入这些公共 行为.会产生大量重复代码,并且不利用维护.AOP就是为了解决这个 ...
- Spring xml中进行面向切面的配置
Spring xml中进行面向切面的配置 XML: <?xml version="1.0" encoding="UTF-8"?> <beans ...
- spring学习总结二-----面向切面编程(AOP)思想
上一篇spring博客简总结了spring控制反转和依赖注入的相关思想知识点,这篇博文对spring的面向切的编程思想进行简单的梳理和总结. 一.面向切面的思想 与面向对象的纵向关系概念不同,面向切面 ...
- Spring框架使用(控制反转,依赖注入,面向切面AOP)
参见:http://blog.csdn.net/fei641327936/article/details/52015121 Mybatis: 实现IOC的轻量级的一个Bean的容器 Inversion ...
- Spring学习笔记:面向切面编程AOP(Aspect Oriented Programming)
一.面向切面编程AOP 目标:让我们可以“专心做事”,避免繁杂重复的功能编码 原理:将复杂的需求分解出不同方面,将公共功能集中解决 *****所谓面向切面编程,是一种通过预编译方式和运行期动态代理实现 ...
- Spring实战4:面向切面编程
主要内容 面向切面编程的基本知识 为POJO创建切面 使用@AspectJ注解 为AspectJ的aspects注入依赖关系 在南方没有暖气的冬天,太冷了,非常想念北方有暖气的冬天.为了取暖,很多朋友 ...
- Java 面向切面 AOP
参考: :http://www.blogjava.net/supercrsky/articles/174368.html AOP: Aspect Oriented Programming 即面向切面编 ...
随机推荐
- KiCAD原理图更换库
KiCAD原理图库更换 将AD的工程文件转换位KiCAD后,打开原理图,系统会自动压缩生成当前工程库,但是这样将原理图复制粘贴到其他地方时,就找不到库了,原理图就会无法显示器件符号,如何将库替换为我们 ...
- Kali Linux更新和配置
1.用vim打开 /etc/apt/source.list root@kali:~# vim /etc/apt/sources.list #中科大 deb http://mirrors.ustc.ed ...
- 经典排序背包——cf1203F
先把收益为正数的处理掉:策略是挨个扫,扫n遍,碰到能买的就买,然后可以得到一个更新后的r 剩下的就看做是一个背包模型:物品(a,b)表示当背包体积>a时才能装下体积为b的该物品,问最多装几个 无 ...
- Python每日一题 002
做为 Apple Store App 独立开发者,你要搞限时促销,为你的应用生成激活码(或者优惠券),使用 Python 如何生成 200 个激活码(或者优惠券)? 在此生成由数字,字母组成的20位字 ...
- (转)OpenFire源码学习之四:openfire的启动流程
转:http://blog.csdn.net/huwenfeng_2011/article/details/43413233 openfire启动 ServerStarter 启动流程图: 启动的总入 ...
- LInux多线程编程----线程特定数据的处理函数
1.pthread_key_t和pthread_key_create() 线程中特有的线程存储, Thread Specific Data .线程存储有什么用了?他是什么意思了?大家都知道,在多线程程 ...
- CSS:CSS 字体
ylbtech-CSS:CSS 字体 1.返回顶部 1. CSS 字体 CSS字体属性定义字体,加粗,大小,文字样式. serif和sans-serif字体之间的区别 在计算机屏幕上,sans-se ...
- vue中配置可修改的服务器接口api
https://www.jianshu.com/p/377bfd2d9034?utm_campaign 太坑了,找了全网,几乎都不能用,也不知道哪写错了,这个是可以用的.
- (15)centos7 系统服务
centos7 服务启动脚本在 /usr/lib/systemd目录下 1.服务基本操作指令 systemclt [command] [unit] #其中command包括: #start 立即启动 ...
- rpm升级时spec文件执行的流程
转自:https://www.cnblogs.com/zafu/p/7423758.html %pre 和 %post 脚本片段分别在软件包安装前和安装后执行.%preun 和 %postun 脚本片 ...