AOP 的底层使用动态代理实现,分两种情况:
(1)有接口时,使用 JDK 动态代理,创建接口实现类的代理对象。
(2)没有接口时,使用 CGLIB 动态代理,创建当前类的子类的代理对象。
演示 JDK 动态代理
BaseInterface.java
package com.mcc.spring5.aop;
public interface BaseInterface {
public void showInfo();
}
BaseDemo.java
package com.mcc.spring5.aop;
public class BaseDemo implements BaseInterface{
//基本功能
@Override
public void showInfo() {
System.out.println("BaseDemo 中提供的基本功能");
}
}
实现动态代理 BaseDemoProxy.java、myProxyHandler.java
package com.mcc.spring5.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//BaseDemo的代理类,用来拓展BaseDemo实现的BaseInterface的功能
public class BaseDemoProxy {
public static void main(String[] args) {
Class<?>[] interfaces = {BaseInterface.class};
//通过代理类 Proxy.newProxyInstance() 方法生成代理对象 bInter
BaseInterface bInter =(BaseInterface) Proxy.newProxyInstance(BaseDemoProxy.class.getClassLoader(), interfaces, new myProxyHandler(new BaseDemo()));
bInter.showInfo();
}
}
//增强的功能类
class myProxyHandler implements InvocationHandler{
private Object obj;
//通过有参构造器传入要代理的类的对象
myProxyHandler(Object obj){
this.obj = obj;
}
/**
* proxy - 代理对象,相当于 BInter 对象
* method - 当前正在执行的方法
* args - 传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取当前正在执行的方法名
String methodName = method.getName();
//在 showInfo() 方法之前增加功能
System.out.println(methodName + " 方法执行之前增加的功能");
//执行当前方法,传入method方法所在类的对象,即被代理对象obj,以及参数
method.invoke(obj, args);
//在 showInfo() 方法之后增加功能
System.out.println(methodName + " 方法执行之后增加的功能");
return null;
}
}
execution(权限修饰符 返回值类型 类的全路径 方法名称(参数列表))
,..
表示所有参数execution(* com.mcc.spring5.aop.AOPDemo.show(..))
execution(* com.mcc.spring5.aop.AOPDemo.*(..))
execution(* com.mcc.spring5.aop.*.*(..))
步骤:
@Component
生成被增强类和增强类对象@Aspect
注解,表示该类为代理类注解(以下注解都需使用 value=“切入点表达式” 指定对哪个类中的哪个方法进行增强) | 类型 |
---|---|
@Before(value="切入点表达式") |
前置通知 |
@AfterReturning |
后置通知 |
@Around |
环绕通知 |
@AfterThrowing |
异常通知 |
@After |
最终通知 |
代码:
Spring 配置文件 aopAspectJ.xml
<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.mcc.spring5.aop.aspectj">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
被增强类 User.java
package com.mcc.spring5.aop.aspectj;
import org.springframework.stereotype.Component;
//User 类是原有类,当做被增强类
@Component//创建User对象
public class User {
public void showInfo() {
System.out.println("User 中的 showInfo 方法");
}
}
增强类 UserProxy.java
package com.mcc.spring5.aop.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//UserProxy 类是增强类,用来增强其他类代码逻辑
@Component//创建UserProxy对象
@Aspect//配置该类为代理类
public class UserProxy {
//编写增强逻辑
//前置通知
@Before(value="execution(* com.mcc.spring5.aop.aspectj.User.showInfo(..))")//配置切入点表达式,指定对哪个类中的哪个方法进行增强
public void beforeMethod() {
System.out.println("Before 前置通知");
}
//后置通知
@AfterReturning(value="execution(* com.mcc.spring5.aop.aspectj.User.showInfo(..))")
public void afterReturnningMethod() {
System.out.println("AfterReturning 后置通知");
}
//环绕通知
@Around(value="execution(* com.mcc.spring5.aop.aspectj.User.showInfo(..))")
public void aroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//参数代表当前正在执行的方法
System.out.println("Around 环绕通知——showInfo 方法之前");
proceedingJoinPoint.proceed();//执行当前方法
System.out.println("Around 环绕通知——showInfo 方法之后");
}
//异常通知
@AfterThrowing(value="execution(* com.mcc.spring5.aop.aspectj.User.showInfo(..))")
public void exceptionMethod() {
System.out.println("AfterThrowing 异常通知");
}
//最终通知
@After(value="execution(* com.mcc.spring5.aop.aspectj.User.showInfo(..))")
public void finalMethod() {
System.out.println("After 最终通知");
}
}
测试类 TestAspectJ.java
package com.mcc.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mcc.spring5.aop.aspectj.User;
public class TestAspectJ {
@Test
public void testApsectJ() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aopAspectJ.xml");
User user = context.getBean("user", User.class);
user.showInfo();
/*
Around 环绕通知——showInfo 方法之前
Before 前置通知
User 中的 showInfo 方法
Around 环绕通知——showInfo 方法之后
After 最终通知
AfterReturning 后置通知
*/
context.close();
}
}
步骤:
@Pointcut(value="切入点表达式")
抽取相同的切入点@Before(value="method()")
@Component//创建UserProxy对象
@Aspect//配置该类为代理类
public class UserProxy {
//当多个增强逻辑的切入点相同时,可以将切入点抽取
@Pointcut(value="execution(* com.mcc.spring5.aop.aspectj.User.showInfo(..))")//相同切入点的抽取
public void pointCut() {
}
//编写增强逻辑
//前置通知
@Before(value="pointCut()")//调用抽取的函数就相当于执行切入点表达式
public void beforeMethod() {
System.out.println("Before 前置通知");
}
//后置通知
@AfterReturning(value="pointCut()")
public void afterReturnningMethod() {
System.out.println("AfterReturning 后置通知");
}
}
当有多个代理类同时代理了某个相同类时,如 UserProxy 类和UserProxy2 类都代理了 User 类,此时可以通过在代理类上使用@Order(int i)
注解,设置代理类的优先级,优先级高的类中的通知会先执行。
例:设置 UserProxy 类的优先级为 2,UserProxy2 类的优先级为 1
UserProxy.java
@Component//创建UserProxy对象
@Aspect//配置该类为代理类
@Order(2)//配置当前代理的优先级为2
public class UserProxy {
...
}
UserProxy2.java
@Component
@Aspect
@Order(1)//配置当前代理的优先级为1
public class UserProxy2 {
...
}
使用代理类代替 xml 配置文件
实现方式:
(1)使用@Configuration
声明配置类
(2)使用@ComponentScan(basePackages={""})
开启组件扫描
(3)使用@EnableAspectJAutoProxy(proxyTargetClass=true)
开启 AOP 生成代理对象,即扫描 @Aspect 注解
配置类 AoopConfig.java
package com.mcc.spring5.aop.aspectj;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
//AOP 配置类,代替 xml 配置文件
@Configuration//声明配置类
@ComponentScan(basePackages={"com.mcc.spring5.aop.aspectj"})//开启组件扫描
@EnableAspectJAutoProxy(proxyTargetClass=true)//开启 AOP 生成代理对象
public class AopConfig {
}
测试
@Test
public void testAopConfig() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
User user = context.getBean("user", User.class);
user.showInfo();
context.close();
}
步骤:
(1)使用
标签创建被增强类和增强类对象
(2)使用
标签配置 AOP
(3)在 AOP 配置标签内,使用
标签配置切入点
(4)在 AOP 配置标签内,使用
标签配置切面
标签可根据需要添加的通知类型,自行选择配置文件 aopAspectJXML.xml
<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"
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.xsd">
<bean id="book" class="com.mcc.spring5.aop.aspectjXML.Book">bean>
<bean id="bookProxy" class="com.mcc.spring5.aop.aspectjXML.BookProxy">bean>
<aop:config>
<aop:pointcut expression="execution(* com.mcc.spring5.aop.aspectjXML.Book.showInfo(..))" id="showInfo"/>
<aop:aspect ref="bookProxy">
<aop:before method="beforeMethod" pointcut-ref="showInfo"/>
aop:aspect>
aop:config>
beans>
被增强类 Book.java
package com.mcc.spring5.aop.aspectjXML;
public class Book {
public void showInfo() {
System.out.println("Book 的 showInfo 方法");
}
}
增强类 BookProxy.java
package com.mcc.spring5.aop.aspectjXML;
public class BookProxy {
//增强的前置通知
public void beforeMethod() {
System.out.println("Before 方法");
}
}
测试类 TestAspectJXML.java
package com.mcc.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mcc.spring5.aop.aspectjXML.Book;
public class TestAspectJXML {
@Test
public void testAopConfig() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aopAspectJXML.xml");
Book book = context.getBean("book", Book.class);
book.showInfo();
/*
Before 方法
Book 的 showInfo 方法
*/
context.close();
}
}