当前位置: 移动技术网 > IT编程>开发语言>Java > Spring 笔记(一)

Spring 笔记(一)

2018年06月21日  | 移动技术网IT编程  | 我要评论

湖北经济学院法商学院教务网,阿牧卡,北京到燕郊

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

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

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的参考资料见

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网