欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring 笔记(一)

程序员文章站 2022-03-14 08:57:42
有时候我们在一个地方需要一个对象,但是这个对象的创建方式和属性随时有可能变化,此时,我们必须将对象的创建方式放在外部,而且可以对每一个需要创造的对象进行自定义。 这样,我们需要一个对象的时候,不必去写类似于: 这类带有复杂的、依赖于底部实现的、难以修改的创建对象的方式。 Spring 就是这样的一个 ......

有时候我们在一个地方需要一个对象,但是这个对象的创建方式和属性随时有可能变化,此时,我们必须将对象的创建方式放在外部,而且可以对每一个需要创造的对象进行自定义。

这样,我们需要一个对象的时候,不必去写类似于:

ComplicatedObject a = new 
ComplicatedObject(arg1, arg2, arg3);
a.son = new anotherComplicatedSubObject();

这类带有复杂的、依赖于底部实现的、难以修改的创建对象的方式。

Spring 就是这样的一个对象容器,它可以方便的进行控制反转并进行依赖注入。

我们先注意如何从容器中获得一个对象。

可以用

BeanFactory factory = new XmlBeanFactory(new ClassPathResource("cn/atcast/a_hello/applicationContext.xml"));

来构造一个bean工厂。但这个方法已经被废弃。

现在推荐使用应用上下文作为容器:

ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");

User user = (User) ac.getBean("user");

这样就可以从容器中得到一个User对象了。

生成该容器时,其中所有的对象就已经被创建了。

注意,得到的ClassPathXmlApplicationContext类的对象必须在最后销毁,调用destory()方法。

在XML文件里的配置有很多地方需要注意。

<bean id="user2" class="cn.atcast.b_create_obj.User" init-method="init_user" destroy-method="destroy_user" scope="singleton" lazy-init="false">
    
</bean>

idclass是必须的,表示了一个具有唯一创造方式的一种类的对象。

init-methoddestroy-method分别表示创建对象之后和销毁之前所要求调用的方法。

scope表示对象的生存周期,也可以理解为对象在spring容器中的创建方式有singletonprototype两种;

  • 取值为singleton时,容器中创建时只存在一个实例,所有引用此bean都是单一实例,即单例模式。这是Spring容器默认的作用域。
  • 取值为prototype时,spring容器在进行输出prototype的bean对象时,会每次都重新生成一个新的对象给请求方。

在Spring 2.0之后,为支持web应用的ApplicationContext,增强另外三种:requestsessionglobal session类型,它们只实用于web程序,通常是和XmlWebApplicationContext共同使用。

被注入的对象可以非常复杂,可以有多个不同的构造函数,有不同的getter和setter方法。或者是通过一个工厂类创建。

带参数的构造器必须在bean对象中写出每个构造器方法的参数,索引用index表示。

<!-- 2. 带参数构造器 -->
<bean id="user2" class="cn.atcast.b_create_obj.User">
    <constructor-arg index="0" type="int" value="100"></constructor-arg>
        <!-- 引用类型必须写全名 -->
    <constructor-arg index="1" type="java.lang.String" value="Jack"></constructor-arg>
</bean>

如果构造参数并不是基本类型或字符串,我们可以不用值,而用一个引用作为构造的参数。这个引用是Spring容器中的另外一种具有唯一构造方式的对象,有唯一的id。

<bean id="str" class="java.lang.String">
        <constructor-arg value="Jack"></constructor-arg>
    </bean>
<bean id="user3" class="cn.atcast.b_create_obj.User">
        <constructor-arg index="0" type="int" value="100"></constructor-arg>
        <constructor-arg index="1" type="java.lang.String" ref="str"></constructor-arg>
    </bean>

工厂类简单一些。先创建工厂,再创建对象。工厂作为一个额外的bean,用factory-bean获得对象,返回factory-method给出的对象。此外,如果工厂创建对象的方法是静态的,那就不必生产工厂的bean了。

<!-- 3. 工厂类创建对象 -->
<bean id="factory" class="cn.atcast.b_create_obj.ObjectFactory"></bean>
<bean id="user4" factory-bean="factory" factory-method="getInstance"></bean>
<bean id="user5" class="cn.atcast.b_create_obj.ObjectFactory" factory-method="getStaticInstance"></bean>

如果需要被创造的对象必须使用构造函数定义所有的值,那么这个对象每增加一个属性就必须添加一个构造函数的参数。这显然与开闭原则所背离。(即:“一个软件实体应当对扩展开放,对修改关闭。”)

我们可以使用setter对这个对象进行生成。

<bean id="user2" class="cn.atcast.c_property.User" scope="prototype">
        <property name="id" value="101"></property>
        <property name="name" value="Jack"></property>
    </bean>

在构造对象初始化之后,对象会对每一个property用调用其setter。

上文中提到的,为一个对象生成所用的其他对象,可以放在property内部成为内部bean。这样可以加强XML文件的层次化。

Spring3.0中提供了另一种方法,利用XML中的命名空间,可以直接给创建好的对象的属性注入值。只需要使用:p:<propety-name>=".."就可以了,这样就不必写property子句了。

<bean id="userDao" class="cn.atcast.c_property.UserDao"></bean>
<bean id="userService" class="cn.atcast.c_property.UserService" p:userDao-ref="userDao"></bean>
<bean id="userAction" class="cn.atcast.c_property.UserAction" p:userService-ref="userService"></bean>

这是不是还是很复杂?假设我们有一个长长的依赖链条:App <- UserAction <- UserServive <- UserDao,在XML中创建对象的时候就要依次添加属性。Spring的自动装配功能可以帮助我们减少工作。一旦一个对象有依赖的对象,Spring会在XML文件中寻找这个对象的类型,如果有一个唯一的对象,它就会自动成为其属性。

<bean id="userDao" class="cn.atcast.d_auto.UserDao"></bean> 
<bean id="userService" class="cn.atcast.d_auto.UserService"></bean>
<bean id="userAction" class="cn.atcast.d_auto.UserAction"></bean>

此时的依赖链条已经就绪。生成的userAction中就已经含有 userService。注意Spring根据类型自动装配: 必须确保IOC容器中只有一个该类型的对象。

我们能不能再简化一些——谁喜欢写XML?首先,XML是类型不安全的,打错了类型名或者方法名时,编译可以通过,但是运行就会崩溃。我们希望能利用Java严格的类型检查。

在Spring中,我们也可以利用注解。这时只需在XML中的beans句中写下:

<context:component-scan base-package="cn.atcast.e_anno"></context:component-scan>

默认情况下自动扫描指定路径下的包(含所有子包),将带有@Component@Repository@Service@Controller标签的类自动注册到spring容器。再调用应用上下文时,Spring就会利用注解生成对象容器了。

Spring 2.5 中除了提供 @Component注解外,还定义了几个拥有特殊语义的注解,它们分别是:@Repository@Service@Controller

@Service用于标注业务层组件

@Controller用于标注控制层组件(如struts中的action)

@Repository用于标注数据访问组件,即DAO组件

@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

Spring2.5为我们引入了组件自动扫描机制,他在类路径下寻找标注了上述注解的类,并把这些类纳入进spring容器中管理。

我们还是用上面的依赖链条举例。这里最底层的UserDao用

@Repository   // 在持久层可以选择用这个注解
public class UserDao {...}

表示一个持久层组件。

@Service   // 表示业务逻辑层的组件
public class UserService {
    @Resource   //根据类型查找
    private UserDao userDao;
    ...
}

表示一个服务层组件,这里用@Resource进行了注入。

同理,对于控制层:

@Controller  // 控制层的组件
public class UserAction {
    //@Resource(name = "userService")
    @Resource
    private UserService userService;
}

这里也给出了一般的写法,@Resource有两个重要的属性,分别是nametype。 Spring将name属性解析为bean的名字(最需要重新理解的就是这个bean名字),而type属性则解析为bean的类型。

如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。 在上文中就自动注入了这个类唯一的对象。

getBean的默认名称是类名(头字母小写),如果想自定义,(“aaaaa”)这样来指定。
这种bean默认是“singleton”的,如果想改变,(“prototype”)来改变。

private ApplicationContext ac = new ClassPathXmlApplicationContext("cn/atcast/e_anno/bean.xml");
UserAction userAction = (UserAction) ac.getBean("userAction");

使用注解的不足之处是,你只能用注解注入你自己的类。你无法修改别人的代码使之支持注解。

Spring 2.5的参考资料见