Aware和InitializingBean都与Bean的生命周期管理相关。
Aware接口:
InitializingBean接口:
下面的MyBean类分别实现了BeanNameAware, ApplicationContextAware,InitializingBean三个接口:
- public class MyBean implements BeanNameAware, ApplicationContextAware,InitializingBean {
- private static final Logger log = LoggerFactory.getLogger(MyBean.class);
-
- /**
- * 重写BeanNameAware的方法,可以获取当前bean的名称
- */
- @Override
- public void setBeanName(String name) {
- log.info("bean is {},bean name is:{}", this,name);
- }
-
- /**
- * 重写ApplicationContext的方法,允许Bean实现类获取对Spring应用上下文(ApplicationContext)的引用。
- */
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- log.info("bean is {},bean applicationContext is:{}", this,applicationContext);
- }
-
- /**
- * 重写InitializingBean的方法用于在Bean初始化完成后执行特定的逻辑。
- */
- @Override
- public void afterPropertiesSet() throws Exception {
- log.info("bean is {},init", this);
- }
- }
- public class A05 {
- public static void main(String[] args) {
- GenericApplicationContext context = new GenericApplicationContext();
-
- context.registerBean("mybean", MyBean.class);
-
- context.refresh();
- context.close();
- }
- }

上面的功能,通过@Autowired @PostConstruct 等注解也可以实现,为什么还要实现接口?原因是因为,解析@Autowired @PostConstruct 等注解需要通过后处理器,而Aware等接口属于Spring的内置功能,不需要任何扩展(在上述案例的主类中,使用的是GenericApplicationContext,不会加任何后处理器)。扩展在某些时候可能会失效,而内置功能不会失效。
演示使用@Autowired @PostConstruct 等注解实现同等功能:
- public class MyBean2 {
-
- private static final Logger log = LoggerFactory.getLogger(MyBean2.class);
-
- @Autowired
- public void getApplicationContext(ApplicationContext applicationContext) {
- log.info("bean is {},bean applicationContext is:{}", this,applicationContext);
- }
-
- @PostConstruct
- public void init(){
- log.info("init bean");
- }
- }
在主类中需要加入后处理器:
- public class A05 {
- public static void main(String[] args) {
- GenericApplicationContext context = new GenericApplicationContext();
-
- context.registerBean("mybean2", MyBean2.class);
-
- context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
- context.registerBean(CommonAnnotationBeanPostProcessor.class);
-
-
- context.refresh();
- context.close();
- }
- }
运行结果与实现ApplicationContextAware,InitializingBean相同。

演示一种注解失效的情况:
- /**
- * 演示后处理器扩展失效的情况
- */
- @Configuration
- public class MyConfig1 {
-
- private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);
-
- @Autowired
- public void setApplicationContext(ApplicationContext applicationContext){
- log.info("注入 Application Context");
- }
-
- @PostConstruct
- public void init(){
- log.info("init");
- }
-
- /**
- * 想要获取MyConfig1中的BeanFactoryPostProcessor,就先要把MyConfig1类先创建好
- * 提前创建导致扩展功能失效 因为1、beanFactory后处理器 2、bean后处理器 这样的顺序
- * @return
- */
- @Bean
- public BeanFactoryPostProcessor beanPostProcessor(){
- return beanFactory -> {
- log.info("beanPostProcessor");
- };
- }
- }
- public class A05Application {
- public static void main(String[] args) {
- /*
- @AutoWired等注解是spring的扩展功能,需要通过后处理器实现
- 而实现BeanNameAware, ApplicationContextAware,InitializingBean 等接口是spring的内置功能
- 为了防止扩展功能失效
- */
-
-
- GenericApplicationContext context = new GenericApplicationContext();
- context.registerBean("myConfig", MyConfig1.class);
-
- context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
- context.registerBean(CommonAnnotationBeanPostProcessor.class);
- context.registerBean(ConfigurationClassPostProcessor.class);
-
- //执行顺序:1、beanFactory后处理器 2、bean后处理器 3、初始化所有单例
- context.refresh();
- context.close();
- }
- }
此时发现,只有beanPostProcessor()成功执行

原因在于,调用GenericApplicationContext的.refresh(); 方法初始化时,其执行顺序是:
1、beanFactory后处理器
2、bean后处理器
3、初始化所有单例
如下图所示:
如果要初始化BeanFactoryPostProcessor,首先要把 MyConfig1类创建完成。相当于下图的顺序:

很明显此时是先执行了类的初始化,最后再注册后处理器。这样就导致了无法解析@Autowired @PostConstruct 等注解。但是在创建类及初始化时,会执行Aware和InitializingBean相关接口:
- /**
- * 演示后处理器扩展失效的情况解决方案
- * 使用实现接口的方式
- */
- @Configuration
- public class MyConfig2 implements ApplicationContextAware, InitializingBean {
-
- private static final Logger log = LoggerFactory.getLogger(MyConfig2.class);
-
- /**
- * 想要获取MyConfig1中的BeanFactoryPostProcessor,就先要把MyConfig1类先创建好
- * 提前创建导致扩展功能失效 因为1、beanFactory后处理器 2、bean后处理器 这样的顺序
- * @return
- */
- @Bean
- public BeanFactoryPostProcessor beanPostProcessor(){
- return beanFactory -> {
- log.info("beanPostProcessor");
- };
- }
-
- @Override
- public void afterPropertiesSet() throws Exception {
- log.info("init");
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- log.info("注入 Application Context");
- }
- }
此时可以观察到,初始化和注入Application Context的操作都成功执行。

Bean初始化和销毁通常有三种方式:
初始化的三种方式:
- /**
- * 演示三种初始化的执行顺序
- */
- public class Bean1 implements InitializingBean {
-
- private static final Logger log = LoggerFactory.getLogger(Bean1.class);
-
- @PostConstruct
- public void init1(){
- log.info(" @PostConstruct方式");
- }
-
-
- /**
- * Invoked by the containing {@code BeanFactory} after it has set all bean properties
- * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
- *
This method allows the bean instance to perform validation of its overall
- * configuration and final initialization when all bean properties have been set.
- *
- * @throws Exception in the event of misconfiguration (such as failure to set an
- * essential property) or if initialization fails for any other reason
- */
- @Override
- public void afterPropertiesSet() throws Exception {
- log.info("实现InitializingBean接口方式");
- }
-
- public void init3(){
- log.info("@Bean设置initMethod方式");
- }
- }
销毁的三种方式:
- /**
- * 演示三种销毁方法的执行顺序
- */
- public class Bean2 implements DisposableBean {
-
-
- private static final Logger log = LoggerFactory.getLogger(Bean2.class);
-
- @PreDestroy
- public void destory1(){
- log.info("@PreDestroy方式");
- }
-
-
- /**
- * Invoked by the containing {@code BeanFactory} on destruction of a bean.
- *
- * @throws Exception in case of shutdown errors. Exceptions will get logged
- * but not rethrown to allow other beans to release their resources as well.
- */
- @Override
- public void destroy() throws Exception {
- log.info("实现DisposableBean接口方式");
- }
-
- public void destory3(){
- log.info("@Bean设置destroyMethod方式");
- }
- }
- @SpringBootApplication
- public class A06Application {
- public static void main(String[] args) {
- ConfigurableApplicationContext context = SpringApplication.run(A06Application.class, args);
- context.close();
- }
-
- @Bean(initMethod = "init3")
- public Bean1 bean1(){
- return new Bean1();
- }
-
- @Bean(destroyMethod = "destory3")
- public Bean2 bean2(){
- return new Bean2();
- }
- }
三种方式的先后顺序:


在 Spring 中,Scope(作用域)是指 Bean 的生命周期范围,即在应用程序中创建的 Bean 对象在何时被创建、存在多久以及何时被销毁。Spring 框架提供了多种作用域,每种作用域都决定了 Bean 实例的生命周期和可见性。
列举一下常见的作用域:
下面通过案例演示Application、Request、Session三种作用域:
- @Scope("application")
- @Component
- public class BeanForApplication {
-
-
- private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);
-
- @PreDestroy
- public void destory(){
- log.info("destory");
- }
- }
- @Scope("request")
- @Component
- public class BeanForRequest {
-
-
- private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);
-
- @PreDestroy
- public void destory(){
- log.info("Destory");
- }
- }
- @Component
- @Scope("session")
- public class BeanForSession {
-
- private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);
-
- @PreDestroy
- public void destroy() {
- log.info("destory");
- }
- }
- @Controller
- public class TestController {
-
- @Autowired
- @Lazy
- private BeanForApplication beanForApplication;
-
- @Autowired
- @Lazy
- private BeanForRequest beanForRequest;
-
- @Autowired
- @Lazy
- private BeanForSession beanForSession;
-
- /**
- * 在同一个浏览器内,每次请求beanForRequest都会不一样
- * 在不同的浏览器中,每次请求beanForRequest都不一样,第一次请求时beanForSession不一样
- * 同一个程序下,beanForApplication都是一样的
- *
- * 每次请求beanForRequest创建 销毁
- * beanForSession 到期时销毁
- * @param req
- * @param httpSession
- */
- @GetMapping("/test")
- public void test(HttpServletRequest req, HttpSession httpSession) {
- System.out.println("BeanForApplication:"+beanForApplication);
- System.out.println("BeanForRequest:"+beanForRequest);
- System.out.println("BeanForSession:"+beanForSession);
- }
- }
主启动类:
- @SpringBootApplication
- public class A07Application {
- public static void main(String[] args) {
- ConfigurableApplicationContext context = SpringApplication.run(A07Application.class, args);
-
- }
- }
使用同一浏览器,第一次执行:

第二次执行:

可以得出结论:同一浏览器(http会话)中,每次请求都会创建一个新的 Bean 实例,该实例仅在当前 HTTP 请求内有效。
使用不同浏览器,浏览器一:

浏览器二:

可以得出结论:不同浏览器(http会话)都会产生一个新的实例,并且在每个会话中,每次请求都会创建一个新的 Bean 实例,该实例仅在当前 HTTP 请求内有效。
定义两个多例Bean:
- @Component
- @Scope("prototype")
- public class F1 {
- }
- @Component
- @Scope("prototype")
- public class F2 {
- }
在配置类中通过@Autowired注入F1和F2
- @Component
- public class E {
-
- @Autowired
- private F1 f1;
-
- @Autowired
- private F2 f2;
-
- public F1 getF1() {
- return f1;
- }
-
- public F2 getF2() {
- return f2;
- }
- }
在主类中获取三次F1和F2
- @ComponentScan("com.itbaima.a08_1")
- public class A08Test {
- private static final Logger log = LoggerFactory.getLogger(A08Test.class);
-
- public static void main(String[] args) {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A08Application.class);
-
- E e = context.getBean(E.class);
-
- log.info("{}",e.getF1());
- log.info("{}",e.getF1());
- log.info("{}",e.getF1());
-
- System.out.println("<<<<<<<<<<<<<<<<<<<<<");
-
- log.info("{}",e.getF2());
- log.info("{}",e.getF2());
- log.info("{}",e.getF2());
- }
- }
期望的结果是每次获取到的F1和F2都是不同的实例:

与预期结果不符。为什么定义的Bean是多例,每次却获取到了同一个实例?
当我们将一个 Prototype Bean 注入到一个 Singleton Bean 中时, Prototype Bean 注入的是该 Prototype Bean 的实例,而不是一个代理对象。这意味着,虽然 Prototype Bean 是多例的,但在 Singleton Bean 中的引用始终是同一个实例,导致 Prototype Bean 的多例特性失效。
- @Autowired
- private F1 f1;
-
- @Autowired
- private F2 f2;
这就是单例注入多例Bean失效问题
使用@Lazy 注解。 @Lazy 注解可以延迟 Bean 的初始化,即在第一次被请求时才进行初始化,而不是在容器启动时就创建。
Spring 容器在初始化 Singleton Bean 时不会立即初始化注入的 Prototype Bean,而是在首次使用 Prototype Bean 时才进行初始化,从而保证每次获取的都是新的实例。
- @Autowired
- @Lazy
- private F1 f1;
F1每次都是不同的实例:

在声明多例时:
@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
告诉 Spring 在注入 Prototype Bean 时使用代理对象,而不是直接注入 Prototype Bean 的实例。这样,每次从 Singleton Bean 中获取 Prototype Bean 时,实际上会通过代理对象获取一个新的 Prototype Bean 实例,从而保证了 Prototype Bean 的多例特性。
在 Singleton Bean 中通过 ApplicationContext 手动获取 Prototype Bean。每次需要使用 Prototype Bean 时,都通过 ApplicationContext 获取新的实例。
- //通过applicationContext 的 getBean
- @Autowired
- private ApplicationContext applicationContext;
-
- public F4 getF4(){
- return applicationContext.getBean(F4.class);
- }
使用ObjectFactory。ObjectFactory是 Spring 提供的一种工厂模式,它的实现是延迟加载的,所以每次调用 getObject()方法都会返回一个新的 Prototype Bean 实例,其原理与@Lazy类似,都是实现延迟加载。
- //通过对象工厂的方式
- @Autowired
- private ObjectFactory<F3> f3;
-
- public F3 getF3(){
- return f3.getObject();
- }
Spring 中的动态代理是指在运行时生成代理对象,而不是在编译时就确定代理对象的类型。Spring 中常用的动态代理有两种方式:基于 JDK 的动态代理和基于 CGLIB 的动态代理。
两者之间的区别与联系:
实现方式:
即:JDK动态代理需要目标类和代理类实现同一个接口,CGLIB动态代理时会生成目标类的子类。
性能:
但是从是否反射的角度上,CGLIB因为无需反射,性能是高于JDK动态代理的
适用范围:
两者的共同点:
JDK 动态代理和 CGLIB 动态代理的目的都是为了实现对目标类的增强,例如实现 AOP(面向切面编程)。在 Spring 中,无论是使用 JDK 动态代理还是 CGLIB 动态代理,开发者都是通过配置来决定使用哪种代理方式。Spring 会根据目标类是否实现接口来自动选择使用 JDK 动态代理还是 CGLIB 动态代理。JDK 动态代理和 CGLIB 动态代理都是在运行时生成代理对象,动态地在目标方法的执行前后插入额外的逻辑,实现对目标方法的增强。
补充:动态代理与静态代理的区别:
静态代理:在编译时就已经确定代理类和被代理类的关系,代理类是通过手动编写代码或者工具生成的,代理类和被代理类在编译时就确定了。
动态代理:在运行时动态生成代理对象,不需要预先知道代理类和被代理类的关系,可以动态地在运行时创建代理对象,使得代理类的生成更加灵活。
首先创建一个接口:
- public interface Inter {
- void foo();
- }
实现类:
- public class InterImpl implements Inter {
-
- private static final Logger log = LoggerFactory.getLogger(InterImpl.class);
-
- @Override
- public void foo() {
- log.info("foo");
- }
- }
测试类:
- public class JdkProxyDemo {
-
- private static final Logger log = LoggerFactory.getLogger(JdkProxyDemo.class);
-
- public static void main(String[] args) {
- InterImpl inter = new InterImpl();
- //模拟jdk动态代理
- ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();//加载运行期间动态生成的字节码
- Inter instance = (Inter) Proxy.newProxyInstance(classLoader, new Class[]{Inter.class}, (o, method, objects) -> {
- System.out.println("before");
- //通过反射调用 参数一:调用方法所在的对象 参数二:调用方法所需的参数
- Object invoke = method.invoke(inter, args);
- System.out.println("after");
- return invoke;
- });
-
- instance.foo();
- }
- }
newProxyInstance( ClassLoader loader, Class>[] interfaces,InvocationHandler h ) 是Proxy类下的一个静态方法:
简而言之,可以用目标类获取一个类加载器,还需要传入被代理对象所实现的接口,并且在InvacationHandler内编写被代理类方法增强的逻辑,以及通过反射调用被代理类中的某个方法。

模拟JDK动态代理的实现,首先创建一个待增强的目标类及接口:
- public interface Foo {
-
- void foo();
-
- int bar();
- }
- public class Target implements Foo{
-
-
- private static final Logger log = LoggerFactory.getLogger(Target.class);
-
- @Override
- public void foo() {
- log.info("foo");
- }
-
- @Override
- public int bar() {
- log.info("bar");
- return 100;
- }
- }
上面提到过,当目标类实现了接口时,Spring 就会使用 JDK 动态代理来创建代理对象。在运行时,Spring 创建一个实现了目标接口的代理类。 所以创建一个代理类实现Foo接口:
- /**
- * 实现Foo接口,重写foo()和bar()方法
- */
- public class $Proxy0 implements Foo {
-
-
- @Override
- public void foo() {
- //foo方法增强逻辑
-
- //调用目标方法
- }
-
- @Override
- public int bar() {
- //bar方法增强逻辑
-
- //调用目标方法
- return 0;
- }
- }
在测试类中:
- /**
- * 模拟jdk动态代理的实现
- */
- public class A11Application {
-
- private static final Logger log = LoggerFactory.getLogger(A11Application.class);
-
- public static void main(String[] args) {
- //在创建$Proxy0对象时,通过有参构造指定 增强的具体逻辑
- $Proxy0 proxy0 = new $Proxy0();
- proxy0.bar();
- proxy0.foo();
- }
-
-
- }
这样写已经可以简单的实现Target中foo()方法和bar()方法的增强,但是,此时的增强逻辑是在编译时就写死的,很显然不符合动态代理的概念。我们需要的是,在运行期间动态的对目标类中的方法进行增强。
引入一个InvacationHandler接口:
- public interface InvacationHandler {
-
- /**
- * @param proxy 代理类对象
- * @param method 需要增强的方法
- * @param objects 需要增强的方法的参数
- * @return
- * @throws Throwable
- */
- Object invoke($Proxy0 proxy, Method method, Object[] objects) throws Throwable;
-
- }
应该在测试类创建$Proxy0对象时,将InvacationHandler作为参数传入,在InvacationHandler中动态的编写增强的逻辑,在$Proxy0中加入构造:
- InvacationHandler invacationHandler;
-
- public $Proxy0(InvacationHandler invacationHandler) {
- this.invacationHandler = invacationHandler;
- }
foo()方法和bar()方法中调用invacationHandler的invoke()方法,其中第二个参数,需要我们在$Proxy0类加载时,初始化带增强的方法:
- static Method foo;
- static Method bar;
-
- static {
- try {
- foo = Foo.class.getMethod("foo");
- bar = Foo.class.getMethod("bar");
- } catch (NoSuchMethodException e) {
- throw new NoSuchMethodError(e.getMessage());
- }
- }
$Proxy0类改造完成:
- public class $Proxy0 implements Foo {
-
- InvacationHandler invacationHandler;
-
- public $Proxy0(InvacationHandler invacationHandler) {
- this.invacationHandler = invacationHandler;
- }
-
- static Method foo;
- static Method bar;
-
- static {
- try {
- foo = Foo.class.getMethod("foo");
- bar = Foo.class.getMethod("bar");
- } catch (NoSuchMethodException e) {
- throw new NoSuchMethodError(e.getMessage());
- }
- }
-
- @Override
- public void foo() {
- try {
- invacationHandler.invoke(this,foo,new Object[0]);
- } catch (RuntimeException | Error e) {
- throw e;
- }catch (Throwable throwable){
- throw new UndeclaredThrowableException(throwable);
- }
- }
-
- @Override
- public int bar() {
- try {
- Object result = invacationHandler.invoke(this, bar, new Object[]{100});
- return ((int) result);
- } catch (RuntimeException | Error e) {
- throw e;
- }catch (Throwable throwable){
- throw new UndeclaredThrowableException(throwable);
- }
- }
- }
在创建 $Proxy0对象时,就需要通过创建invacationHandler匿名内部类的方式传递一段逻辑:

最终测试类:
- public class A11Application {
-
- private static final Logger log = LoggerFactory.getLogger(A11Application.class);
-
- public static void main(String[] args) {
- //在创建$Proxy0对象时,通过有参构造指定 增强的具体逻辑
- $Proxy0 proxy0 = new $Proxy0(new InvacationHandler() {
- @Override
- public Object invoke($Proxy0 proxy, Method method, Object[] objects) throws Throwable {
- System.out.println("before");
- //通过反射调用Target中的方法,objects
- Object invoke = method.invoke(new Target(), objects);
- return invoke;
- }
- });
- proxy0.bar();
- proxy0.foo();
- }
-
-
- }
下面我们借助arthas工具查看JDK动态代理的源码实现:
安装工具:cmd打开终端->curl -O https://alibaba.github.io/arthas/arthas-boot.jar
运行工具:java -jar arthas-boot.jar
注意:使用工具时保持程序处在运行状态,且需要获取要查看类的类名信息

选择对应的类:


得到生成代理类的源码:
- /*
- * Decompiled with CFR.
- *
- * Could not load the following classes:
- * com.itbaima.a10.jdkproxy.Inter
- */
- package com.sun.proxy;
-
- import com.itbaima.a10.jdkproxy.Inter;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.UndeclaredThrowableException;
-
- public final class $Proxy2
- extends Proxy
- implements Inter {
- private static Method m1;
- private static Method m2;
- private static Method m3;
- private static Method m0;
-
- public $Proxy2(InvocationHandler invocationHandler) {
- super(invocationHandler);
- }
-
- static {
- try {
- m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- m3 = Class.forName("com.itbaima.a10.jdkproxy.Inter").getMethod("foo", new Class[0]);
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- return;
- }
- catch (NoSuchMethodException noSuchMethodException) {
- throw new NoSuchMethodError(noSuchMethodException.getMessage());
- }
- catch (ClassNotFoundException classNotFoundException) {
- throw new NoClassDefFoundError(classNotFoundException.getMessage());
- }
- }
-
- public final boolean equals(Object object) {
- try {
- return (Boolean)this.h.invoke(this, m1, new Object[]{object});
- }
- catch (Error | RuntimeException throwable) {
- throw throwable;
- }
- catch (Throwable throwable) {
- throw new UndeclaredThrowableException(throwable);
- }
- }
-
- public final String toString() {
- try {
- return (String)this.h.invoke(this, m2, null);
- }
- catch (Error | RuntimeException throwable) {
- throw throwable;
- }
- catch (Throwable throwable) {
- throw new UndeclaredThrowableException(throwable);
- }
- }
-
- public final int hashCode() {
- try {
- return (Integer)this.h.invoke(this, m0, null);
- }
- catch (Error | RuntimeException throwable) {
- throw throwable;
- }
- catch (Throwable throwable) {
- throw new UndeclaredThrowableException(throwable);
- }
- }
-
- public final void foo() {
- try {
- this.h.invoke(this, m3, null);
- return;
- }
- catch (Error | RuntimeException throwable) {
- throw throwable;
- }
- catch (Throwable throwable) {
- throw new UndeclaredThrowableException(throwable);
- }
- }
- }
与JDK动态代理的模拟实现类似。不同点在于,源码中加上了方法中共有的.equals()、.hashCode()、.toString()方法。
JDK生成代理类,并没有经历源码阶段,编译阶段,而是直接生成了字节码(ASM)。ASM技术是在运行期间动态生成字节码。
可以通过插件查看生成的字节码:

准备一个继承了反射包下Proxy类的代理类和接口:
- public class $Proxy0 extends Proxy implements Foo {
-
-
- protected $Proxy0(InvocationHandler h) {
- super(h);
- }
-
- @Override
- public void foo(){
- try {
- h.invoke(this,foo,null);
- } catch (Throwable e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- static Method foo;
- static {
- try {
- foo = Foo.class.getMethod("foo");
- } catch (NoSuchMethodException e) {
- throw new NoSuchMethodError(e.getMessage());
- }
- }
- }
- public interface Foo {
-
- void foo() throws Throwable;
- }
然后将代理类和接口进行编译(Ctrl+Shift+F9)
右键如图所示,生成文件。

文件中一些字节码指令的简单理解:
构建一个类:
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "com/itheima/$Proxy0", null, "java/lang/reflect/Proxy", new String[]{"com/itheima/Foo"});
定义类中的方法和成员变量:
- {
- fv = cw.visitField(ACC_STATIC, "foo", "Ljava/lang/reflect/Method;", null, null);
- fv.visitEnd();
- }
生成byte字节码数组:
return cw.toByteArray();
然后我们将$Proxy0Dump字节码写入$Proxy0.class文件:
- public class TestProxy {
- public static void main(String[] args) throws Exception {
- //将$Proxy0Dump字节码写入$Proxy0.class文件
- byte[] dump = $Proxy0Dump.dump();
-
- FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class");
- fileOutputStream.write(dump,0,dump.length);
- fileOutputStream.close();
- }
- }
发现字节码文件和$Proxy0一样:

整体流程:
- public class TestProxy {
- public static void main(String[] args) throws Throwable {
- //生成$Proxy0Dump字节码
- byte[] dump = $Proxy0Dump.dump();
-
- //加载$Proxy0Dump字节码
- ClassLoader classLoader = new ClassLoader(){
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- return super.defineClass(name,dump,0,dump.length);
- }
- };
- //得到类对象
- Class<?> loadClass = classLoader.loadClass("com.itheima.$Proxy0");
-
- //利用反射创建对象
- Constructor<?> constructor = loadClass.getConstructor(InvacationHandler.class);
- Foo foo = (Foo) constructor.newInstance(new InvacationHandler() {
- @Override
- public Object invoke($Proxy0 proxy, Method method, Object[] objects) throws Throwable {
- System.out.println("before invoke");
- System.out.println("调用方法");
- return null;
- }
- });
-
- foo.foo();
-
-
- }
- }
JDK动态代理中,当调用invoke() 方法到达一定次数时(17)次,会触发反射优化。(类似于JUC锁批量重偏向)
- public class TestMethodInvoke {
- public static void main(String[] args) throws Exception {
- Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
- for (int i = 0; i < 17; i++) {
- foo.invoke(null,i);
- show(i,foo);
- System.in.read();
- }
- }
-
- // 方法反射调用时, 底层 MethodAccessor 的实现类
- private static void show(int i, Method foo) throws Exception {
- Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
- getMethodAccessor.setAccessible(true);
- Object invoke = getMethodAccessor.invoke(foo);
- if (invoke == null) {
- System.out.println(i + ":" + null);
- return;
- }
- Field delegate = Class.forName("jdk.internal.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
- delegate.setAccessible(true);
- System.out.println(i + ":" + delegate.get(invoke));
- }
-
- public static void foo(int i){
- System.out.println("第"+i+"次调用");
- }
- }
前16次反射调用的是JDK本地的api,效率较低。

第十七次优化成了正常调用方法,没有通过反射,所以性能较高:

编写目标类:
- /**
- * 待增强的目标类
- */
- public class Target {
-
- public void save1(){
- System.out.println("save1 ...");
- }
-
- public void save2(int i){
- System.out.println("save2 ..."+ i);
- }
-
- public void save3(long j){
- System.out.println("save3 ..."+ j);
- }
- }
编写代理类,CGLIB 动态代理则是通过生成目标类的子类来实现的,不需要目标类和代理类实现一个共同的接口:
- /**
- * CGLIB 动态代理则是通过生成目标类的子类来实现的
- */
- public class MockCglibProxy extends Target{
-
- MethodInterceptor interceptor;
-
- public void setInterceptor(MethodInterceptor interceptor) {
- this.interceptor = interceptor;
- }
-
- static Method save1;
- static Method save2;
- static Method save3;
-
- static {
- try {
- save1 = Target.class.getMethod("save1");
- save2 = Target.class.getMethod("save2", int.class);
- save3 = Target.class.getMethod("save3", long.class);
- } catch (NoSuchMethodException e) {
- throw new NoSuchMethodError(e.getMessage());
- }
- }
-
- @Override
- public void save1() {
- try {
- interceptor.intercept(this,save1,new Object[0],null);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void save2(int i) {
- try {
- interceptor.intercept(this,save2,new Object[]{10},null);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- }
-
-
- @Override
- public void save3(long j) {
- try {
- interceptor.intercept(this,save3,new Object[]{20L},null);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- }
-
- }
测试类:
- public class TestProxy {
- public static void main(String[] args) {
- Target target = new Target();
-
- MockCglibProxy mockCglibProxy = new MockCglibProxy();
- mockCglibProxy.setInterceptor(new MethodInterceptor() {
- @Override
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- System.out.println("before");
- return method.invoke(target,objects);
- }
- });
-
- target.save1();
- target.save2(0);
- target.save3(0);
- }
- }
而CGLIB和JDK动态代理最大的区别在于:
方法代理参数,可以保证每次方法都是正常调用,而不是通过反射。
首先我们将interceptor.intercept() 方法中所需的MethodProxy方法参数进行补充创建:
定义原有方法:
- public void saveSuper1(){
- super.save1();
- }
-
- public void saveSuper2(int i){
- super.save2(i);
- }
-
- public void saveSuper3(long j){
- super.save3(j);
- }
初始化MethodProxy:
- //初始化methodProxy
- static MethodProxy saveSuper1;
- static MethodProxy saveSuper2;
- static MethodProxy saveSuper3;
创建对象:
- static {
- try {
- save1 = Target.class.getMethod("save1");
- save2 = Target.class.getMethod("save2", int.class);
- save3 = Target.class.getMethod("save3", long.class);
- //创建methodProxy对象
- //参数一:目标类 参数二:代理类 参数三:方法参数 参数四:增强方法 参数五:原有方法
- saveSuper1 = MethodProxy.create(Target.class, MockCglibProxy.class,"()V","save1","saveSuper1");
- saveSuper2 = MethodProxy.create(Target.class, MockCglibProxy.class,"(I)V","save2","saveSuper2");
- saveSuper3 = MethodProxy.create(Target.class, MockCglibProxy.class,"(J)V","save3","saveSuper3");
- } catch (NoSuchMethodException e) {
- throw new NoSuchMethodError(e.getMessage());
- }
- }
将创建的MethodProxy对象传入增强方法中:
- //以下是带增强的方法。。。。
- @Override
- public void save1() {
- try {
- interceptor.intercept(this,save1,new Object[0],saveSuper1);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void save2(int i) {
- try {
- interceptor.intercept(this,save2,new Object[]{10},saveSuper2);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- }
-
-
- @Override
- public void save3(long j) {
- try {
- interceptor.intercept(this,save3,new Object[]{20L},saveSuper3);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- }
在主类中,使用
在调用methodProxy.invoke();methodProxy.invokeSuper() 时,又会产生一个新的代理类,从而避免反射:
生成的代理类的父类是FastClass类型:

简单来说,FastClass的作用:为代理类中的每个方法生成一个索引,并将方法的调用逻辑封装在一个数组中。当代理类的方法被调用时,CGLIB 会根据方法的索引直接定位到对应的调用逻辑,从而避免了每次都要通过反射查找方法的开销。
所以FastClass是CGLIB避免反射的关键。
下面同样通过模拟FastClass实现类的方式进行分析,首先模拟结合目标使用的methodProxy.invoke(); 对应的FastClass实现类:
在public Object invoke(int index, Object target, Object[] args)方法中,没有使用反射的方式。
- /**
- * 模拟FastClass的子类实现,配合目标使用,对应 method.invoke
- */
- public class TargetFastClass {
-
- Signature s1 = new Signature("save1","()V");
- Signature s2 = new Signature("save2","(I)V");
- Signature s3 = new Signature("save3","(J)V");
-
- /**
- * 获取方法的编号
- * 执行时机: 代理类中 MethodProxy.create创建时
- * @param signature
- * @return
- */
- public int getIndex(Signature signature){
- if (s1.equals(signature)){
- return 0;
- }else if (s2.equals(signature)){
- return 1;
- }else if (s3.equals(signature)){
- return 2;
- }
- return -1;
- }
-
- /**
- * 执行编号对应的方法
- * 执行时机 调用method.invoke
- * @param index
- * @param target
- * @param args
- * @return
- */
- public Object invoke(int index, Object target, Object[] args){
- if (index == 0){
- //正常调用目标的方法,没有通过反射
- ((Target) target).save1();
- }else if (index == 1){
- ((Target) target).save2(((int) args[0]));
- }else if (index == 2){
- ((Target) target).save3(((long) args[0]));
- }
- return null;
- }
-
- }
然后模拟结合目标使用的methodProxy.invokeSuper(); 对应的FastClass实现类:
- /**
- * 模拟FastClass的子类实现,配合代理类使用,对应 methodProxy.invokeSuper
- * 生成时机:代理对象中MethodProxy.create的执行
- */
- public class ProxyFastClass {
-
- Signature s1 = new Signature("saveSuper1","()V");
- Signature s2 = new Signature("saveSuper2","(I)V");
- Signature s3 = new Signature("saveSuper3","(J)V");
-
- /**
- * 获取代理类中方法的编号(有三个增强方法,三个原始方法)
- * 获取的都是原始方法的编号
- * 执行时机: 代理类中 MethodProxy.create创建时
- * @param signature
- * @return
- */
- public int getIndex(Signature signature){
- if (s1.equals(signature)){
- return 0;
- }else if (s2.equals(signature)){
- return 1;
- }else if (s3.equals(signature)){
- return 2;
- }
- return -1;
- }
-
- /**
- * 执行编号对应的方法
- * 执行时机 调用method.invoke
- * @param index
- * @param proxy
- * @param args
- * @return
- */
- public Object invoke(int index, Object proxy, Object[] args){
- if (index == 0){
- //正常调用目标的方法,没有通过反射
- ((MockCglibProxy) proxy).saveSuper1();
- }else if (index == 1){
- ((MockCglibProxy) proxy).saveSuper2(((int) args[0]));
- }else if (index == 2){
- ((MockCglibProxy) proxy).saveSuper3(((long) args[0]));
- }
- return null;
- }
-
- public static void main(String[] args) {
- ProxyFastClass fastClass = new ProxyFastClass();
- int index = fastClass.getIndex(new Signature("saveSuper1","()V"));
- System.out.println(index);
- fastClass.invoke(index,new MockCglibProxy(),new Object[0]);
-
- }
- }
此时public int getIndex(Signature signature) 方法获取的是代理类中三个原始方法的序号。(代理类中一共有六个方法,三个原始方法,三个增强方法),原因是增强的逻辑在主类中已经通过.setInterceptor() 传入,不需要再次调用代理中的增强方法。并且如果再次调用代理中的增强方法,会发生interceptor.intercept() 循环调用的问题。
CGLIB与JDK动态代理性能上的区别在于,JDK动态代理是在达到一定的次数后取消反射机制,正常调用方法。而CGLIB运用methodProxy.invoke()和methodProxy.invokeSuper() 进行了优化,保证方法无需反射。
JDK调用一个方法就对应一个代理。而CGLIB是一个代理类对应两个FastClass(配合目标、配合代理),每个FastClass可以对应多个方法。