jdk动态代理和cglib的动态代理
1.springAOP的实现原理
springAOP的实现原理,就是根据动态代理实现的。如果我们为spring的某个bean配置了切面,那么spring在创建这个bean的时候,实际上创建这个bean的代理的对象,我们对bean的方法的调用,实际上调用的是这个代理类重写的代理方法。springAOP使用了两种动态代理,分别是jdk的动态代理和cglib的动态代理
2.jdk动态代理
spring默认使用jdk的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理
2.1实现原理
jdk的动态代理是基于反射实现的,jdk的通过反射,生成一个代理类,这个代理类实现了原来接口的全部方法,并对接口中定义的所有方法进行了代理,当我们通过代理对象执行原来的那个类的方法时,代理类底层会通过反射机制,回调我们实现的invocationHandler接口的invoke方法。并且这个代理类是Prox的子类
3.cglib动态代理
3.1 实现原理
cglib实现动态代理的原理是,底层采用了ASM字节码的生成框架,直接对需要代理的字节码进行操作,生成这个类的一个子类,并重写了类的所有可以重写的方法,在重写的过程中,将我们定义的额外的逻辑(简单理解为spring中的切面)织入到方法中,对方法进行了增强。而通过字节码操作生成的代理类,和我们自己编写并编译后的类没有太大的区别
4 测试jdk动态代理
需要一个接口和它的一个实现类
public interface Human {
void display();
}
@Component
public class Student implements Human {
@Override
public void display() {
System.out.println("I am a student");
}
}
定义一个切面
@Aspect
@Component
public class HumanAspect {
//代表包下面的任何类的任何方法
@Before("execution(* com.example.mall.dynamicProxyTest.service..*.*(..))")
public void before(){
System.out.println("before student");
}
}
Java
类的方式进行配置
@Configuration
@ComponentScan(basePackages = "com.example.mall.dynamicProxyTest")
@EnableAspectJAutoProxy
public class AopConfig {
}
测试
ApplicationContext context =
new AnnotationConfigApplicationContext(AopConfig.class);
// 注意,这里只能通过Human.class获取,而无法通过Student.class,因为在Spirng容器中,
// 因为使用JDK动态代理,Ioc容器中,存储的是一个类型为Human的代理对象
Human bean = context.getBean(Human.class);
bean.disPlay();
//输出代理类的父类,以此判断是JDK还是CGLib
System.out.println(bean.getClass().getSuperclass());
输出结果
before student
我是学生
class java.lang.reflect.Proxy
通过上面的输出结果,我们发现,代理类的父类是Proxy
,也就意味着果然使用的是JDK
的动态代理。
如果使用 Human bean = context.getBean(Student.class);就会报错
No qualifying bean of type 'com.example.mall.dynamicProxyTest.service.Student' available
5测试cglib动态代理
一个Teacher类
@Component
public class Teacher {
public void disPlay() {
System.out.println("我是老师");
}
}
测试类
ApplicationContext context =
new AnnotationConfigApplicationContext(AopConfig.class);
Teacher bean = context.getBean(Teacher.class);
bean.disPlay();
System.out.println(bean.getClass().getSuperclass());
输出结果
before student
我是老师
class com.example.mall.dynamicProxyTest.service.Teacher
因为CGLib
动态代理是生成了Teacher个代理对象也是Teacher类型(子类也是父类类型),所以可以通过Teacher.class
获取。
可以看到,AOP
成功生效,并且代理对象所属类的父类是Teacher
,验证了我们之前的说法。
把disPlay改为final
@Component
public class Teacher {
public final void disPlay() {
System.out.println("我是老师");
}
}
输出结果
我是老师
class com.example.mall.dynamicProxyTest.service.Teacher
可以看到,输出的父类仍然是Teacher
,也就是说Spring
依然使用了CGLib
生成代理。但是我们发现,我们为display
方法配置的前置通知并没有执行,也就是代理类并没有为display
方法进行代理。这也验证了我们之前的说法,CGLib
无法代理final
方法,因为子类无法重写父类的final
方法。
6总结
Spring AOP的实现是通过动态代理,并且有两种实现方式,分别是
JDK动态代理和
CGLib动态代理。
Spring默认使用
JDK动态代理,只有在类没有实现接口时,才会使用
CGLib`。