SpringDay(02)

16 篇文章 1 订阅

依赖注入

什么是依赖注入

就是在Spring容器内容将各个对象的依赖关系建立好的操作

最终的效果是从Spring容器中获得的对象是包含了被依赖对象的

(最终的效果是从Spring容器中获得的关羽是手拿青龙偃月刀的!)

为什么需要依赖注入

如果不使用依赖注入,我们需要从Spring容器中获得相应对象,再通过编写代码建立依赖关系

这样就会有代码的冗余,多个依赖关系的确定会造成代码臃肿

怎么使用依赖注入

@Bean
public Hero guanYu(){
    Hero h=new Hero();
    h.setName("关羽");
    h.setAge(25);
    return h;
}

上面的注入关羽是没有青龙偃月刀的

测试输出结果:关羽使用null战斗

要想在这个方法中为关羽赋值青龙偃月刀需要编写的代码如下

注意!!!删除DragonBlade类上本来有的组件扫描注解,否则可能导致异常!!!

@ComponentScan("cn.tedu.hero")
public class Config {

    @Bean
    public DragonBlade blade(){
        return new DragonBlade();
    }
    @Bean
    //方法的参数为DragonBlade类型
    //这个参数编写后,spring容器会自动从当前容器中所有内容中搜索
    //只要有DragonBlade类型的对象就会自动赋值到这个参数中
    public Hero guanYu(DragonBlade blade){
        Hero h=new Hero();
        h.setName("关羽");
        h.setAge(25);
        h.setDragonBlade(blade);
        return h;
    }
}

依赖注入注意事项

如果现在程序中有两把青龙偃月刀

    @Bean
    public DragonBlade blade1(){
        return new DragonBlade();
    }
    @Bean
    public DragonBlade blade2(){
        return new DragonBlade();
    }
    @Bean
    //方法的参数为DragonBlade类型
    //这个参数编写后,spring容器会自动从当前容器中所有内容中搜索
    //只要有DragonBlade类型的对象就会自动赋值到这个参数中
    public Hero guanYu(DragonBlade blade){
        Hero h=new Hero();
        h.setName("关羽");
        h.setAge(25);
        h.setDragonBlade(blade);
        return h;
    }

在运行之前的测试代码后发生异常:

expected single matching bean but found 2: blade1,blade2

原因是Spring容器中有2把青龙偃月刀

Spring并不能决定将哪一把赋给关羽的方法中

所以发生了异常

解决方法时,注入关羽的方法的参数名,需要匹配其中一个青龙偃月刀的id

代码如下

@ComponentScan("cn.tedu.hero")
public class Config {

    //现在Spring容器中注入两把青龙偃月刀
    @Bean
    public DragonBlade blade1(){//id为blade1
        return new DragonBlade();
    }
    @Bean
    public DragonBlade blade2(){//id为blade2
        return new DragonBlade();
    }
    @Bean
    //方法的参数为DragonBlade类型
    //这个参数编写后,spring容器会自动从当前容器中所有内容中搜索
    //只要有DragonBlade类型的对象就会自动赋值到这个参数中
    //但是如果当前Spring容器中有两个或以上的DragonBlade对象
    //就需要按照DragonBlade对象的id来声明这个方法参数的属性名
    //如果方法参数的名称没有匹配任何Spring容器中的id,则会发生异常
    public Hero guanYu(DragonBlade blade2){
        Hero h=new Hero();
        h.setName("关羽");
        h.setAge(25);
        h.setDragonBlade(blade2);
        return h;
    }

}

没有类型匹配报异常

唯一类型匹配会成功

多个类型匹配看ID

组件扫描情况下依赖注入的实现

编写青龙偃月刀类

@Component
public class DragonBlade {

    private String name="青龙偃月刀";

    @Override
    public String toString() {
        return name;
    }
}

编写关羽类

注意使用@Autowired来自动装配需要的属性

@Component
public class GuanYu implements Serializable {

    private String name="关羽";
    //@Autowired英文翻译为自动装配
    //表示这个注解下面声明的属性Spring会自动将合适的类型的对象赋给它使用
    //当前Spring容器中包含唯一匹配DragonBlade类型的对象,那么它会自动赋值
    @Autowired
    private DragonBlade dragonBlade;

    public void fight(){
        System.out.println(name+"使用"+dragonBlade+"战斗");
    }
	// getset略    
}

配置类

@ComponentScan("cn.tedu.hero2")
public class Config {
}

最后进行测试

使用Set方法注入

在上面的组件扫描实现的依赖注入的代码中

实际上Spring还允许使用Set方法上添加@Autowired的方式来实现依赖注入

代码如下

@Component
public class GuanYu implements Serializable {

    private String name="关羽";
    //@Autowired英文翻译为自动装配
    //表示这个注解下面声明的属性Spring会自动将合适的类型的对象赋给它使用
    //当前Spring容器中包含唯一匹配DragonBlade类型的对象,那么它会自动赋值
    private DragonBlade dragonBlade;

    public void fight(){
        System.out.println(name+"使用"+dragonBlade+"战斗");
    }
	...//省略其它get\set

    public DragonBlade getDragonBlade() {
        return dragonBlade;
    }
	//Set方法上添加@Autowired注解也能实现依赖注入功能
    @Autowired
    public void setDragonBlade(DragonBlade dragonBlade) {
        this.dragonBlade = dragonBlade;
    }
}

Ioc\DI的解耦

什么是耦合?

所谓耦合就是两个有依赖关系的类

他们的依赖关系是定死的,比如关羽必须使用青龙偃月刀

这样的耦合会造成关羽无法使用其他武器战斗,这明显是不合理的

会使程序造成可维护性和可扩展性差的情况

什么是解耦

解耦就是解除两个类之间定死的依赖关系

我们的程序应该追求较低的耦合性,而形成低耦合程序非常重要的原则就是

形成依赖的属性声明为接口而不是具体类

集体解耦操作如下

步骤1:

声明一个接口

// 这是一个武器接口
public interface Weapon {

    //实现武器接口必须重写toString方法
    public String toString();

    //我们通过接口能够降低程序的耦合性
    //在依赖武器的对象中声明时,声明接口类型对象,而不声明具体类
}

步骤2:

青龙偃月刀实现这个接口

@Component
public class DragonBlade implements Weapon, Serializable {

    private String name="青龙偃月刀";

    @Override
    public String toString() {
        return name;
    }
}

步骤3:

在GuanYu类中原本依赖青龙偃月刀的代码

替换为依赖Weapon接口

@Component
public class GuanYu implements Serializable {

    private String name="关羽";
    //@Autowired英文翻译为自动装配
    //表示这个注解下面声明的属性Spring会自动将合适的类型的对象赋给它使用
    //当前Spring容器中包含唯一匹配DragonBlade类型的对象,那么它会自动赋值
    // 为了实现解耦,我们声明为了Weapon接口
    // 这时自动装配任何实现了Weapon接口的类对象
    @Autowired
    private Weapon weapon;
    //为了降低程序的耦合性,将原声明的具体类DragonBlade
    //修改为声明接口Weapon
    //private DragonBlade dragonBlade;

    public void fight(){
        System.out.println(name+"使用"+weapon+"战斗");
    }
	//省略get\set
}

测试运行

习题:

将我们之前编写的Person和Pen类解耦

建议创建一个接口WriteAble包含toString方法的声明

Pen类实现WriteAble接口

Person类中原文声明Pen的位置修改为声明WriteAble

并实现依赖注入测试运行

实现依赖注入对象的更换

之前关羽都是使用青龙偃月刀

现在我们新编写一个武器:方天画戟SkyLancer也实现Weapon接口

研究观察怎么样让关羽更换方天画戟战斗

定义方天画戟类

@Component
public class SkyLancer implements Weapon, Serializable {

    private String name="方天画戟";
    @Override
    public String toString() {
        return name;
    }
}

注释青龙偃月刀的@Component注解

//@Component   一旦注释这个类对象就不会Spring容器中
public class DragonBlade implements Weapon, Serializable {

    private String name="青龙偃月刀";

	....
}

现在Spring容器中的唯一的Weapon类型的对象就是方天画戟

关羽类没有修改任何代码,只能获得方天画戟作为武器

测试运行,输出:关羽使用方天画戟战斗

当@Autowired匹配多个类型对象的注意事项

上面的代码如果青龙偃月刀和方天画戟同时在Spring容器中会发生下列异常

expected single matching bean but found 2: dragonBlade,skyLancer

异常和之前使用@Bean注入时遇到的一样

原因是Spring容器中有青龙偃月刀和方天画戟两个武器

Spring无法抉择将哪一个对象赋值给关羽的weapon属性中,所以发生异常

解决方法有两个:

  1. 声明武器类型的属性名为其中一把武器的id

    代码如下

    @Component
    public class GuanYu implements Serializable {
    
        private String name="关羽";
        //@Autowired英文翻译为自动装配
        //表示这个注解下面声明的属性Spring会自动将合适的类型的对象赋给它使用
        //当前Spring容器中包含唯一匹配DragonBlade类型的对象,那么它会自动赋值
        // 为了实现解耦,我们声明为了Weapon接口
        // 这时自动装配任何实现了Weapon接口的类对象
        @Autowired
        // @Qualifier注解来决定Spring容器中具体id的对象注入到weapon属性中
        //@Qualifier("dragonBlade")//指定了id为dragonBlade的对象赋值到weapon属性中
        private Weapon skyLancer;
        //为了降低程序的耦合性,将原声明的具体类DragonBlade
        //修改为声明接口Weapon
        //private DragonBlade dragonBlade;
    
        public void fight(){
    
            System.out.println(name+"使用"+skyLancer+"战斗");
        }
    
     	....getset略
    
        public Weapon getSkyLancer() {
            return skyLancer;
        }
    
        public void setSkyLancer(Weapon skyLancer) {
            this.skyLancer = skyLancer;
        }
    }
    

    这种方法的缺点很明显,在修改属性名后,有一系列使用该属性名的位置都需要修改

    可维护性不好

  2. 使用@Qualifier注解来指定要使用的对象的ID

    代码如下

    @Component
    public class GuanYu implements Serializable {
    
        private String name="关羽";
        //@Autowired英文翻译为自动装配
        //表示这个注解下面声明的属性Spring会自动将合适的类型的对象赋给它使用
        //当前Spring容器中包含唯一匹配DragonBlade类型的对象,那么它会自动赋值
        // 为了实现解耦,我们声明为了Weapon接口
        // 这时自动装配任何实现了Weapon接口的类对象
        @Autowired
        // @Qualifier注解来决定Spring容器中具体id的对象注入到weapon属性中
        @Qualifier("dragonBlade")//指定了id为dragonBlade的对象赋值到weapon属性中
        private Weapon weapon;
        //为了降低程序的耦合性,将原声明的具体类DragonBlade
        //修改为声明接口Weapon
        //private DragonBlade dragonBlade;
    
        public void fight(){
    
            System.out.println(name+"使用"+weapon+"战斗");
        }
    
       ..省略get\set
    
        public Weapon getWeapon() {
            return weapon;
        }
    
        public void setWeapon(Weapon weapon) {
            this.weapon = weapon;
        }
    }
    

同时使用@Component和@Bean实现依赖注入

我们将现有的武器青龙偃月刀和方天画戟类上的@Component删除

在Config类中使用@Bean注入这两个对象

代码如下

@ComponentScan("cn.tedu.hero2")
public class Config {
    @Bean
    public DragonBlade db() {
        return new DragonBlade();
    }
    @Bean
    public SkyLancer lan(){
        return new SkyLancer();
    }
}

这样的话在GuanYu类中使用的weapon属性在赋值时实际上获得就是@Bean注入的对象了

这种操作在Spring中是完全允许的

需要注意的就是使用@Qualifier注解执行正确的对象id即可

习题

将我们上面习题中编写的Pen和Pencil使用@Bean的方式注入

并在Person类中获得依赖注入并输出结果

使用Spring获得properties文件信息

步骤1:

首先将我们的jdbc.properties文件复制到我们的resources文件夹

步骤2:

pom.xml文件中添加mysql和druid连接池的依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>

步骤3:

使用Spring提供的功能获得properties文件中的信息

注意如果jdbc.properties是复制到idea中的,可能需要ReBuild项目

//指定配置文件的位置
//classpath实际上就是指resources文件夹
@PropertySource("classpath:jdbc.properties")
public class Config {

    //这个自动装配是Spring获取Spring中自带的Environment类型对象
    //这个对象会对@PropertySource声明的配置文件进行解析
    @Autowired
    Environment env;
    @Bean
    public DataSource dataEnv(){
        System.out.println(env);
        DruidDataSource ds=new DruidDataSource();
        //使用env对象为ds设置参数
        ds.setUrl(env.getProperty("db.url"));
        ds.setDriverClassName(env.getProperty("db.driver"));
        ds.setUsername(env.getProperty("db.username"));
        ds.setPassword(env.getProperty("db.password"));
        ds.setMaxActive(
                env.getProperty("db.maxActive",Integer.class));
        ds.setInitialSize(
                env.getProperty("db.initialSize",Integer.class));
        return ds;
    }

}

然后就可以测试了

测试代码如下

 @Test
    public void test() throws SQLException {
        DataSource ds=ctx.getBean("dataEnv",DataSource.class);
        try(Connection conn=ds.getConnection()){
            String sql="select username from vrduser where id=4";
            PreparedStatement ps=conn.prepareStatement(sql);
            ResultSet rs=ps.executeQuery();
            while(rs.next()){
                System.out.println(rs.getString(1));
            }
        }
    }

实际上还有另外一种方式获得properties文件中的信息

代码如下

@PropertySource("classpath:jdbc.properties")
public class ConfigValue {

    //这个方法无需自动装配Environment对象

    //@Value实际上会自动从@PropertySource指定的配置文件中获得信息
    //@Value("${}")  ${}中的内容是properties文件中的name(键)
    // 这个键对应的值会自动赋值到@Value注解之后的参数中!
    @Bean
    public DataSource dataValue(
            @Value("${db.driver}") String driver,
            @Value("${db.url}") String url,
            @Value("${db.username}") String username,
            @Value("${db.password}") String password,
            @Value("${db.maxActive}") int maxActive,
            @Value("${db.initialSize}") int initialSize
    ){
        DruidDataSource ds=new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setMaxActive(maxActive);
        ds.setInitialSize(initialSize);
        return ds;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值