目的是 在pom.xml中未加入某个依赖时(即某个坐标未加载时)我们通过自定义注解的方式让某个对象的实例Bean不被创建
注意:
- 这个并非必须掌握,是老师在演示SpringBoot如何自动识别pom.xml中的依赖并自动生成bean的
- 此自定义注解由原始的非动态自定义注解改进,动态的添加类来实现自定义注解
代码结构:
使用Spring Initializer项目,按顺序创建
domain.User
package com.lwz.springboot_autoconfigure_confidition01.domain;
public class User {
}
config.UserConfig
package com.lwz.springboot_autoconfigure_confidition01.config;
import com.lwz.springboot_autoconfigure_confidition01.condition.ConditionOnClass;
import com.lwz.springboot_autoconfigure_confidition01.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfig {
// @Conditional(ClassCondition.class) //根据源码分析,返回true user的Bean将会被创建 反之false user的Bean将不会被创建
// /*
// 可以发现 @Conditional 这个注解的value是指 <? extends Condition> 所有继承Condition的类的类型
// 于是在 @Condition中 有一个 matches()方法,用来返回是true 还是 false 作用是上面那句话 //......
// 但是现在的意思就是我这个@Conditional()中可以传入 一个继承Condition的类,该类中可以重写matches()方法
// 用来diy你想满足如何的条件,然后返回一个boolean的值,从而控制 某个坐标存在 时是否要创建该坐标对应类的bean
// */
// /*
// 也就是说通过@Conditional里的类重写的方法来决定该注解处的对象是否可以被生成Bean实例
// */
//创建一个Bean
@Bean
@ConditionOnClass({"redis.clients.jedis.Jedis","com.alibaba.fastjson.JSON"}) //多坐标引入,有一个坐标不存在即返回
//false,不创建Bean
// @ConditionOnClass("com.alibaba.fastjson.JSON") //单一的坐标引入
public User user(){
return new User();
}
}
condition.ConditionOnClass
package com.lwz.springboot_autoconfigure_confidition01.condition;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
String[] value();
}
Condition.ClassCondition
package com.lwz.springboot_autoconfigure_confidition01.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;
import java.util.Map;
public class ClassCondition implements Condition {
/**
*
* @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param metadata 注解元对象。可以获取注解定义的属性值
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
/*
在Spring的IOC容器中有一个User的 Bean,现要求:
1.导入Jedis坐标后,加载该Bean,没导入,则不加载。
*/
//有Condition01 改进的 不再将forName属性里的值 (即全类名)写死
// 2.需求:导入通过注解的属性值value指定坐标后创建Bean
//思路:判断 全类名.class 的文件是否存在
//获取注解的属性值 value
Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
System.out.println(map);
String[] value = (String[]) map.get("value");
boolean flag = true;
try {
for (String classname : value) {
Class<?> cls = Class.forName(classname);
}
} catch (ClassNotFoundException e) {
flag = false;
}
return flag;
}
}
自我描述:
1.自己创建一个类User,即之后我们所要使用的对象
2.然后创建一个UserConfig的配置类,标注@Configuration,此类用来生成Bean
3.然后创建ClassCondition类用来继承父类Condition并重写其matches方法,旨在之后可以自定义方法返回true/false
4.创建ConditionOnClass自定义注解,必须要加上@Conditional里的元注解(访问快捷键 鼠标中键),元注解旨在和该ConditionOnClass上标注的@Conditional的元注解保持一致,否则运行时会报错,@Conditional中需要加入你的ClassCondition.class 为啥只能加这个 这时可以查看@Conditional的内容,规定value的泛型只能为继承Condition的子类(说人话就是,这个子类中的重写matches的方法就是为了能让你DIY自己的方法以来满足生成所需要的true或false,然后控制Config类中标识该注解的Bean的生成)
5.在ConditionOnClass类中也可发现定义的value是一个String数组,在Configure中使用时,括号中可以填入多个全类名{“…”,“…”},然后需要在ClassCondition类中使用 metadata(注解元对象)获取注解定义的属性值,这里我们便需要从config的@ConditionOnClass注解中利用getName()方法获取其属性值 比如该例子:{value=[redis.clients.jedis.Jedis, com.alibaba.fastjson.JSON]} 然后通过key值“value”来获取map的值,之后强转成数组,利用forEach循环和反射机制,循环判断该数组中(即pom.xml中)有无对应的坐标依赖导入以此来控制true或false的输出,然后控制Config类中标识该注解的Bean的生成,这便是动态的添加全类名以判断pom.xml坐标中是否加入对应的依赖,这也是SpringBoot通过pom.xml判断是都存在相关依赖来加载相应的Bean