1.Spring定义
Spring 是一个开源框架,Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson在其著作 Expert One-On-One J2EE Development and Design 中阐述的部分理念和原型衍生而来。
它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE应用程序开发提供集成的框架。Spring 使用基本的 JavaBean 来完成以前只可能由 EJB 完成的事情。然而,Spring 的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 的核心是控制反转(IOC)和面向切面(AOP)。简单来说,Spring 是一个分层的 轻量级开源框架。
2.为什么使用Spring
- 方便解耦,简化开发
- Spring 就是一个大工厂,可以将所有对象创建和依赖关系维护,交给 Spring 管理
- AOP 编程的支持,Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
- 声明式事务的支持只需要通过配置就可以完成对事务的管理,
- 而无需手动编程方便程序的测试,Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序
- 方便集成各种优秀框架,Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持
- 降低 JavaEE API 的使用难度,Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等),都提供了封装,使这些 API 应用难度大大降低
3.Spring环境搭建
3.1 创建Spring项目,并引入相关依赖
3.1.1项目创建
3.1.2 引入spring的依赖
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>4.2.5.RELEASE</spring.version>
</properties>
<!--Spring核心基础依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<!--日志相关-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--测试相关-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
3.2添加配置文件
配置文件的名字可以任意设置,建议设置为applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
3.3 添加相应的模块
在项目中添加dao、model、service层代码
3.4将bean的实例化交给Spring
<bean name="userDao" class="com.tledu.spring.dao.impl.UserDaoImpl" />
<!--name 属性名字/ref 引用的bean的名字-->
<bean id="userService" class="com.tledu.spring.service.UserService">
<property name="userDao" ref="userDao" />
</bean>
4.通过构造方法注入
<bean name="userDao" class="com.tledu.spring.dao.impl.UserDaoImpl" />
<bean id="userService" class="com.tledu.spring.service.UserService">
<constructor-arg ref="userDao"/>
</bean>
5.普通属性注入
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl">
<!-- 如果不是指向对象,直接用value设置值就行 -->
<property name="daoId" value="82"></property>
<property name="daoStatus" value="good"></property>
</bean>
6.scope
spring中scope是一个非常关键的概念,简单说就是对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。
目前,scope的取值有5种取值:
在Spring 2.0之前,有singleton和prototype两种;
在Spring 2.0之后,为支持web应用的ApplicationContext,增强另外三种:request,session和global session类型,它们只实用于web程序,通常是和XmlWebApplicationContext共同使用
6.1singleton
此取值时表明容器中创建时只存在一个实例,所有引用此bean都是单一实例。
也就是说创建对象是单例模式,并且如果不进行设置,默认就行单例
6.2prototype
每次获取都创建一个新对象,并且这个对象的生命周期不归Spring管理
6.3配置scope方式
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl">
<!-- 如果不是指向对象,直接用value设置值就行 -->
<property name="daoId" value="82"></property>
<property name="daoStatus" value="good"></property>
</bean >
<!-- scope singleton : 单例 只创建一个,默认就是
prototype : 每一次getBean 都会创建一个新的实例化对象
request,session : 需要特殊环境支持 -->
<bean id="userService" class="com.tledu.zrz.service.UserService"
scope="singleton">
<!-- 构造方法注入 -->
<constructor-arg>
<ref bean="userDao" />
</constructor-arg>
</bean>
7.集合属性注入
7.1相关类
UserDaoImpl中提供对应的变量
private List<String> lists;
private Set<String> sets;
private Map<String, String> maps;
public List<String> getLists() {
return lists;
}
public void setLists(List<String> lists) {
this.lists = lists;
}
public Set<String> getSets() {
return sets;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
public Map<String, String> getMaps() {
return maps;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
7.2配置文件
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl">
<property name="lists" >
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<property name="sets" >
<!-- set不可重复,重复不添加,所以只有第一个三 -->
<set>
<value>3</value>
<value>3</value>
<value>5</value>
<value>4</value>
</set>
</property>
<property name="maps">
<!-- mapkey不可重复,重复key不添加,value覆盖 -->
<map>
<entry key="1" value="2"></entry>
<entry key="1" value="3"></entry>
<entry key="2" value="2"></entry>
</map>
</property>
</bean>
<bean id="userService" class="com.tledu.zrz.service.UserService"
scope="singleton">
<!-- 构造方法注入 -->
<constructor-arg>
<ref bean="userDao" />
</constructor-arg>
</bean>
8.自动装配
上面我们进行对象注入的时候有两种方式
1 set方法注入
<property name="userDao" ref="userDao" />
2 构造方法注入
<constructor-arg>
<ref bean="userDao"/>
</constructor-arg>
现在我们来学习自动注入,就是不需要指定以上两种方式
Autowire : 自动装配,两种取值
1 byName
2 byType
8.1byName
byName是根据setter方法名字进行匹配,如果找不到,就不赋值
如 setUserDao 方法 就会找userDao,如果 bean的ID为 UserDao 也一样找不到,区分大小写
设置方式
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl">
</bean>
<bean id="userService" class="com.tledu.zrz.service.UserService"
autowire="byName">
</bean>
8.2byType
byType是根据setter方法的参数列表中的数据类型进行匹配,如果beans.xml中出现了多个相同类型的对象,就会报错
如 setUserDao(UserDao userDao) 方法 就会找UserDao,如果是接口,就找对应的实现类对象
设置方式
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl">
</bean>
<bean id="userService" class="com.tledu.zrz.service.UserService"
autowire="byType">
</bean>
注意 : 使用自动装配,需要有公共的无参构造,虽然这里就算是私有化构造方法也依然可以创建对象,但是还是提供一个公共的比较好,万一别的框架需要呢
9.生命周期和迟加载
之前servlet的生命周期
构造方法 -- init -- service -- destroy
那么spring创建对象的生命周期呢?
Spring中是没有init、service和destroy的,但是我们可以指定某个方法在创建完对象之后执行,某个方法在最后销毁的时候执行
9.1生命周期
9.1.1相关类
UserService中提供对应的方法
public void init(){
System.out.println("init--------");
}
public void destroy(){
System.out.println("destroy----------");
}
public UserService() {
System.out.println("service构造方法");
}
9.1.2配置文件
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl" >
</bean>
<!--
init-method : 用于设置初始化的方法
destory-method : 用于设置销毁资源的方法
-->
<bean id="userService" class="com.tledu.zrz.service.UserService"
autowire="byName" init-method="init" destroy-method="destroy" >
</bean>
9.2迟加载
Spring容器默认是在执行
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
进行解析的时候创建对象并调用init,
如果我们不想让某个类在解析的时候就创建对象,而是用到的时候在创建对象的话,就需要设置迟加载
比如想要对userService迟加载可以这样设置
9.2.1相关类
在UserDaoImpl和UserService中的构造方法添加输出语句进行测试
public UserDaoImpl(){
System.out.println("Dao构造方法");
}
public UserService() {
System.out.println("service构造方法");
}
9.2.2配置文件
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl" >
<bean id="userService" class="com.tledu.zrz.service.UserService" scope="prototype"
autowire="byName" init-method="init" destroy-method="destroy" lazy-init="true" >
</bean>
为了使用方便,Spring还提出了default-lazy-init="true"
比如我们通过xml创建了10个对象,这10个对象都需要迟加载,那么每个bean标签中都设置lazr-init是比较麻烦的
于是我们可以在beans标签中添加default-lazy-init="true" 对所有的bean进行迟加载
default-lazy-init 和 lazy-init 可以同时存在,比如 10个对象中 只有一个不需要迟加载,那么可以使用 default-lazy-init = true 全部设置迟加载,然后再去指定的bean中添加 lazy-init=false 就可以
<?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" default-lazy-init="true" >
10.IOC注解
10.1 XML和注解的区别
10.1.1 XML配置
优点有:
- XML配置方式进一步降低了耦合,使得应用更加容易扩展,即使对配置文件进一步修改也不需要工程进行修改和重新编译。
- 在处理大的业务量的时候,用XML配置应该更加好一些。因为XML更加清晰的表明了各个对象之间的关系,各个业务类之间的调用。同时spring的相关配置也能一目了然。
缺点有:
配置文件读取和解析需要花费一定的时间,配置文件过多的时候难以管理,无法对配置的正确性进行校验,增加了测试难度。
10.1.2annotation配置
优点有:
- 在class文件中,可以降低维护成本,annotation的配置机制很明显简单
- 不需要第三方的解析工具,利用java反射技术就可以完成任务
- 编辑期可以验证正确性,查错变得容易
- 提高开发效率
缺点有:
- 如果需要对于annotation进行修改,那么要重新编译整个工程
- 业务类之间的关系不如XML配置那样容易把握。
- 如果在程序中annotation比较多,直接影响代码质量,对于代码的简洁度有一定的影响
10.2注解注入使用步骤
10.2.1Autowired
10.2.1.1导包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
10.2.1.2 开启注解的支持
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入注解约束 -->
<!-- 使用注解形式自动装配 -->
<context:annotation-config />
<context:component-scan base-package="需要扫描的包名"/>
</beans>
10.2.1.3业务类
//类名上添加Component注解
@Component
public class UserService {
private IUserDao userDao;
//需要注入的变量上添加@Autowired
@Autowired
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
}
//获取bean时需要类名小驼峰
application.getBean("addressServiceImpl")
10.2.1.4知识点
@Autowired(自动封装)
该注解可以加在set方法上或者直接加载属性上,如果写在setter方法上,就会通过setter方法进行注入,如果写在变量上,就直接通过反射设置变量的值,不经过setter方法。
注入时,会从spring容器中,找到一个和这个属性数据类型匹配的实例化对象注入进来,默认使用byType,根据类型匹配。
如果只能找到一个这个数据类型的对象的时候,就直接注入该对象。
如果找到了多个同一个类型的对象的时候,就会自动更改为byName来进行匹配,根据set方法对应的参数列表的局部变量名来匹配。
@Qualifier :
以指定名字进行匹配
private IUserDao userDao;
@Autowired
public void setUserDao(@Qualifier(“userDao2”)IUserDao userDao){};
这时候就不会按照userDao来进行匹配了,而是强制使用userDao2来进行比配,也就不会按照类型匹配了
10.2.1.5配置文件
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入注解约束 -->
<!-- 使用注解形式自动装配 -->
<context:annotation-config />
<bean name="userDao" class="com.tledu.zrz.dao.impl.UserDaoImpl">
<property name="daoId" value="1"></property>
</bean>
<bean name="userDao2" class="com.tledu.zrz.dao.impl.UserDaoImpl">
<property name="daoId" value="2"></property>
</bean>
<bean id="userService" class="com.tledu.zrz.service.UserService">
</bean>
</beans>
10.2.2Resource
Resource这个注解是javaEE的,在javax包下,所以不需要导入其他jar包
@Resource默认使用byName的方式,按照名字匹配,可以写在setter方法上也可以写在变量上
先匹配set方法的名字,匹配不上再匹配方法参数列表的名字
如果还是匹配不上就会转换为byType,根据类型匹配
当然我们也可以指定名字
@Resource(name=”userDao”)
就相当于 Autowired和Qualifier 一起使用
相关的还有一个 @Inject 根据type匹配,通过named指定名字,自行学习
导包javax.inject.Inject
10.2.2.1业务类
@Resource(name="userDao")
public void setUserDao(UserDao userDao) {
System.out.println("--------------");
this.userDao2 = userDao;
}
10.3注解实例化使用步骤
10.3.1配置文件
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 使用注解形式自动装配 -->
<context:annotation-config />
<!-- 使用注解形式实例化对象 -->
<context:component-scan base-package="com.tledu.zrz" />
</beans>
10.3.2业务类
所有需要实例化对象的类上面都加上@Component
默认是以类名首字母小写作为名字进行存储
可以使用@Component(“xxx”) 或者@Component(value=”xxx”)来设置名字
@Component(value="userDao")
public class UserDaoImpl implements UserDao {
@Component
public class User {
@Component("userService")
public class UserService {
10.3.3 注解分类
@Controller :WEB 层 ,就是和页面交互的类
@Service :业务层 ,主要处理逻辑
@Repository :持久层 ,就是Dao操作数据库
这三个注解是为了让标注类本身的用途清晰,Spring 在后续版本会对其增强
@Component: 最普通的组件,可以被注入到spring容器进行管理
@Value :用于注入普通类型. 可以写在变量上和setter方法上
@Autowired :自动装配,上面描述比较详细,可以参照上面
@Qualifier:强制使用名称注入.
@Resource 相当于: @Autowired 和@Qualifier 一起使用
@Scope: 设置对象在spring容器中的生命周期
取值 :
singleton:单例
prototype:多例
@PostConstruct :相当于 init-method
@PreDestroy :相当于 destroy-method
10.3.4注解的区别
@Component 最普通的组件,可以被注入到spring容器进行管理
@Repository 作用于持久层
@Service 作用于业务逻辑层
@Controller 作用于表现层(spring-mvc的注解)
10.4新注解
10.4.1Configuration
作用: 用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class)。
属性: value:用于指定配置类的字节码
package com.tledu.zrz.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfiguration {
}
10.4.2ComponentScan
作用: 用于指定 spring 在初始化容器时要扫描的包。
作用和在 spring 的 xml 配置文件中的: <context:component-scan base-package="com.tledu.zrz.spring"/>是一样的。
属性: basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
@Configuration
@ComponentScan("com.tledu.zrz.spring")
public class SpringConfiguration {
}
10.4.3 Bean
作用: 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
属性: name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
10.4.4 PropertySource
作用: 用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
属性: value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath
package com.tledu.zrz.config;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
}
10.4.5 Import
作用: 用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。
属性: value[]:用于指定其他配置类的字节码。
@Configuration
@ComponentScan("com.tledu.zrz.spring")
@Import({ JdbcConfig.class})
public class SpringConfiguration {
}
// jdbc的配置
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {}
10.4.6 通过注解获取容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);