进一步了解Spring(二)

bean相关

  1. bean的作用域
    • 四种: prototype, request, session, singleton
    • 若scope="singleton", 则会在初始化容器时就创建此对象.
      <bean id="person2" class="top.binwenhome.spring.bean.Person" scope="singleton">
              <property name="id" value="222"></property>
              <property name="name" value="李四"></property>
          </bean>
    • 而多例即原型的bean, 会在使用时创建.
  2. bean的生命周期
    • springIOC容器可以管理bean的生命周期, Spring允许在bean生命周期内特
      定的时间点执行指定的任务.
    • SpringIOC容器对bean生命周期管理的过程
      • 通过构造器或工厂方法创建bean实例 
      • 为bean的属性设置值和对其他bean的应用(依赖注入)
      • bean的初始化
      • 使用bean
      • 当容器关闭时, 调用bean的销毁方法.
    • 配置bean时, 通过init-methoddestroy-method属性为bean指定初始化和销毁方法.
      • Person
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public class Person {
        
            private Integer id;
            private String name;
        
            public void init() {
                System.out.println("初始化bean");
            }
        
            public void destory() {
                System.out.println("销毁bean");
            }
        }
      • 配置文件
            <bean id="person" class="bean.Person" init-method="init" destroy-method="destory">
                <constructor-arg name="id" value="333"></constructor-arg>
                <constructor-arg name="name" value="张三"></constructor-arg>
            </bean>
  3. bean的后置处理器
    • bean后置处理器允许在调用初始化方法前后对bean进行额外的处理.
    • bean后置处理器对Spring管理的bean逐一全部处理
      • 典型应用: 检查bean属性的正确性. 
    • bean的后置处理器需要实现接口BeanPostProcessor. 实现两个抽象方法.
      • postProcessBeforeInitialization(Object bean, String beanName) 初始化之前处理.
        • 参数: 要处理的bean和其id.
        • 返回值: 经过处理后的新的bean
      • postProcessAfterInitialization(Object bean, String beanName) 初始化之后处理.
    • 添加bean后置处理器后bean的生命周期
    • 代码演示
      • PostHandler
        public class PostHandler implements BeanPostProcessor {
        
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                System.out.println(beanName + "aa");
                Person p = (Person) bean;
                if("张三".equals(p.getName())) {
                    p.setName("张无忌");
                    p.setId(10001);
                } else {
                    p.setName("郭靖");
                    p.setId(1001);
                }
                return p;
            }
        
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                return bean;
            }
        }
      • 配置文件
            <bean id="person" class="bean.Person" init-method="init" destroy-method="destory">
                <constructor-arg name="id" value="333"></constructor-arg>
                <constructor-arg name="name" value="张三"></constructor-arg>
            </bean>
        
            <bean class="handler.PostHandler"></bean>

引用外部资源文件

  1. 介绍
    • 当bean的配置信息逐渐增多时, 查找和修改一些bean的配置信息就变得困难. 这时可以将一部分信息提取到bean配置文件的外部, 以properties格式的属性文件保存起来, 同时在bean的配置文件中引用properties属性文件中内容, 从而实现一部分属性值在发生变化时仅修改properties文件即可. 
    • 这种技术多用于连接数据库的基本信息.
  2. 要引入外部配置文件时, 用${ }括住.
  3. 方式一
        <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
            <property name="location" value="classpath:conf/db.properties"></property>
        </bean>
    
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driver}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
  4. 方式二
    • 要引入context名称空间.
    • 使用<context:property-placeholder/>标签, location属性.
          <context:property-placeholder location="classpath:conf/db.properties" />
      
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="driverClassName" value="${jdbc.driver}"></property>
              <property name="url" value="${jdbc.url}"></property>
              <property name="username" value="${jdbc.username}"></property>
              <property name="password" value="${jdbc.password}"></property>
          </bean>

自动装配

  1. 自动装配的概念
    • 手动注入: 以value或ref的方式明确指定属性值都是手动装配.
    • 自动装配: 根据指定的装配规则, 不需要明确指定, Spring自动将匹配的属性值注入到bean中.
    • 自动装配只支持非字面量的属性.
  2. 装配模式
    • 在bean标签中使用autowire属性, 有两个值: byName, byType 
    • 根据名称自动装配
      • 某个bean的id和要赋值的属性名一致
        <bean id="person" class="bean.Person" autowire="byName">
                <property name="id" value="222"></property>
                <property name="name" value="张三"></property>
            </bean>
        
            <bean id="car" class="bean.Car">
                <property name="brand" value="bench"></property>
                <property name="price" value="300000D"></property>
            </bean>
        
        这里Person中有car属性
    • 根据类型自动装配
      • 将类型匹配的bean作为属性注入到另一个bean中
        <bean id="person" class="top.binwenhome.spring.bean.Person" autowire="byType">
                <property name="id" value="222"></property>
                <property name="name" value="张三"></property>
            </bean>
        
            <bean id="car" class="top.binwenhome.spring.bean.Car">
                <property name="brand" value="bench"></property>
                <property name="price" value="300000D"></property>
            </bean>
      • 若IOC容器中有多个与目标bean类型一致的bean, 则Spring无法判定哪个bean最适合该属性, 故无法自动装配. 
      • 兼容性: 目标bean类型的父类型或接口, 也能自动装配.
  3. 选用建议: 这两种都太笨拙, 实际都不用, 应使用注解实现自动装配.

通过注解配置bean

  • 相对于xml方式, 通过注解配置bean更加简洁和优雅, 并且和MVC组件化开发的理念十分契合.
  • 使用注解标识组件
  1. 普通组件: @Component
    • 标识一个受SpringIOC容器管理的组件. 
    • 该注解加在类上. 
    • 若一个类加上了该注解, 则Spring会对这个类产生的对象进行管理.
  2. 持久化层组件: @Repository
    • 标识一个受SpringIOC容器管理的持久层组件.
  3. 业务逻辑层组件: @Service
    • 标识一个受SpringIOC容器管理的业务逻辑层组件.
  4. 表述层控制器组件: @Controller
    • 标识一个受SpringIOC容器管理的表述层控制器组件.
  5. 组件命名规则
    • 默认使用组件的类名的首字母变小写作为bean的id
    • 或使用组件注解的value属性指定bean的id, 如@Controller(value="aaa")
  6. 注意: 实际上Spring并没有能力标识一个组件到底是不是它所标记的类型.
    • 如, 将@Respository用在一个表述层控制器组件上也不会报错.
    • 所以@Respository, @Service, @Controller仅仅让开发人员明确当前组件.
  7. 举例
    @Repository
    public class UserDAO {
    }
    
    @Service
    public class UserService {
    }
    
    @Controller
    public class UserController {
    }
  • 扫描组件
  1. 组件被上述注解标识后还需通过Spring进行扫描才能被侦测到. 
  2. 必须加入spring-aop的jar包
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring.version}</version>
            </dependency>
  3. 在xml配置文件中使用<context:component-scan/>标签
    • 指定被扫描的package: base-package属性, 这样Spring容器会扫描这个基包及其子包中所有类.
      <!--
              <context:component-scan>: 扫描组件, 对设置的包下面的类进行扫描, 会将加上注解的类作为Spring的组件进行加载.
              组件: 指Spring中管理的bean.
              作为Spring的组件进行加载: 会自动在spring的配置文件中生成相对的bean, 这些bean的id以类的首字母变小写为值.
          -->
          <context:component-scan base-package="ioc" />
      
          <!-- 相当于生成了以下等代码 -->
          <bean id="userController" class="ioc.controller.UserController"></bean>
    • 当需要扫描多个包时可用逗号分隔. 
  4. 若希望扫描特定的类而非基包下所有类, 用包含与排除
    • 包含: <context:include-filter>子标签
      • 在设定的包结构下, 再次通过注解或类型具体包含到某个或某几个类.
      • 在使用包含时, 一定要设置use-default-filters="false", 将默认的过滤(扫描包下所有类)关闭.
      • 当type="annotation"时, expression填注解的全类型.
      • 当type="assignable"时, expression填全类名(也会包含其子类)
            <context:component-scan base-package="ioc" use-default-filters="false">
                <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
                <context:include-filter type="assignable" expression="ioc.service.UserService"/>
            </context:component-scan>
    • 排除: <context:exclude-filter>子标签
      • 在设定的包结构下, 扫描所有的类的同时排除某个或某几个类.
      • 在使用排除时, 一定要设置use-default-filters="true", 将默认的过滤(扫描包下所有类)打开.
            <context:component-scan base-package="ioc" use-default-filters="true">
                <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
                <context:exclude-filter type="assignable" expression="ioc.dao.UserDAO"/>
            </context:component-scan>

基于注解的自动配置

  • 组件装配
  1. 需求
    • Controller组件中往往需要用到Service组件的实例, Service组件中往往需要用到Repository组件的实例. 
    • Spring可以通过注解的方式帮我们实现属性的装配.
  2. 实现依据
    • 在指定要扫描的包时, <context:component-scan>会自动注册一个bean的后置处理器: AutowiredAnnotationBeanPostProcessor的实例.
    • 该后置处理器可以自动装配标记了@Autowired, @Resource@Inject的属性. 
  3. @Autowired注解
    • 会默认使用byType的方式, 此时要求spring只有一个bean为其赋值. 
      • 当byType实现不了装配时, 会自动切换到byName, 此时要求spring容器中有一个bean的id和属性名一致.
    • 构造器, 普通字段, 一切具有参数的方法都可以应用@Autowired注解.
    • 默认情况下, 所有使用@Autowired注解的属性都需要被设置, 当Spring找不到匹配的bean装配属性时, 会抛出异常.
    • 若某一属性允许不被设置, 可设置@Autowired的required属性为false.(装配不成功也能继续往下执行) 
    • 若自动装配时, 匹配到多个能够赋值的bean, 可使用@Qualifier(value="beanId")指定使用的bean.
      • @Autowired和@Qualifier(value="beanId")可以一起作用于带形参的方法上, 此时@Qualifier(value="beanId")所指定的bean作用于形参.
  4. @Resource注解
    • @Resource注解要求提供一个bean名称的属性(按名称注入), 若该属性为空, 则自动采用标注处的变量或方法名作为bean的名称.
  5. @Inject
    • @Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性.
  • 代码
    • 持久层
      @Repository
      public class UserDAOImpl implements UserDAO {
      
          public void addUser() {
              System.out.println("UserDAO: 添加成功");
          }
      }
      
      @Repository
      public class UserOtherDAOImpl implements UserDAO {
      
          public void addUser() {
              System.out.println("UserOtherDAO: 添加成功");
          }
      }
    • 业务层
      @Service
      public class UserService {
      
          @Autowired
          @Qualifier("userOtherDAOImpl")
          private UserDAO userDAO;
      
          public void addUser() {
              userDAO.addUser();
          }
      }
    • 控制层
      @Controller("aaa")
      public class UserController {
      
          public UserController() {
              System.out.println("UserController");
          }
      
          @Autowired
          private UserService userService;
      
          public void addUser() {
              userService.addUser();
          }
      }
    • 测试
      public class TestBySpring {
      
          public static void main(String[] args) {
              //初始化容器
              ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("applicationContext.xml");
      
              UserController uc = cac.getBean("aaa", UserController.class);
              uc.addUser();
      
              UserService us = cac.getBean("userService", UserService.class);
              System.out.println(us);
      
              UserDAOImpl ud = cac.getBean("userDAOImpl", UserDAOImpl.class);
              System.out.println(ud);
      
      
              cac.close();
          }
      }

@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称

原文地址:https://www.cnblogs.com/binwenhome/p/12997189.html