Spring4深入理解IOC&DI04----Bean配置方式(全类名,工厂方法,FactoryBean),配置形式(基于XML和注解),泛型依赖注入

参考代码下载github:https://github.com/changwensir/java-ee/tree/master/spring4

一、Bean的配置方式

    Bean 的配置方式:通过全类名(反射)、通过工厂方法(静态工厂方法 & 实例工厂方法)、FactoryBean

1-1.通过静态工厂方式配置Bean

  --• 调用 静态工厂方法 创建 Bean 是将 对象创建的过程封装到静态方法中 . 当客户端需要对象时 , 只需要简单地调用静态方法 , 而不同关心创建对象的细节 .
  --• 要声明通过静态方法创建的 Bean, 需要在 Bean class 属性里指定拥有该工厂的方法的类 , 同时在 factory-method 属性里指定工厂方法的名称 . 最后 , 使用 < constrctor-arg > 元素为该方法传递方法参数 .
1-2.通过实例工厂方式配置Bean

  • 实例工厂方法 : 将对象的创建过程封装到另外一个对象实例的方法里 . 当客户端需要请求对象时 , 只需要简单的调用该实例方法而不需要关心对象的创建细节 .
  • 要声明通过实例工厂方法创建的 Bean
    – bean factory-bean 属性里指定拥有该工厂方法的 Bean
    – factory-method 属性里指定该工厂方法的名称
    – 使用 construtor-arg 元素为工厂方法传递方法参数

/**
 * InstanceCarFactory:实例工厂方法
 * 实例工厂的方法,即需要先创建工厂本身,再调用工厂的实例方法来返回bean的实例
 *
 * @author Lcw 2015/11/12
 */
public class InstanceCarFactory {
    private Map<String, Car> cars = null;

    public InstanceCarFactory() {
        cars = new HashMap<String, Car>();
        cars.put("audi", new Car("audi", 500000));
        cars.put("ford", new Car("ford", 600000));
    }

    public Car getCar(String brand) {
        return cars.get(brand);
    }
}

/**
 * StaticCarFactory:静态工厂方法:直接调用某一个类的静态方法就可以返回Bean实例
 */
public class StaticCarFactory {
    private static Map<String , Car> cars = new HashMap<String, Car>();

    static {
        cars.put("audi",new Car("audi", 30000));
        cars.put("ford",new Car("ford", 40000));
    }

    //静态工厂方法: 注意:要在配置文件XML里配置Car而不是StaticCarFactory
    public static Car getCar(String name) {
        return cars.get(name);
    }
}
<?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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--通过静态工厂方法来配置bean, 注意不是配置静态工厂方法实例,而是配置bean实例-->
    <!--factory-method: 指向静态工厂方法的名字
        constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数-->
    <bean id="car1" class="Spring4_IOC.bean.factory.StaticCarFactory"
            factory-method="getCar">
        <constructor-arg value="audi"/>
    </bean>

    <!--配置实例工厂的实例-->
    <bean id="carFactory" class="Spring4_IOC.bean.factory.InstanceCarFactory"/>
    <!-- 通过实例工厂方法来配置bean-->
    <bean id="car2" factory-bean="carFactory" factory-method="getCar">
        <constructor-arg value="ford"/>
    </bean>
</beans>
    /**
     * Bean 的配置方式:-、通过全类名(反射);二、通过工厂方法(静态工厂方法 & 实例工厂方法);三、FactoryBean
     *
     * 静态工厂方法:直接调用某一个类的静态方法就可以返回Bean实例
     * 实例工厂方法:实例工厂的方法,即需要先创建工厂本身,再调用工厂的实例方法来返回bean的实例
     */
    @Test
    public void testStaticFactory() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring4_IOC/beans-factory.xml");
        //测试静态工厂方法
        Car car1 = (Car) ctx.getBean("car1");
        System.out.println(car1);

        //实例工厂方法
        Car car2 = (Car) ctx.getBean("car2");
        System.out.println(car2);
    }
1-3.实现 FactoryBean 接口在 Spring IOC 容器中配置 Bean

Spring 中有两种类型的 Bean, 一种是普通 Bean, 另一种是工厂 Bean, FactoryBean .
工厂 Bean 跟普通 Bean 不同 , 其返回的对象不是指定类的一个实例 , 其返回的是该工厂 Bean getObject 方法所返回的对象

/**
 * CarFactoryBean:自定义的factoryBean需要实现FactoryBean
 */
public class CarFactoryBean implements FactoryBean<Car> {
    private String brand;
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }

    public CarFactoryBean() {
    }
    //下面是三个实现方法
    //返回bean的对象
    public Car getObject() throws Exception {
        return new Car("BMW" , 500000);
    }

    //返回bean的类型Class<?>  中?表示一个未知的类
    public Class<?> getObjectType() {
        return Car.class;
    }

    public boolean isSingleton() {
        return true;
    }
}
<?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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--
        通过factoryBean来配置Bean的实例
        class:指向FactoryBean的全类名
        property:配置FactoryBean的属性

        但实际返回的实例确是FactoryBean的getObject()返回的实例-->
   <bean id="car" class="Spring4_IOC.bean.CarFactoryBean">
       <property name="brand" value="BMW"/>
   </bean>
</beans>
   /**
     * Bean 的配置方式:-、通过全类名(反射);二、通过工厂方法(静态工厂方法 & 实例工厂方法);三、FactoryBean
     *
     * 测试FactoryBean
     * Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂Bean, 即FactoryBean
     */
    @Test
    public void testFactoryBean() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring4_IOC/beans-beanFactory.xml");
        Car car = (Car) ctx.getBean("car");  //注意这个在类是Car!
        System.out.println(car);
    }

二、Bean的配置形式

    配置形式:基于 XML 文件的方式;基于注解的方式(基于注解配置 Bean;基于注解来装配 Bean 的属性)
在 classpath 中扫描组件
组件扫描 (componentscanning): Spring 能够 classpath 下自动扫描 , 侦测和实例化具有特定注解的组件 .
特定组件包括 :
@Component: 基本注解 , 标识了一个受 Spring 管理的组件
@ Respository : 标识持久层组件
@Service: 标识服务层 ( 业务层 ) 组件
@Controller: 标识表现层 组件
对于扫描到的组件 , Spring 有默认的命名策略 : 使用非限定类名 , 第一个字母小写 . 也可以 在注解中通过 value 属性值标识组件的名称
当在组件类上使用了特定的注解之后 , 还需要在 Spring 的配置文件中 声明 < context:component-scan >
base-package 属性指定一个需要扫描的基类包 Spring 容器将会扫描这个基类包里及其子包中的所有类 .
当需要扫描多个包时 , 可以使用逗号分隔 .
如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类,示例:
< context:include-filter > 子节点表示要包含的目标类
< context:exclude-filter > 子节点表示 要排除在外的 目标
< context:component-scan > 下可以拥有若干个 < context:include-filter > < context:exclude-filter > 子节点

以下 类那放在这个文件下Spring4_IOC.annotation

@Component
public class TestObject {
}
respository接口跟实现类

public interface UserRepository {
    void save();
}
//接口实现一般用接口名
@Repository("userRepository")
public class UserRepositoryImpl implements UserRepository {
    public void save() {
        System.out.println("UserRepository Save...");
    }
}
Service层跟controller
@Service
public class UserService {
   //@Autowired(required=false) //如果没有这个类,输出为null
   @Autowired
    private UserRepository userRepository;

    public void add() {
        System.out.println("UserService add...");
        userRepository.save();
    }
}
@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void execute() {
        System.out.println("UserController execute...");
        userService.add();
    }
}
<?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.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

   <!--指定Spring IOC容器扫描的包-->
    <!--可以通过resource-pattern指定扫描的资源-->
    <!--<context:component-scan base-package="Spring4_IOC.annotation"
            resource-pattern="repository/*.class"></context:component-scan>-->

    <!--exclude-filter: 子节点指定排除哪些指定表达式的组件-->
    <!--include-filter:子节点指定包含哪些表达式的组件,该子节点需要use-default-filters="false"配合使用-->
    <context:component-scan base-package="Spring4_IOC.annotation" >
        <!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
        <!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->

        <!--<context:exclude-filter type="assignable" expression="com.changwen.spring4.annotation.repository.UserRepository"/>-->
        <!--<context:include-filter type="assignable" expression="com.changwen.spring4.annotation.repository.UserRepository"/>-->
    </context:component-scan>
</beans>
    @Test
    public void testAnnotation() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring4_IOC/beans-annotation.xml");

        TestObject to = (TestObject) ctx.getBean("testObject");
        System.out.println(to);

        //类UserController第一个字母小写 userController
        UserController userController = (UserController) ctx.getBean("userController");
        System.out.println(userController);
        userController.execute();

        UserService userService = (UserService) ctx.getBean("userService");
        System.out.println(userService);

        UserRepository userRepository = (UserRepository) ctx.getBean("userRepository");
        System.out.println(userRepository);
    }
< context:include-filter > < context:exclude-filter > 节点支持多种类型的过滤表达式

2-1.组件装配

< context:component-scan > 元素还会自动注册 AutowiredAnnotationBeanPostProcessor 实例 , 该实例可以自动装配具有 @ Autowired @Resource @Inject 注解 的属性 .
1).使用 @Autowired自动装配Bean
@ Autowired 注解自动装配 具有兼容类型 的单个 Bean 属性
构造器 , 普通字段 ( 即使是非 public), 一切具有参数的方法都可以应用 @ Authwired 注解
默认 情况下 , 所有使用 @ Authwired 注解的属性都需要被设置 . Spring 找不到匹配的 Bean 装配属性时 , 会抛出异常 , 若某一属性允许不被设置 , 可以设置 @ Authwired 注解的 required 属性为 false
默认 情况下 , IOC 容器里存在多个类型兼容的 Bean , 通过类型的自动装配将无法工作 . 此时可以在 @Qualifier 注解里提供 Bean 的名称 . Spring 允许对方法的入参标注 @ Qualifiter 已指定注入 Bean 的名称
@ Authwired 注解也可以应用在 数组类型 的属性上 , 此时 Spring 将会把所有匹配的 Bean 进行自动装配 .
@ Authwired 注解也可以应用在 集合属性 , 此时 Spring 读取该集合的类型信息 , 然后自动装配所有与之兼容的 Bean.
@ Authwired 注解用 java.util.Map 上时 , 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值

2).使用 @Resource@Inject自动装配Bean

Spring 还支持 @Resource @Inject 注解,这两个注解和 @ Autowired 注解的功用类似
@Resource 注解要求提供一个 Bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 Bean 的名称
@Inject @ Autowired 注解 一样也是按类型匹配注入的 Bean ,但没有 reqired 属性
建议使用 @ Autowired 注解

三、泛型依赖注入

Spring 4.x 中可以为子类注入子类对应的泛型类型的成员变量的引用


public class BaseRepository<T> {
}
public class BaseService<T> {
    @Autowired
    protected BaseRepository<T> repository;

    public void add() {
        System.out.println("add...");
        System.out.println(repository);
    }
}
@Service
public class UserService extends BaseService<User> {
}
public class User {
    private String userName;
    private List<Car> cars;
    private String wifeName;

    public String getWifeName() {
        return wifeName;
    }

    public void setWifeName(String wifeName) {
        System.out.println("setWifhName: " + wifeName);
        this.wifeName = wifeName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public List<Car> getCars() {
        return cars;
    }

    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

    public User() {
        System.out.println("User's Construtor...");
    }

    @Override
    public String toString() {
        return "User [userName=" + userName + ", cars=" + cars + "]";
    }

    public void init(){
        System.out.println("init method...");
    }

    public void destroy(){
        System.out.println("destroy method...");
    }

}
@Repository
public class UserRepository extends BaseRepository<User>{
}
配置文件
<?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.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="Spring4_IOC.di">

  </context:component-scan>
</beans>
    /**
     * Spring 4.x 新特性:泛型依赖注入
     */
    @Test
    public void testDI() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring4_IOC/beans-generic.xml");
        Spring4_IOC.di.UserService userService = (Spring4_IOC.di.UserService) ctx.getBean("userService");
        System.out.println(userService);
        userService.add();
    }
Spring4_IOC.di.UserService@225a8897
add...
Spring4_IOC.di.UserRepository@65bd831f
整合多个配置文件
Spring 允许通过 <import> 将多个配置文件引入到一个文件中,进行配置文件的集成。这样在启动 Spring 容器时,仅需要指定这个合并好的配置文件就可以。
i mport 元素的 resource 属性支持 Spring 的标准的路径资源



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值