Spring源码(3)Aware接口、初始化和销毁方法、@Scope、@Primary、动态代理

1、目标

本文的主要目标是学习Spring源码中Aware接口、初始化和销毁方法、@Scope注解、@Primary注解的使用

2、Aware接口

@Component
public class MyBeanAware implements BeanNameAware, ApplicationContextAware {
    @Override
    public void setBeanName(String name) {
        System.out.println("beanName = " + name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("applicationContext = " + applicationContext);
    }
}

实现BeanNameAware接口,可以重写setBeanName方法
实现ApplicationContextAware接口,可以重写setApplicationContext方法

在这里插入图片描述

3、初始化和销毁方法

初始化方法有3种方式,依次的顺序是@PostConstruct注解指定初始化方法、实现InitializingBean接口并重写afterPropertiesSet方法、@Bean注解上指定initMethod=xxx

销毁方法有3种方式,依次的顺序是@PreDestroy注解指定销毁方法、实现DisposableBean接口并重写destroy方法、@Bean注解上指定destroyMethod=xxx

4、@Scope注解

Scope有单例singleton、多例prototype、request、session、application

单例对象中包含多例对象的属性,多次获取这个属性是是单例的吗还是多例?

结论:还是单例的,因为只会依赖注入1次多例对象

@Component
public class A {
    @Autowired
    private B b;
    public B getB() {
        return b;
    }
}

A对象是单例的

@Scope(value = "prototype")
@Component
public class B {
}

B对象设置多例的,即@Scope(value=“prototype”)

@SpringBootApplication
public class Demo02Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo02Application.class);
        A a = applicationContext.getBean("a", A.class);
        System.out.println(a.getB().getClass());
        System.out.println(a.getB());
        System.out.println(a.getB());
        System.out.println(a.getB());
    }
}

多个获取A对象中的属性B对象,A对象是单例对象,B对象是多例的,但是B对象只会依赖注入1次,因此多次获取B对象仍然是同一个对象

在这里插入图片描述

多次获取的B对象是同一个对象,说明只会依赖注入1次

如果希望属性是多例对象的话,多次获取可以得到多个不同的对象怎么办?

① @Lazy注解:@Lazy注解会创建属性的CGLIB动态代理对象,因此每次获取这个多例对象的时候动态代理对象会创建一个新的对象

@Component
public class A {
    @Lazy
    @Autowired
    private B b;
    public B getB() {
        return b;
    }
}

A对象是单例对象,依赖注入的B对象是多例对象,因此用@Lazy注解进行懒加载,会创建一个CGLIB动态代理对象,每次获取实际的B对象会创建一个新的B对象

在这里插入图片描述

B对象是CGLIB动态代理对象,每次获取B对象都会创建一个新的B对象

② 在被代理对象上的@Scope注解上添加proxyMode是TARGET_CLASS,表示会使用CGLIB动态代理对象创建一个新的对象

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class B {
}

被代理对象B对象指定proxyMode=ScopedProxyMode.TARGET_CLASS

在这里插入图片描述

创建了CGLIB动态代理对象,每次获取B对象都会创建一个新的B对象

③ ObjectFactory工厂的getObject方法可以创建多个对象

@Component
public class A {
    @Autowired
    private ObjectFactory<B> b;
    public B getB() {
        return b.getObject();
    }
}

依赖注入ObjectFactory工厂对象,通过getObject方法可以创建B对象

在这里插入图片描述

创建CGLIB动态代理对象,多次获取B对象可以得到多个B对象

④ ApplicationContext容器的getBean方法可以获取多例对象的多个对象

@Component
public class A {
    @Autowired
    private ApplicationContext applicationContext;
    public B getB() {
        return applicationContext.getBean(B.class);
    }
}

依赖注入容器对象ApplicationContext对象,通过getBean方法多次获取B对象

在这里插入图片描述

创建CGLIB动态代理对象,每次获取B对象可以创建一个新的B对象

5、@Primary注解

@Primary 是 Spring Framework 中的一个注解,用于标记某个 Bean 作为候选 Bean 时的首选项。它的主要作用是在存在多个符合条件的 Bean 时,指定一个 Bean 为首选 Bean

如果只有一个 Bean 实现了接口或类,Spring 会自动选择这个 Bean,而不需要 @Primary

如果没有 @Primary 注解,并且有多个候选 Bean,那么在自动注入时会导致NoUniqueBeanDefinitionException 异常,Spring 会提示有多个 Bean 可用

public interface PaymentService {
    void processPayment();
}

@Component
public class CreditCardPaymentService implements PaymentService {
    @Override
    public void processPayment() {
        // Credit Card payment processing
    }
}

@Component
@Primary
public class PayPalPaymentService implements PaymentService {
    @Override
    public void processPayment() {
        // PayPal payment processing
    }
}

PaymentProcessor 会自动注入 PayPalPaymentService,因为它被标记为 @Primary

@Service
@RequireArgsConstructor
public class PaymentProcessor {
    private final PaymentService paymentService;

    @Autowired
    public PaymentProcessor(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void process() {
        paymentService.processPayment();
    }
}

PaymentProcessor 会自动注入 PayPalPaymentService,因为它被标记为 @Primary

6、动态代理

6.1 JDK动态代理

public class Demo02Test {
    public interface Foo {
        public void work();
    }

    public class Target implements Foo {
        @Override
        public void work() {
            System.out.println("target work");
        }
    }

    @Test
    public void f1() {
        Target target = new Target();
        ClassLoader classLoader = Demo02Test.class.getClassLoader();
        // JDK动态代理对象可以转换成接口,因为JDK动态代理是基于接口的,Target目标类和代理类是兄弟关系,它们都实现了Foo接口
        Foo foo = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, (proxy, method, args) -> {
            System.out.println("before invoke");
            // 方法.invoke(对象, 参数)
            Object res = method.invoke(target, args);
            System.out.println("after invoke");
            return res;
        });
        foo.work();
    }
}

通过Proxy.newProxyInstance方法可以创建JDK动态代理类,JDK动态代理对象可以转换成接口,因为JDK动态代理是基于接口的,Target目标类和代理类是兄弟关系,它们都实现了Foo接口

如果目标类Target类是final类型的,也可以,因为Target目标类和代理类是兄弟关系

在这里插入图片描述

调用JDK动态代理对象的方法

6.2 CGLIB动态代理

public class Demo03Test {
    public static class Target {
        public void work() {
            System.out.println("target work");
        }
    }

    @Test
    public void f1() {
        Target target = new Target();
        // Target是父类,代理类是子类,代理类继承了Target类,因此Target类不能是final类型的,它的方法也不能是final类型的
        // 使用MethodInterceptor类作为CallBack的子类
        Target targetProxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before target");
                // 使用反射调用
                //Object res = method.invoke(target, args);
                // 没有使用反射调用
                Object res = methodProxy.invoke(target, args);
                System.out.println("after target");
                return res;
            }
        });
        targetProxy.work();
    }
}

创建一个静态类Target,通过Enhancer.create方法可以创建CGLIB动态代理对象,第一个参数是父类Target,第二个参数用Callback的子类MethodInterceptor,重写intercept方法,method.invoke是使用反射调用,methodProxy.invoke没有使用反射调用,代理类是Target类的子类

在这里插入图片描述

调用CGLIB动态代理对象的方法

 public static final class Target {
        public void work() {
            System.out.println("target work");
        }
    }

如果目标类Target类是final类型的,则表示不能继承目标类,而CGLIB动态代理对象的类是继承了目标类,因此会报错

在这里插入图片描述

因此,CGLIB动态代理对象的目标类不能是final类型的,因为CGLIB动态代理类继承了目标类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值