020:Spring核心注解
1 常用注解源码分析课程安排
课程内容:
1、使用@Condition多条件注册bean对象
2、@Import注解快速注入第三方bean对象
3、深度分析SpringBoot EnableXXX注册底层实现原理
4、基于ImportBeanDefinitionRegistrar注册bean
5、基于FactoryBean注册bean对象
6、FactoryBean与BeanFactory之间的区别
为什么@Enablexxx能够自动注入bean的对象?
通过@Import注解进行整合。
FactoryBean与BeanFactory有什么区别?
FactoryBean主要是做注册对象,BeanFactory主要是做获取对象。
2 @Condition条件注册bean使用
@Condition
Condition 是在spring4.0 增加的条件注解,通过这个可以功能可以实现选择性的注入Bean操作。
实现案例
在Spring容器加载中,如果当前环境是WIN10操作系统就装配win10实体类、其他系统就不装配。
实体类
public class Win10Entity {
// 只有在当前运行到win10环境,才会注入到spring ioc容器中
}
配置类
@Configuration
public class MyConfig {
@Bean
@Conditional({MyCondition.class})
public Win10Entity win10Entity(){
return new Win10Entity();
}
}
Condition配置
public class MyCondition implements Condition {
/**
*
* @param conditionContext 获取到当前的上下文
* @param annotatedTypeMetadata 获取当前注解的信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if ("Windows 10".equals(property)) {
return true;
}
return false;
// return true 可以注册改对象
// return false 不能注册改对象
}
}
测试类
public class V1Spring {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Win10Entity win10Entity = (Win10Entity) applicationContext.getBean("win10Entity");
System.out.println(win10Entity); //com.mayikt.v1.entity.Win10Entity@12405818
}
}
修改代码Conditoin类控制条件
public class MyCondition implements Condition {
/**
*
* @param conditionContext 获取到当前的上下文
* @param annotatedTypeMetadata 获取当前注解的信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if ("Windows 10".equals(property)) {
return false;
}
return true;
// return true 可以注册改对象
// return false 不能注册改对象
}
}
运行结果:
3 @Import注解与@Bean注解区别
为什么要使用@Import注解?
@Bean应用场景:注册加载外部jar包,代码冗余。
@Import注解
@Import是用来整合所有在@Configuration注解中定义的bean配置;
默认注册beanid为 全路径地址
实验案例
配置类
@Configuration
@Import(Win10Entity.class)
public class MyConfig {
// Import作用主要是将外部的jar包注入到springioc容器中 @Import(Win10Entity.class)等同于@Bean
// Import注册的bean对象 id为当前类全路径
}
启动类
public class V1Spring {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Win10Entity win10Entity = (Win10Entity) applicationContext.getBean("com.mayikt.v1.entity.Win10Entity");
System.out.println(win10Entity);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for(String i : beanDefinitionNames){
System.out.println(i);
}
}
}
运行结果:
总结:@Import注解与@Bean注解区别
Bean注解注册的bean的id是以方法名称 @Import以当前类完整路径地址注册,相对来说@Import注入类更加方便
共同应用场景:都是引入外部的jar包
4 @EnableXXX功能性注解实现原理
需要注入的bean
public class PayEntity {
}
public class MyPayEntity {
}
EnablePayEntity注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({PayEntity.class, MyPayEntity.class})
public @interface EnablePayEntity {
// 只要在启动的时候加入该EnablePayEntity 就会将PayEntity实体类注入到spring ioc容器
// 使用Enable注解的话,底层实际上在调用@Import(PayEntity.class)
// 内部开发一个框架,目前不支持SpringBoot,可以使用Enablexxx注入
}
配置类
@Configuration
@EnablePayEntity
public class MyConfig {
}
运行结果:
5 使用实现ImportSelector注册bean
ImportSelector
ImportSelector接口是至spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在
实验案例
需要注入的bean
public class MemberEntity {
}
public class MsEntity {
}
MyImportSelector
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.mayikt.v1.entity.MemberEntity","com.mayikt.v1.entity.MsEntity"};
}
}
配置类
@Configuration
@Import(MyImportSelector.class)
public class MyConfig {
}
运行结果:
6 使用ImportBeanDefinitionRegistrar注册bean
ImportBeanDefinitionRegistrar手动注册bean
实验案例
需要注入的bean
public class SmsEntity {
}
ImportBeanDefinitionRegistrar手动注册
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param annotationMetadata 注解的信息
* @param beanDefinitionRegistry
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// spring容器中 bean的信息都是有BeanDefinition描述 手动注册到ioc容器中
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(SmsEntity.class);
beanDefinitionRegistry.registerBeanDefinition("smsEntity",rootBeanDefinition);
// spring ioc 底层通过beanDefinitionMap存放 线程是安全的
}
}
配置类 或者MyImportBeanDefinitionRegistrar类上加注解@Component注入
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class MyConfig {
}
运行结果:
7 使用FactoryBean注册对象
FactoryBean与BeanFactory区别
FactoryBean往ioc容器中存储对象 注入对象
BeanFactory从ioc工厂中获取bean对象
BeanFactory是个Factory,也就是IOC容器或对象工厂,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。
对FactoryBean而言,首先它是个Bean,但这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
实验案例
需要注入的bean
public class MyEntity {
}
MyFactoryBean
public class MyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new MyEntity();
}
@Override
public Class<?> getObjectType() {
return MyEntity.class;
}
// 往IOC容器中注入对象
@Override
public boolean isSingleton() {
// 默认情况为true,为单例;false为多例
return true;
}
}
配置类
@Configuration
public class MyConfig {
@Bean
public MyFactoryBean myFactoryBean(){
return new MyFactoryBean();
}
}
测试类
public class V1Spring {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
MyEntity myFactoryBean1 = (MyEntity) applicationContext.getBean("myFactoryBean");
MyEntity myFactoryBean2 = (MyEntity) applicationContext.getBean("myFactoryBean");
System.out.println(myFactoryBean1 == myFactoryBean2); //true
}
}
8 @Service与@Compent注解区别
@Service、@Repository等注入对象底层还是使用@Component进行注入,只是为了更好的区分应用场景,注意要加上扫包范围
实验案例 编写类似@Service功能的注解
自定义注解@Mayikt
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Mayikt {
}
需要注入的bean
@Mayikt
public class MayiktEntity {
}
配置类
@Configuration
@ComponentScan("com.mayikt.v1.entity")
public class MyConfig {
}
运行结果:
9 @Primary与@Qualifier区别
一个接口下有两个实现类,如果用@Autowired进行获取
public interface UserService {
public void add();
}
@Component
public class UserServiceImpl01 implements UserService {
@Override
public void add() {
System.out.println("UserServiceImpl01");
}
}
@Component
public class UserServiceImpl02 implements UserService {
@Override
public void add() {
System.out.println("UserServiceImpl02");
}
}
引用类
@Component
public class OrderService {
@Autowired
private UserService userService;
public void add() {
userService.add();
}
}
配置类
@Configuration
@ComponentScan(value="com.mayikt")
public class MyConfig {
}
启动类
public class V1Spring {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
OrderService orderService = (OrderService) applicationContext.getBean("orderService");
orderService.add();
}
}
运行结果:
因为@Autowired默认的情况下使用类型查找
3种方案默认使用UserServiceImpl02类:
- @Resource设置根据名称进行查找
@Component
public class OrderService {
@Autowired
@Resource(name = "userServiceImpl02")
private UserService userService;
public void add() {
userService.add();
}
}
- @Qualifier根据名称进行查找
@Component
public class OrderService {
@Autowired
@Qualifier("userServiceImpl02")
// @Resource(name = "userServiceImpl02")
private UserService userService;
public void add() {
userService.add();
}
}
- @Primary设置默认类 类似SpringBoot多数据源 设置默认的数据源(优先级)
@Component
@Primary
public class UserServiceImpl02 implements UserService {
@Override
public void add() {
System.out.println("UserServiceImpl02");
}
}