SpringIOC

Spring框架

Spring 是众多开源java项目中的一员,基于分层的javaEE应用一站式轻量级开源框架,主要核心是 IOC(控制反转/依赖注入)与 AOP(面向切面)两大技术,实现项目在开发过程中的轻松解耦,提高项目的开发效率。

  1. 在项目中引入 Spring 立即可以带来下面的好处 降低组件之间的耦合度,实现软件各层之间的解耦。可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。 容器提供单例模式支持,开发人员不再需要自己编写实现代码。 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。

Spring框架源码

Spring 总共大约有20个模块,由1300多个不同的文件构成。而这些组件被分别整合在核心容器(Core Container)、Aop(Aspect Oriented Programming)和设备支持(Instrmentation)、数据访问及集成(Data Access/Integeration)、Web、报文发送(Messaging)、测试6个模块集合中。

  1. 核心容器: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,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。

  2. Spring-AOP:Spring-aop是Spring的另一个核心模块, 在Spring中,他是以JVM的动态代理技术为基础,然后设计出了一系列的Aop横切实现,比如前置通知、返回通知、异常通知等。通过其配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。

  3. 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 等。

  4. Web 模块:由Spring-web、Spring-webmvc、Spring-websocket和Spring-webmvc-portlet 4个模块组成,Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

  5. 报文发送:即Spring-messaging模块。
    Spring-messaging是Spring4 新加入的一个模块,主要职责是为Spring 框架集成一些基础的报文传送应用。

  6. 单元测试:即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对象

    1. public class UserService {
    2. /**
    3. * @Resource注解实现自动注入 1.注解默认通过属性字段名称查找对应的bean对象(属性字段名称与bean标签的id属性值一致)
    4. * 2.如果属性字段名称不一样,则会通过类型(class)类型
    5. * 3.属性字段可以提供set方法也可以不提供
    6. * 4.注解可以声明在属性字段上,或者set方法级别
    7. * 5.可以设置注解的name属性,name属性值要与bean标签的id属性值一致(如果设置了name属性,则会按照name属性查询bean对象)
    8. * 6.当注入接口时,如果只有一个实现类,则正常实例化;如果接口有多个实现类,则需要使用name属性指定需要被实例恶化的bean对象
    9. */
    10. //1.根据类型注入
    11. @Resource
    12. private UserDao userDao;
    13. //2.根据指定name属性来注入
    14. @Resource(name = "UserDaoImpl1")
    15. private IUserDao iUserDao;
    16. //3.通过set方法注入
    17. @Resource
    18. public void setUserDao(UserDao userDao) {
    19. this.userDao = userDao;
    20. }
    21. public void test() {
    22. userDao.test();
    23. iUserDao.test();
    24. System.out.println("userService test...");
    25. }
    26. }


bean对象

  1. public class UserDao {
  2. public void test() {
  3. System.out.println("userDao test...");
  4. }
  5. }


接口

  1. public interface IUserDao {
  2. public void test();
  3. }

实现接口的类1

  1. public class UserDaoImpl1 implements IUserDao {
  2. @Override
  3. public void test() {
  4. System.out.println("UserDaoImpl1 test...");
  5. }
  6. }

实现接口的类2

  1. public class UserDaoImpl2 implements IUserDao {
  2. @Override
  3. public void test() {
  4. System.out.println("UserDaoImpl1 test...");
  5. }
  6. }

@Autowired注解

@Autowired注解实现自动化注入:

  • 默认通过类型(Class类型)查找bean对象 与属性字段的名称无关
  • 属性可以提供set方法,也可以不提供
  • 注解可以声明在属性级别或者set方法级别
  • 可以添加@Qualifier结合使用,通过value属性值查找bean对象(value属性值必须要设置,且要与bean标签的id属性值对应)
  1. public class AddService {
  2. /**
  3. * 1.默认通过类型(Class类型)查找bean对象 与属性字段的名称无关
  4. * 2.属性可以提供set方法,也可以不提供
  5. * 3.注解可以声明在属性级别或者set方法级别
  6. * 4.可以添加@Qualifier结合使用,通过value属性值查找bean对象(value属性值必须要设置,且要与bean标签的id属性值对应)
  7. */
  8. @Autowired
  9. @Qualifier(value = "ad2")
  10. private IAddDao addDao;
  11. public void test() {
  12. addDao.test();
  13. System.out.println("AddService test...");
  14. }
  15. }

接口

  1. public interface IAddDao {
  2. public void test();
  3. }

接口的实现类1

  1. public class AddDao implements IAddDao {
  2. public void test() {
  3. System.out.println("AddDao test...");
  4. }
  5. }

接口的实现类2

  1. public class AddDao2 implements IAddDao {
  2. @Override
  3. public void test() {
  4. System.out.println("AddDao2 test...");
  5. }
  6. }

SpringIOC 扫描器

  1. 实际开发中,bean的数量非常多,采用手动配置bean的方式已无法满足生产需要,Spring这时候同样提供了扫描的方式,对扫描到的bean对象统一进行管理,简化开发配置,提高开发效率
  1. Spring IOC 扫描器
  2. 作用:bean对象统一进行管理,简化开发配置,提高开发效率
  3. 1、设置自动化扫描的范围
  4. 如果bean对象未在指定包范围,即使声明了注解,也无法实例化
  5. 2、使用指定的注解(声明在类级别) bean对象的id属性默认是 类的首字母小写
  6. Dao层:
  7. @Repository
  8. Service层:
  9. @Service
  10. Controller层:
  11. @Controller
  12. 任意类:
  13. @Component
  14. 注:开发过程中建议按照指定规则声明注解

1.设置自动化扫描的范围

  1. <!-- 设置自动化扫描的范围 -->
  2. <context:component-scan base-package="com.yf"/>

2.使用特定的注解

@Service(Service层)

  1. @Service
  2. public class UpdateService {
  3. @Resource(name = "updateDao2")
  4. private IUpdateDao updateDao;
  5. public void test() {
  6. updateDao.test();
  7. System.out.println("UpdateService test...");
  8. }
  9. }

定义接口

  1. public interface IUserDao {
  2. public void test();
  3. }

实现接口的类

@Repository(Dao层)

  1. @Repository
  2. public class UpdateDao1 implements IUpdateDao {
  3. @Override
  4. public void test() {
  5. System.out.println("UpdateDao1 test...");
  6. }
  7. }
  1. @Repository
  2. public class UpdateDao2 implements IUpdateDao {
  3. @Override
  4. public void test() {
  5. System.out.println("UpdateDao2 test...");
  6. }
  7. }

Bean的生命周期和作用域

Bean的作用域

默认情况下,我们从Spring容器中拿到的对象均是单例的,对于bean的作用域类型如下:

  1. **注意: lazy-init是懒加载, 如果等于true时作用是指Spring容器启动的时候不会去实例化这个bean, 而是在程序调用时才去实例化. 默认是falseSpring容器启动时实例化.**
  2. 默认情况下,被管理的bean只会IOC容器中存在一个实例,对于所有获取该Bean的操作Spring容器将只返回同一个Bean
  3. 容器在启动的情况下就实例化所有singleton bean对象,并缓存与容器中

lazy-init属性(懒加载)
如果为false,则在IOC容器启动时会实例化bean对象,默认false
如果为true,则IOC容器启动时不会实例化Bean对象,在使用bean对象时才会实例化

lazy-init设置为false有什么好处?
1)可以提前发现潜在的配置问题
2)Bean 对象存在于缓存中,使用时不用再去实例化bean,加快程序运行效率

什么对象适合作为单例对象?
一般来说对于无状态或状态不可改变的对象适合使用单例模式。(不存在会改变对象状态的成员变量)
比如:controller层、service层、dao层

什么是无状态或状态不可改变的对象?

  1. 实际上对象状态的变化往往均是由于属性值得变化而引起的,比如user 姓名属性会有变化,属性姓名的变化一般会引起user对象状态的变化。对于我们的程序来说,无状态对象没有实例变量的存在,保证了线程的安全性,service 层业务对象即是无状态对象。线程安全的。

Bean的生命周期

  1. 对比已经学过的servlet 生命周期(容器启动装载并实例化servlet类,初始化servlet,调用service方法,销毁servlet)。
  2. 同样对于Spring容器管理的bean也存在生命周期的概念
  3. Spring中,Bean的生命周期包括Bean的定义、初始化、使用和销毁4个阶段

Spring 模拟用户登录流程

Dao层(查询用户记录)

1.定义javaBean User.java

  1. /**
  2. * User 用户实体类
  3. */
  4. public class User {
  5. private String userName; // 用户名称
  6. private String userPwd; // 用户密码
  7. public String getUserName() {
  8. return userName;
  9. }
  10. public void setUserName(String userName) {
  11. this.userName = userName;
  12. }
  13. public String getUserPwd() {
  14. return userPwd;
  15. }
  16. public void setUserPwd(String userPwd) {
  17. this.userPwd = userPwd;
  18. }
  19. }

2.编写Dao层 UserDao.java

  1. @Repository
  2. public class UserDao {
  3. private final String USERNAME = "admin";
  4. private final String USERPWD = "admin";
  5. /**
  6. * 通过用户名称查询用户对象
  7. * @param userName
  8. * @return
  9. */
  10. public User queryUserByUserName(String userName){
  11. User user = null;
  12. // 判断用户名称是否正确
  13. if(!USERNAME.equals(userName)){
  14. // 如果不正确,返回null
  15. return null;
  16. }
  17. // 如果正确,将用户名称和密码设置到user对象中
  18. user = new User();
  19. user.setUserName(USERNAME);
  20. user.setUserPwd(USERPWD);
  21. return user;
  22. }
  23. }

Service层(业务逻辑处理)

1.定义业务处理返回消息模型 MessageModel.java

  1. /**
  2. * 定义业务处理返回消息模型
  3. * 封装返回结果
  4. */
  5. public class MessageModel {
  6. private Integer resultCode = 1; // 结果状态码 1=成功,0=失败
  7. private String resultMsg = "操作成功!"; // 结果提示信息
  8. public Integer getResultCode() {
  9. return resultCode;
  10. }
  11. public void setResultCode(Integer resultCode) {
  12. this.resultCode = resultCode;
  13. }
  14. public String getResultMsg() {
  15. return resultMsg;
  16. }
  17. public void setResultMsg(String resultMsg) {
  18. this.resultMsg = resultMsg;
  19. }
  20. }

2.编写Service层 UserService.java

  1. @Service
  2. public class UserService {
  3. @Resource
  4. private UserDao userDao;
  5. /**
  6. * 验证用户登录
  7. * @param userName
  8. * @param userPwd
  9. * @return
  10. */
  11. public MessageModel userLoginCheck(String userName, String userPwd){
  12. // 定义业务处理返回消息模型
  13. MessageModel messageModel = new MessageModel();
  14. // 判断用户名称是否非空
  15. if(null == userName || "".equals(userName.trim())){
  16. messageModel.setResultCode(0);
  17. messageModel.setResultMsg("用户名不能为空!");
  18. return messageModel;
  19. }
  20. // 判断用户密码是否为空
  21. if(null == userPwd || "".equals(userPwd.trim())){
  22. messageModel.setResultCode(0);
  23. messageModel.setResultMsg("密码不能为空!");
  24. return messageModel;
  25. }
  26. // 通过用户名称查询用户对象
  27. User user = userDao.queryUserByUserName(userName);
  28. // 判断用户对象是否为空
  29. if(null == user){
  30. messageModel.setResultCode(0);
  31. messageModel.setResultMsg("该用户不存在!");
  32. return messageModel;
  33. }
  34. // 如果用户对象不为空,判断密码是否正确
  35. if(!user.getUserPwd().equals(userPwd)){
  36. messageModel.setResultCode(0);
  37. messageModel.setResultMsg("用户密码不正确!");
  38. return messageModel;
  39. }
  40. // 登录成功
  41. messageModel.setResultMsg("登录成功!");
  42. return messageModel;
  43. }
  44. }

Controller层(接收请求)

1.编写Controller层 UserController.java

  1. @Controller
  2. public class UserController {
  3. @Resource
  4. private UserService userService;
  5. /**
  6. * 用户登录
  7. * @param userName
  8. * @param userPwd
  9. * @return
  10. */
  11. public MessageModel login(String userName, String userPwd){
  12. // 调用Dao层判断用户登录操作,返回结果
  13. MessageModel messageModel = userService.userLoginCheck(userName, userPwd);
  14. return messageModel;
  15. }
  16. }

通过 JUnit 进行测试

  1. public class TestLogin {
  2. @Test
  3. public void test() {
  4. // 得到Spring容器上下文环境
  5. ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
  6. // 得到UserController实例化对象
  7. UserController userController = (UserController) ac.getBean("userController");
  8. // 传入参数调用UserController的方法,返回封装类
  9. MessageModel messageModel= userController.login("admin", "admin");
  10. System.out.println("状态码:" + messageModel.getResultCode() + ",提示信息:" + messageModel.getResultMsg());
  11. }
  12. }