Spring-AOP
Aspect Oriented Programing:面向切面编程
相对oop(面向对象)来说,aop关注的不再是某个类,而是层与层之间的切入
AOP主要在日志记录,性能统计,安全控制,事务控制等方面实现公共功能的复用
AOP降低模块之间的耦合度,提高业务代码的聚合
提高代码的复用
保持系统的扩展性
AOP基本概念
Joinpoint(连接点)
指在切入点中拦截到的所有方法,一个连接点即代表一个方法的执行
Pointcut(切入点)
对连接点拦截的定义,即定义拦截某些或某种方法
Advice(通知)
拦截连接点后所作的操作
前置通知 before():在执行方法前执行的通知
异常通知 afterThrow():在方法出现异常 时执行的通知
最终通知 after():不管方法是否异常都会执行的通知
返回通知 afterReturn():方法正常结束后执行的通知
环绕通知 around():将前四种方法集中在一个方法中,可以自由定义执行哪些通知,是唯一具有返回值的方法,也是最强大的一种通知类型
Aspect(切面)
为poincut与advice的结合,poincut定义拦截的方法,advice定义拦截方法的后续操作,而Aspect则是对横切面的整体抽象
Target(目标对象)
即被代理的对象
Weave(织入)
将切面引用到目标对象并生成代理对象的过程
Introduction(引入)
在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程
AOP 注解实现(推荐使用)
<!--spring AOP实现需要引入的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!-- 配置spring环境 -->
<?xml version="1.0" encoding="UTF-8"?>
<!--
spring-AOP配置
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
-->
<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
https://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.lysxd"/>
<!-- 开启aop环境 -->
<aop:aspectj-autoproxy/>
</beans>
/*
定义目标类
*/
@Component
public class Target01{
public void targetTest(){
System.out.println("TargetTest...");
}
}
//定义Aspect
@Compoent
//标识为AOP
@Aspect
public class TestAspect{
/*
定义pointcut
使用execution执行匹配
* com.lysxd.annotation..*.*(..)
* 指被所有修饰符修饰的类(public|private|protected)
com.lysxd.annotation 指切入点指定的包
* com.lysxd.annotation.*.*(..) 指在com.lysxd.annotation包下的所有子类的方法(*可改为指定类和方法)
* com.lysxd.annotation..*.*(..)指在com.lysxd.annotation包及子包下的所有子类的方法(*可改为指定类和方法)
*/
@Pointcut("execution(* com.lysxd.annotation..*.*(..))")
public void cut(){};
//定义前置通知且引入pointcut
@Before(value="cut()")
public void before(){
System.out.println("前置通知:在目标方法执行之前执行的方法");
}
//定义异常通知
@AfterThrowing(value="cut()")
public void afterThrowing(){
System.out.println("异常通知:在目标方法执行发生异常时执行的方法");
}
//定义最终通知
@After(value="cut()")
public void after(){
System.out.println("最终通知:不管目标方法时候异常都会执行的方法");
}
//定义返回通知
@AfterReturning(value="cut()")
public void afterReturning(){
System.out.println("返回通知:在目标方法正常结束时执行的方法");
}
//定义环绕通知
@Around(value="cut()")
public Object around(ProceedingJinPoint psj)throws Throwable{
Object result = null;
System.out.println("环绕方法开始执行");
System.out.println("方法开始时间为:"+System.currentTimeMillis());
//可以获取方法的形参,为数组
Object[] args = psj.getArgs();
if(null!=args && args.length>0){
for(Object arg:args){
System.out.println("方法的形参为:"+arg);
}
}
//执行目标方法
result = psj.proceed();
System.out.println("方法结束时间为:"+System.currentTimeMillis());
return result;
}
}
/*
切面测试
*/
public class Test(){
@Test
public void test01(){
//根据配置文件构建IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-annotation.xml");
}
//配置目标类时系统自动将首字母配置为小写
Target01 target01 = (Target01)ac.getBean("target01");
//调用目标方法
target01.targetTest();
}
/*
将特定注解标识的类定义为切面
使用@annotation 执行匹配
*/
//定义注解
@Target(METHOD) //注解可用在方法上
@Retention(RUNTIME) //在类执行时保留
public @interface AnnotationPointCut{
//可以定义注解的value,default为空
String value() default "";
}
//定义连接点
@AnnotationPointCut(value = "test02")
public void test02(){
System.out.println("test02...");
}
@AnnotationPointCut(value = "test03")
public void test03(){
System.out.println("test03...");
}
//匹配注解的方法
@Pointcut("@annotation (com.lysxd.annotation.AnnotationPointCut)")
public void annotationCut(){};
//定义环绕方法
@Around(value = "annotationCut()")
public Object Around(ProceedingJoinPoint psj)throws Throwable(){
Object result = null;
System.out.println("环绕方法开始执行");
System.out.println("方法开始时间为:"+System.currentTimeMillis());
//执行目标方法
result = psj.proceed();
System.out.println("方法结束时间为:"+System.currentTimeMillis());
return result;
}
/*
可以指定特质value的AnnotationPointCut注解
*/
//定义环绕方法
@Around(value = "annotationCut()")
public Object Around(ProceedingJoinPoint psj)throws Throwable(){
Object result = null;
/*
获取指定value注解标识的方法
获取方法签名
根据方法签名获取方法,继而获取当前方法的注解
获取当前方法注解的值
*/
MethodSingnature methodSingtrue = (MethodSingnature) psj.getSingtrue();
AnnotationPointCut annotationPointCut = methodSingtrue.getMethod().getAnnotation(AnnotationPointCut.calss);
if(null != annotationPointCut){
//当注解的value为test02时
if("test02".equals(annotationPointCut.getvalue())){
System.out.println("注解value为test02的连接点");
}
if("test03".equals(annotationPointCut.getvalue())){
System.out.println("注解value为test03的连接点");
}
}
//执行目标方法
result = psj.proceed();
return result;
}
AOP 配置XML实现
AOP-XML配置方式中各类方法的顺序会受到配置文件中的配置顺序的影响
<!--spring AOP实现需要引入的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!-- 配置spring环境 -->
<?xml version="1.0" encoding="UTF-8"?>
<!--
spring-AOP配置
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
-->
<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
https://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.lysxd"/>
<!-- 开启aop环境 -->
<aop:aspectj-autoproxy/>
<!-- 配置aop -->
<aop:config>
<!-- 配置Aspect -->
<aop:aspect ref="TestAspect02" />
<!-- 配置pointcut -->
<aop:pointcut id="lysxd" expression="execution (* com.lysxd.aopxml)" />
<!-- 定义前置通知 method:方法名 pointcut-ref:引用定义的切入点 -->
<aop:before method="before" pointcut-ref="lysxd" />
<!-- 定义异常通知 -->
<aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="lysxd" />
<!-- 定义最终通知 -->
<aop:after method="after" pointcut-ref="lysxd" />
<!-- 定义返回通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="lysxd" />
<!-- 定义环绕通知 -->
<aop:around method="around" pointcut-ref="lysxd" />
</aop:aspect>
</aop:config>
</beans>
/*
定义Aspect
*/
@Component
@Aspect
public class LoginAopXML {
public void before(){
System.out.println("前置通知-->目标类方法执行前执行的方法");
}
public void after(){
System.out.println("最终通知-->目标类是否异常都会执行的方法");
}
public void afterThrowing(Exception e){
System.out.println("异常通知-->目标类出现异常时执行的方法,当此方法执行后整个方法结束"+e);
}
public void afterReturning(){
System.out.println("返回通知-->目标类方法执行结束后执行的方法");
}
public Object around(ProceedingJoinPoint psj) throws Throwable {
Object result = null;
System.out.println("环绕方法开始执行");
Long startTime = System.currentTimeMillis();
System.out.println("方法开始时间为:"+startTime);
//获取方法的形参
Object[] args = psj.getArgs();
//当数组非空时kkj\8遍历方法的形参
if(null != args && args.length > 0){
for (Object arg : args) {
System.out.println("方法形参-->"+arg);
}
}
//执行目标方法
result = psj.proceed();
Long endTime = System.currentTimeMillis();
System.out.println("方法结束时间为:"+startTime);
return result;
}
}