Spring学习总结(1)- IOC
一、Spring框架概述
Spring是一个开源免费的框架,为了解决企业应用开发的复杂性而创建。Spring框架是一个轻量级的解决方案,可以一站式地构建企业级应用。Spring是模块化的,所以可以只使用其中需要的部分。可以在任何web框架上使用控制反转(IoC),也可以只使用Hibernate集成代码或JDBC抽象层。它支持声明式事务管理、通过RMI或web服务实现远程访问,并可以使用多种方式持久化数据。它提供了功能全面的MVC框架,可以透明地集成AOP到软件中。
Spring被设计为非侵入式的,这意味着你的域逻辑代码通常不会依赖于框架本身。在集成层(比如数据访问层),会存在一些依赖同时依赖于数据访问技术和Spring,但是这些依赖可以很容易地从代码库中分离出来。
Spring框架是基于Java平台的,它为开发Java应用提供了全方位的基础设施支持,并且它很好地处理了这些基础设施,所以你只需要关注你的应用本身即可。
Spring可以使用POJO(普通的Java对象,plain old java objects)创建应用,并且可以将企业服务非侵入式地应用到POJO。这项功能适用于Java SE编程模型以及全部或部分的Java EE。
那么,做为开发者可以从Spring获得哪些好处呢?
1,不用关心事务API就可以执行数据库事务;
2,不用关心远程API就可以使用远程操作;
3,不用关心JMX API就可以进行管理操作;
4,不用关心JMS API就可以进行消息处理。
①JMX:Java Management eXtension,Java管理扩展,是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
②JMS:Java Message Service,Java消息服务,是Java平台上有关面向消息中间件(MOM)的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。
1,Spring框架特征与功能
轻量 |
从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。 |
控制反转Ioc |
Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。 |
面向切面Aop |
Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。 |
容器 |
Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。 |
框架 |
Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。 |
MVC |
Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架,Spring MVC是一个非常受欢迎的轻量级Web框架。 |
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
2,Spring组成
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。Spring结构如下:
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器 |
核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。 |
Spring 上下文 |
Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 |
Spring AOP |
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。 |
Spring DAO |
JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。 |
Spring ORM |
Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。 |
Spring Web 模块 |
Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。 |
Spring MVC 框架 |
MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。 |
Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。
二,Eclipse中创建Maven Web项目
创建Maven Project
创建一个简单的项目,不使用模板
"Group Id"中输入项目的基本包名,在"Artifact Id"中输入项目名,"Packaging"选择war打包方式
刚创建出来的Maven项目报错,说找不到Web.xml文件:
鼠标右键项目-->Properties-->Project Facets如下所示:
选择java版本为1.8(我系统中的JDK版本是1.8的),并去掉Dynamic Web Module,点击Apple应用,如下图:
重新勾选上Dynamic Web Module,并选择Version,如下所示:
点击Further configuration available...,创建webRoot目录,点击ok,如下所示:
点击Apply,点击ok
这样就不会再报找不到Web.xml的错误了。
完整的项目结果如下:
三,IoC
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
四,使用XML方式实现IOC(控制反转)
pom.xml文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>Zender</groupId> <artifactId>SpringDemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.0.RELEASE</spring.version> </properties> <dependencies> <!-- Spring Core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </project>
1,创建对象
创建2个实体类:Person,ExtendedAttribute
Person:
public class Person { private String name; private int age; private ExtendedAttribute exAttribute; public Person(String name, int age, ExtendedAttribute exAttribute) { this.name = name; this.age = age; this.exAttribute = exAttribute; } public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public ExtendedAttribute getExAttribute() { return exAttribute; } public void setExAttribute(ExtendedAttribute exAttribute) { this.exAttribute = exAttribute; } public String toStringNameAge() { return "Person [name=" + name + ", age=" + age + "]"; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", sex=" + exAttribute.getSex() + ", addrs=" + exAttribute.getAddrs() + "]"; } }
ExtendedAttribute:
//Person的扩展属性类 public class ExtendedAttribute { private String sex; private String addrs; public ExtendedAttribute(String sex, String addrs) { this.sex = sex; this.addrs = addrs; } public ExtendedAttribute() { } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddrs() { return addrs; } public void setAddrs(String addrs) { this.addrs = addrs; } }
IOCBeans.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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 1,无参构造方法创建对象 --> <bean id="person" class="com.zender.bean.Person"></bean> <!--,2,有参构造方法创建对象 --> <bean id="person2" class="com.zender.bean.Person"> <constructor-arg name="name" value="Zender"/> <constructor-arg name="age" value="21"/> </bean> <!-- 也可是使用索引来确定参数 --> <!-- <bean id="person2" class="com.zender.bean.Person"> <constructor-arg name="0" value="Zender"/> <constructor-arg name="1" value="21"/> </bean> --> <!-- 3,有参构造方法创建对象 --> <bean name="personExtendedAttribute" class="com.zender.bean.ExtendedAttribute"> <constructor-arg name="sex" value="男"/> <constructor-arg name="addrs" value="重庆"/> </bean> <bean id="person3" class="com.zender.bean.Person"> <constructor-arg name="name" value="Zender"/> <constructor-arg name="age" value="21"/> <constructor-arg name="exAttribute" ref="personExtendedAttribute"/> </bean> </beans>
测试类:
public class PersonTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("IOCBeans.xml"); //使用无参构造创建对象 Person p = (Person) context.getBean("person"); System.out.println(p.toStringNameAge()); //使用有参构造创建对象 p = (Person) context.getBean("person2"); System.out.println(p.toStringNameAge()); //使用有参构造属性赋值 p = (Person) context.getBean("person3"); System.out.println(p.toString()); } }
运行结果:
2,对象的作用域
从IOC容器中取回的对象默认是单例的:
public class PersonTest2 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("IOCBeans.xml"); Person p = (Person) context.getBean("person"); Person p2 = (Person) context.getBean("person"); System.out.println(p == p2); } }
运行结果:
我们可以使用scope属性指定作用域,作用域有一下7个:
修改id为person的bean:
<bean id="person" class="com.zender.bean.Person" scope="prototype"></bean>
运行结果:
3,延迟初始化bean
ApplicationContext实现的默认行为就是再启动时将所有 singleton bean提前进行实例化。 通常这样的提前实例化方式是好事,因为配置中或者运行环境的错误就会被立刻发现,否则可能要花几个小时甚至几天。如果你不想 这样,你可以将单例bean定义为延迟加载防止它提前实例化。延迟初始化bean会告诉Ioc容器在第一次需要的时候才实例化而不是在容器启动时就实例化。
在XML配置文件中,延迟初始化通过<bean/>元素的lazy-init属性进行控制,比如:
<bean id="person" class="com.zender.bean.Person" lazy-init="true"></bean>
4,初始化bean,执行回调函数方法
在XML配置文件中,回调函数方法通过init-method属性和destroy-method属性进行控制,比如:
person:
public class Person { private String name; private int age; private ExtendedAttribute exAttribute; public Person(String name, int age, ExtendedAttribute exAttribute) { this.name = name; this.age = age; this.exAttribute = exAttribute; } public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public ExtendedAttribute getExAttribute() { return exAttribute; } public void setExAttribute(ExtendedAttribute exAttribute) { this.exAttribute = exAttribute; } public void init(){ System.out.println("调用person的init方法!"); } public void destroy(){ System.out.println("调用person的destroy方法!"); } }
修改id为person的bean:
<bean id="person" class="com.zender.bean.Person" init-method="init" destroy-method="destroy"></bean>
测试代码:
public class PersonTest2 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("IOCBeans.xml"); Person p = (Person) context.getBean("person"); } }
运行结果:
五,使用注解方式实现IOC(控制反转)
在使用传统的xml配置完成IOC的,如果内容比较多则配置需花费很多时间,通过注解可以减轻工作量,但注解后修改要麻烦一些,偶合度会增加,应该根据需要选择合适的方法。例如:
项目结果整体如下:
User实体类:
public class User { private String name; private String pwd; public User(String name, String pwd) { this.name = name; this.pwd = pwd; } public User() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User [name=" + name + ", pwd=" + pwd + "]"; } }
iUserDao接口:
public interface iUserDao { public String addUser(User u); }
UserDao类:
@Component public class UserDao implements iUserDao { @Override public String addUser(User u) { return "添加用户" + u.getName() + "成功!"; } }
注意:
在类上增加了一个注解Component,在类的开头使用了@Component注解,它可以被Spring容器识别,启动Spring后,会自动把它转成容器管理的Bean。使用Compont注解时不指定名称时,Spring会使用默认的命名机制,即简单类名且第一个字母小写。
除了@Component外,Spring提供了3个功能基本和@Component等效的注解,分别对应于用于对DAO,Service,和Controller进行注解:
1,@Repository 用于对DAO实现类进行注解。
2,@Service 用于对业务层注解,但是目前该功能与 @Component 相同。
3,@Constroller用于对控制层注解,但是目前该功能与 @Component 相同。
UserService类:
@Component public class UserService { @Autowired private iUserDao userDao; public void UserServiceAddUser(User u){ String text = userDao.addUser(u); System.out.println(text); } }
注意:
这里使用了自动装配,在iUserDao成员变量上增加了一个注解@Autowired,该注解的作用是:
可以对成员变量、方法和构造函数进行注解,来完成自动装配的工作,通俗来说就是会根据类型从容器中自动查到到一个Bean给iUserDao成员变量。
装配注解主要有:@Autowired、@Qualifier、@Resource,它们的特点是:
1、@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入,@Resource注解是由J2EE提供,而@Autowired是由spring提供,故减少系统对spring的依赖建议使用@Resource的方式;如果Maven项目是1.5的JRE则需换成更高版本的。例如:
@Resource(name="iUserDao2") private iUserDao userDao;
2、@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用,例如:
@Autowired private iUserDao userDao;
3、@Resource和@Autowired都可以书写注解在字段或者该字段的setter方法之上。
4、@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的注解对象是成员变量、方法入参、构造函数入参。
5、@Qualifier("XXX") 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。例如:
@Autowired @Qualifier("iUserDao2") private iUserDao userDao;
6、@Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个,通过属性required可以设置非必要。
@Autowired(required=true) @Qualifier("iUserDao2") private iUserDao userDao;
7、@Resource装配顺序
1,如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
2,如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
3,如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常。
4,如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
IOCBeans.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:p="http://www.springframework.org/schema/p" 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-4.3.xsd"> <context:component-scan base-package="com.zender"></context:component-scan> </beans>
注意:
粗斜体字是新增的xml命名空间与模式约束文件位置。增加了注解扫描的范围,指定了一个包,可以通过属性设置更加精确的范围如:
<context>标记常用属性配置:
resource-pattern:对指定的基包下面的子包进行选取。
<context>子标记:
include-filter:指定需要包含的包。
exclude-filter:指定需要排除的包。
例如:
<!-- aspectj类型,扫描com.zender.dao下所有的类,排除entity下所有的类 --> <context:component-scan base-package="com.zender" > <context:include-filter type="aspectj" expression="com.zender.dao. *" /> <context:exclude-filter type="aspectj" expression="com.zender.entity.*.*" /> </context:component-scan>
1,resource-pattern:仅希望扫描特定的类而非基包下的所有类。
2,include-filter:需要包含的目标类型。
3,exclude-filter:需要排除的目标类型。
4,type表示采的过滤类型,共有如下5种类型:
过滤类型 |
例如 |
解释 |
annotation |
org.example.SomeAnnotation |
注解了SomeAnnotation类型的类 |
assignable |
org.example.SomeClass |
所有扩展或者实现SomeClass的类 |
aspectj |
org.example..*Service+ |
AspectJ语法表示org.example包下所有包含Service的类及其子类 |
regex |
org\.example\.Default.* |
Regelar Expression,正则表达式 |
custom |
org.example.MyTypeFilter |
通过代码过滤,实现org.springframework.core.type.TypeFilter接口 |
例如:
<!-- 扫描注解了org.springframework.stereotype.Repository的类 --> <context:component-scan base-package="com.zender"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" /> </context:component-scan>
测试类:
public class IOCTest { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("IOCBeans.xml"); UserService us = context.getBean("userService", UserService.class); us.UserServiceAddUser(new User("Zender","123")); } }
运行结果: