Spring框架(一)

时间:2023-03-09 19:26:47
Spring框架(一)

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 是一个分层的 JavaSE/EEfull-stack( 一站式) 轻量级开源框架。

JavaEE 开发分成三层结构:

* WEB 层:Spring MVC.
* 业务层:Bean 管理:(IOC)
* 持久层:Spring 的 JDBC 模板.ORM 模板用于整合其他的持久层框架.

Spring的优势:

方便解耦,简化开发

  Spring 就是一个大工厂,可以将所有对象创建和依赖关系维护,交给 Spring 管理

AOP 编程的支持

  Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能

声明式事务的支持

  只需要通过配置就可以完成对事务的管理,而无需手动编程

方便程序的测试

  Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序

方便集成各种优秀框架

  Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持

降低 JavaEE API 的使用难度

  Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等),都提供了封装,使这些 API 应用难度大大降低

SpringIOC底层实现原理:

Spring框架(一)

相关概念:

依赖注入和 控制反转

IOC   inversion of control  控制反转

DI   Dependency Injection  依赖注入

IoC是什么

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

用图例说明一下,传统程序设计如图,都是主动去创建相关对象然后再组合起来:

Spring框架(一)

传统应用程序示意图

当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了

IoC能做什么

IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

IoC和DI

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

●谁依赖于谁:当然是应用程序依赖于IoC容器;

●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;

●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;

●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

面向对象五大原则:

单一职责原则(SRP)
开放封闭原则(OCP)
里氏替换原则(LSP)
依赖倒置原则(DIP)
接口隔离原则(ISP)

单一职责原则(SRP)

• 一个类应该仅有一个引起它变化的原因(最简单,最容易理解却最不容易做到的一个设计原则)
职员类例子:
比如在职员类里,将工程师、销售人员、销售经理这些情况都放在职员类里考虑,其结果将会非常混乱,在这个假设下,职员类里的每个方法都要if else判断是哪种情况,从类结构上来说将会十分臃肿,并且上述三种的职员类型,不论哪一种发生需求变化,都会改变职员类!这个是大家所不愿意看到的!

开放封闭原则(OCP)

• 既开放又封闭,对扩展是开放的,对更改是封闭的!

• 扩展即扩展现行的模块,当我们软件的实际应用发生改变时,出现新的需求,就需要我们对模块进行扩展,使其能够满足新的需求!

更改封闭即是在我们对模块进行扩展时,勿需对源有程序代码和DLL进行修改或重新编译文件!

这个原则对我们在设计类的时候很有帮助,坚持这个原则就必须尽量考虑接口封装,抽象机制和多态技术!

里氏替换原则(LSP)

• 子类可以替换父类并且出现在父类能够出现的任何地方

• 这个原则也是在贯彻GOF倡导的面向接口编程!
在这个原则中父类应尽可能使用接口或者抽象类来实现!

子类通过实现了父类接口,能够替父类的使用地方!
通过这个原则,我们客户端在使用父类接口的时候,通过子类实现!
意思就是说我们依赖父类接口,在客户端声明一个父类接口,通过其子类来实现
这个时候就要求子类必须能够替换父类所出现的任何地方,这样做的好处就是,在根据新要求扩展父类接口的新子类的时候而不影响当前客户端的使用!

依赖倒置原则(DIP)

• 传统的结构化编程中,最上层的模块通常都要依赖下面的子模块来实现,也
称为高层依赖低层!
所以DIP原则就是要逆转这种依赖关系,让高层模块不要依赖低层模块,所以称之为依赖倒置原则!

ISP 接口隔离原则

• 这个原则的意思是:使用多个专门的接口比使用单个接口要好的多!

这个我有体会,在我实际编程中,为了减少接口的定义,将许多类似的方法都放在一个接口中,最后发现,维护和实现接口的时候花了太多精力,而接口所定义的操作相当于对客户端的一种承诺,这种承诺当然是越少越好,越精练越好,过多的承诺带来的就是你的大量精力和时间去维护!

反射机制:

1, 通过spring来获取一个对象的实例
2, 通过spring进行属性注入
  setter方法注入
  构造器注入
  接口注入
  p标记的使用
  <bean p:username=""></bean>
3, 将一个对象注入到另一个对象<ref bean="...">
4, AutoWired(byType, byName)
5, scope, lazy-init, init-method, destroy-method(相当的不重要)
  scope="singleton(单例) / prototype(原型)"
  lazy-init="true" // 延迟加载
  ___init-method="" destory-method=""(不要和prototype一起使用)
  autowire

例子:

下载并引入相关jar包:

结构:

Spring框架(一)

配置相关文件:

 <?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="user" class="com.model.User">
     <!-- 通过Spring給类注入属性,通过空参构造方法 -->
         <property name="username">
             <value>hanqi</value>
         </property>
         <property name="password">
             <value>123</value>
         </property>    

         <!--
         另一种通过带参构造方法
         <constructor-arg index="0" value="name1"></constructor-arg>
         <constructor-arg index="1" value="pwd1"></constructor-arg>
         -->
     </bean>

 </beans>

model包:

 package com.model;

 public class User {
     private String username;
     private String password;

     public User() {
         super();
         System.out.println("调用User类的空参构造方法");
         // TODO Auto-generated constructor stub
     }
     public User(String username, String password) {
         super();
         this.username = username;
         this.password = password;
         System.out.println("调用User类的带参构造方法 ");
     }
     public String getUsername() {
         return username;
     }
     public void setUsername(String username) {
         this.username = username;
     }
     public String getPassword() {
         return password;
     }
     public void setPassword(String password) {
         this.password = password;
     }
     @Override
     public String toString() {
         return "User [username=" + username + ", password=" + password + "]";
     }

 }

测试1:

 package com.test;

 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;

 import com.model.User;

 public class Test {
     public static void main(String[] args) {
         ApplicationContext ac=new ClassPathXmlApplicationContext("Spring-all.xml");//调用此方法时类已经实例化好了
         User u=(User)ac.getBean("user");//拿到实例化的类,带有默认的值

         System.out.println(u);
         ((ClassPathXmlApplicationContext)ac).close();

     }
 }

Spring框架(一)

测试2:

 package com.test;

 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;

 import com.model.User;

 public class Test {
     public static void main(String[] args) {
         ApplicationContext ac=new ClassPathXmlApplicationContext("Spring-all.xml");//调用此方法时类已经实例化好了

         User u=(User)ac.getBean("user");//拿到实例化的类

         u.setUsername("u");
         u.setPassword("p");
         System.out.println(u);
         ((ClassPathXmlApplicationContext)ac).close();

     }
 }

Spring框架(一)

测试3:

 package com.test;

 import java.lang.reflect.Method;

 import com.model.User;

 public class Test2 {
     public static void main(String[] args) {
         try {
             //实例化一个类,<?>代表可以是任何类型
             Class<?> cla=Class.forName("com.model.User");
             //cla.getConstructor(parameterTypes);获取类里的构造方法
             //cla.getFields();//获取类里的成员变量

             //获得对象实例
             User u=(User)cla.newInstance();
             //u.setUsername("直接调用set设置的");

             //获取类里的所有方法
             Method[] me=cla.getMethods();

             Object obj=null;
             Object obj1=null;

             for(Method m:me){
                 //找到方法,回调方法反射
                 if("setUsername".equals(m.getName())){
                     obj=m.invoke(u, "回调反射");
                 }
             }
             for(Method m:me){
                 if("getUsername".equals(m.getName())){
                     obj1=m.invoke(u);
                 }
             }
             //set方法没有返回值,打印null
             System.out.println("setUsername方法的返回值:"+obj);
             //直接打印对象,属性null
             System.out.println("对象:"+u);
             //
             System.out.println("getUsername方法的返回值:"+obj1);

         } catch (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
 }

Spring框架(一)