本文主要讲解的是AOP的内容,代码中将用Java配置代替xml配置。
Spring的AOP的存在则是为了解耦。虽然OOP是很好的编程思想,但是还是会出现大量的代码复制的情况,从而会造成代码的冗余。在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加到一组类上,而AOP的出现则弥补了OOP的不足,Spring支持AspectJ声明的注解式切面编程。AOP即可以动态注入,使得一些重复使用的代码使用注解即可完成,而不需要重复编写,使程序更加简洁灵活。
本文的示例主要是对AspectJ编程中基于注解拦截和基于方法规则拦截两种方式。
1.pom文件
首先在pom文件中配置本文示例中需要的jar包。主要包括spring本身注解相关的包以及aspectj相关的包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.carson</groupId>
<artifactId>aop</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!-- sring aop 支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!--aspectj 支持-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.5</version>
</dependency>
</dependencies>>
</project>
2.基于注解拦截
1.编写拦截规则的注解
其中注解本身没有功能,和xml一样,就是一种配置。注解的功能的实现来自于使用这个注解的地方。
package com.carson.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 编写拦截规则的注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
String name();
}
2.编写使用注解的被拦截类
package com.carson.aop;
import org.springframework.stereotype.Service;
/**
* 编写使用注解的被拦截类
*/
@Service
public class DemoAnnotationService {
@Action(name="注解式拦截的div操作")
public void div(){
System.out.println("=======div========");
System.out.println(" 15/3=5");
System.out.println("=======end========");
};
}
3.编写使用方法规则被拦截类
这种形式比较常见,就是一个普通的类,然后在切面中与切点关联即可。
package com.carson.aop;
import org.springframework.stereotype.Service;
/**
* 编写使用方法规则被拦截类
*/
@Service
public class DemoMethodService {
public void add(){
System.out.println("=========加法==========");
System.out.println(" 3+5=8");
System.out.println("==========end==========");
};
}
package com.carson.aop;
import org.springframework.stereotype.Service;
@Service
public class DemoAfterService {
public void write(int x,int y){
System.out.println("该方法有两个参数,分别为:"+x+","+y);
}
}
4.编写切面
这个切面类中会将以上两种规则都分别关联起来。其中@After、@Before、@Around定义建言,可直接将拦截规则(切点)作为参数。同时,也可以用@PointCut定义拦截规则。另外,符合条件的每一个被拦截处称为连接点(JoinPoint)。
package com.carson.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
/**
* 编写切面
*/
@Aspect//通过@Aspect注解声明一个切面
@Component//通过@Component让此切面成为Spring容器管理的Bean。
public class LogAspect {
@Pointcut("@annotation(com.carson.aop.Action)")//通过@PointCut注解声明切点
public void annotationPointCut(){};
@Pointcut(value="execution(* com.carson.aop.DemoAfterService.*(..))")
public void pointCut(){};
@After("annotationPointCut()")
public void after(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
System.out.println("注解式拦截,在方法执行之后拦截,"+action.name());//通过反射可获得注解上的属性,然后做日志记录相关的操作
}
@Before(value = "execution(* com.carson.aop.DemoMethodService.*(..))")//通过@Before注解声明一个建言,此建言直接使用拦截规则作为参数
public void before(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法规则式拦截在方法执行之前拦截,"+method.getName());
}
@After("pointCut()")
public void around(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
Object[] args = joinPoint.getArgs();
int ans = 0;
for(int i=0;i<args.length;i++){
ans+=(int)args[i];
}
System.out.println("在执行"+method.getName()+"之后计算"+method.getName()+"方法中各参数的和为:"+ans);
}
}
5.编写配置类
package com.carson.aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* 配置类
*/
@Configuration
@ComponentScan("com.carson.aop")
@EnableAspectJAutoProxy//使用@EnableAspectJAutoProxy注解开启Spring对AspectJ的支持
public class AopConfig {
}
以上用配置类来代替xml文件,其中配置类通过一些特定的注解实现了配置需求。
6.编写运行类
package com.carson.aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);//读取配置类,正式使用注解开启了Spring对AspectJ的支持
DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class);
DemoMethodService demoMethodService = context.getBean(DemoMethodService.class);
DemoAfterService demoAfterService = context.getBean(DemoAfterService.class);
demoAnnotationService.div();
System.out.println("- - - - - - - - - -");
demoMethodService.add();
System.out.println("- - - - - - - - - -");
demoAfterService.write(3,5);
context.close();
}
}
运行结果:
一月 09, 2019 9:46:01 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@191beef: startup date [Wed Jan 09 21:46:00 CST 2019]; root of context hierarchy
=======div========
15/3=5
=======end========
注解式拦截,在方法执行之后拦截,注解式拦截的div操作
- - - - - - - - - -
方法规则式拦截在方法执行之前拦截,add
=========加法==========
3+5=8
==========end==========
- - - - - - - - - -
一月 09, 2019 9:46:01 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@191beef: startup date [Wed Jan 09 21:46:00 CST 2019]; root of context hierarchy
该方法有两个参数,分别为:3,5
在执行write之后计算write方法中各参数的和为:8
Process finished with exit code 0
可以看出,基于方法规则拦截方式更易理解,而基于注解拦截能够很好地控制拦截的粒度和获得更丰富的信息。在初学时可以先从基于方法规则拦截方式入手,然后逐步加深理解。