Java 18:Spring 1(装配Bean)

来源:互联网 发布:windows 2008 server 编辑:程序博客网 时间:2024/05/23 18:11

本来不想对Spring学习过程写详细的博客的,因为觉得Spring还是工程上的东西,实践大于理论,况且各种配置什么比较繁琐,这一写,没个七八篇下不来。但是学习了《Spring实战》以后比较烦,感觉这本书并不那么好,其他就不说了,上面好些代码有问题就不能忍了,作者从理论介绍的角度还可以,但是从初学者接地气的方面,就不太好了。例子比较复杂、抽象,代码大小写问题屡见不鲜,不少代码运行不了,大量用了JUnit,我又不会那个。然后三种配置方式交替说明,虽然全面,但是也容易混乱,所以,还是一步一步地从头学起,不能光依赖这本书了。后文主要还是顺着《Spring实战》的顺序学习,但很多具体实现是网上的博客的例子。

-----------------------------------------------------------------------------------------------------------------------------------------


Spring诞生之初,主要是代替更加重量级的企业级Java技术,尤其是EJB,它增强了“简单老式Java对象”POJO的功能,使其具备了之前只有EJB和其他企业级Java规范才有的功能。

EJB:Enterprise Java Beans(EJB)称为Java 企业Bean,是sun的JavaEE服务器端组建模型。设计目标与核心应用是部署分布式应用程序。EJB相比现在的框架,更加重量级,更加繁琐,用的人已经比较少。

POJO:普通的Java bean,是为了避免和EJB混淆创造的简称。具有一部分getter/setter方法的类就可以称作POJO

Java Bean:比POJO复杂,Java Bean是可重用的平台独立的软件组件,没有严格的规范,但通常情况下,由于Java Bean是被容器(比如Tomcat)所创建的,因此应该具有无参的构造器。另外,一般还需要实现Serialzable接口用于实现Bean的持久性。还有人说,提供getter、setter也是要求之一。

Java Bean主要用来存储状态信息,EJB则可以存储业务逻辑。


总之,Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的Java Bean实现之前只有EJB能完成的事。虽然Spring用Bean来表示组件,但是并不需要遵循 Java Bean规范,一个Spring组件可以是任何形式的POJO。为了降低Java开发的复杂性,Spring采取以下几种策略:

1、基于POJO的轻量级、最小侵入的编程

2、依赖注入、面向接口实现松耦合

3、基于切面和惯例进行声明式编程

3、基于切面和模板减少样板式代码


1、依赖注入:

Spring极力避免自身的API弄乱你的应用代码,许多框架要求继承它们的类或接口,而导致应用于框架绑死,比如EJB2时期的无状态会话Bean。居于Spring构建应用,通常没有任何痕迹说明你使用了Spring,即使使用了Spring注解,它依然是一个POJO。

按照传统的做法,每个对象管理自己依赖的对象,这将导致高度耦合和难以测试的代码。而通过依赖注入,对象之间的依赖关系将由第三方管理。

//传统依赖关系public class Knight{private RescueDamseQuest quest;public Knight(){this.quest=new RescueDamseQuest();//生命周期由自己创建管理,且限定到具体的实现类}public void embackQuest(){quest.embark();}}
//采用构造器注入方式public class Knight{private Quest quest;//不用声明为具体实现类,只需要给一个接口,使得Knight能响应任意实现public Knight(Quest quest){this.quest=quest;//自动注入进来,不需要显式声明}public void embackQuest(){quest.embark();}}
依赖注入的最大好处就是松耦合,创建组件之间协作行为成为“装配”,最普遍的就是将之用XML表示。


2、应用切面

面向切面编程AOP,允许把遍布应用各处的功能分离出来形成可重用的组件。诸如日志、安全、事务管理这些系统服务经常要融入到其他业务组件中,这些系统服务称为横切关注点,因为它们横跨多个系统。

关注点分散到多个组件中,带来了复杂性,出现重复代码,使自身代码混乱。AOP能够使这些服务模块化。并以声明式的方式将它们应用到需要的组件中,结果就是这些组件会有更高的内聚性,更加专注于自身的业务。


3、使用模板消除样本模板

类似于JDBC,真正查询数据库的代码可能只有一两行,其他都是重复的操作,Spring的JabcTemplate可以让你关注核心业务,免去大量的模板代码


在Spring中,应用对象存在于Spring容器中,Spring负责创建对象、装配它们,配置并管理它们的整个生命周期,从new到死亡。容器是Spring框架的核心,Spring容器使用DI(依赖注入)管理组件。Spring有多种容器实现:bean工厂、和基于Bean工厂构建的“应用上下文”。应用上下文比Bean factory更加高级,也是我们主要用到的地方。

常见的应用的上下文:

AnnotationConfigApplicationContext:从Java配置类加载Spring应用的上下文

AnnotationConfigWebApplicationContext:从Java配置类加载Spring Web应用的上下文

ClassPathXmlApplicationContext:从类路径下的XML文件加载上下文

FileSystemXmlApplicationContext:从文件系统下的XML加载上下文

XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上下文

首先,这些名字比较复杂,但是《Spring实战》几行里出现了多处大小写问题让我很不爽(难道买到了假的书?)。可以看出,主要是XML和Java配置类两种方式进行上下文的配置。对于配置文件的路径、读取方式,书里面好多也没写清楚,包括网上的很多例子,这是前期敲代码给我造成很多困扰的地方。


Bean的生命周期:

比较复杂,可能也比较重要,但现在看不懂,步骤太多,先留着。


一、装配Bean:

Spring3种装配:1、XML    2、Java显式装配    3、隐式的Bean发现机制和自动装配。 3种方式可以混合使用,也有一些不同的应用场景,比如使用第三方Bean的时候,就没法使用注解来自动装配。

建立一个叫beans.xml的文件来说明Bean之间的依赖关系,前面提到的几种上下文中,有基于Web和不基于Web之分。我们知道Web中有WEF-INF、Web.xml等固有的文件和文件夹,这也是让我比较混乱的地方,不知道各种配置文件的路径怎么表示,这里先从啥都没有的普通Java项目开始,这里的代码主要来自:http://blog.csdn.net/heyutao007/article/details/5981555,很通俗易懂,感谢,Java配置部分的配置则来源于于书上的例子(《Spring实战》最喜欢Java配置方式了)。

(1)XML装配Bean:

Boss.java

package com.baobaotao;public class Boss {private Car car;private Office office;        //以及两个字段对应的set、get方法,这种xml基本配置情况下,任意一个Bean没有两个方法会创建Bean失败public String toString(){return "car:"+car+"  office:"+office;}//输出用,并非必须}

Car.java

package com.baobaotao;public class Car {private String brand;private Double price;//同样的篇幅原因,省略get、set,自己要补上public String toString(){return brand+" "+price;}}
Office.java

package com.baobaotao;public class Office {private String officeNo;public String toString(){return officeNo;}//get、set}
beans.xml:后面使用的是ClassPathXmlApplicationContext,意味着默认路径是类路径,也就是src目录下,和package同路径

<?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="boss" class="com.baobaotao.Boss">    <property name="car" ref="car"/>    <property name="office" ref="office"/>    </bean>          <bean id="office" class="com.baobaotao.Office">             <property name="officeNo" value="001"/>         </bean>         <bean id="car" class="com.baobaotao.Car" scope="singleton">             <property name="brand" value=" 红旗 CA72"/>             <property name="price" value="2000"/>         </bean>     </beans>    

这里的注意点,也只是beans后面的引用不要打错或者缺少。offfice、car设置了初值。注意三个Bean我们都没有显式设置任何构造函数和设置域

如果,我们想要借助构造函数来设置Bean,就需要在java类里显式声明构造函数,比如Car(String,Double),然后XML就以构造函数方式填写参数,用<constructor-arg>而不是<property>(spring3引入的c-命名空间可以代替constructor,但我最讨厌这些标签表达式了):

<bean id="car" class="com.baobaotao.Car" scope="singleton">     
        <constructor-arg  value=" 红旗 CA72"/>     
        <constructor-arg  value="2000"/>     
 </bean>   

最后的效果是一样的。注意:构造器注入需要对应的构造函数,property属性设置则需要对应的getter、setter方法

main函数

    public static void main(String[] args) {             String[] locations = {"beans.xml"};             ApplicationContext ctx =                  new ClassPathXmlApplicationContext(locations);    //这种上下文的基本路径就是类路径,src下         Boss boss =ctx.getBean(Boss.class);         Boss boss2 =(Boss) ctx.getBean("boss");  //根据id和类名都可以从上下文得到实例,但是根据id需要进行类型强转        System.out.println(boss2);       }  


上述就是最最基本的XML配置方法,Spring2.5以后有了自动化的装配,减小了XML的使用,能够将显式配置降低。

自动装配:Autowired

beans.xml:

    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>   //增加使用注解的功能        <bean id="boss" class="com.baobaotao.Boss"/>  //介绍对两个property的声明
Boss.java:  只修改了Boss类,而Car和Office还不会用注解进行设置,不做改变

public class Boss {@Autowiredprivate Car car;@Autowiredprivate Office office;public String toString(){return "car:"+car+"  office:"+office;}}
使用@Autowired进行域的自动装配,并且可以省略getter和setter的声明

另一种对“构造函数”的自动装配,效果也是一样

public class Boss {private Car car;private Office office;@Autowiredpublic Boss(Car car,Office office){this.car=car;this.office=office;}public String toString(){return "car:"+car+"  office:"+office;}}
最后一种针对setter的自动装配:

public class Boss {private Car car;private Office office;@Autowiredpublic void setCar(Car car) {this.car = car;}@Autowiredpublic void setOffice(Office office) {this.office = office;}public String toString(){return "car:"+car+"  office:"+office;}}
以上三种情况,最终的效果是一样的。

(2)、XML组件扫描:

不仅希望将依赖关系通过注解来实现,也能通过组件扫描,让Bean也不需要显式在XML里声明:

beans.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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">      //有了几个新增的,一定要写对,不然会报错      <context:component-scan base-package="com.baobaotao" /> //对baobaotao进行组件扫描,不用再显式声明Boss              <bean id="office" class="com.baobaotao.Office">     //依赖的这些类,不会自动装配设置初值,先留着(上面用于注解的一句话也不需要了)        <property name="officeNo" value="001"/>         </bean>         <bean id="car" class="com.baobaotao.Car" scope="singleton">             <property name="brand" value=" 红旗 CA72"/>             <property name="price" value="2000"/>         </bean>     </beans>  
Boss.java

@Component   //Component代表扫描时,当成Bean,也就是实现了组件自动扫描的功能public class Boss {@Autowiredprivate Car car;@Autowiredprivate Office office;public String toString(){return "car:"+car+"  office:"+office;}}


以上就是XML声明的组件扫描+自动装配功能。Spring还提供了不用XML的方式。设置XML挺烦的,还有那么多Schema要考虑,下面编写了一个Java类来表示自动装配的功能:

(3)Java配置类实现组件扫描+自动装配

BossConfig.java

@Configuration@ComponentScan     //功能和xml里的组件扫描一样public class BossConfig {}
Boss.java、Car.java、Office.java

@Componentpublic class Boss {@Autowiredprivate Car car;@Autowiredprivate Office office;public String toString(){return "car:"+car+"  office:"+office;}}
@Componentpublic class Car {private String brand="brand";private Double price=(double) 2000;public String toString(){return brand+" "+price;}}
@Componentpublic class Office {private String officeNo="no";public String toString(){return officeNo;}}
main方法

        ApplicationContext ctx =new AnnotationConfigApplicationContext(BossConfig.class);     //换成相应的上下文        Boss boss =(Boss) ctx.getBean(Boss.class);          System.out.println(boss); 

(4)纯Java代码装配Bean

用Java代码装配Bean的作用我感觉和XML基本一样,只是把XML的功能移到一个Java Config类里,那和前面的自动装配有什么不同?只能说,Java Config更加显式一点,每一个Bean都是明明白白的设置的(虽然感觉@Component也够明显的了),并且所有的Bean是集中放置,这就和XML更接近了,当然由于Java允许多种装配方式混用,所以用着用着就搞不清它们之间的界限了。

CDplayer.java:  没有什么额外的东西,就是一个普通的POJO

public class CDplayer {private Cd cdprivate;public CDplayer(Cd cd) {this.cdprivate=cd;}public String toString(){return "player:"+cdprivate.name;}}
Cd.java :依赖的另一个Bean

public class Cd {public String name;public Cd(String name){this.name=name;}}
Config.java  :证明Bean的配置类,和前面组件扫描的配置类的区别是1、多了每个Bean的专门声明  2、少了ComponentScan设置

@Configurationpublic class Config {@Beanpublic CDplayer cdplayer(Cd cd){return new CDplayer(cd);}@Beanpublic Cd cd(){return new Cd("as");}}
有了前面的基础,相信这些代码都不难懂,@Bean注解表明了CDplayer和Cd都变成了Bean,需要注意的是,这里Bean的名字可以显式声明,否则会以函数名作为名字,也就是cdplayer和cd。

最后的测试也和前面一样:

main函数:

public static void main(String[] args) {// TODO Auto-generated method stub        ApplicationContext ctx =new AnnotationConfigApplicationContext(Config.class);           CDplayer boss = ctx.getBean(CDplayer.class);             System.out.println(boss);   }

原创粉丝点击