1.Spring AOP 概述
1.1.何为AOP?
AOP 是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善
实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面理解为一个动态过程(
在对象运行时动态织入一些功能。)
1.2.AOP要解决什么问题?
实际项目中通常会将系统两大部分:核心关注点和非核心关注点
思考?
编程过程中首先要完成的是什么?核心关注点(核心业务)
非核心关注点如何切入到系统中?硬编码(违背OCP),AOP(推荐)
AOP就是要在基于OCP在不改变原有系统核心业务代码的基础上动态添加一些扩展功能。
1.3.AOP实际项目应用场景?
AOP 通常应用于日志的处理,事务处理,权限处理,缓存处理等等。
1.4.AOP底层原理实现分析?
AOP底层基于代理机制实现功能扩展:(了解)
1)假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)
2)假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。
2.Spring AOP 编程实现
2.1.AOP 基本步骤
step1:创建maven java 项目
step2:添加aop依赖
step3:配置aop 核心(基于xml,基于注解)
step4:定义核心业务(核心关注点):推荐先写接口再写实现类
step5:定义扩展业务(非核心关注点)
step6:基于配置实现非核心业务的切入
step7:编写测试类进行单元测试
2.2.AOP 基于xml实现
通过AOP为核心业务类添加日志处理
2.2.1.创建项目添加依赖
创建maven java 项目然后添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--
Spring AOP的实现依托于Aspect框架
所以要引用1.8.5有问题
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
2.2.2.添加spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
</beans>
2.2.3.创建核心业务类
创建接口
public interface HelloService {
void sayHello(String msg);
}
创建接口实现类
public class HelloServiceImpl implements HelloService {
public void sayHello(String msg) {
//假设这条语句是我们系统中的核心业务
System.out.println(msg);
}
}
2.2.4.创建日志处理类
public class LoggingAspect {
public void beforeMethod(){
System.out.println("method start");
}
public void afterMethod(){
System.out.println("method end");
}
}
2.2.5.配置bean对象
<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 核心业务对象 -->
<bean id="helloService"
class="spring.beans.HelloServiceImpl"/>
<!-- 配置非核心业务对象(日志处理对象):切面 -->
<bean id="log"
class="spring.aop.LoggingAspect"/>
<!-- AOP配置(切入点,切面) -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut
expression="within(spring.beans.HelloServiceImpl)" //核心业务
id="logPointCut"/>
<!-- 配置日志处理 -->
<aop:aspect ref="log" >
<aop:before method="beforeMethod"
pointcut-ref="logPointCut"/>
<aop:after method="afterMethod"
pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
</beans>
2.2.6.编写测试类
//1.初始化spring容器
ClassPathXmlApplicationContext ctx=
new ClassPathXmlApplicationContext(
"applicationContext.xml");
//2.获取Bean对象
HelloService helloService=
ctx.getBean("helloService",HelloService.class);
//3.执行业务
helloService.sayHello("CGB1710");//proxy的方法
//4.释放资源
ctx.close();
2.3.Spring AOP 基于注解方式实现
2.3.1.Spring AOP 项目创建
1.创建maven java 项目
2.添加AOP依赖
3.编写核心业务组件(MessageService接口与MessageServiceImpl实现类)
4.编写扩展业务组件(TxManager)
5.配置核心业务组件以及扩展业务组件(组件扫描及AOP注解应用)
6.编写单元测试(junit)
2.3.2.Spring AOP 核心业务组件定义
定义MessageService接口
public interface MessageService {
void sendMsg(String msg);
}
定义MessageServiceImpl实现类实现SayHelloService接口
@Service
public class MessageServiceImpl implements MessageService{
public void sendMsg(String msg) {
System.out.println(msg);
}
}
2.3.3.Spring AOP 切面业务组件定义
@Component
@Aspect
public class TxManager {
@Pointcut("within(com.company.spring.service.MessageServiceImpl)")
public void pointCut() {}
@Before("pointCut()")
public void beginTx() {
System.out.println("begin transaction");
}
@After("pointCut()")
public void endTx() {
System.out.println("end transaction");
}
}
其中:
@Component 表示此组件由Spring对象管理
@Aspect 用于定义切面(对切入点和通知的封装)
@Pointcut 用于定义切入点(用于织入扩展功能的点)
@Before 用于定义前置通知(业务方法之前执行)
@After 用于定义最终通知(业务方法执行完成以后执行)
2.3.4.Spring AOP 配置文件启用AOP功能
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 自动扫描该包 -->
<context:component-scan base-package="cn.tedu.aop" />
<!-- 使aspectj注解生效,自动为目标对象生成代理对象 -->
<aop:aspectj-autoproxy/>
</beans>
说明:基于AOP注解的方式配置AOP时,因为注解要应用在类或方法的上面,所以会由一定的侵入性。但这种侵入性属于声明式侵入性,所以范畴同样也是属于弱耦合范围之内(强耦合一般是在业务代码内部的直接耦合)。
2.3.5.Spring AOP 测试
ClassPathXmlApplicationContext ctx=
new ClassPathXmlApplicationContext("aop.xml");
MessageService msgService=
ctx.getBean("messageServiceImpl",MessageService.class);
msgService.sendMsg("hello");
ctx.close();
3.Spring AOP 编程增强
3.1.Spring AOP 切入点表达式增强
Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:
指示符 作用
1. bean 用于匹配指定类型内的方法执行
2. within 用于匹配指定类型内的方法执行
3. execution 用于匹配方法执行的连接点
3.1.1.bean 表达式
bean应用于类级别,实现粗粒度的控制:
bean(UserServiceImpl)) 指定一个类
bean(*Service) 指定所有的后缀为service的类
例如:
@Aspect
@Component
public class LoggingAspect {
@Before("bean(*Service)")
public void beforeMethod() {
System.out.println("beforeMethod");
}
@After("bean(*Service)")
public void afterMethod() {
System.out.println("afterMethod");
}
}
3.1.2.within 表达式
within应用于类级别,实现粗粒度的控制:
within(aop.service.UserServiceImpl)) 指定类,只能指定一个类
within(aop.service.*)) 只包括当前目录下的类
within(aop.service..*)) 指定当前目录包含所有子目录中的类
@Aspect
@Component
public class LoggingAspect {
@Before("within(aop.Service.*)")
public void beforeMethod() {
System.out.println("beforeMethod");
}
@After("within(aop.Service.*)")
public void afterMethod() {
System.out.println("afterMethod");
}
}
3.1.3.execution 表达式
execution方法级别,细粒度的控制:
语法:execution(返回值类型 包名.类名.方法名(参数列表))
execution(void aop.service.UserServiceImpl.addUser()) 匹配方法
execution(void aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为字符串
execution(* aop.service...(..)) 万能配置
例如:
@Aspect
@Component
public class TimingAspect {
@Before("execution(* cn.tedu..*Service.*(..))")
public void timeBeforeMethod(JoinPoint point) {
String method=point.getSignature().getName();
System.out.println("timeBeforeMethod:"+method);
}
/**方法切入点 (execution: 执行)*/
@After("execution(* cn.tedu..*Service.update(..))")
public void timeAfterMethod(JoinPoint point) {
String method=point.getSignature().getName();
Object arg=point.getArgs()[0];
System.out.println("timeAfterMethod:"+method+"("+arg+")");
}
}
3.2.Spring AOP 配置增强
3.2.1.Spring AOP xml 方式配置增强
切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置
<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:before method="beforeMethod" pointcut-ref="pc"/>
<aop:after method="afterMethod" pointcut-ref="pc"/>
<aop:after-returning method="returnMethod"
pointcut-ref="pc"/>
<aop:after-throwing method="throwMethod"
pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
切入点及环绕通知的配置
<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
3.2.2.Spring AOP 注解方式配置增强
切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置
@Aspect
@Component
public class LoggingAspect {
@Before("bean(xxxService)")
public void beforeMethod() {
System.out.println("beforeMethod");
}
@After("bean(xxxService)")
public void afterMethod() {
System.out.println("afterMethod");
}
@AfterReturning(pointcut="bean(xxxService)",returning="result")
public void afterReturningMethod(Object result) {
System.out.println("afterReturnMethod.result="+result);
}
@AfterThrowing("bean(xxxService)")
public void afterThrowingMethod() {
System.out.println("afterThrowingMethod");
}
}
切入点及环绕通知的配置
@Component
@Aspect
public class TxManager {
@Pointcut("execution(com.company.spring.service..*.*(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
System.out.println("事务开始");
Object result = joinPoint.proceed();
System.out.println("事务结束");
return result;
}
}
3.3.Spring AOP 切面顺序配置
当业务系统中存在多个切面时,哪个切面优先执行,哪个切面要延后执行,我们可以通过配置切面 执行顺序实现,序号小的会优先执行。
3.3.1.基于xml方式配置
<!-- 自动扫描该包 -->
<context:component-scan base-package="com.company.spring" />
<!-- AOP配置 -->
<aop:config>
<aop:pointcut id="pc"
expression="execution(*
com.company.spring.service..*.*(..))"/>
<aop:aspect ref="loggingAspect" order="1">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
<aop:aspect ref="txManager" order="2">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
3.3.2.基于注解方式配置
事务切面
/**权限检测的切面
* 通过Order注解定义Order切面的执行顺序
* */
@Order(1)
@Aspect
@Service
public class PermissionAspect {
@Before("execution(* com..*.*(..))")
public void check(){
System.out.println("权限检测");
}
}
日志切面
**将其理解为一个切面
* 切面中通常会有两部分内容
* 1)切入点(PointCut)
* 2)增强功能(Advice)
* */
@Order(2)
@Aspect
@Service
public class TimeAspect {
/**定义切入点*/
//within表达式为全类名或者"包.*"
//@Pointcut("within(com.spring.beans.*)")
//@Pointcut("within(com.spring.beans.OrderServiceImpl)")
//bean表达式中的内容为具体bean对象的id
//@Pointcut("bean(orderService)")
//execution表达式
//第一个"*"代表任意的返回值类型
//第二个"*"代表是任意的类
//第三个"*"代表任意的方法
//第一个".."代表任意的子包(多个包)
//第二个".."代表任意参数个数,任意参数类型
@Pointcut("execution(* com..*.*(..))")
public void pointcut(){}
/**增强功能:前置通知(业务方法执行之前执行)*/
@Before("pointcut()")
public void begin(){
System.out.println("start:"+System.nanoTime());
}
/**增强功能:最终通知(业务方法执行最后执行,
*无论业务方法是否执行成功,此功能都要执行)*/
@After("pointcut()")
public void end(){
System.out.println("end:"+System.nanoTime());
事务切面
Order(3)
@Aspect
@Service
public class TxAspect {
@Before("bean(orderService)")
public void startTx(){
System.out.println("开启事务");
}
@AfterReturning("bean(orderService)")
public void commitTx(){
System.out.println("提交事务");
}
@AfterThrowing("bean(orderService)")
public void rollbackTx(){
System.out.println("回滚事务");
}
@After("bean(orderService)")
public void closeResource(){
System.out.println("释放资源");
}
}
/**
* try{
* @Before
* saveOrder();
* @AfterReturning
* return;
* }catch(Exception e){
* @AfterThrowing
* throw e;
* }finally{
* @after
* }