参考:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-api
AOP意味面向切面编程,是通过预编译方式和运行期动态代理,实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
1、方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。
2、连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvic
3、切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上
4、引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口
5、目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
6、织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
1、有图有真相
2、 github源码:https://github.com/jxq0816/spring-aop-demo
3、代码组织结构
1、 pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.week7i.share</groupId>
<artifactId>spring-aop-demo</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>spring-aop-demo Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>4.3.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--JSP Standard Tag Library,JSP标准标签库-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--AOP-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--Code Generation Library 是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口
它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.10</version>
</dependency>
</dependencies>
<build>
<finalName>spring-aop-demo</finalName>
</build>
</project>
2、web.xml
注意:aop的配置文件必须放在servlet的param-value中,才可以在springmvc上AOP,最初放在了context-param的param-value中,折腾了一天才搞定。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<!-- applicationContext.xml默认地址目录是/WEB-INF-->
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContext.xml
</param-value>
</context-param>
<!-- 加入ContextLoaderListener 启动Web容器时,自动装配ApplicationContext的配置信息-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--DispatcherServlet是前置控制器,拦截匹配的请求,Servlet拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理-->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/dispatcherServlet.xml
classpath:/dispatcherServlet-aop-01.xml
classpath:/dispatcherServlet-aop-02.xml
classpath:/dispatcherServlet-aop-03.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
</web-app
配置文件定义Advice通知对象、Aspect切面、Pointcut切点,并通过定义aop:after连接Adivicef通知方法和Pointcut切点
1、 dispatcherServlet-aop-01.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.weeking.share"/>
<!-- 被代理对象 -->
<bean id="math" class="com.weeking.share.service.aop01.MathUtils"></bean>
<!-- 切面 -->
<bean id="aspect" class="com.weeking.share.service.aop01.AspectProxy"></bean>
<aop:config proxy-target-class="true">
<!-- 切点 -->
<aop:pointcut id="pointCut" expression="execution(public * com.weeking.share.service.aop01.MathUtils.*(..))" />
<!--切面 -->
<aop:aspect ref="aspect">
<!--连接通知方法与切点 -->
<aop:before method="before" pointcut-ref="pointCut"/>
<aop:after method="after" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
2、 AspectProxy.java
package com.weeking.share.service.aop01;
import org.aspectj.lang.JoinPoint;
/**
* 切面类,其方法为通知
*
*/
public class AspectProxy {
public void before(JoinPoint jp){
System.out.println("----------AOP01 before execute----------");
System.out.println(jp.getSignature().getName());
}
public void after(JoinPoint jp){
System.out.println("----------AOP01 after execute----------");
}
}
3、MathUtils.java
package com.weeking.share.service.aop01;
/**
* 被代理的目标类
*/
public class MathUtils{
//加
public int add(int n1,int n2){
int result=n1+n2;
System.out.println(n1+"+"+n2+"="+result);
return result;
}
//减
public int sub(int n1,int n2){
int result=n1-n2;
System.out.println(n1+"-"+n2+"="+result);
return result;
}
//乘
public int mut(int n1,int n2){
int result=n1*n2;
System.out.println(n1+"X"+n2+"="+result);
return result;
}
//除
public int div(int n1,int n2){
int result=n1/n2;
System.out.println(n1+"/"+n2+"="+result);
return result;
}
}
4、测试方法
@Test
public void testAop01() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:dispatcherServlet-aop-01.xml");
MathUtils math = ctx.getBean("math", MathUtils.class);
int n1 = 100, n2 = 5;
math.add(n1, n2);
math.sub(n1, n2);
math.mut(n1, n2);
math.div(n1, n2);
}
5、运行结果
1、 dispatcherServlet-aop-02.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.weeking.share"/>
<aop:config>
<!--定义切点-->
<aop:pointcut
expression="execution(public * com.weeking.share.service.aop02.service.LoginService.*(..)) and @annotation(com.weeking.share.service.aop02.annotion.Operate)"
id="pointCut"/>
<!--定义切面 当执行同一个切入点时,不同切面的执行先后顺序是由“每个切面的order属性”而定的.
order越小,则该该切面中的通知越先被执行。-->
<aop:aspect ref="aspectProxy02" order="1">
<!--连接通知方法与切点-->
<aop:after method="doAfter" pointcut-ref="pointCut" />
</aop:aspect>
</aop:config>
<bean id="aspectProxy02" class="com.weeking.share.service.aop02.AspectProxy02"></bean>
</beans>
2、Operate.java
package com.weeking.share.service.aop02.annotion;
import com.weeking.share.service.aop02.ennum.OperateEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target(ElementType.METHOD)
public @interface Operate {
public OperateEnum status();
}
3、OperateEnum.java
package com.weeking.share.service.aop02.ennum;
public enum OperateEnum {
USERNAME("username");
private String type;
OperateEnum(String type) {
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
4、AspectProxy02.java
package com.weeking.share.service.aop02;
import com.weeking.share.service.aop02.annotion.Operate;
import com.weeking.share.service.aop02.ennum.OperateEnum;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
public class AspectProxy02 {
public void doAfter(JoinPoint jp) throws NoSuchMethodException, SecurityException {
System.out.println("---------- AOP02 after execute ----------");
MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = signature.getMethod();
System.out.println("method : "+method.getName());
Method realMethod = jp.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());
System.out.println("realMethod : "+method.getName());
Operate operate = realMethod.getAnnotation(Operate.class);
Object[] args = jp.getArgs();
if( args.length > 0 && operate != null) {
OperateEnum status = operate.status();
System.out.println("keys:"+status);
System.out.print("values:");
for(int i=0;i<args.length;i++){
System.out.print(args[i]);
}
}
System.out.println();
}
}
5、LoginService.java
package com.weeking.share.service.aop02.service;
import com.weeking.share.service.aop02.annotion.Operate;
import com.weeking.share.service.aop02.ennum.OperateEnum;
import org.springframework.stereotype.Service;
@Service
public class LoginService {
@Operate(status= OperateEnum.USERNAME)
public String login(String username){
System.out.println("login service username:"+username);
return username;
}
}
6、测试方法
@Test
public void testAop02() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:dispatcherServlet-aop-02.xml");
LoginService service = (LoginService)ctx.getBean(LoginService.class);
service.login("jiang");
}
7、运行结果
1、dispatcherServlet-aop-03.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.weeking.share"/>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
2、AspectProxy03.java
package com.weeking.share.service.aop03;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 切面类,横切逻辑
*
*/
@Component
@Aspect
public class AspectProxy03 {
@Before("execution(* com.weeking.share.service.aop03.MathService.*(..))")
public void before(JoinPoint jp){
System.out.println("---------- AOP03 before execute ----------");
MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = signature.getMethod();
System.out.println("method : "+method.getName());
Object[] args = jp.getArgs();
if( args.length>0) {
System.out.print("param:");
for(int i=0;i<args.length;i++){
System.out.print(args[i]+" ");
}
}
System.out.println();
}
@After("execution(* com.weeking.share.service.aop03.MathService.*(..))")
public void after(JoinPoint jp){
System.out.println("---------- AOP03 after execute ----------");
}
}
3、MathService.java
package com.weeking.share.service.aop03;
import org.springframework.stereotype.Service;
@Service
public class MathService {
//加
public int add(int n1,int n2){
int result=n1+n2;
System.out.println(n1+"+"+n2+"="+result);
return result;
}
//减
public int sub(int n1,int n2){
int result=n1-n2;
System.out.println(n1+"-"+n2+"="+result);
return result;
}
//乘
public int mut(int n1,int n2){
int result=n1*n2;
System.out.println(n1+"X"+n2+"="+result);
return result;
}
//除
public int div(int n1,int n2){
int result=n1/n2;
System.out.println(n1+"/"+n2+"="+result);
return result;
}
}
4、测试方法
@Test
public void testAop03() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:dispatcherServlet-aop-03.xml");
MathService math = ctx.getBean(MathService.class);
int n1 = 100, n2 = 5;
math.add(n1, n2);
math.sub(n1, n2);
math.mut(n1, n2);
math.div(n1, n2);
}
5、运行结果
1、LoginController.java
package com.weeking.share.controller;
import com.weeking.share.service.aop02.annotion.Operate;
import com.weeking.share.service.aop02.ennum.OperateEnum;
import com.weeking.share.service.aop02.service.LoginService;
import com.weeking.share.service.aop03.MathService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Created by jiangxingqi on 2017/7/25.
*/
@Controller
public class LoginController {
@Autowired
private LoginService loginService;
@Autowired
private MathService mathService;
@RequestMapping(value = "/login")
public String login(){
String username=loginService.login("jiangxingqi");
return "login";
}
@RequestMapping(value = "/math")
public String math(){
mathService.add(1,2);
return "login";
}
}
2、HTTP请求http://localhost:8084/login
控制台输出显示AOP成功执行