1. AOP的实现方式
-
使用AspectJ的编译器来改动class类文件实现增强(使用不广泛) ----- 编译阶段
这种对class类文件增强的, 也可以增强static静态方法, 而通过代理方式就无法实现静态方法的增强
可通过查看编译后class文件反编译后的java代码验证
-
agent增强(使用不广泛) ----- 类加载阶段
这种方式的增强也会使得代理类在内部自己调用自己的方法也获得增强, 原因是这种方式是通过类加载阶段进行的增强, 在通过this.xxFunc()调用自己本类的方法也是触发代理类的方法。
可通过arthas等工具查看jvm内存中增强后的类的反编译后的java代码验证
- Proxy代理方式实现AOP(绝大多数的选择)
- JDK 代理 (只能代理实现了接口的类, 而且也只能代理那些接口方法, 代理类与目标类同级关系)
- CGLIB 代理 (能代理类和接口, 但该类或代理的方法不能使用final修饰, 因为是基于继承进行增强的, 代理类与目标类是父 / 子 关系)
代理创建的两个时机:
- 初始化之后
- 实例创建之后, 依赖注入之前(有循环依赖时), 并暂存于二级缓存 earlySingletonObjects
JDK实现的代理:
public class JdkProxyDemo {
interface Foo{
void foo();
}
// 将被用作jdk代理的实例类
static class Target implements Foo{
@Override
public void foo() {
System.out.println("target foo");
}
// 这个方法无法被代理,因为不是来自接口的方法
public void test(){
System.out.println("test");
}
}
// jdk代理 只能针对接口代理
// cglib代理
public static void main(String[] args) {
// 目标对象
Target target = new Target();
ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();//用来加载在运行期间动态生成的字节码
Foo proxyInstance = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
// 目标.方法(参数)
// method.invoke(proxy, args); 错误写法, 这里的proxy代表代理类自身, 传入proxy会陷入无限循环
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
proxyInstance.foo();
}
}
Cglib实现的代理:
注意: 在MethodInterceptor实现的intercept()方法中,传入了一个参数methodProxy, 通过该对象调用methodProxy.invoke(target, args)也能执行目标对象, 但其内部不是通过反射的方式。
public class CglibProxyDemo {
static class Target{
public void foo(){
System.out.println("target foo");
}
}
public static void main(String[] args){
Target target = new Target();
Target proxyInstance = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args); //Spring使用的方式
//Object result = methodProxy.invoke(target, args); 非反射方式调用, 而且可以不用目标就调用
System.out.println("after...");
return result;
}
});
proxyInstance.foo();
}
}
2.Spring中的两种切面
@Aspect
: 高级切面, 可以包含多组通知和切面
Advisor
: 低级切面, 适合框架内部使用, 只有一个通知和一个切面
Spring在处理这两个切面时, @Aspect
切面最终还是转为Advisor
两种方式使用示例:
public class AspectDemo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
static class Target1{
public void foo(){
System.out.println("target1 foo");
}
}
static class Target2{
public void bar(){
System.out.println("target bar");
}
}
//高级切面,Aspect
@Aspect
static class Aspect1{
@Before("execution(* foo())")
public void before(){
System.out.println("aspect1 before...");
}
@After("execution(* foo())")
public void after(){
System.out.println("aspect1 after...");
}
}
//低级切面, 由一个切点(AspectJExpressionPointcut)和一个通知(MethodInterceptor)组成
@Configuration
static class Config{
@Bean
public Advisor advisor3(MethodInterceptor advice3){
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut , advice3);
}
@Bean
public MethodInterceptor advice3(){
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("advice3 before...");
Object result = methodInvocation.proceed();
System.out.println("advice3 after...");
return result;
}
};
}
}
}