JAR-IOC-DI

搓炸包

先弄一个空的Maven项目

  • 注意JDK版本,项目中的有些API不支持过旧的或者过新的Java版本
  • 骨架选择quickStart,理论上其他骨架也能行,但是咸鱼只试过这一个骨架

在空项目中编辑配置文件

其他的已经搓腻了,这次搓炸包注意build标签中的内容:

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.3.0</version>
        <configuration>
          <archive>
            <manifestEntries>
                <!-- 此处写主类的全限定类名 -->
              <Main-Class>club.saltfish.App</Main-Class> 
            </manifestEntries>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>

这样的配置可以配合Maven构建一个炸包,在有Java环境的计算机中双击炸包可以从主类开始运行。

另附把Jar包安装到本地Maven仓库的指令:

mvn install:install-file -Dfile=炸包路径.jar -DgroupId=club.saltfish -DartifactId=组件名 -Dversion=1.0 -Dpackaging=jar

导入Maven项目中

按照常规的导入项目依赖的方式就可以了

<dependency>
    <groupId>club.saltfish</groupId>
    <artifactId>common-pojo</artifactId>
    <version>1.0</version>
</dependency>

一般写完artifactID之后,其余配置项都自动补充了。

将炸包中的对象注入到容器中

首先,依赖中的Jar包都是只读属性(不允许编辑),所以给依赖中的Jar包添加@Component之类的注解来完成注入是不现实的。

玩法一:@Bean

假设一个炸包中有如下结构:

└─club └─saltfish ├─config │ CommonConfig.java │ └─pojo Country.java Province.java

那么在一个配置类(@Configuration)中,写一个调用构造器的方法:

    @Bean
    public Country country(){
        return  new Country(); ;
    }

就可以利用一个@Bean注解把这个对象丢进IOC容器里,在容器中,这个对象的引用是country,即方法名。

配置类必须在启动类所在包及其子包中。

@Bean注解中课添加参数:该类在容器中的名称。

依赖注入

这个简单,只需要在方法形参中添加对应的对象就行。

//对象默认的名字是: 方法名
//@Bean("aa")
//如果方法的内部需要使用到ioc容器中已经存在的bean对象,那么只需要在方法上声明即可,spring会自动的注入
@Bean
public Province province(Country country){
    return new Province();
}

同理,我们可以在调用构造器的时候直接给它初始化掉,或者注入依赖:

@Bean
//假设我们已经在IOC容器中注入了一个User对象
public Country Country(User user){
    Country country = new Country;
    country.setName("China");//初始化成员
    country.setLocation("Asia");//初始化成员
    country.setUser(user);//依赖注入
    return province;
}

甚至可以在application配置文件中(以yml为例)配置一个类的成员属性值:

country:
 name: China
 location: Aisa
 user:
  name: saltfish
  age: 90
  health: dying

在注入的时候就需要:

@Configuration
@ConfigurationProperties(prefix = "country")
public class Commonconfig {
    @Bean
    //假设我们已经在IOC容器中注入了一个User对象
    public Country country(){
        Country country = new Country(@Value("${name}")String name,@Value("${location}")String location,User user);
        //初始化User略,也是@Value
        return new Country(name,value,user)
    }
}

用配置初始化普通类,同理。

玩法二:@import

在启动类上添加注解@Import(配置类.class),即可注入配置类中注入的对象。

@Import('Configuration.class')
@SpringBootApplication
public class LearnMybatisApplication {

    public static void main(String[] args) {
        SpringApplication.run(LearnMybatisApplication.class, args);
    }

}

路径从根包开始数。

也可以一次性传入很多个配置类(配置类数组):

@Import({Configuration1.class,Configuration2.class,Configuration3.class,})
@SpringBootApplication
public class LearnMybatisApplication {

    public static void main(String[] args) {
        SpringApplication.run(LearnMybatisApplication.class, args);
    }
}

但是这样并不优雅简洁。我们可以使用另一个类专门负责统筹配置类:

使用ImportSelector接口

public class CommonImportSelector implements ImportSelector {
    @Override//重写方法,
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //返回的字符串的值是配置类的全限定类名
        return new String[]{"club.saltfish.config.Configuration1.class","club.saltfish.config.Configuration2.class","club.saltfish.config.Configuration3.class",}
    }

然后在启动类中:

@Import(CommonImportSelector.class)
@SpringBootApplication
public class LearnMybatisApplication {

    public static void main(String[] args) {
        SpringApplication.run(LearnMybatisApplication.class, args);
    }
}

读取配置文件注入

先写一个配置文件:

club.saltfish.config.Commonfig

这个配置文件每一行只写一个类的全限定类名。

这个配置文件是任意格式的文本,需要用Stream读取:

public class CommonImportSelector implements ImportSelector {
    @Override//重写方法,
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //读取配置文件的内容
        List<String> imports = new ArrayList<>();
        InputStream is = CommonImportSelector.class.getClassLoader().getResourceAsStream("上述配置文件路径");//开一个流读取
        BufferedReader br = new BufferedReader(new InputStreamReader(is));//buffer封装
        String line = null;
        while((line = br.readLine())!=null){
            imports.add(line);//循环读取每一行,即每个类
        }
        is.close();
        return imports.toArray(new String[0]);
    }

其中Stream读取文件可能引发异常,为保证代码简洁容易理解,不做异常处理,请自行try-catch。

组合技:自己搓注解

自定义注解:

新建一个注解@Interface),添加两个源注解:

@Target(ElementType.TYPE)//源注解一:表示定义的注解是类级注解
@Retention(RetentionPolicy.RUNTIME)//源注解二:表示注解会保留到运行时
@Import(CommonImportSelector.class)//之前的注解:导入Selector
public @interface EnableCommonConfig {
    
}

接口中不用写任何东西,直接在启动类中添加自己做的注解:

@SpringBootApplication
@EnableCommonConfig
@ComponentScan("club.saltfish")
public class SpringbootRegisterApplication {
    public static void main(String[] args){
        SpringApplication.run(SpringbootRegisterApplication.class, args);
    }

}

就完事了。

感觉自定义注解能干大事,但是才疏学浅,压根不会。浅浅找了些资料去了解如何能写一些代码,去操作注解过的对象或者方法。似乎是需要用到Java的反射机制,能在运行时(RUNTIME)动态的“玩弄”对象。但是看起来是个很深奥、底层、复杂的知识系统,先不打算拿下它。留待彻底玩明白Spring系列后再来淦它。