AOP(Aspect Oriented Programming)字面翻译为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
这样的翻译,如果大家不了解这项技术的话,肯定会发懵。其实,面向切面编程,简单地理解就是为了降低代码耦合度,减少重复代码量,增加系统灵活性而采用的一种编程规范。主要用到的技术就是动态代理,分为JDK动态代理,和CGLIB动态代理。关于JDK动态代理,小编在前面的文章:Java — 动态代理 完整步骤 有介绍到,有兴趣的小伙伴可以去翻看一下。这篇文章咱们先把AOP动态代理放一下,小编先演示一下如何通过AspectJ 注解方式来实现AOP操作,其他内容在后续的文章中会一一展示。好了,话不多说,Let go!
1,编写 连接点(aop术语),即切入的某个方法所在的实体类
@Component //需要加@Component注解,这样会被IOC容器管理
public class Skill {
// 自定义方法
public void eat(){
System.out.println("eat rice");
}
}
2,编写 增强类(aop术语),即aop代码所在的类
@Component //需要加@Component注解,这样会被IOC容器管理
@Aspect //加@Aspect注解,被ioc识别为增强类
public class AopClass {
/**
* 切入点表达式:
* execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
* eat(..) 表示 eat方法及参数列表
*/
/**
* 前置增强,即在调用被切入方法 Skill.eat() 之前调用的方法
*/
@Before(value = "execution(* com.chaoge.nacos.demo.test.spring.entity.Skill.eat(..))")
public void before(){
System.out.println("Aop:前置");
}
/**
* 后置增强,即在调用被切入方法 Skill.eat() 之后调用的方法
*/
@After(value = "execution(* com.chaoge.nacos.demo.test.spring.entity.Skill.eat(..))")
public void after(){
System.out.println("Aop:后置");
}
/**
* 后置返回增强,即在调用被切入方法 Skill.eat() 返回结果之后调用的方法
*/
@AfterReturning(value = "execution(* com.chaoge.nacos.demo.test.spring.entity.Skill.eat(..))")
public void afterReturning(){
System.out.println("Aop:后置返回");
}
/**
* 环绕增强,即在调用被切入方法 Skill.eat() 前后都会调用的方法
* @param joinPoint
* @throws Throwable
*/
@Around(value = "execution(* com.chaoge.nacos.demo.test.spring.entity.Skill.eat(..))")
public void round(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Aop:环绕前");
joinPoint.proceed();
System.out.println("Aop:环绕后");
}
/**
* 异常增强,即在调用被切入方法 Skill.eat() 出现异常时候调用的方法
*/
@AfterThrowing(value = "execution(* com.chaoge.nacos.demo.test.spring.entity.Skill.eat(..))")
public void excetion(){
System.out.println("Aop:异常");
}
}
3,编写Spring 配置文件:AopConfig.xml。文件头部包括 自定义 xmlns:context,xmlns:aop 的编写,如果有疑惑的小伙伴,可以翻看小编之前的文章:Spring — IOC 容器 之 Bean管理XML方式(外部属性文件) 进行对比观察,即可理解。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!-- xmlns:context="http://www.springframework.org/schema/context"-->
<!-- xmlns:aop="http://www.springframework.org/schema/aop"-->
<!-- 为自定义配置上下文管理-->
<!-- 开启注解扫描,扫描有 @Controller @Service @Component @Repository 等注解的类,识别其为bean-->
<!-- base-package标签指明需要扫描有注解的类所在包路径-->
<context:component-scan base-package="com.chaoge.nacos.demo.test.spring.entity"/>
<!-- 开启aspectJ扫描 扫描有@Aspect注解的类,识别其为增强类-->
<aop:aspectj-autoproxy/>
</beans>
4,使用
public class BeanTest {
@Test
public void test8(){
ApplicationContext context = new ClassPathXmlApplicationContext("com/chaoge/nacos/demo/test/spring/beanConfig/AopConfig.xml");
Skill skill = context.getBean("skill", Skill.class);
skill.eat();
}
}
5,输出
D:\jdk\jdk1.8.0_171\bin\java.exe
Aop:环绕前
Aop:前置
eat rice
Aop:后置返回
Aop:后置
Aop:环绕后
6,改进
在上面2步骤中,增强类中每一个增强方法都配置了切入点表达式,这样就显得冗余,为了简化代码,可以使用@Pointcut注解对切入点进行统一管理
@Component
@Aspect
public class AopClass {
@Pointcut(value = "execution(* com.chaoge.nacos.demo.test.spring.entity.Skill.eat(..))")
public void point(){
}
/**
* 切入点表达式:
* execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
* eat(..) 表示 eat方法及参数列表
*/
/**
* 前置增强,即在调用被切入方法 Skill.eat() 之前调用的方法
*/
@Before(value = "point()")
public void before(){
System.out.println("Aop:前置");
}
/**
* 后置增强,即在调用被切入方法 Skill.eat() 之后调用的方法
*/
@After(value = "point()")
public void after(){
System.out.println("Aop:后置");
}
/**
* 后置返回增强,即在调用被切入方法 Skill.eat() 返回结果之后调用的方法
*/
@AfterReturning(value = "point()")
public void afterReturning(){
System.out.println("Aop:后置返回");
}
/**
* 环绕增强,即在调用被切入方法 Skill.eat() 前后都会调用的方法
* @param joinPoint
* @throws Throwable
*/
@Around(value = "point()")
public void round(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Aop:环绕前");
joinPoint.proceed();
System.out.println("Aop:环绕后");
}
/**
* 异常增强,即在调用被切入方法 Skill.eat() 出现异常时候调用的方法
*/
@AfterThrowing(value = "point()")
public void excetion(){
System.out.println("Aop:异常");
}
}