Spring学习笔记(3)

1. Spring的自动装配

问题引入:
先前,我们对象引用的属性都是依靠xml文件中的set和构造注入来完成的,这样的方式需要书写大量的标签以及复杂的过程,因此,为了简化依赖注入的形式,故而引入自动装配!

1.1 自动装配的形式

分为两种:

  1. ByName(通过属性名称进行注入)
  2. ByType(通过方法中参数的类型进行注入 更常用!)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <!--1.构建user对象-->
   <bean id="user" class="com.jt.pojo.User">
      <!--根据name属性查找对象的setId()方法 将value当做参数调用set方法完成赋值 -->
      <property name="id" value="100"></property>
     <!-- <property name="name" value="&lt;马超&gt;"></property>-->
      <property name="name">
         <value><![CDATA[<马超>]]></value>
      </property>
   </bean>

   <!--2.构建Dao对象
      根据面向接口编程
         Id:接口的名称
         class:实现类的包路径
   -->
   <bean id="userDao" class="com.jt.dao.UserDaoImpl"></bean>

   <!--3.构建Service
      自动装配: 程序无需手动的编辑property属性
      autowire="byName" 根据属性的名称进行注入
         1.找到对象的所有的set方法 setUserDao()
         2.setUserDao~~~~set去掉~~~UserDao~~~~首字母小写~~~userDao属性
         3.Spring会根据对象的属性查询自己维护的Map集合,根据userDao名称,查找Map
         中的Key与之对应,如果匹配成功.则能自动调用set方法实现注入(必需有set方法)
      autowire="byType"
         1.找到对象的所有的set方法 setUserDao()
         2.根据set方法找到方法中参数的类型UserDao.class
         3.Spring根据自己维护对象的Class进行匹配.如果匹配成功则实现注入(set方法)
      -->
   <bean id="userService" class="com.jt.service.UserServiceImpl" autowire="byName">
   </bean>

   <!--4.构建Controller-->
   <bean id="userController" class="com.jt.controller.UserController" autowire="byName"/>

</beans>

2. Spring注解开发

2.1 Spring注解模式

说明:
Spring为了简化xml配置方式,则研发注解模式.
Spring为了程序更加的严谨,通过不同的注解标识不同的层级 但是注解的功能一样

1).@Controller 用来标识Controller层的代码 相当于将对象交给Spring管理
2).@Service 用来标识Service层代码
3).@Repository 用来标识持久层
4).@Component 万用注解

总结:
相当于对象不需要通过xml配置文件来管理了,写一个注解,Spring就会帮你自动创建并且管理bean对象了

2.1.1 注解使用原理:

/**
 * <bean id="类名首字母小写~~userDaoImpl"  class="UserDaoImpl.class" />
 * 如果需要修改beanId则手动添加value属性即可
 */
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao{

    @Override
    public void addUser(User user) {
        System.out.println("链接数据库执行insert into :"+user);
    }
}

注解默认不生效的,如果想要这些注解生效,需要开启包扫描!

2.1.2 编辑配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

   <!--1.构建user对象-->
   <bean id="user" class="com.jt.pojo.User">
      <property name="id" value="100"></property>
      <property name="name">
         <value><![CDATA[<马超>]]></value>
      </property>
   </bean>

   <!--2.
      让注解生效,开启包扫描
      包路径特点: 给定包路径,则自动扫描同包及子孙包中的类
      base-package: 根据指定的包路径 查找注解
      写方式: 多个包路径 使用,号分隔
   -->
   <!--<context:component-scan base-package="com.jt.controller,com.jt.service,com.jt.dao"></context:component-scan>-->
   <context:component-scan base-package="com.jt"/>
   
   <!--业务需求1: 只想扫描@controller注解
       属性说明: use-default-filters="true"
                默认规则 :true  表示可以扫描其他注解
                        :false  按照用户指定的注解进行加载,默认规则不生效
   -->
   <context:component-scan base-package="com.jt" use-default-filters="false">
      <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
   </context:component-scan>
	
	<!--业务需求2: 不想扫描@controller注解-->
   <context:component-scan base-package="com.jt">
      <!--通过包扫描 可以加载其他的注解 排除Controller注解-->
      <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller"/>
   </context:component-scan>
</beans>

总结:
1.包扫描需要先配置头标签,规则为:

xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context/spring-context.xsd

2.包扫描可以对同一包下的所有注解进行扫描
<context:component-scan base-package="com.jt.controller,com.jt.service,com.jt.dao">


3.只想扫描某个注解:include
4.排除某个注解:exclude

2.2 Spring属性的管理

2.2.1 管理对象的引用

关于注解的说明:
前面我们学习了在xml文件中属性的自动装配,由于逐渐采用注解模式开发,现在我们逐步脱离xml文件,因此在java文件中怎么实现自动装配呢?我们需要使用特定的注解!

  • 1.@Autowired: 可以根据类型/属性名称进行注入 首先按照类型进行注入如果类型注入失败,则根据属性名称注入
  • 2.@Qualifier: 如果需要按照名称进行注入,则需要额外添加@Qualifier
  • 3.@Resource(type = “xxx.class”,name=“属性名称”)
  • 关于注解补充: 由于@Resource注解 是由java原生提供的,不是Spring官方的.所以不建议使用
    上述的属性的注入在调用时 自动的封装了Set方法,所以Set方法可以省略不写

2.2.2 管理业务数据(pojo对象):@Bean注解

@Bean作用:
通过该注解,可以将业务数据实例化之后,交给Spring容器管理. 但是@Bean注解应该写到配置类中.

package com.jt.config;

import com.jt.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration  //标识我是一个配置类 相当于application.xml
                //设定包扫描的路径
@ComponentScan("com.jt")//如果注解中只有value属性 则可以省略
public class SpringConfig {

    /*
        1.Spring配置文件写法 <bean id="方法名称"  class="返回值的类型" />

        2.执行@Bean的方法 将方法名称当做ID,返回值的对象当做value
                         直接保存到Map集合中
    * */
    @Bean
    public User user(){
        User user = new User();
        user.setId(101);
        user.setUsername("Spring容器");
        return user;
    }
}

总结:
@Bean需在配置类中完成!
@Configuration ====配置类的标识
@ComponentScan(“包扫描路径”) ====设定包扫描的路径
@Bean管理的对象,id名为方法名,class为返回值的对象

2.2.3 Spring动态获取外部数据

说明:在pojo对象的属性赋值中,我们都是通过手动赋值,可是诞生了一个问题,值被我们写死了,并且如果有多个对象,需要书写大量的set代码,不便于管理,下面引入新的业务数据管理方式:properties文件+注解

2.2.3.1 编辑properties文件

在这里插入图片描述

# 规则: properties文件
# 数据结构类型:  k-v结构
# 存储数据类型:  只能保存String类型
# 加载时编码格式: 默认采用ISO-8859-1格式解析 中文必然乱码
user.id=1001
# Spring容器获取的当前计算机的名称 所以user.name慎用
# user.name=你好啊哈哈哈
user.username=鲁班七号
2.2.3.2 编辑配置类

1.@PropertySource
注解用法: @PropertySource(“classpath:/user.properties”)
注解说明: @PropertySource 作用: 加载指定的pro配置文件 将数据保存到Spring容器中

2.@Value作用:将值赋予属性
@Value(123) 将123值赋值给Id
@Value("${user.id}") 在Spring容器中查找key=user.id的数据.通过${}语法获取

package com.jt.config;

import com.jt.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration  //标识我是一个配置类 相当于application.xml
                        //设定包扫描的路径
@ComponentScan("com.jt")//如果注解中只有value属性 则可以省略
//@PropertySource 作用: 加载指定的pro配置文件 将数据保存到Spring容器中
// encoding:指定字符集编码格式
@PropertySource(value = "classpath:/user.properties",encoding = "UTF-8")
public class SpringConfig {
    //定义对象属性 准备接收数据
    //@Value(123)   将123值赋值给Id
    //@Value("${user.id}")  在Spring容器中查找key=user.id的数据.通过${} 进行触发    @Value("${user.id}")

    @Value("${user.id}")
    private Integer id;
    @Value("${user.username}")
    private String username;

    /*
        1.Spring配置文件写法 <bean id="方法名称"  class="返回值的类型" />

        2.执行@Bean的方法 将方法名称当做ID,返回值的对象当做value
                         直接保存到Map集合中
    * */
    @Bean
    public User user(){
        User user = new User();
        user.setId(id);
        user.setUsername(username);
        return user;
    }
}

总结:
1.看到Source一般就要写classpath
2.由于我们保存properties文件时的编码使用的是UTF-8,而编码时使用的是其它编码,必然出现中文乱码,解决方案:设置encoding属性!
3.@Value(${user.id})底层实质上是去找getId方法,所以pojo对象中的get、set方法是要添加的

2.3 Spring注解开发的那些事

2.3.1 注解的说明:

注解作用: 一些复杂的程序 以一种低耦合度的形式进行调用
元注解:
@Target({ElementType.TYPE}) 标识注解对谁有效 type:类 method:方法有效
@Retention(RetentionPolicy.RUNTIME) 运行期有效(大)
@Documented 该注解注释编译到API文档中.
 
由谁加载:由Spring内部的源码负责调用.
在这里插入图片描述
说明:Spring容器有严格的执行步骤,就像一个工厂一样,Spring容器首先加载xml配置文件,看是否存在bean需要实例化对象,没有则执行下一步,检查是否有实现工厂的实现类,如果没有则加载注解,如果类被注解标识则实例化。

2.3.2 关于Spring工厂模式说明

1.Spring源码中创建对象都是采用工厂模式 接口:BeanFactory(顶级接口)
2.Spring开发中需要手动的创建对象时,一般采用 FactoryBean(业务接口)

3.关于bean对象注入问题说明 一般需要检查注解是否正确配置
错误展示:

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.jt.service.UserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.jt.service.UserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

2.3.3 优化xml配置文件

说明: 随着软件技术发展,xml配置文件显得臃肿 不便于操作,所以Spring后期提出了配置类的思想.
将所有的配置文件中的内容,写到java类中.

编辑配置类:

@Configuration  //标识我是一个配置类 相当于application.xml
                //设定包扫描的路径
@ComponentScan("com.jt")//如果注解中只有value属性 则可以省略
public class SpringConfig {

}

编辑测试代码

 //通过配置类测试代码
    @Test
    public void testAnno(){
        ApplicationContext context =
                new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController userController = context.getBean(UserController.class);
        userController.addUser();
    }

总结:
使用new AnnotationConfigApplicationContext(配置类);读取配置类
配置类是一个类,所以读取时通过类名.class 获取即可

2.3.4 Spring注解模式执行过程

1).当程序启动Spring容器时 AnnotationConfigApplicationContext 利用beanFactory实例化对象
2).根据配置类中的包扫描开始加载指定的注解(4个). 根据配置文件的顺序依次进行加载
在这里插入图片描述
3).当程序实例化Controller时,由于缺少Service对象,所以挂起线程 继续执行后续逻辑.
当构建Service时,由于缺少Dao对象,所以挂起线程 继续执行后续逻辑.
当实例化Dao成功时,保存到Spring所维护的Map集合中. 执行之前挂起的线程.
所以以此类推 所有对象实现封装.最终容器启动成功
在这里插入图片描述
4). 根据指定的注解/注入指定的对象.之后统一交给Spring容器进行管理.最终程序启动成功。

3. Spring中常见问题

3.1 接口多实现类情况说明

原则: Spring中规定 一个接口最好只有一个实现类.
业务需求: 要求给UserService接口提供2个实现类.

3.1.1 案例分析

编辑实现类A
在这里插入图片描述

编辑实现类B
在这里插入图片描述
实现类型的注入

package com.jt.controller;

import com.jt.pojo.User;
import com.jt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    /**
     * @Autowired:
     *   首先根据属性的类型进行注入,
     *   如果类型不能匹配,则根据属性的名称进行注入.
     *
     * 如果添加了@Qualifier("userServiceA") 则根据属性名称注入
     *  如果名称注入失败,则报错返回.
     */
    @Autowired
    @Qualifier("userServiceC")
    private UserService userService;

    public void addUser(){
        User user = new User();
        user.setId(101);
        user.setUsername("不知火舞|王昭君");
        userService.addUser(user);
    }

}

3.2 测试类中获取接口对象的说明

问题说明:
    如下代码:
在这里插入图片描述
在getBean时,如果获取的是接口,程序会报错吗?如果不会,结果又是如何?

答: 如果获取的接口只有一个实现类,底层会为接口进行注入,因此,程序能否运行取决于接口是否有多个实现类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值