第2章 Spring中的Bean

2.1 Bean的配置

Bean本质是Java中的类。Spring可以被看做一个大型工厂,这个工厂的作用就是生产和管理Spring容器zho中的Bean。想在项目中使用这个工厂,就需要对Spring的配置文件进行配置。

Spring支持XML和Properties两种格式的配置文件。常用XML文件配置,该方式通过XML文件来注册并管理Bean之间的依赖关系。

XML配置文件的根元素是<beans>,包含了多个<bean>子元素,每个子元素地定义一个Bean。

<bean>元素的常用属性及其子元素

如果在Bean中未指定id和name,则Spring会把class的值当做id使用。

getBean( id/name/class );

 

2.2 Bean的实例化:

2.21 构造器实例化:Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean

package com.itheima.instance.constructor;
public
class Bean1 { <?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-4.3.xsd">
    <bean id="bean1" class="com.itheima.instance.constructor.Bean1" />
</beans 1 package com.itheima.instance.constructor;
package com.itheima.instance.constructor;
import org.springframework.context.ApplicationContext;
import 
    org.springframework.context.support.ClassPathXmlApplicationContext;
public class InstanceTest1 {
    public static void main(String[] args) {
        // 定义配置文件路径
        String xmlPath = "com/itheima/instance/constructor/beans1.xml";
        // ApplicationContext在加载配置文件时,对Bean进行实例化
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        Bean1 bean = (Bean1) applicationContext.getBean("bean1");
        System.out.println(bean);
    }
}

如果xml文件不在包里,就不需要在路径上加入包名;如果在包里,需要加上包名。

输出类Bean1的地址:

com.itheima.instance.constructor.Bean1@234bef66

 

2.22静态工厂方式实例化:要求开发者创建一个静态工厂的方法来创建Bean的实例,其Bean配置中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类,同时还需要使用factory-method属性来指定所创建的静态工厂方法。

package com.itheima.instance.static_factory;
public class Bean2 {
}
package com.itheima.instance.static_factory;
public class MyBean2Factory {    
    //使用自己的工厂创建Bean2实例
    public static Bean2 createBean222(){
        return new Bean2();
    }
}

 

<?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-4.3.xsd">
    <bean id="bean2" class="com.itheima.instance.static_factory.MyBean2Factory" 
        factory-method="createBean222" />
    <!-- factory-method后面等于的东西是方法 -->
</beans>

 

package com.itheima.instance.static_factory;
public class MyBean2Factory {    
    //使用自己的工厂创建Bean2实例
    public static Bean2 createBean222(){
        return new Bean2();
    }
}

输出类Bean2的地址:

com.itheima.instance.static_factory.Bean2@7921b0a2

说白了就是,有一个类叫Bean2,要实例化Bean2,这种方法不直接实例化它,要再创一个工厂类MyBean2Factory,工厂类里有个方法返回实例化的Bean2,return new Bean2(),先new再返回,并且这个方法名和Factory-method对应。

 

2.23实例工厂方法实例化:采用实例工厂,不再是静态工厂,采用直接创建Bean实例的方式。

package com.itheima.instance.factory;
public class Bean3  {
}
package com.itheima.instance.factory;
public class MyBean3Factory {
    public MyBean3Factory() {
        System.out.println("bean3工厂实例化中");
    }
    //创建Bean3实例的方法
    public Bean3 createBean111(){
        return new Bean3();
    }
}
<?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-4.3.xsd">
    <!-- 配置工厂 -->
    <bean id="myBean3Factory" 
            class="com.itheima.instance.factory.MyBean3Factory" />
    <!-- 使用factory-bean属性指向配置的实例工厂,
          使用factory-method属性确定使用工厂中的哪个方法-->
    <bean id="bean3" factory-bean="myBean3Factory" 
           factory-method="createBean111" />
</beans>
package com.itheima.instance.factory;
import org.springframework.context.ApplicationContext;
import 
    org.springframework.context.support.ClassPathXmlApplicationContext;
public class InstanceTest3 {
    public static void main(String[] args) {
        // 指定配置文件路径
        String xmlPath = "com/itheima/instance/factory/beans3.xml";
        // ApplicationContext在加载配置文件时,对Bean进行实例化
        ApplicationContext applicationContext = 
                new ClassPathXmlApplicationContext(xmlPath);
        System.out.println(applicationContext.getBean("bean3"));
        
        //顺便尝试构造器实例化
        String xmlPath2 = "com/itheima/instance/factory/beans3.xml";
        ApplicationContext applicationContext2 = 
                new ClassPathXmlApplicationContext(xmlPath2);
        System.out.println(applicationContext2.getBean("myBean3Factory"));
    }
} 

输出:

bean3工厂实例化中
com.itheima.instance.factory.Bean3@7921b0a2

bean3工厂实例化中
com.itheima.instance.factory.MyBean3Factory@32d992b2

这种方式说白了就是:有一个类Bean3,要实例化它,不直接实例化,再造一个工厂类MyBean3Factory来return new Bean3();

在xml文件中,先配置实例工厂类的bean;再配置要类Bean3的bean,并通过factory-bean指明是哪个实例工厂类(谁)要实例它,再通过factory-method指明是哪个方法来返回实例后的它。

(回顾旧知识:实例工厂类MyBean3Factory里面有一个无参构造函数,在实例MyBean3Factory的时候自动执行该方法,构造函数要求是无参无返回值,方法名和类名一样)

 

 

2.3 Bean的作用域

Spring 4.3中为Bean的实例定义了7种作用域。

singleton和prototype最常用,解释如下。

1.singleton作用域:

Spring容器默认的作用域,当Bean作用域为singleton时,Spring容器就会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同一个Bean实例。singleton作用域对于无会话状态的Bean(如Dao组件、Service组件)来说,是最理想的选择。演示如下:

package com.itheima.scope;
public class Scope  {
}
<?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">
            
    <bean id="scope" class="com.itheima.scope.Scope" scope="singleton"/> 
</beans>
package com.itheima.scope;
import org.springframework.context.ApplicationContext;
import 
    org.springframework.context.support.ClassPathXmlApplicationContext;
public class ScopeTest {
    public static void main(String[] args) {
        // 定义配置文件路径
        String xmlPath = "com/itheima/scope/beans4.xml";
        // 加载配置文件
        ApplicationContext applicationContext = 
                    new ClassPathXmlApplicationContext(xmlPath);
        // 输出获得实例
        System.out.println(applicationContext.getBean("scope"));
        System.out.println(applicationContext.getBean("scope"));
    }
}

输出:

com.itheima.scope.Scope@234bef66

com.itheima.scope.Scope@234bef66

2.prototype作用域:

对需要保持会话状态的Bean(如Struts2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。

xml文件中的<bean>改一下,<bean id="scope" class="com.itheima.scope.Scope" scope="prototype" />

输出:

com.itheima.scope.Scope@5a61f5df
com.itheima.scope.Scope@3551a94

 

2.4 Bean的生命周期

例如singleton和prototype作用域的生命周期。

singleton关闭时销毁,prototype长时间不用自动销毁。

Bean生命周期流程图:

 

2.5 Bean的装配方式:

Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean的装配方式,如基于XML的装配、基于注解(Annotation)的装配和自动装配(其中最常用的是基于注解的装配)。

2.51 基于XML的装配:设值注入(Setter Injectiion)和构造注入(Constructor Injection)

构造注入要求:提供默认无参构造方法,为所有属性提供setter方法

设值注入要求:Bean类必须提供一个默认的无参构造方法,并且为需要注入的属性提供对应的setter方法

 

package com.itheima.assemble;
import java.util.List;
public class User {
    private String username;
    private Integer password;
    private List<String> list;
    /**
     * 1.使用构造注入 
     * 1.1提供带所有参数的有参构造方法。
     */
    public User(String username, Integer password, List<String> list) {
        super();// 调用父类的无参构造方法,可以不写
        this.username = username;
        this.password = password;
        this.list = list;
    }
    /**
     * 2.使用设值注入 
     * 2.1提供默认空参构造方法 ;
     * 2.2为所有属性提供setter方法。
     */
    public User() {//无参构造方法
        super();//// 调用父类Object的无参构造方法,可以不写
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(Integer password) {
        this.password = password;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    @Override
    public String toString() { // 重写父类的Object的toString方法
        return "User [username=" + username + ", password=" + password +
                ", list=" + list + "]";
    }
}

 

<?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-4.3.xsd">
    <!--1.使用构造注入方式装配User实例 -->
    <bean id="user1" class="com.itheima.assemble.User">
        <constructor-arg index="0" value="tom" />
        <constructor-arg index="1" value="123456" />
        <constructor-arg index="2">
            <list>
                <value>"constructorvalue1"</value>
                <value>"constructorvalue2"</value>
            </list>
        </constructor-arg>
    </bean>
    <!--2.使用设值注入方式装配User实例 -->
    <bean id="user2" class="com.itheima.assemble.User">
        <property name="username" value="张三"></property>
        <property name="password" value="654321"></property>
        <!-- 注入list集合 -->
        <property name="list">
            <list>
                <value>"setlistvalue1"</value>
                <value>"setlistvalue2"</value>
            </list>
        </property>
    </bean>
</beans>

构造注入:constructor-arg元素中的index表示的是有参构造方法的参数顺序,value表示注入的值。

设值注入:property元素中的name表示要注入的属性名,value表示要注入的值,子元素list同样表示要注入的值。

 

 

package com.itheima.assemble;
import org.springframework.context.ApplicationContext;
import 
    org.springframework.context.support.ClassPathXmlApplicationContext;
public class XmlBeanAssembleTest {
    public static void main(String[] args) {
        // 定义配置文件路径
        String xmlPath = "com/itheima/assemble/beans5.xml";
        // 加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        // 构造方式输出结果
        System.out.println(applicationContext.getBean("user1"));
        // 设值方式输出结果
        System.out.println(applicationContext.getBean("user2"));
    }
}

 

为了能看到结果,重写toString方法。

输出:

User [username=tom, password=123456, list=["constructorvalue1", "constructorvalue2"]]
User [username=张三, password=654321, list=["setlistvalue1", "setlistvalue2"]]

 

 

2.52 基于注解(Annotation)的装配

XML文件可以实现Bean的装配工作,但是Bean多时,XML文件臃肿巨大,为后续的维护和升级带来困难。为此,提供了对注解技术的全面支持。

 

 

package com.itheima.annotation;
public interface UserDao {
    public void save();
}

 

package com.itheima.annotation;
import org.springframework.stereotype.Repository;

/**首先使用@Repository注解将UserDaoImpl类标识为Spring中的Bean,相当于配置文件中的
<bean id="userDao" class="com.itheima.annotation.UserDaoImpl"/> 很普通的配置
*/
@Repository("userDao") 
public class UserDaoImpl implements UserDao{
   public void save(){
      System.out.println("userdao...save...");
   }
}
package com.itheima.annotation;
public interface UserService {
    public void save();
}
package com.itheima.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

/**使用@Service注解将UserServiceImpl类标识为Spring中的Bean,相当于
<bean id="userService" class="com.itheima.annotation.UserServiceImpl"/> 很普通的配置*/
@Service("userService") 

public class UserServiceImpl implements UserService{
    
    /**使用@Resource注解标注属性uesrDao上,相当于配置文件中的
     <property name="userDao" ref="userDao"/>,
     不是普通的配置,依赖注入,将id为userDao的Bean实例注入到userService实例中
     */
    @Resource(name="userDao") 
    private UserDao userDao;
    public void save() {
         //调用userDao中的save方法
        this.userDao.save();
        System.out.println("userservice....save...");
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
package com.itheima.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;

/**使用@Controller 注解标注了 UserController类,这相当于配置文件中编写
 <bean id="userController" class="com.itheima.annotation.UserController"/>;普通配置
*/
@Controller("userController")

public class UserController {
    /**
     @Resource 注解标注在userService属性上,相当于配置文件中编写
     <property name="userService" ref="userService"/>,依赖注入
     */
    @Resource(name="userService")
    private UserService userService;
    public void save(){
        this.userService.save();
        System.out.println("userController...save...");
    }
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context-4.3.xsd">
      
     <!-- 凡出现context都是约束信息,使用 context 命名空间 ,在配置文件中开启相应的注解处理器 -->
     <context:annotation-config /> 
    <!--使用 context 命名空间 ,通知Spring扫描指定包下所有Bean类,进行注解解析-->   
     <context:component-scan base-package="com.itheima.annotation" /> 
</beans>
package com.itheima.annotation;
import org.springframework.context.ApplicationContext;
import 
   org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationAssembleTest {
    public static void main(String[] args) {
        // 定义配置文件路径
        String xmlPath = "com/itheima/annotation/beans6.xml";
        // 加载配置文件
        ApplicationContext applicationContext = 
                new ClassPathXmlApplicationContext(xmlPath);
        // 获取UserController实例
        UserController userController = 
          (UserController) applicationContext.getBean("userController");
        // 调用UserController中的save()方法
        userController.save();
    }
}

输出:

userdao...save...
userservice....save...
userController...save...

说白了,先配置类Dao,再把Dao用依赖注入的方式注入到Service,再把Service用依赖注入的方式注入到Controller。注解的配置和注入形式不一样,最后在控制层调用say方法,层层往下,先到服务层,再到数据层,数据层(Dao层)是底层,再层层往上分别调用say方法,因此输出顺序是Dao,Service,Controller。

(上述案例中用@Autowired注解替换@Resource也可以达到同样效果)

 

2.53 自动装配

注解方式装配一定程度减少配置文件的代码量,但是也有企业项目是没有使用注解方式开发的,自动装配也可以减少代码量。

Bean元素中包含一个autowire属性,通过属性值来自动装配Bean元素。所谓自动装配,就是将一个Bean自动地注入到其他Bean的Property中。autowire属性有5个值。

 

 

XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context-4.3.xsd">
        <!-- 使用bean元素的autowire属性完成自动装配 -->
    <bean id="userDao" class="com.itheima.annotation.UserDaoImpl" />
    <bean id="userService" 
      class="com.itheima.annotation.UserServiceImpl" autowire="byName" />
    <bean id="userController" 
      class="com.itheima.annotation.UserController" autowire="byName"/>
</beans>

用于配置userService和userController的<bean>元素中除了id和class属性外,还增加了autowire属性,并将其属性值设置为byName。在默认情况下,配置文件中要通过ref来装配Bean,但设置了autowire=“byName”后,Spring会自动寻找userService Bean中的属性,并将其属性名称与配置文件中定义的Bean做匹配。由于UserServiceImpl中定义了userDao属性及其setter方法,这与配置文件中的id为userDao的Bean相匹配,所以Spring会自动地将id为userDao的Bean装配到id为userService的Bean中。自动装配同样能完成依赖注入。

 

posted @ 2019-05-18 17:42  守林鸟  阅读(752)  评论(0编辑  收藏  举报