Spring 基础知识

Spring

Spring就是一个Bean工厂,Spring容器中的产品就是bean,Sprint能生产什么产品,完全取决于配置文件中的配置;对于开发者来说,使用Spring框架主要做两件事:开发bean,配置bean。对于Spring容器来说,它要做的是,根据配置文件来创建bean实例(Ioc:Inversion of Control),并调用bean实例的方法完成“依赖注入”(Dependency Injection)。Spring(特点)包括控制反转、依赖注入、AOP、与多种持久层技术整合。

一、Bean的装配

Spring具有非常大的灵活性,它提供了三种主要的装配机制:

1) 在XML中进行显式配置。
2) 在Java中进行显式配置。
3) 隐式的bean发现机制和自动装配(注解)。

1:在XML中进行显示配置

2:在Java中进行显式配置

  2.1 在进行显式配置时,JavaConfig是更好的方案,因为它更为强大、类型安全并且对重构友好。因为它就是Java代码,就像应用程序中的其他Java代码一样。

    @Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。      

    使用JavaConfig声明Bean:

    @Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。
    默认情况下,bean的ID与带有@Bean注解的方法名是一样的。在本例中,bean的名字将会是sgtPeppers。如果你想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定一个不同的名字。

 1 package soundsystem;
 2 import org.springframework.context.annotation.Bean;
 3 import org.springframework.context.annotation.Configuration;
 4 
 5 @Configuration
 6 public class CDPlayerConfig {
 7   
 8   //在JavaConfig中声明bean
 9   @Bean(name="loneyHearsClubBand")
10   public CompactDisc compactDisc() {
11     return new SgtPeppers(); //这里源码没有贴出,CompactDisc是一个接口,SgtPeppers是这个接口的一个实现类
12   }
13   
14   
15   @Bean
16   public CDPlayer cdPlayer(CompactDisc compactDisc) {  //JavaConfig的注入:
17     return new CDPlayer(compactDisc);   
18   }
19 
20 }

    cdPlayer()方法请求一个CompactDisc作为参数。当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配一个CompactDisc到配置方法之中。

  

3:自动装配

3.1 Spring从两个角度来实现自动化装配:

  3.1.1 创建bean,定义普通的JAVA类,并在类上加上@Component 注解即可,这样Spring就会把这个当初Bean自动创建实例了。

  3.1.2 启用组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。

    spring会去自动扫描base-package对应的路径或者该路径的子包下面的java文件,如果扫描到文件中带有@Service,@Component,@Repository,@Controller等这些注解的类,则把这些类注册为bean;

    组件扫描的启动方式又有两种

    a)一是在spring的xml配置文件中通过 <context:component-scan>标签配置:使用xml方式启动组件扫描,配置完成后,Spring启动就会自动扫描指定包以及子包下的所有Bean类,要注意配置上语义约束(代码5/8/9行);    

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans         
 7         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 8         http://www.springframework.org/schema/context 
 9         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
10         
11     <!-- 自动扫描指定包及子包下所有Bean类 -->
12     <context:component-scan base-package="com.linwei.mybeans" use-default-filters="true"/>
13 
14 </beans>

  注意: 测试的时候发现使用Spring3.0,如果使用了xml配置启用组件扫描,就不能在相同的包下面再使用 @ComponentScan注解 在Java类中启用组件扫描,否则会出现如下图所示的错误,使用Spring4.0.6版本就不会出现这种报错。 

                

    测试代码如下:引入相关JAR包,测试可以通过。测试代码如下:    

 1 package com.linwei.test;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Test;
 6 import org.junit.runner.RunWith;
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.test.context.ContextConfiguration;
 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
10 
11 import com.linwei.mybeans.Person;
12 
13 
14 @RunWith(SpringJUnit4ClassRunner.class)   //创建spring上下文
15 //@ContextConfiguration(classes=CdPlayConfig.class)  //告诉Spring在CdPlayConfig类中加载配置
16 @ContextConfiguration(locations="classpath*:/applicationContext.xml") 
17 public class CdPlayTest {
18 
19     @Autowired
20     private Person p ;
21     
22     @Test
23     public void cdShouldNotBeNull(){
24 //        assertNotNull(p);  //断言
25         System.out.println(p);
26     }
27 }

    

        b)二是使用注解 @ComponenScan annotation  (这中方式在《Spring实战第四版》这本书上按照书上的例子做实验需要使用Spring4.0.6或更新版本,若使用Spring3.0会报错,因找不到bena而无法创建Spring上下文,就是跟上面那个图片一样的错误。)

    自定义Bean:    

 1 package com.linwei.mybeans;
 2 
 3 import org.springframework.stereotype.Component;
 4 
 5 /* 该类使用了@Component注解
 6  * 这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
 7  * 没有必要显式配置SgtPeppersbea 
 8  */
 9 @Component
10 public class Person {
11     public String name ;
12 
13     public String getName() {
14         return name;
15     }
16 
17     public void setName(String name) {
18         this.name = name;
19     }
20     
21 }

    Spring应用上下文中所有的bean都会给定一个ID。上面例子中没有明确地为 Person 设置ID,但Spring会根据类名为其指定一个ID。具体来讲,这个bean所给定的ID为 person,也就是将类名的第一个字母变为小写,如果想为这个bean设置不同的ID,这样使用Component标签 @Component("yongPerson")。

    定义AppConfig配置类

 1 package com.linwei.mybeans;
 2 
 3 import org.springframework.context.annotation.ComponentScan;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 /*
 7  * AppConfig类并没有显式地声明任何bean,只不过它使用了@ComponentScan注解
 8  * 这个注解能够在Spring中启用组件扫描,相当于在xml配置了 context:component-scan 标签;
 9  * @ComponentScan默认会扫描与配置类相同的包(当前包以及这个包下的所有子包),查找带有@Component注解的类,Spring中自动为其创建一个bean
10  */
11 
12 @Configuration
13 @ComponentScan  //该注解启用组件扫描
14 //@ComponentScan(basePackages="com.linwei.mybeas","com.linwei.otherBeans") //指定扫描基础包位置
15 //@ComponentScan(basePackageclasses={Person.class,Animal.class}) //数组中类所在的包作为扫描的基础包位置
16 public class AppConfig {
17 
18 }

  

  定义测试类

 1 package com.linwei.test;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Test;
 6 import org.junit.runner.RunWith;
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.test.context.ContextConfiguration;
 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
10 
11 import com.linwei.mybeans.AppConfig;
12 import com.linwei.mybeans.Person;
13 
14 
15 @RunWith(SpringJUnit4ClassRunner.class)   //创建spring上下文
16 @ContextConfiguration(classes=AppConfig.class)  //告诉Spring在CdPlayConfig类中加载配置
17 public class CdPlayTest {
18 
19     @Autowired
20     private Person p ;
21     
22     @Test
23     public void cdShouldNotBeNull(){
24 //        assertNotNull(p);  //断言
25         System.out.println(p);
26     }
27 }

  Spring配置文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans         
 7         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 8         http://www.springframework.org/schema/context 
 9         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
10         
11 
12 </beans>

  运行结果:

   3.1.3 自动装配(autowiring):通过使用 @Autowired 注解实现自动装配;

  构造器上添加了@Autowired注解,这表明当Spring创建CDPlayerbean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。  

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;

  @Autowired
  public CDPlayer(CompactDisc cd) {
    this.cd = cd;
  }

  public void play() {
    cd.play();
  }

}

  @Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。比如说,如果CDPlayer有一个setCompactDisc()方法,那么可以采用如下的注解形式进行自动装配

@Autowired
public void setCompactDisc(CompactDisc cd){
     this.cd = cd ;
}

二、依赖注入DI(Dependency Injection)

 

2、配置依赖(两种注入方式):setter注入、构造注入

* 2.1 设值(setter)注入:使用属性的set方法来注入被依赖对象,并在bean.xml中配置好<bean.../>的子表签<proterty.../>,简单直观;

  2.1.1 Spring可以为任何java对象注入任何类型的属性,只要该Java对象为该属性提供了对应的set方法即可。当我们在XML文件中使用<proterty.../> 元素为配置依赖注入时,其底层的本质就是调用该Bean对象的Setter方法,每个<proterty.../>对应调用一次setter方法。

  2.1.2 设置属性的值(value/ref/bean/list、set、map、props):

  • value:用于指定字符串类型、基本数据类型的值; 如:下配置文件:  
<bean id="exampleBean" class="com.crazyit.app.service.ExampleBean">
    <!-- 指定int类型的值 -->
    <property name="integerProperty" value="1" />
    <!-- 指定 double 类型的值 -->
    <property name="doubleProperty" value="2.3" />
</bean>
  • ref:用于指定引用类型的属性值;
<bean id="steelAxe" class="com.crazyit.app.service.impl.SteelAxe" />
<bean id="chinese" class="com.crazyit.app.service.impl.Chinese">
    <!-- 引用容器中另一个bean -->
    <property name="axe" ref="steelAxe" />
</bean>
  • 自动装配注入:Spring可以自动装配Bean与Bean之间的依赖关系,即无须使用ref显示指定依赖Bean。通过<beans../> 元素的 default-autowire属性指定,也可通过<bean../>元素的autowire属性指定,同一个容器里,可以让某些Bean自动装配,而另一些Bean不用自动装配; autowire 可以分为 byType 和 byName;
    • byName:根据属性名自动装配,BeanFactory查找容器中全部Bean,找出其中 id 属性与属性同名的Bean来完成注入,如果找不到就不进行注入; 
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/benas"
     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-3.0.xsd> <bean id="chinese" class="com.crazyit.app.service.impl.Chinese" autowire="byName" /> <bean id="gundog" class="com.crazyit.app.service.impl.Gundog" />
</beans>

   上面的配置文件指定了byName自动装配策略,则要求com.crazyit.app.service.impl.Chinese类中提供如下依赖注入方法:

/*
 * 依赖关系必需的setter方法,因为要通过名字装配 set + Bean名,首字母大写
 */
public void setGundog(Dog dog){
    this.dog = dog;
}
    •  byType:根据属性类型自动装配,BeanFactory查找容器中全部Bean,如果找到一个与依赖属性类型相同的Bean,就自动注入该属性;如果找不到就不进行装配,如果找到多个就会抛出异常; 
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/benas"
     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-3.0.xsd> <bean id="chinese" class="com.crazyit.app.service.impl.Chinese" autowire="byType" /> <bean id="gundog" class="com.crazyit.app.service.impl.Gundog" /> </beans>
/*
 * 依赖关系必需的setter方法
 * 使用按类型自动装配,setter方法的参数类型与容器中的Bean类型相同
 * 程序中Gundog实现了Dog接口;
 */
public void setDog(Dog dog){
    this.dog = dog;
}
  • 注入集合值

   如果Bean的属性是个集合,则可以使用元素,<list.../>、<set.../>、<map.../>、<props.../>元素分别设置类型为List、Set、Map、Properties的集合属性值;

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/benas"
     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-3.0.xsd>
     
    <!--定义Bean -->
    <bean id="stoneAxe" class="com.crazyit.app.service.impl.StoneAxe" />    
    <bean id="steelAxe" class="com.crazyit.app.service.impl.SteelAxe" />
    <!--定义chinese Bean -->
    <bean id="chinese" class="com.crazyit.app.service.impl.Chinese" >  
        <property name="schools">
            <!-- 为 List 属性配置属性值  -->
            <list>
                <value>小学</value>
                <value>中学</value>
                <value>大学</value>
            </list>
        </porperty>    
        
        <property name="scores">
            <!-- 为 Map 属性配置属性值  -->
            <map>
                <entry key="数学"  value="87" />
                <entry key="语文"  value="89" />
                <entry key="英语"  value="80" />
            </map>
        </porperty>
        
        <property name="phaseAxes">
            <!-- 为 Map 属性配置属性值  -->
            <map>
                <entry key="原始社会"  value-ref="stoneAxe" />
                <entry key="农业社会"  value-ref="steelAxe" />
            </map>
        </porperty>
        
        <property name="health">
            <!-- 为 Porperties 属性配置属性值  -->
            <props>
                <prop key="身高" >175</prop>
                <prop key="体重" >50</prop>
            </props>
        </porperty>
        
        <property name="axes">
            <!-- 为 Set 属性配置属性值  -->
            <set>
                <value>普通字符串</va;ue>
                <bean class="com.crazyit.app.service.impl.SteelAxe" />
                <ref local="stoneAxe">
            </set>
        </porperty>
    </bean>
</beans>

  由于集合元素可以是基本数据类型,也可以是引用数据类型,所以<list.../>、<set.../>、<map.../>可接受如下子元素;(Properties类型的key和value都只能是字符串)

  • value:指定集合元素是基本数据类型或者字符串类型;
  • ref: 指定集合元素是容器中另一个Bean实例;
  • bean: 指定集合元素是一个嵌套Bean;
  • list/set/map及props:指定集合元素又是集合;

* 2.2 构造注入:提供一个构造注入所需的带参数的构造方法,并在bean.xml中配置好<bean.../>的子表签<construtor.../>,通过此方法可以控制注入顺序;

3、ApplicationContext 是 BeanFactory 的子接口,增强了BeanFactory的功能,因此ApplicationContext完全可以作为Spring的容器来使用,而且功能更强。(也就是说BeanFactory 和 ApplicationContext都可作为Spring的容器)

4、ApplicationContext的事件处理机制是观察者模式的实现,通过ApplicationEvent类(容器事件)、ApplicationListener接口(监听器),可以实现ApplicationContext的事件处理。如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean 将自动被触发。

5、Spring容器中Bean的作用域:

  通过Spring创建Bean实例时,不仅可以完成Bean的实例化工作,还可以为Bean指定作用域。

  • singleton 单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例;【常用,且是Spring的缺省作用域】
  • prototyp 原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例;【常用】
  • request 对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效;
  • session 对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效;
  • global session 每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效;   
<bean id="empServiceImpl" class="cn.csdn.service.EmpServiceImpl" scope="singleton">

  Java在创建实例时,需要进行内存申请,销毁实例时,需要完成垃圾回收,这些工作都将会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大,而singleton作用域的实例一旦创建成功,可以重复使用,因此,除非必要,否则尽量避免将Bean设置成prototype作用域。

  request、session作用域的Bean只对Web应用才真正有效,实际上通常只会将Web应用的控制器Bean才制定成request作用域,而且必须在Web应用中增加额外的配置才会生效哦,必须将HTTP请求对象绑定到为该请求提供服务的线程上,这使得具有request和session作用域的Bean实例能够在后面的调用链中被访问到。

  为此,可以有两种方式配置:采用Listener配置,或采用Filter配置。采用任何一种配置,程序就可以在spring配置文件中使用request,session作用域了。

a、在Web应用的web.xml文件中增加如下Listener配置:

<web-app>
   ...
   <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
</web-app>

b、Filter配置方式:

<web-app>
   ...
   <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
   ...
</web-app>

6、Spring的注解介绍:“零配置”即只使用注解,不用在XML文件中进行配置了。

  如果不使用XML配置,Spring怎么知道应该把哪些java类当成Bean类处理呢?这就需要使用Annotation(注解)了,Spring通过使用一些特殊的Annotation来标注Bean类,Spring提供了如下借个Annotation来标注Spring Bean;

  • @Component :标注一个普通的Spring Bean类;
  • @Controller :标注一个控制器组组件类;
  • @Service : 标注一个业务逻辑组件类;
  • @Repository :标注一个DAO组件类;

  如果我们定义一个普通的Java bean,则直接使用 @Component 标注即可,但如果用@Controller、@Service、@Repository来标注这些Bean类,这些Bean类将被作为特殊的JavaEE组件对待,也许更好地被工具处理,或与切面进行关联。  在Spring未来的版本中,@Controller、@Service、@Repository也许还能携带更多的语义,因此,如果需要在JavaEE应用中使用这些标注时,尽量考虑使用@Controller、@Service、@Repository来代替通用的 @Component 标注。

  接下来需要在Spring 的配置文件中指定搜索路径,Spring将会自动搜索该路径下的所有Java类,并根据这些java类来创建bean实例。 

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/benas"
     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-3.0.xsd>

    <!-- 自动扫描指定包及子包下所有Bean类 -->
    <context:component-scan base-package="com.crazyit.app.service"/>
</beans>

 @Resurce 有一个name属性,在默认情况下,Spring将这个值解释为需要被注入的Bean实例的名字,也就是说,使用 @Resurce 与 <property.../>元素的 ref 属性有相同的效果,如下Bean类:
@Resurce Annotation 指定 stoneAxe 注入set方法,也就是将容器中的 stoneAxe Bean 作为setAxe方法的参数传入; 

@Componet
public class Chinese implements Person{
    private Axe axe;
    
    @Resurce(name="stoneAxe")
    public void setAxe(Axe axe){
        this.axe = axe;
    }
    //实现Person的useAxe方法
    public void useAxe(){
        //调用axe的chop()方法;
        System.out.println(axe.shop());
    }
}

 @Resurce 不仅可以修饰setter方法,也可以直接修饰 filed ,使用 @Resurce 时还可以忽略name属性(如果修饰filed省略name属性,则name属性默认与filed同名;如果修饰setter方法省略name属性,则name属性默认是该setter方法去掉前面set子串、首字母小写后得到的字符串)。

 @Componet
public class Chinese implements Person{
    @Resurce(name="stoneAxe")
    private Axe axe;    
    
    //实现Person的useAxe方法
    public void useAxe(){
        //调用axe的chop()方法;
        System.out.println(axe.shop());
    }
}

 二、AOP

1、Spring的两种‘后处理器’:

  • Bean后处理器:对容器中的Bean进行后处理,对Bean功能进行额外强化 ;
  • 容器后处理器:这种后处理器对IoC容器进行后处理,用于增强容器功能;

a)Bean后处理器会在Bean实例创建成功之后,对Bean实例进行进一步的增强处理,Bean后处理器必须实现BeanPostProcessor接口,BeanPosstProcessor接口包含下面两个方法;

  • Object postProcessBeforInitalization(Object bean,String name)throws BeansException;
  • Object postPorcessAfterInitalization(Object bean,String name)throws BeansException;

  Bean后处理器必须实现这两个方法,才能在目标Bean初始化之前、之后分别被回调,用于对容器中的Bean实例进行增强处理。

  

 1 public class MyBeanPostProcessor implements BeanPostProcessor{
 2 
 3     @Override
 4     public Object postProcessBeforeInitialization(Object bean, String beanName)
 5             throws BeansException {
 6         // TODO Auto-generated method stub
 7         System.out.println("Bean后处理器在初始化之前对"+beanName+"进行增强处理...");
 8         return null;
 9     }
10 
11     @Override
12     public Object postProcessAfterInitialization(Object bean, String beanName)
13             throws BeansException {
14         // TODO Auto-generated method stub
15         System.out.println("Bean后处理器在初始化之后对"+beanName+"进行增强处理...");
16         if(bean instanceof HelloWorld){
17             HelloWorld hello = (HelloWorld)bean;
18             hello.setInfo("后处理修改bean实例的属性值");
19         }
20         return null;
21     }
22 
23 }

b)容器后处理器;必须要实现BeanFactoryPostProcessor接口,该接口包含下面这个方法:

  postProcessBeanFactory(ConfigurableBeanFactory beanFractory);使用ApplicationContext作为Spring容器(BeanFactory也可作为Spring的容器),那么该Spring容器会自动搜索容器所有实现了BeanPostPorcessor接口的类,并将它们注册成Bean后处理器,也会自动搜索容器中所有实现了BeanFactoryPostProcessor接口的类,并将它注册成容器后处理器。(如果使用BeanFactory作为容器,则后处理器的Bean在XML配置文件里配置,配置方法和普通bean的配置一样;)

  Spring已经自己提供了几个常用的容器后处理器,程序可以配置多个容器后处理器,可设置order属性来控制容器后处理器的执行顺序。

  • PropertyPlaceholderConfigurer:属性占位符配置器(下面会介绍);
  • PorpertyOverrideConfigurer:重写占位符配置器(自己查资料);

   下面详细介绍一下属性占位符配置器:

  PropertyPlaceholderConfigurer,它负责读取Propertries属性文件里的属性值,并将这些属性值设置成Spring配置文件的元数据。这种配置的优势是可以将部分类似的配置(比如数据库的URL,用户名密码)放倒特定的属性文件中,如果只需求修改这部分配置,则无须修改Spring配置文件,修改属性文件即可。

  如下applicationContext.xml配置了PropertyPlaceholderConfigurer后处理器,在配置数据源Bean时,使用了属性文件中的属性值。

 1 <xml version="1.0" encoding="GBK"?>
 2 <!--  Spring配置文件根元素,使用spring-beans-3.0.xsd语义约束  -->
 3 <beans xmls:....>
 4 
 5 ...
 6 <bean calss="org.springframwork.beans.factory.config.PropertyPlaceholderConfigurer">
 7     <property name="locations">
 8         <list>
 9               <value>dbconn.properties</value>
10               <!--  如果有多个属性文件,依次在下面列出来  -->
11              <value>wawa.properties</value>
12         </list>    
13     </property>
14 </bean>
15 ...
16 <bean id="dataSource" calss="com.mchange.v2.c3p0.ComboPooledDataSource" destory-method="close">
17     <property name="driverClass" value="${jdbc.driverClassName}"/>
18     <property name="jdbcUrl" value="${jdbc.url}"/>
19     <property name="user" value="${jdbc.username}"/>
20     <property name="password" value="${jdbc.password}"/>
21 </bean>
22 
23 </beans>

可以看到上面配置文件中,配置driverClass、jdbcUrl等信息时,没有直接设置属性值,这表明Spring容器将从propertyConfigurer指定属性文件中搜索这些Key对应的value,并为该Bean的属性值设置这些Value值。ApplicationContext会自动检测部署在容器中的容器后处理器,无须额外注册,因此,只需提供如下Properties文件即可;

1 jdbc.dirverClassName=com.mysql.jdbc.Driver
2 jdbc.url=jdbc:mysql://localhost:3306/javaee
3 jdbc.username=root
4 jdbc.password=123456

 2、AOP的概念

   面向切面编程的术语:

  • 切面Aspect:业务流程运行的某个特定步骤,也就是应用运行过程中的关注点,关注点可以横切多个对象。
  • 连接点Joinpoint:程序执行过程中明确的点,如方法的调用,异常的抛出,Spring AOP中,连接点只是方法的调用;
  • 增强处理Advice:AOP框架在特定的切入点执行的增强处理。处理有 around 、befor 、after等类型;
  • 切入点Pointcut:可以插入增强处理的连接点。也就是,当某个连接点满足制定要求时,该连接点将被添加增强处理,该连接点也就成了切入点了;例如:

       pointcout xxxPointcut():excution(void H*.say*())  

  每个方法的调用都是相当于连接点,但是,如果该方法属于 H 开头的类,并且方法名以 say 开头,该方法的执行就变成了切入点。

  • 目标对象:被AOP框架增强处理的对象,也被成为被增强对象。
  • AOP代理:AOP框架创建的对象,简单说,代理就是对目标对象的加强,Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理。

Spring默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。Spring推荐使用面向接口编程,因此业务对象通常都会实现一个或多个接口,此时默认使用JDK动态代理,但也可以强制使用CGLIB。

AOP编程的三个部分:

(1)定义普通业务组件;

(2)定义切入点,一个切入点可能横切多个业务组件;

(3)定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作。

上面第一个部分是最平常不过的事情,所以无须额外说明,那么进行AOP编程的关键就是定义切入点和定义增强处理。一旦定义了切入点和增强处理,AOP框架就会自动生成AOP代理,Spring有如下两种方式定义切入点和增强处理:

(a)基于Annotation的零配置方式,使用@Aspect,@Pointcut等Annotation来标注切入点和增强处理。

(b)基于XML配置文件管理方式;使用Spring配置文件来定义切入点和增强处理。

 2.1 使用AspectJ实现AOP

  AspectJ是一个基于JAVA语言的AOP框架,提供了强大的AOP功能,AspectJ采用的是编译时增强,所以AspectJ需要使用自己的编译器来编译Java问价,还需要织入器;而Spring AOP采用的是运行时生成动态代理的发那个是来增强目标对象,Spring只是使用了和AspectJ一样的注解,Spring框架可识别并根据这些Annotation来生成AOP代理。但是并没有AspectJ的编译器或者织入器。

2.2 Spring AOP 

  Spring中的AOP代理由Spring的IoC容器负责生成、管理,其依赖关系也由IoC容器负责管理,Spring默认用Java动态代理来创建AOP代理,也可以使用CGLIB代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理。

  Spring目前仅支持将方法作为连接点,如果需要把对Field的访问和更新也作为增强处理的连接点,则可以考虑使用AspectJ。

2.2.1 基于Annocation的零配置方式

  1.修改Spring配置文件,启用AspectJ支持;

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/benas"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
<!-- 启动@AspectJ支持,自动增强 -->
<aop:aspectj-autoproxy/>
<bean class="org.springframework.aop.aspectj.annotion.AnnotationAwareAspectJAutoProxyCreator"/>

</beans>

  上面配置文件中的 AnnotationAwareAspectJAutoProxyCreator 是一个Bean后处理器,该Bean后处理器将会为容器中的Bean生成AOP代理。 

  

  2.定义切面Bean

  使用@Aspect标注一个java类,Spring将会自动识别该java类,并将该Bean作为切面处理。这种bean与普通Bean的配置没有任何区别。一样使用<bean.../>元素配置;

  当我们使用@Aspect来修饰一个java类后,spring将不会把该Bean当作组件Bean处理,因此负责自动增强的后处理Bean将会略过该Bean,不会对该Bean进行任何增强处理。

  3.定义增强处理

  3.1定义Before增强处理

  当我们在一个切面类里使用@Before来标注一个方法时,该方法将作为Befroe增强处理。使用@Before标注时,通常需要制定一个value属性值,该属性值指定一个切入点表达式(即可以是一个已有的切入点,也可以直接定义切入点表达式),用来制定该增强处理将被织入到哪些切入点。

 1 //定义切面
 2 @Aspect
 3 public class BeforeAdviceTest{
 4     //匹配org.crazyit.app.service.impl 包下所有类的所有方法的执行作为切入点
 5    @Before("excution(* org.crazyit.app.service.impl.*.*(..))")
 6    public void authority(){
 7         System.out.println("模拟执行权限检查");
 8    }
 9     
10 }

  3.2定义AfterReturing增强处理

  

 1 //定义一个切面
 2 @Aspect
 3 public class AfterReturningAdiviceTest{
 4     //匹配org.crazyit.app.service.impl包下所有类的所有方法的执行作为切入点
 5     @AfterReturining(returning="rvt",pointcut="excution(* org.crazyit.app.service.imp.*.*(..))")
 6     public void log(){
 7         System.out.println("获取目标方法返回值:"+rvt);
 8         System.out.println("模拟记录日志功能....");
 9     }
10 
11 }

  3.3定义AfterThrowing增强处理

  3.4定义After增强处理

    3.5Around增强处理

 2.2.2 基于XML配置文件的处理 <aop:config.../>元素

  实际上,使用XML配置文件和上面的@AspectJ方式的实质是一样的,同样需要指定相关数据:配置切面、切入点、增强处理,所需要的信息完全一样,只是提供这些信息的位置不同而已,使用XML配置方式是通过XML文件来提供这些信息;使用@AspectJ方式则是通过Annotation来提供这些信息;

  (一般情况下使用XML配置方式多一些,因为增强处理可能是别人已经写好的,你需要用的时候直接配置上去即可,特别是一些已经封装好的增强处理;除非是需要你自己写切面类和增强处理。)

  需要注意的是:当我们使用<aop:config.../>方式进行配置时,可能与Spring的自动代理方式相互冲突,例如我们使用了<aop:aspectj-autoproxy/>或者类似方式显式启动了自动代理,则它可能会导致问题(例如有些增强处理没有被织入),因此建议:要么全部使用<aop:config.../>配置方式,要么全部使用自动代理方式,不要把两者混合使用。

  a)定义一个普通的Bean,然后配置成切面Bean

  当普通Bean定义完成后,通过在<aop:aspect.../>元素中使用ref属性来引用该Bean,就可以将该Bean转换成一个切面Bean了。

<aop:config>
    <!-- 将容器中的afterAdviceBean转换成切面Bean,切面Bean的新名称为:afterAdviceAspect  -->
    <aop:aspect id="afterAdviceAspect" ref="afterAdviceBean">
          <!-- 定义一个Before增强处理直接制定切入点表达式,以切面Bean中的authority()方法作为增强处理方法 -->
         <aop:befor pointcut="excution(* org.crazyit.app.service.impl.*.*(..))" method="authority" />
          ...
    </aop:aspect>
</aop:cofig>

<!-- 定义一个普通Bean实例,该Bean实例将作为Aspect Bean  -->
<bean  id="afterAdviceBean" class="lee.AfterAdviceTest"  />

     <aop:aspect.../>元素的属性:id 定义该切面的标识名;ref 指定所引用的普通Bean作为切面Bean;order指定该切面的Bean的优先级,order属性值越小,该切面的优先级越高。

   b)配置增强处理(上面xml配置蓝色部分)

    使用XML一样可以配置Before、After、AfterReturning、AfterThrowing和Around 5种增强处理:

    <aop:befor.../> <aop:after.../>  <aop:after-returning.../>  <aop:after-throwing.../> <aop:around.../> 这五个元素都不支持子元素,但通常可指定如下属性:

  • pointcut:指定一个切入表达式,Spring将在匹配该表达式的连接点时织入该增强处理;
  • pointcut-ref:指定一个已经存在的切入点名称,通常pointcut和poincut-ref两个属性只需要其中之一;
  • method:指定一个方法名,指定该切面Bean的这个方法作为增强处理。
  • throwing:该属性只对<after-throwing../>元素有效,用于指定一个形参名,AfterThrowing增强处理方法可通过该形参访问目标方法所抛出的异常;
  • returning:该属性只对<after-returning../>元素有效,用于指定一个形参名,AfterReturning增强处理方法可通过该形参访问目标方法的返回值。

   既然应用选择使用XML配置来配置增强处理,所有切面类里定义面、切入点和增强处理的Annotation全都可删除了。

    c)配置切入点

    类似与@AspectJ方式,允许定义切入点来重用切入点表达式(上面2.2.1中很好理解并没有记录下来),Spring提供了<aop:pointcut.../>元素来定义切入点。当把<aop:pointcut.../>元素作为<aop:config.../>的子元素定义时,表明该切入点可被多个切面共享;当把<aop:pointcut.../>元素作为<aop:aspect.../>子元素定义时,表明该切入点只能在对应的切面中有效。

<!-- 定义一个简单的切入点 -->
<aop:pointcut id="mypointcut" expression="exuction{* org.crazyit.app.service.impl.*.*(..)}">
原文地址:https://www.cnblogs.com/crazytrip/p/6418560.html