将与核心业务无关的代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。
优点:
● 第一:代码复用性增强。
● 第二:代码易维护。
● 第三:使开发者更关注业务逻辑。
public class UserService{
public void do1(){
System.out.println("do 1");
}
public void do2(){
System.out.println("do 2");
}
public void do3(){
System.out.println("do 3");
}
public void do4(){
System.out.println("do 4");
}
public void do5(){
System.out.println("do 5");
}
// 核心业务方法
public void service(){
try{
//Joinpoint连接点
do1(); //Pointcut切点
//Joinpoint连接点
do2(); //Pointcut切点
//Joinpoint连接点
do3(); //Pointcut切点
//Joinpoint连接点
do5(); //Pointcut切点
//Joinpoint连接点
} catch (Exception e){
//Joinpoint连接点
}finallly{
//Joinpoint连接点
}
}
}
(注:连接点和切入点的区别)
举个例子,开车到高速路口有很多的出口(连接点),但是我们实际上只从一个出口出去(切入点)
切点表达式用来定义通知(Advice)往哪些方法上切入。
语法:
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
访问控制权限修饰符:
● 可选项。
● 没写,就是4个权限都包括。
● 写public就表示只包括公开的方法。
返回值类型:
● 必填项。
● *
表示返回值类型任意。
全限定类名:
● 可选项。
● 两个点“…”代表当前包以及子包下的所有类。
● 省略时表示所有的类。
方法名:
● 必填项。
● *
表示所有方法。
● set*表示所有的set方法。
形式参数列表:
● 必填项
● () 表示没有参数的方法
● (…) 参数类型和个数随意的方法
● (*
) 只有一个参数的方法
● (*
, String) 第一个参数类型随意,第二个参数是String的。
异常:
● 可选项。
● 省略时表示任意异常类型。
Spring对于AOP的实现主要有三种方式:
● 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
● 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
● 第三种方式:Spring框架自己实现的AOP,基于XML配置方式。
依赖
<repositories>
<repository>
<id>repository.spring.milestoneid>
<name>Spring Milestone Repositoryname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>6.0.0-M2version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>6.0.0-M2version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
dependencies>
spring配置文件
<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 com.sdnu.spring6.service;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserService { //目标类
public void login(){ //目标方法
System.out.println("系统正在进行身份验证");
}
}
切面
package com.sdnu.spring6.service;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("logAspect")
@Aspect //切面类需要注解标注
public class LogAspect { //切面 = 通知 + 切点
@Before("execution(* com.sdnu.spring6.service.UserService.*(..))")
public void myMethod(){
System.out.println("一个通知--》增强代码");
}
}
spring配置文件
<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">
<context:component-scan base-package="com.sdnu.spring6.service"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
beans>
测试
package com.sdnu.spring6.Test;
import com.sdnu.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAopTest {
@Test
public void testBefore(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.login();
}
}
● 前置通知:@Before 目标方法执行之前的通知
● 后置通知:@AfterReturning 目标方法执行之后的通知
● 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
● 异常通知:@AfterThrowing 发生异常之后执行的通知
● 最终通知:@After 放在finally语句块中的通知
4.2的例子是前置通知,后置通知也类似。
环绕通知:
//环绕在前置通知之前,后置通知之后
@Around("execution(* com.sdnu.spring6.service..*(..))")
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前环绕");
//执行目标代码
joinPoint.proceed();
System.out.println("后环绕");
}
异常通知:
@AfterThrowing("execution(* com.sdnu.spring6.service..*(..))")
public void afterThrowingAdvice(){
System.out.println("异常通知");
}
最终通知:
@After("execution(* com.sdnu.spring6.service..*(..))")
public void afterAdvice(){
System.out.println("最终通知");
}
@Pointcut("execution(* com.sdnu.spring6.service..*(..))")
public void universalPointcut(){
}
@Before("universalPointcut()")
public void myMethod(JoinPoint joinPoint){
System.out.println("一个通知--》增强代码");
Signature signature = joinPoint.getSignature();//获取目标方法的方法签名
System.out.println(signature.getName());
}
Spring6Config
package com.sdnu.spring6.service;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration //代替spring配置文件
@ComponentScan({"com.sdnu.spring6.service"}) //组件扫描
@EnableAspectJAutoProxy(proxyTargetClass = true) //启用aspect的自动代理机制
public class Spring6Config {
}
测试
@Test
public void testNoXml(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.logout();
}
<bean id="vipService" class="com.sdnu.spring6.service.VipService"/>
<bean id="timerAspect" class="com.sdnu.spring6.service.TimerAspect"/>
<aop:config>
<aop:pointcut id="p" expression="execution(* com.sdnu.spring6.service.VipService.*(..))"/>
<aop:aspect ref="timerAspect">
<aop:around method="time" pointcut-ref="p"/>
aop:aspect>
aop:config>
账户类
package com.sdnu.spring6.service;
import org.springframework.stereotype.Service;
@Service
public class AccountService {
public void transfer(){
System.out.println("正在转账");
}
public void withdraw(){
System.out.println("正在取钱");
}
}
订单类
package com.sdnu.spring6.service;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
public void generate(){
System.out.println("正在生成订单");
}
public void cancel(){
System.out.println("订单已经取消");
}
}
切面
package com.sdnu.spring6.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class TransactionAspect {
@Around("execution(* com.sdnu.spring6.service..*(..))")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
try {
//前环绕
System.out.println("开启事务");
//执行目标
proceedingJoinPoint.proceed();
//后环绕
System.out.println("关闭事务");
} catch (Throwable e) {
System.out.println("回滚事务");
e.printStackTrace();
}
}
}
spring配置文件
<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">
<context:component-scan base-package="com.sdnu.spring6.service"/>
<aop:aspectj-autoproxy/>
beans>
测试
package com.sdnu.spring.spring6.test;
import com.sdnu.spring6.service.AccountService;
import com.sdnu.spring6.service.OrderService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopRealAppTest {
@Test
public void testTransaction(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
accountService.transfer();
accountService.withdraw();
orderService.generate();
orderService.cancel();
}
}