AOP面向切面编程
底层原理: 动态代理
动态代理的核心思想: 借助父类或接口的多态性特点, 在形式上, 通过调用原来存在的父类或接口中的方法, 而实际执行的是子类或实现类的代理类中的增强方法
JDK动态代理(针对接口)
public class JDKProxyMain {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
// Proxy.newProxyInstance()即为动态字节码技术,直接写入到JVM中,而不需要类加载器来加载字节码文件
// 之所以这里仍然需要传入一个类加载器,是因为需要类加载器用于创建Class对象,任意一个类加载器即可
// 而.class字节码会由JVM自动分配类加载器,但是动态代理则需要手动提供一个类加载器
UserService proxy = (UserService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
// proxy: 代表Proxy.newProxyInstance()所创建的代理对象,基本上不使用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDKProxy做一些事情");
Object result = method.invoke(userService, args);
return result;
}
});
proxy.login("root", "123456");
}
}
CGLIB动态代理(针对类)
public class CglibProxyMain {
public static void main(String[] args) {
UserServiceImpl userServiceImpl = new UserServiceImpl();
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(ClassLoader.getSystemClassLoader());
// 设置原始类(假设其没有实现接口)
enhancer.setSuperclass(userServiceImpl.getClass());
// 设置方法增强(额外功能)
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib输出");
Object result = method.invoke(userServiceImpl, objects);
return result;
}
});
UserServiceImpl userServiceImplProxy = (UserServiceImpl) enhancer.create();
userServiceImplProxy.login("root", "987654");
}
}
切入点表达式
语法结构
execution(<权限修饰符和返回类型> <全类名>.<方法名称>(<参数列表>))@annotation(<注解的全类名>)
注意第一个
*同时表示权限修饰符和返回值类型没有要求参数列表中使用
..来表示对参数没要求,而不是继续使用通配符*。
如果对参数有要求,只需要设置参数类型即可,例如(String, int)或(String, ..)。自定义的类型需要使用全限定名全类名中使用
..来表示一级子包或多级子包
package dao;
public interface UserDao {
private int add(int x, int y);
public String update(String id);
}
案例1:方法切入点
对
com.atguigu.dao.UserDao类里面的add()进行增强切入点表达式:
execution(* com.atguigu.dao.UserDao.add(..))
*表示任意, 可以是任意修饰符, 任意返回类型, 任意类名, 任意方法…
案例2:类切入点
对
com.atguigu.dao.UserDao类里面的所有方法进行增强切入点表达式:
execution(* com.atguigu.dao.UserDao.*(..))
案例3:包切入点
对
com.atguigu.dao包里面的所有类, 类中的所有方法进行增强切入点表达式:
execution(* com.atguigu.dao.*.*(..))
重用切入点(优化)
- 新建方法, 使用
@Pointcut来抽取出公共的切入点 - 在通知中为
value属性赋值上面的方法即可提取该方法中的切入点表达式
//用来保存切入点表达式的函数
@Pointcut(value = "execution(* dao.UserDaoImpl.add())")
private final void pointcut() {
}
/**
* 通过方法名提取切入点b
*/
@Before(value = "pointcut()")
public void before() {
System.out.println("before()前置增强执行中...");
}
切入点案例演示
案例一:方法表达式
import lombok.Data;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import java.lang.reflect.Method;
public class MyPointCutV1 {
@Data
static class Person {
private String name;
private int age;
public void hello() {
System.out.println("hello, world");
}
public void hi() {
System.out.println("hi, world");
}
}
public static void main(String[] args) throws NoSuchMethodException {
//1. 创建一种切入点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
//2. 设置要匹配的切入点表达式
pointcut.setExpression("execution(* hello())");
//3. 准备测试方法
Method helloMethod = Person.class.getMethod("hello");
Method hiMethod = Person.class.getMethod("hi");
//4. 用切入点表达式去匹配方法
boolean b1 = pointcut.matches(helloMethod, Person.class);
boolean b2 = pointcut.matches(hiMethod, Person.class);
//5. 查看匹配结果
System.out.println(b1);//true
System.out.println(b2);//false
}
}
案例二:注解表达式
import lombok.Data;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.Method;
public class MyPointCutV2 {
@Data
static class Person {
private String name;
private int age;
@Transactional
public void hello() {
System.out.println("hello, world");
}
public void hi() {
System.out.println("hi, world");
}
}
public static void main(String[] args) throws NoSuchMethodException {
//1. 创建一种切入点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
//2. 设置要匹配的切入点表达式
pointcut.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
//3. 准备测试方法
Method helloMethod = Person.class.getMethod("hello");
Method hiMethod = Person.class.getMethod("hi");
//4. 用切入点表达式去匹配方法
boolean b1 = pointcut.matches(helloMethod, Person.class);
boolean b2 = pointcut.matches(hiMethod, Person.class);
//5. 查看匹配结果
System.out.println(b1);//true
System.out.println(b2);//false
}
}
案例三:自定义匹配规则
import lombok.Data;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.Method;
public class MyPointCutV3 {
@Data
@Transactional
static class Person {
private String name;
private int age;
public void hello() {
System.out.println("hello, world");
}
public void hi() {
System.out.println("hi, world");
}
}
public static void main(String[] args) throws NoSuchMethodException {
//1. 自定义匹配规则
// 对于一个特定的注解就需要一个实现类
StaticMethodMatcherPointcut pointcut = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 获取方法上的注解
MergedAnnotations annotations = MergedAnnotations.from(method);
//1.1 判断方法上是否有@Transactional注解
if (annotations.isPresent(Transactional.class)) {
return true;
}
//1.2 判断类上是否有@Transactional注解
//MergedAnnotations.SearchStrategy.TYPE_HIERARCHY是搜索继承树上的类和接口是否含有@Transactional注解
annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
if (annotations.isPresent(Transactional.class)) {
return true;
}
return false;
}
};
//3. 准备测试方法
Method helloMethod = Person.class.getMethod("hello");
Method hiMethod = Person.class.getMethod("hi");
//4. 用切入点表达式去匹配方法
boolean b1 = pointcut.matches(helloMethod, Person.class);
boolean b2 = pointcut.matches(hiMethod, Person.class);
//5. 查看匹配结果
System.out.println(b1);//true
System.out.println(b2);//true
}
}
切面案例演示
案例一
import lombok.Data;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import java.lang.reflect.Method;
import java.util.List;
/**
* 由@Aspect注解标注(高级切面)
*/
@Aspect
public class MyAspectV1 {
@Configuration
static class Config {
/**
* Advisor低级切面
*
* @param advice
* @return
*/
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello(..))");
//通过切入点和增强来创建切面
return new DefaultPointcutAdvisor(pointcut, advice);
}
@Bean
public MethodInterceptor advice() {
return invocation -> {
System.out.println("########before##########");
Object result = invocation.proceed();
System.out.println("########after##########");
return result;
};
}
}
@Data
static class Person {
private String name;
private int age;
public void hello() {
System.out.println("hello, world");
}
public void hi() {
System.out.println("hi, world");
}
}
@Pointcut("execution(* hello(..))")
private void helloPointCut() {
}
/**
* 每个增强会对应一个低级切面Advisor(InstantiationModelAwarePointcutAdvisor)
*
* @param joinPoint
* @return
*/
@Around(value = "helloPointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
// 原始方法之前执行
System.out.println("around: 功能执行之前");
Object result = null;
try {
result = joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
//原始方法之后执行
System.out.println("around: 功能执行之后");
return result;
}
/**
* 每个增强对生成一个低级切面(InstantiationModelAwarePointcutAdvisor)
*/
@Before(value = "helloPointCut()")
public void before() {
// 原始方法之前执行
System.out.println("before: 功能执行之前");
}
public static void main(String[] args) throws Exception {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean("aspect", MyAspectV1.class);
applicationContext.registerBean("person", Person.class);
applicationContext.registerBean("config", Config.class);
//识别@Configuration注解
applicationContext.registerBean(ConfigurationClassPostProcessor.class);
//识别@Aspect高级切面相关的注解, 两个连接点: 依赖注入之前或者初始化之后
applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
applicationContext.refresh();
// 通过反射去调用protect方法
AnnotationAwareAspectJAutoProxyCreator creator = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
Method method = creator.getClass().getDeclaredMethod("findCandidateAdvisors");
method.setAccessible(true);
List<Advisor> advisors = (List<Advisor>) method.invoke(creator);
advisors.forEach(System.out::println);
}
}
案例二
import lombok.Data;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import java.lang.reflect.Method;
/**
* 由@Aspect注解标注(高级切面)
*/
@Aspect
public class MyAspectV2 {
@Configuration
static class Config {
/**
* Advisor低级切面
*
* @param advice
* @return
*/
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello(..))");
//通过切入点和增强来创建切面
return new DefaultPointcutAdvisor(pointcut, advice);
}
@Bean
public MethodInterceptor advice() {
return invocation -> {
System.out.println("########before##########");
Object result = invocation.proceed();
System.out.println("########after##########");
return result;
};
}
}
@Data
static class Person {
private String name;
private int age;
public void hello() {
System.out.println("hello, world");
}
public void hi() {
System.out.println("hi, world");
}
}
@Pointcut("execution(* hello(..))")
private void helloPointCut() {
}
/**
* 每个增强会对应一个低级切面Advisor(InstantiationModelAwarePointcutAdvisor)
*
* @param joinPoint
* @return
*/
@Around(value = "helloPointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
// 原始方法之前执行
System.out.println("around: 功能执行之前");
Object result = null;
try {
result = joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
//原始方法之后执行
System.out.println("around: 功能执行之后");
return result;
}
/**
* 每个增强对生成一个低级切面(InstantiationModelAwarePointcutAdvisor)
*/
@Before(value = "helloPointCut()")
public void before() {
// 原始方法之前执行
System.out.println("before: 功能执行之前");
}
public static void main(String[] args) throws Exception {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean("aspect", MyAspectV2.class);
applicationContext.registerBean("person", Person.class);
applicationContext.registerBean("config", Config.class);
//识别@Configuration注解
applicationContext.registerBean(ConfigurationClassPostProcessor.class);
//识别@Aspect高级切面相关的注解, 两个连接点: 依赖注入之前或者初始化之后
applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
applicationContext.refresh();
// 通过反射去调用protect方法
AnnotationAwareAspectJAutoProxyCreator creator = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
Method method = AbstractAutoProxyCreator.class.getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
method.setAccessible(true);
Object o1 = method.invoke(creator, new MyAspectV2(), "aspect-02", "aspect-02");
Object o2 = method.invoke(creator, new Person(), "person-02", "person-02");
System.out.println("o1.getClass() = " + o1.getClass());//不匹配切入点, 因此不会生成代理类
System.out.println("o2.getClass() = " + o2.getClass());//匹配切入点,生成代理类
Person person = (Person) o2;
person.hello();//调用增强方法
}
}
案例三:高级切面如何被解析成为低级切面
import org.aspectj.lang.annotation.*;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@Aspect
public class MyAspectV3 {
@Pointcut("execution(* hello())")
public void helloPointCut() {
}
@Before(value = "helloPointCut()")
public void before() {
System.out.println("@Before: 方法执行前增强");
}
public static void main(String[] args) {
SingletonAspectInstanceFactory factory = new SingletonAspectInstanceFactory(new MyAspectV3());
List<Advisor> beforeAdvisors = new ArrayList<>();
for (Method method : MyAspectV3.class.getMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析@Before注解
Before annotation = method.getAnnotation(Before.class);
String expression = annotation.value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 增强
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面类
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(advice);
beforeAdvisors.add(advisor);
} else if (method.isAnnotationPresent(After.class)) {
// 解析@After注解
// TODO
} else if (method.isAnnotationPresent(Around.class)) {
// 解析@Around注解
// TODO
}
}
beforeAdvisors.forEach(System.out::println);
}
}