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>
id
和class
是必须的,表示了一个具有唯一创造方式的一种类的对象。
init-method
和destroy-method
分别表示创建对象之后和销毁之前所要求调用的方法。
scope
表示对象的生存周期,也可以理解为对象在spring容器中的创建方式有singleton
和prototype
两种;
- 取值为
singleton
时,容器中创建时只存在一个实例,所有引用此bean都是单一实例,即单例模式。这是Spring容器默认的作用域。 - 取值为
prototype
时,spring容器在进行输出prototype的bean对象时,会每次都重新生成一个新的对象给请求方。
在Spring 2.0之后,为支持web应用的ApplicationContext,增强另外三种:request
,session
和global 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
有两个重要的属性,分别是name
和type
。 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的参考资料见
下一篇: 干IT太苦怎么办