SpringIOC
Spring框架
Spring 是众多开源java项目中的一员,基于分层的javaEE应用一站式轻量级开源框架,主要核心是 IOC(控制反转/依赖注入)与 AOP(面向切面)两大技术,实现项目在开发过程中的轻松解耦,提高项目的开发效率。
在项目中引入 Spring 立即可以带来下面的好处 降低组件之间的耦合度,实现软件各层之间的解耦。可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。 容器提供单例模式支持,开发人员不再需要自己编写实现代码。 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。
Spring框架源码
Spring 总共大约有20个模块,由1300多个不同的文件构成。而这些组件被分别整合在核心容器(Core Container)、Aop(Aspect Oriented Programming)和设备支持(Instrmentation)、数据访问及集成(Data Access/Integeration)、Web、报文发送(Messaging)、测试6个模块集合中。
核心容器:Spring-beans 和 Spring-core 模块是 Spring 框架的核心模块,包含控制反转(Inversion of Control, IoC)和依赖注入(Dependency Injection, DI),核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,工厂模式的实现。BeanFactory 使用控制反转(IOC) 思想将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring 上下文Spring Context:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring-Expression 模块是统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法、操作数组、集合等。它的语法类似于传统EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。Spring-AOP:Spring-aop是Spring的另一个核心模块, 在Spring中,他是以JVM的动态代理技术为基础,然后设计出了一系列的Aop横切实现,比如前置通知、返回通知、异常通知等。通过其配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。
Spring Data Access(数据访问):由Spring-jdbc、Spring-tx、Spring-orm、Spring-jms和Spring-oxm 5个模块组成 Spring-jdbc 模块是 Spring 提供的JDBC抽象框架的主要实现模块,用于简化 Spring JDBC。
Spring-tx 模块是SpringJDBC事务控制实现模块。使用Spring框架,它对事务做了很好的封装,通过它的Aop配置,可以灵活的配置在任何一层。
Spring-Orm 模块是ORM框架支持模块,主要集成 hibernate, Java Persistence API (JPA) 和 Java Data Objects (JDO) 用于资源管理、数据访问对象(DAO)的实现和事务策略。
Spring-Jms 模块(Java Messaging Service)能够发送和接受信息。
Spring-Oxm 模块主要提供一个抽象层以支撑OXM(OXM 是 Object-to-XML-Mapping 的缩写,它是一个O/M-mapper,将java对象映射成 XML 数据,或者将 XML 数据映射成 java 对象),例如:JAXB, Castor, XMLBeans, JiBX 和 XStream 等。Web 模块:由Spring-web、Spring-webmvc、Spring-websocket和Spring-webmvc-portlet 4个模块组成,Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
报文发送:即Spring-messaging模块。
Spring-messaging是Spring4 新加入的一个模块,主要职责是为Spring 框架集成一些基础的报文传送应用。单元测试:即Spring-test模块。Spring-test模块主要为测试提供支持
SpringIOC(xml)
略
SpringIOC(注解)
对于 bean 的注入,除了使用 xml 配置以外,可以使用注解配置。注解的配置,可以简化配置文件,提高开发的速度,使程序看上去更简洁。对于注解的解释,Spring对于注解有专门的解释器,对定义的注解进行解析,实现对应bean对象的注入。通过反射技术实现。
@Resource注解
@Resource注解实现自动注入(反射)
默认根据属性字段名称查找对应的bean对象(属性字段名称与bean标签的id属性值相等)
如果属性字段名称未找到,则会通过类型(Class类型)查找
属性可以提供set方法,也可以不提供set方法
注解可以声明在属性级别或者set方法级别
可以设置name属性,name属性值必须与bean标签的id属性值一致;如果设置了name属性值,就会按照name’属性值查找bean对象
当注入接口时,如果接口只有一个实现类则正常实例化;如果接口存在多个实现,则需要使用name属性指定需要被实例化的bean对象
public class UserService {
/**
* @Resource注解实现自动注入 1.注解默认通过属性字段名称查找对应的bean对象(属性字段名称与bean标签的id属性值一致)
* 2.如果属性字段名称不一样,则会通过类型(class)类型
* 3.属性字段可以提供set方法也可以不提供
* 4.注解可以声明在属性字段上,或者set方法级别
* 5.可以设置注解的name属性,name属性值要与bean标签的id属性值一致(如果设置了name属性,则会按照name属性查询bean对象)
* 6.当注入接口时,如果只有一个实现类,则正常实例化;如果接口有多个实现类,则需要使用name属性指定需要被实例恶化的bean对象
*/
//1.根据类型注入
@Resource
private UserDao userDao;
//2.根据指定name属性来注入
@Resource(name = "UserDaoImpl1")
private IUserDao iUserDao;
//3.通过set方法注入
@Resource
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
userDao.test();
iUserDao.test();
System.out.println("userService test...");
}
}
bean对象
public class UserDao {
public void test() {
System.out.println("userDao test...");
}
}
接口
public interface IUserDao {
public void test();
}
实现接口的类1
public class UserDaoImpl1 implements IUserDao {
@Override
public void test() {
System.out.println("UserDaoImpl1 test...");
}
}
实现接口的类2
public class UserDaoImpl2 implements IUserDao {
@Override
public void test() {
System.out.println("UserDaoImpl1 test...");
}
}
@Autowired注解
@Autowired注解实现自动化注入:
- 默认通过类型(Class类型)查找bean对象 与属性字段的名称无关
- 属性可以提供set方法,也可以不提供
- 注解可以声明在属性级别或者set方法级别
- 可以添加@Qualifier结合使用,通过value属性值查找bean对象(value属性值必须要设置,且要与bean标签的id属性值对应)
public class AddService {
/**
* 1.默认通过类型(Class类型)查找bean对象 与属性字段的名称无关
* 2.属性可以提供set方法,也可以不提供
* 3.注解可以声明在属性级别或者set方法级别
* 4.可以添加@Qualifier结合使用,通过value属性值查找bean对象(value属性值必须要设置,且要与bean标签的id属性值对应)
*/
@Autowired
@Qualifier(value = "ad2")
private IAddDao addDao;
public void test() {
addDao.test();
System.out.println("AddService test...");
}
}
接口
public interface IAddDao {
public void test();
}
接口的实现类1
public class AddDao implements IAddDao {
public void test() {
System.out.println("AddDao test...");
}
}
接口的实现类2
public class AddDao2 implements IAddDao {
@Override
public void test() {
System.out.println("AddDao2 test...");
}
}
SpringIOC 扫描器
实际开发中,bean的数量非常多,采用手动配置bean的方式已无法满足生产需要,Spring这时候同样提供了扫描的方式,对扫描到的bean对象统一进行管理,简化开发配置,提高开发效率
Spring IOC 扫描器
作用:bean对象统一进行管理,简化开发配置,提高开发效率
1、设置自动化扫描的范围
如果bean对象未在指定包范围,即使声明了注解,也无法实例化
2、使用指定的注解(声明在类级别) bean对象的id属性默认是 类的首字母小写
Dao层:
@Repository
Service层:
@Service
Controller层:
@Controller
任意类:
@Component
注:开发过程中建议按照指定规则声明注解
1.设置自动化扫描的范围
<!-- 设置自动化扫描的范围 -->
<context:component-scan base-package="com.yf"/>
2.使用特定的注解
@Service(Service层)
@Service
public class UpdateService {
@Resource(name = "updateDao2")
private IUpdateDao updateDao;
public void test() {
updateDao.test();
System.out.println("UpdateService test...");
}
}
定义接口
public interface IUserDao {
public void test();
}
实现接口的类
@Repository(Dao层)
@Repository
public class UpdateDao1 implements IUpdateDao {
@Override
public void test() {
System.out.println("UpdateDao1 test...");
}
}
@Repository
public class UpdateDao2 implements IUpdateDao {
@Override
public void test() {
System.out.println("UpdateDao2 test...");
}
}
Bean的生命周期和作用域
Bean的作用域
默认情况下,我们从Spring容器中拿到的对象均是单例的,对于bean的作用域类型如下:
**注意: lazy-init是懒加载, 如果等于true时作用是指Spring容器启动的时候不会去实例化这个bean, 而是在程序调用时才去实例化. 默认是false即Spring容器启动时实例化.**
默认情况下,被管理的bean只会IOC容器中存在一个实例,对于所有获取该Bean的操作Spring容器将只返回同一个Bean。
容器在启动的情况下就实例化所有singleton 的 bean对象,并缓存与容器中
lazy-init属性(懒加载)
如果为false,则在IOC容器启动时会实例化bean对象,默认false
如果为true,则IOC容器启动时不会实例化Bean对象,在使用bean对象时才会实例化
lazy-init设置为false有什么好处?
1)可以提前发现潜在的配置问题
2)Bean 对象存在于缓存中,使用时不用再去实例化bean,加快程序运行效率
什么对象适合作为单例对象?
一般来说对于无状态或状态不可改变的对象适合使用单例模式。(不存在会改变对象状态的成员变量)
比如:controller层、service层、dao层
什么是无状态或状态不可改变的对象?
实际上对象状态的变化往往均是由于属性值得变化而引起的,比如user类 姓名属性会有变化,属性姓名的变化一般会引起user对象状态的变化。对于我们的程序来说,无状态对象没有实例变量的存在,保证了线程的安全性,service 层业务对象即是无状态对象。线程安全的。
Bean的生命周期
对比已经学过的servlet 生命周期(容器启动装载并实例化servlet类,初始化servlet,调用service方法,销毁servlet)。
同样对于Spring容器管理的bean也存在生命周期的概念
在Spring中,Bean的生命周期包括Bean的定义、初始化、使用和销毁4个阶段
Spring 模拟用户登录流程
Dao层(查询用户记录)
1.定义javaBean User.java
/**
* User 用户实体类
*/
public class User {
private String userName; // 用户名称
private String userPwd; // 用户密码
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
}
2.编写Dao层 UserDao.java
@Repository
public class UserDao {
private final String USERNAME = "admin";
private final String USERPWD = "admin";
/**
* 通过用户名称查询用户对象
* @param userName
* @return
*/
public User queryUserByUserName(String userName){
User user = null;
// 判断用户名称是否正确
if(!USERNAME.equals(userName)){
// 如果不正确,返回null
return null;
}
// 如果正确,将用户名称和密码设置到user对象中
user = new User();
user.setUserName(USERNAME);
user.setUserPwd(USERPWD);
return user;
}
}
Service层(业务逻辑处理)
1.定义业务处理返回消息模型 MessageModel.java
/**
* 定义业务处理返回消息模型
* 封装返回结果
*/
public class MessageModel {
private Integer resultCode = 1; // 结果状态码 1=成功,0=失败
private String resultMsg = "操作成功!"; // 结果提示信息
public Integer getResultCode() {
return resultCode;
}
public void setResultCode(Integer resultCode) {
this.resultCode = resultCode;
}
public String getResultMsg() {
return resultMsg;
}
public void setResultMsg(String resultMsg) {
this.resultMsg = resultMsg;
}
}
2.编写Service层 UserService.java
@Service
public class UserService {
@Resource
private UserDao userDao;
/**
* 验证用户登录
* @param userName
* @param userPwd
* @return
*/
public MessageModel userLoginCheck(String userName, String userPwd){
// 定义业务处理返回消息模型
MessageModel messageModel = new MessageModel();
// 判断用户名称是否非空
if(null == userName || "".equals(userName.trim())){
messageModel.setResultCode(0);
messageModel.setResultMsg("用户名不能为空!");
return messageModel;
}
// 判断用户密码是否为空
if(null == userPwd || "".equals(userPwd.trim())){
messageModel.setResultCode(0);
messageModel.setResultMsg("密码不能为空!");
return messageModel;
}
// 通过用户名称查询用户对象
User user = userDao.queryUserByUserName(userName);
// 判断用户对象是否为空
if(null == user){
messageModel.setResultCode(0);
messageModel.setResultMsg("该用户不存在!");
return messageModel;
}
// 如果用户对象不为空,判断密码是否正确
if(!user.getUserPwd().equals(userPwd)){
messageModel.setResultCode(0);
messageModel.setResultMsg("用户密码不正确!");
return messageModel;
}
// 登录成功
messageModel.setResultMsg("登录成功!");
return messageModel;
}
}
Controller层(接收请求)
1.编写Controller层 UserController.java
@Controller
public class UserController {
@Resource
private UserService userService;
/**
* 用户登录
* @param userName
* @param userPwd
* @return
*/
public MessageModel login(String userName, String userPwd){
// 调用Dao层判断用户登录操作,返回结果
MessageModel messageModel = userService.userLoginCheck(userName, userPwd);
return messageModel;
}
}
通过 JUnit 进行测试
public class TestLogin {
@Test
public void test() {
// 得到Spring容器上下文环境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 得到UserController实例化对象
UserController userController = (UserController) ac.getBean("userController");
// 传入参数调用UserController的方法,返回封装类
MessageModel messageModel= userController.login("admin", "admin");
System.out.println("状态码:" + messageModel.getResultCode() + ",提示信息:" + messageModel.getResultMsg());
}
}