spring框架的理解

Spring 框架
Spring 框架是由于软件开发的复杂性而创建的。 Spring 使用的是基本的 JavaBean 来完成以前只可能由 EJB 完成的事情。然而, Spring 的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分 Java 应用都可以从 Spring 中受益。
◆目的:解决企业应用开发的复杂性
◆功能:使用基本的 JavaBean 代替 EJB ,并提供了更多的企业应用功能
◆范围:任何 Java 应用
Spring 是一个轻量级控制反转 (IoC) 和面向切面 (AOP) 的容器框架。
 
spring 框架的核心机制:核心机制是以 BeanFactory 为基础 , 管理 bean benn 之间的依赖的 . spring bean 组织管理 Java 应用中的各组件 , 组件之间的依赖关系和耦合 . 这依赖与 spring 德核心机制 : 依赖注入 .Spring 使用 BeanFactory 作为应用中负责生产和管理各种组件的工厂 , 同事也是组件运行的容器 .BeanFactory 根据配置的文件确定容器中 bean 的实现 . 管理 bean 之间的关系
依赖注入可以称之为 控制反转(IOC)
不管是控制反转还是依赖注入,他们都可以这样理解:当某个 Java 实例(调用者)需要另一个 Java 实例(被调用者)时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。但是在依赖注入 / 控制反转模式下,创建被调用者的工作不再是有调用者来完成,而是由 spring 容器来完成,然后注入调用者。
依赖注入通常有如下两种:
         1    设置注入: IoC 容器使用属性的 setter 方法来注入被依赖的实例。
         2    构造注入: IoC 容器使用构造器来注入被依赖的实例。
依赖注入的体现:
1.设置注入
下面是Person接口,该接口定义了一个Person规范。
public   interface  Person {  
     // 定义使用斧子的方法   
     public   void  useAxe();  
Axe 接口:
public   interface  Axe {  
     //Axe 接口里面有个砍的方法   
     public  String chop();  
}  
Person 的实现类:
public   class  Chinese  implements  Person {  
     private  Axe axe;  
     private  String name;  
      //  设值注入所需的 setter 方法   
     public   void  setAxe(Axe axe) {  
         this .axe = axe;  
    }  
     public   void  setName(String name) {  
         this .name = name;  
    }  
     //  实现 Person 接口的 userAxe 方法   
     public   void  useAxe() {  
         //  调用 axe chop 方法,表明 Person 对象依赖于 Axe 对象   
        System.out.println( " 我是 " +name+ " " +axe.chop());  
    }  
}  
上面的代码实现了 Person 接口的 userAxe() 方法,实现该方法时调用了 axe 的的 chop() 方法,这就是典型的依赖关系。
在这里 Spring 容器的作用就是已松耦合的方式来管理这种调用关系。在上面的 Chinese 类中, Chinese 类并不知道它要调用的 axe 实例在哪里,也不知道 axe 实例是如何实现的,它只是需要调用一个 axe 实例,这个 Axe 实例将由 Spring 容器负责注入。
public   class  StoneAxe  implements  Axe{  
     public  String chop() {  
         return   " 石斧砍柴好慢啊 !!!" ;  
    }  
}  
直到这里,程序依然不知道 Chinese 类和 Axe 实例耦合, Spring 也不知道!实际上, Spring 需要使用 XML 配置文件来指定实例之间的依赖关系。
        Spring 采用了 XML 文件作为配置文件。
        对于本应用的 XML 配置文件如下:
1.   <?xml  version = "1.0"   encoding = "UTF-8" ?>  
2.   <beans  xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"   
3.        xmlns = "http://www.springframework.org/schema/beans"   
4.        xsi:schemaLocation ="http://www.springframework.org/schema/beans  
5.       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" >      <!--  配置 Chinese 实例,其实现类是 Chinese -->   
6.        <bean   id = "chinese"   class = "com.spring.service.impl.Chinese" >   
7.            <!--  StoneAxe 注入给 axe 属性  -->   
8.            <property   name = "axe"   ref = "stoneAxe"   />   
9.            <property   name = "name"   value = " 孙悟空 " />   
10.      </bean>   
11.      <!--  配置 stoneAxe 实例  -->   
12.      <bean   id = "stoneAxe"   class = "com.spring.service.impl.StoneAxe"   />   
13. </beans>
  在配置文件中, Spring 配置 Bean 实例通常会指定两个属性:
 id :指定该 Bean 的唯一标识,程序会通过 id 属性值来访问该 Bean 实例。
 class :指定该 Bean 的实现类,此处不可再用接口,必须是实现类, Spring 容器会使用 XML 解析器读取该属性值,并利用反射来创建该实现类的实例。
从上面可以看出 Bean Bean 之间的依赖关系放在配置文件里组织,而不是写在代码里。通过配置文件的指定, Spring 能够精确地为每个 Bean 注入属性。因此,配置文件里的 <bean…/> 元素的 class 属性值不能是接口,而必须是真正的实现类。
 Spring 会自动接管每个 <bean…/> 定义里的 <property …/> 元素定义, Spring 会在调用无参数的构造器、创建默认的 Bean 实例后,调用相应的 setter 方法为程序注入属性值。 <property…/> 定义的属性值将不再有该 Bean 来主动设置、管理,而是接受 Spring 的注入。
  每个 Bean id 属性是该 Bean 的唯一标识,程序通过 id 属性访问 Bean Bean Bean 的依赖关系也是通过 id 属性关联。
Bean Bean 之间的依赖关系有 Spring 管理, Spring 采用 setter 方法为目标 Bean 注入所依赖的 Bean ,这种方式被称之为设值注入。
总结:
        从上面的实例我们可以看出,依赖注入以配置文件管理 Bean 实例之间的耦合,让 Bean 实例之间的耦合从代码层次分离出来。
       Spring IOC 容器有如下 3 个基本要点:
       1    应用程序的各个组件面向接口编程。面向接口编程可以将各个组件的耦合提升到接口层次,从而有利于项目后期的扩展。
       2    应用程序的各组件不再由程序主动产生,而是由 Spring 容器来负责产生,并初始化。
       3   Spring 采用配置文件、或者 Annotation 来管理 Bean 的实现类、依赖关系, Spring 容器则根据配置文件,利用反射机制来创建时间,并为之注入依赖关系( IOC )。
 
 
二、 构造注入
1.   public class Japanese implements Person{  
2.
3.        private  Axe axe;  
4.        // 默认构造器   
5.        public  Japanese(){  
6.             
7.       }  
8.         
9.        // 构造注入所需的带参数构造器   
10.      public  Japanese(Axe axe){  
11.          this .axe = axe;  
12.     }  
13.       
14.      public   void  useAxe() {  
15.         System.out.println(axe.chop());  
16.     }  
上面的 Chinese 类并没有 setter 方法,仅仅只是提供了一个带 Axe 属性的构造器, Spring 将通过该构造器为 Chinese 注入所依赖的 Bean 实例。
构造注入的配置文件需要做一些修改。为了使用构造注入,使用 <constructor-arg…/> 元素来指定构造器的参数。如下
1.   <?xml  version = "1.0"   encoding = "UTF-8" ?>  
2.   <beans  xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"   
3.        xmlns = "http://www.springframework.org/schema/beans"   
4.        xsi:schemaLocation ="http://www.springframework.org/schema/beans  
5.       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" >
6.        <!--  配置 Japanese 实例  -->   
7.        <bean   id = "japanese"   class = "com.spring.service.impl.Japanese" >   
8.            <!--  使用构造注入   ,为 Japanese 实例注入 SteelAxe 实例 -->   
9.            <constructor-arg   ref = "stoneAxe" />   
10.      </bean>   
11.       
12.      <!--  配置 stoneAxe 实例  -->   
13.      <bean   id = "stoneAxe"   class = "com.spring.service.impl.StoneAxe"   />   
14. </beans>  
------------------------------------------------------------------------
如果没有看懂上面,可以看下面这一段最简单的控制反转,控制反转跟 spring 的依赖注入其实差不了多少,差不多就是一个意思。
如我定义了一个规范的Dao接口类UserDao。
  public interface UserDao {
public void add(Users users);
}
但我有两个不同的去实现这个Dao接口的实现类。
  public class UserDaoImp1 implements UserDao{
@Override
public void add(Users users) {
System.out.println("这是userDao的第一个实现类"+users.getName());
}
}
public class UserDaoImp2 implements UserDao{
@Override
public void add(Users users) {
System.out.println("这是userDao的第二个实现类"+users.getName());
}
}
 
现在我定义了一个接口类型的Servlce层,包括实现类。
//Servilce 接口类
public interface UserService {
public void save(Users users);
}
//Servilce 实现类
public class UserServiceImp implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save(Users users) {
userDao.add(users);
}
}
现在我写了一个测试类去调用service层得到结果。
public class Test {
public static void main(String[] args) {
// 现在创建一个 BeanFactory 工厂
BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过这个工厂去获取 servece
UserService service=(UserService) factory.getBean("userSer");
service.save(new Users(1,"老项"));
}
}
 
现在我准备实现第一个Dao层的实现类。
<!-- 通过设置一个变量指向UserDao接口类的具体路径,我们可以通过不同的指定路径得到不同的结果,如 -->
<bean id="userDao" class="com.pro.dao.UserDaoImp1"></bean>
<!-- 通过设置一个变量指向userSer接口类的具体路径,在property 标签中,指定userSer接口类中的userDao的指向路径为上条语句的路径,ref的值为上条语句的Id -->
<bean id="userSer" class="com.pro.service.UserServiceImp">
<property name="userDao" ref="userDao"></property>
</bean>
假如我又不想实现第一个 Dao 层的实现类,想实现第二个,那么我只需要更改配置文件中的 userDao 指向路径类就可以实现。
-----------------------------------------------------------------------------
Aop的理解:
假设你有一个保存用户信息的方法如下:
public void save (UserInfo user){
this .userService.save(user); }
问题来了,我是一个事儿多的头头,我现在给你个新要求,你给我做个校验,如果 user.getUserName() == "fuck" 时,不保存,并抛出异常。 ok ,这时候你可能想,简单啊,我来,于是代码修改为:
public void save (UserInfo user){
if (user.getUserName() == "fuck" ){
throw new Exception( "Fuck you" );
}
this .userService.save(user);
}
代码很简单对不对?
事儿逼又来了,现在发现这个方法常出错,我希望记下日志,方便日后查到底是哪个用户出错了,怎么改?这时候你又来了,如下:
public void save (UserInfo user){
logger.log( "Saving user :" + user);
if (user.getUserName() == "fuck" ){
throw new Exception( "Fuck you" );
}
this .userService.save(user);
}
注意到问题了么?你每次都在修改保存用户的源码,关键是修改的内容还都和主业务逻辑无关,那么问题来了,万一哪次你手残把原有逻辑改坏了肿么办?或者说,这个方法被加了一大堆和主业务无关的东西,现在让你测试这个方法,怎么测好呢?
这个时候 aop 来救世了,我们通过面相切面的思维方式来考虑这个问题,正好 spring 又可以很好的使用 aop ,那么问题就解决了。只要通过一些配置,就可以指定在 save 方法执行前 / 后执行一些与主业务无关,但又需要的内容,开不开心?
 
代码实现方式 的解释:
@Aspect
public class MyAsp {
public void say(){
System.out.println("我是日志");
System.out.println("---------");
 
}
}
通过在 Spring 的配置文件中去指定它的位置,在 aop 的配置中,利用 aop:pointcut 标签的 expression 去指定它这个 ASP 类要在哪个包下去执行,通过 aop:before aop:after 去指定它要在类运行前 / 后去运行
<bean id="asp" class="com.pro.myasp.MyAsp"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.pro.service..*.*(..))" id="all"/>
<aop:aspect ref="asp">
<!-- 代表先执行 -->
<aop:before method="say" pointcut-ref="all"/>
<!-- 代表后执行 -->
<aop:after method="say" pointcut-ref="all"/>
</aop:aspect>
</aop:config>
通过配置,可以得到一个结果。假如在AOP中,写入了代表先执行的AOP。那么我在调用com.pro.service下的类方法时,如调用一个方法,方法内写了一条 System.out.println("111")输出语句。程序在执行这条之前会先去执行配置文件中的 配置的com.pro.myasp.MyAsp类下的say方法。
结果会是
    我是日志
---------
    111



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值