Springboot装配和注入(最全)及使用场景举例
Springboot装配和注入及使用场景举例,带你真正玩转Spring.......装配:创建bean,并加入IOC容器。注入:创建bean之间的依赖关系。
前言
Springboot装配和注入及使用场景举例,纯干货,带你真正玩转Springboot.......
装配:创建bean,并加入IOC容器。
注入:创建bean之间的依赖关系。
1.注入的几种方式:
- 通过构造函数注入
- 通过属性注入
- 通过Setter方法注入
- 通过@Configuration + @bean,成员方法注入
/**
* @author dlhe
* @date 2023-02-14 15:55
* @description 抽象bean接口
*/
public interface CompanyBean {
public Person getWirePerson();
}
1.1构造器注入:
/**
* @author dlhe
* @date 2023-02-14 15:31
* @description 通过构造函数注入
*/
@Component("companyWireByConstruct")
public class CompanyWireByConstruct implements CompanyBean{
private final Person person;
// 此处可以省略 @Autowired,因为创建bean时会执行构造器
CompanyWireByConstruct(Person person){
this.person =person;
}
@Override
public Person getWirePerson() {
return this.person;
}
}
1.2 属性注入
/**
* @author dlhe
* @date 2023-02-14 15:31
* @description 通过属性注入
*/
@Component("companyWireByProperty")
public class CompanyWireByProperty implements CompanyBean{
@Autowired
private Person person;
@Override
public Person getWirePerson() {
return this.person;
}
}
1.3 Setter方法注入
/**
* @author dlhe
* @date 2023-02-14 15:31
* @description 通过Setter注入
*/
@Component("companyWireBySetter")
public class CompanyWireBySetter implements CompanyBean{
private Person person;
@Autowired
public void setPerson(Person person) {
this.person = person;
}
@Override
public Person getWirePerson() {
return this.person;
}
}
1.4 @Bean方式:
/**
* @author dlhe
* @date 2023-02-14 15:24
* @description springboot中 @Configuration + @bean,成员方法注入
*/
@Configuration
public class CompanyConfig {
@Bean("companyConfigBean")
public CompanyBean company(Person person){
CompanyWireBySetter company =new CompanyWireBySetter();
company.setPerson(person);
return company;
}
}
测试类:
/**
* @author dlhe
* @date 2023-02-14 16:19
* @description
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("companyWireByConstruct")
CompanyBean companyWireByConstruct;
@Autowired
@Qualifier("companyWireBySetter")
CompanyBean companyWireBySetter;
@Autowired
@Qualifier("companyWireByProperty")
CompanyBean companyWireByProperty;
@Autowired
@Qualifier("companyConfigBean")
CompanyBean companyConfigBean;
@Test
public void testCompany(){
System.out.println(companyWireByConstruct.getWirePerson().toString());
}
@Test
public void testCompany1(){
System.out.println(companyWireBySetter.getWirePerson().toString());
}
@Test
public void testCompany2(){
System.out.println(companyWireByProperty.getWirePerson().toString());
}
@Test
public void testCompany3(){
System.out.println(companyConfigBean.getWirePerson().toString());
}
}
2.装配的几种方式:
- xml方式定义
- 扫描包+模式注解--》@componetScan + @componet,@service,@comtroller,@resource,@resposity....
- @Configuration + @Bean ,即方式一的注解方式。
- @EnableConfigurationProperties + @ConfigurationProperties 一般用来装配配置文件。
- @import导入配置类或普通类,多个用逗号隔开
- 实现FactoryBean接口动态构建一个bean(OpenFeign就是基于此实现)
- 实现ImportBeanDefinitionRegistrar接口,批量注册bean(扫描包下的带注解的),Springboot的启动类注解有用到。
- 在resources里面下,新建META-INF/spring.factories 文件里,申明被自动装配的configration,作为jar给别人依赖时会被装配进容器。
- @import + ImportSelector接口,启动类加上自定义@EnableXXX 注解,作为jar给别人依赖时会被装配进容器。
2.1 @import举例:
//如下Man会装配进来并注册
@Import(Man.class)
@Configuration("manImport")
public class ManImport implements ManBean {
Man man;
ManImport(Man man){
this.man = man;
}
@Override
public Man getWireMan() {
return this.man;
}
}
2.2 FactoryBean动态构建bean举例:
场景举例:输入日志接口:分为控制台(console)和日志管理器(Logger)两类,现若只调用日志接口打印方法,如何按日志的级别(方法参数)自动选择输入器输出?
或者进一步,若日志级别0,则用日志管理器类,并按排序的优先级
解决方案:
// 日志父接口
public interface ISpi<T> {
//满足条件
boolean verify(T condition);
/**
* 排序,数字越小,优先级越高
* @return
*/
default int order() {
return 10;
}
}
日志接口和日志实现类:
public interface IPrint extends ISpi<Integer> {
default void execute(Integer level, Object... msg) {
print(msg.length > 0 ? (String) msg[0] : null);
}
void print(String msg);
}
@Component
public class ConsolePrint implements IPrint {
@Override
public void print(String msg) {
System.out.println("console print: " + msg);
}
@Override
public boolean verify(Integer condition) {
return condition <= 0;
}
}
@Slf4j
@Component
public class LogPrint implements IPrint {
@Override
public void print(String msg) {
log.info("log print: {}", msg);
}
@Override
public boolean verify(Integer condition) {
return condition > 0;
}
}
重点:代理对象(FactoryBean实现类)
public class SpiFactoryBean<T> implements FactoryBean<T> {
private Class<? extends ISpi> spiClz;
private List<ISpi> list;
public SpiFactoryBean(ApplicationContext applicationContext, Class<? extends ISpi> clz) {
this.spiClz = clz;
Map<String, ? extends ISpi> map = applicationContext.getBeansOfType(spiClz);
list = new ArrayList<>(map.values());
list.sort(Comparator.comparingInt(ISpi::order));
}
@Override
@SuppressWarnings("unchecked")
public T getObject() throws Exception {
// jdk动态代理类生成
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for (ISpi spi : list) {
if (spi.verify(args[0])) {
// 第一个参数作为条件选择
return method.invoke(spi, args);
}
}
throw new NoSpiChooseException("no spi server can execute! spiList: " + list);
}
};
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{spiClz},
invocationHandler);
}
@Override
public Class<?> getObjectType() {
return spiClz;
}
}
代理对象的装配和注入:
@Configuration
public class PrintAutoConfig {
/*
* 此处注入的是applicationContext上下文,来创建代理对象
*/
@Bean
public SpiFactoryBean printSpiPoxy(ApplicationContext applicationContext) {
return new SpiFactoryBean(applicationContext, IPrint.class);
}
/*
* 注入的是代理对象,创建的是代理实例
*/
@Bean
@Primary
public IPrint printProxy(SpiFactoryBean spiFactoryBean) throws Exception {
return (IPrint) spiFactoryBean.getObject();
}
}
2.3 ImportBeanDefinitionRegistrar批量注册bean
场景:把某个包下带某个注解的类都加到容器
1.定义扫描注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Savey {
}
2.定义启动类注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ImportCustomBeanDefinitionRegistrar.class)
public @interface EnableCustomBean {
/**
* 定义要扫描的包
*/
String[] basePackages() default {};
}
3.生效启动类注解:
@SpringBootApplication
@EnableCustomBean(basePackages = {"com.boot.boot.savey"})
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
4.创建目录及目标bean:
创建com.boot.boot.savey目录,在目录下创建一些要自定义Bean,并给类加上 Savey注解!!
@Savey
@Getter
@Setter
@ToString
public class Money {}
@Savey
@Getter
@Setter
@ToString
public class Work {}
//这个类就不加Savey注解了,不想它加入到容器,不想玩游戏,只想工作!所以不加了!
@Getter
@Setter
@ToString
public class PlayGame {}
5.实现ImportBeanDefinitionRegistrar接口:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取EnableCustoBean注释的属性
final Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableCustomBean.class.getName());
//获取包扫描
ClassPathScanningCandidateComponentProvider pathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);
//添加过滤 带有Savey这个注解的类
pathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Savey.class));
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
for (String basePackages : (String[]) attributes.get("basePackages")) {
candidateComponents.addAll(pathScanningCandidateComponentProvider.findCandidateComponents(basePackages));
}
//注册Bean
for (BeanDefinition candidateComponent : candidateComponents) {
registry.registerBeanDefinition(candidateComponent.getBeanClassName(), candidateComponent);
}
}
6.test
@SpringBootTest
public class ImportCustomScanPackagesDefinitionRegisterTest {
@Resource
ApplicationContext applicationContext;
@Test
public void test_scan_packages() {
//返回容器中所有bean
Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
}
}
-------
输出
-------
com.boot.boot.savey.Money
com.boot.boot.savey.Work
spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.dlhe.config.PersonConfig
2.4 @import + ImportSelector接口
1.定义configration:
@Configuration
@EnableConfigurationProperties(Person.class)
public class PersonConfig implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println(".......created PersonConfig");
}
}
2.实现ImportSelector,指定装配的configration
ublic class MyWireSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{PersonConfig.class.getName()};
}
}
3.定义启动注解:
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
//导入目标Seletor
@Import(MyWireSelector.class)
public @interface EnableOutWireBean {
}
4.注解生效:(外部项目启动类中)
@SpringBootApplication
@EnableOutWireBean
public class Demo1Applicaiotn {
public static void main(String[] args) {
SpringApplication.run(Demo1Applicaiotn.class);
}
}
更多推荐
所有评论(0)