Spring(4)

Spring的Bean的配置形式

1.基于XML的形式(无需讲解)

2.基于注解的形式(需要引入AOP的jar包,此jar包实现了AOP的注解)

当在Spring配置文件中引入类扫描注解命名空间并且指定被扫描的包,Spring就能够从classpath下自动扫描此包以及子包,侦测和实例化具有特定注解的组件

特定的组件包括:

--Component:基本注解,标识了一个受Spring管理的组件

--Respository:标识持久层组件

--Service:标识服务层(业务层)组件

--Controller:标识表现层组件

对于扫描到的组件,Spring有默认的命名策略

--如果未通过value属性标识组件的名称,那么会使用非限定类名,即取类名的首字母小写

--通过value属性标识组件名称

类扫描注解命名空间(Context命名空间):

xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

类扫描注解解析器:

<context:component-scan base-package=""></context:component-scan>

如果希望扫描到指定包下的类而非基包下的所有的类,可以使用resource-pattern属性过滤掉特定的类

<context:component-scan base-package="cn.jc.spring.annotation" resource-pattern="xxx包名/*.class"></context:component-scan>

context注解解析器的子节点

--context:include-filter  表示要包含的目标类

--context:exclude-filter 表示要排除在外的目标类

该注解解析器下可以拥有若干个子节点,子节点支持多种类型的过滤表达式,常用的类别:annotation,assinable

<!-- 通过所有标注了XxxAnnotation注解方式的类来过滤 -->

<!-- 子节点context:exclude-filter 表示指定不包含通过注解方式被SpringIOC容器所管理的那个Bean-->
<!-- <context:component-scan base-package="cn.jc.spring.annotation">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
  </context:component-scan>

-->


<!-- 子节点 context:include-filter 表示指定包含通过注解方法被SpringIOC容器所管理的那个Bean,
但是use-default-filters属性默认值是true,表示使用默认的注解解析器如Component,Respository,Service,Controller
如果改成false,表示使用的是 include-filter指定的那个表达式组件,否则将无法过滤
-->
<!-- <context:component-scan base-package="cn.jc.spring.annotation" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan> -->

<!-- 通过所有继承或者扩展XxxService的类 -->

<!-- 不包含目标类 继承或者扩展那个特定的类-->
<!-- <context:component-scan base-package="cn.jc.spring.annotation">
    <context:exclude-filter type="assignable" expression="cn.jc.spring.annotation.repository.UserRepository"/>
   </context:component-scan>

-->
<!-- 包含目标类继承或者扩展那个特定的类,但是需要使用 use-default-filters设置为false-->
<context:component-scan base-package="cn.jc.spring.annotation" use-default-filters="false">
  <context:include-filter type="assignable" expression="cn.jc.spring.annotation.repository.UserRepository"/>
</context:component-scan>

属性注解

<context:component-scan> 元素除了扫描包及其子包下的类并且将Bean交给Spring容器装配之外,还可以自动注册AutowiredAnnotationBeanPostProcessor实例的后置处理器,该实例可以自动装配

具有@Autowired和@Resource以及@Inject注解的属性

@Autowired注解属性可以放在任意的三个位置

首先会自动装配具有兼容类型的单个bean属性,也就是会根据类型去匹配IOC容器中被装配的Bean

其次如果是找不到,就会根据属性名称和IOC容器中被装配的bean的id的名称相匹配,如果相同则匹配成功

也可以使用注解@Qualifier("")指定属性的名称与所需的被装配的bean的id名称相同(这种情况下通常是在一个借口多个实现类的情况下,为了指明需要使用哪个实现类,可以使用注解@Qualifier("")属性指定使用哪个bean的id),使用这个@Qualifier("")需要使用@Autowired,两个都需要的

--构造器

--普通字段(即使是非public)

直接在字段上加@Autowired,IOC容器会检查这个属性有没有被装配,如果被装配,则自动会给属性赋值

如果发现这个属性没有被IOC所装配,此时加了注解@Autowired会报错的,如果不想被报错,可以加上属性@Autowired(required=true),输出的结果会是null

--带参数的方法(setter方法)

在setter方法上加@Autowired注解,IOC容器中如果属性被装配,那么就可以为属性赋值

如果是需要指定使用哪个bean对象,可以使用@Qualifier("")和@Autowired,放在setter方法上的

也可以把@Qualifier("")放在setter方法的带参数的旁边,这样也是可以的

举个例子:

@Autowired
public void setUserJdbcRepositoryImpl1(@Qualifier("userJdbcRepositoryImpl")UserRepository userJdbcRepositoryImpl1) {
this.userJdbcRepositoryImpl1 = userJdbcRepositoryImpl1;
}

 除了@Autowired和@Qualifier("")可以为属性赋值之外,还可以使用@Resource(name=""),此属性名称要和IOC容器中被管理的bean的id的名字要相同,如果不指明name属性,就会根据类型相匹配

@Inject 也是根据类型匹配为属性赋值,但是没有required属性

Spring的Bean的配置方式

1.通过全类名的形式(反射机制)

id在IOC容器中必须是唯一的

如果id没有指定,Spring自动将权限定性类名作为Bean的名字

id可以指定多个名字,名字之间可以用逗号、分号或者空格隔开

2.通过工厂方法(静态工厂方法和实例工厂方法)

静态工厂方法:简单的说就是通过调用某个类的的静态的方法就可以返回Bean实例,在Spring的配置文件中需要配置静态工厂方法的全类名,指明静态方法,使用constructor-arg来配置静态方法传入的参数

例子:

Bean实例:

public class Car {

    private String broad;

    private double speed;

    public Car(String broad, double speed) {

       this.broad = broad;

       this.speed = speed;

    }

    public Car() {

    }

    @Override

    public String toString() {

       return "Car [broad=" + broad + ", speed=" + speed + "]";

    }

    public void setBroad(String broad) {

       this.broad = broad;

    }

    public void setSpeed(double speed) {

       this.speed = speed;

    }

}

静态工厂类:

/**

 * 静态工厂方法:通过调用某个类的静态方法就可以返回Bean的实例

 * */

public class StaticCarFactory {

    private static Map<String, Car> cars = new HashMap<String, Car>();

    static{

       cars.put("audio", new Car("audio", 100));

       cars.put("ford", new Car("ford", 200));

    }

    //静态方法

    public static Car getCar(String name){

       return cars.get(name);

    }

}

Spring配置文件:

  <!-- 通过静态工厂方法来获取bean实例,注意:不是配置静态工厂方法实例,而是配置bean实例 -->

    <!--

       class属性:指向静态工厂方法的全类名

       factory-method:指向静态工厂方法的名字

       constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数

     -->

     <!-- 实际上是通过静态工厂方法的全类名调用静态方法获取的bean -->

    <bean id="car1" class="cn.jc.spring.factory.StaticCarFactory" factory-method="getCar">

       <constructor-arg value="audio"></constructor-arg>

</bean>

测试类

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");

       Car car = (Car)ctx.getBean("car1");

       System.out.println(car);

实例工厂方法先创建工厂对象本身,再调用工厂的实例方法来返回Bean实例

例子

实例工厂类:

/**

 * 通过调用实例工厂的方法 即先需要创建工厂对象本身,再调用工厂的实例方法来返回bean的实例

 * */

public class InstanceCarFactory {

    private Map<String, Car> cars = null;

    public InstanceCarFactory(){

       cars = new HashMap<String, Car>();

       cars.put("audio", new Car("audio", 1000));

       cars.put("fode", new Car("fode", 2000));

    }

    public Car getCar(String name){

       return cars.get(name);

    }

}

Spring配置文件:

<!-- 配置工厂实例 -->

    <bean id="carFactory" class="cn.jc.spring.factory.InstanceCarFactory"></bean>

    <!--通过工厂实例配置工厂方法从而获取bean-->

    <bean id="car2" factory-bean="carFactory" factory-method="getCar">

       <constructor-arg value="fode"></constructor-arg>

</bean>

测试类

Car car2 = (Car)ctx.getBean("car2");

System.out.println(car2);

3.通过FactoryBean的方式

通过实现FactoryBean的接口来调用getObject的方法,从而获取Bean实例

例子

实现FactoryBean接口的实现类

package cn.jc.spring.factorybean;

import org.springframework.beans.factory.FactoryBean;

/**

 * 使用FactoryBean的方式来配置Bean

 * 自定义的FactoryBean需要实现spring提供的FactoryBean接口

 * */

public class CarFactoryBean implements FactoryBean<Car> {

    private String brand;

    public void setBrand(String brand){

       this.brand = brand;

    }

    /**

     * 返回bean的对象

     */

    public Car getObject() throws Exception {

       return new Car(brand, 20000);

    }

    /**

     * 返回bean的类型

     */

    public Class<?> getObjectType() {

       return Car.class;

    }

    /**

     * 是否是单实例的

     */

    public boolean isSingleton() {

       return true;

    }

}

Bean类: 

package cn.jc.spring.factorybean;

public class Car {

    private String broad;

    private double speed;

    public Car(String broad, double speed) {

       this.broad = broad;

       this.speed = speed;

    }

    public Car() {

    }

    @Override

    public String toString() {

       return "Car [broad=" + broad + ", speed=" + speed + "]";

    }

    public void setBroad(String broad) {

       this.broad = broad;

    }

    public void setSpeed(double speed) {

       this.speed = speed;

    }

}

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">

    <!-- 通过FactoryBean来配置bean的实例

       class:指向FactoryBean的全类名

       property:配置FactoryBean的属性    

       返回的实例是FactoryBean的getObject()方法返回的实例

        -->

    <bean id="car" class="cn.jc.spring.factorybean.CarFactoryBean">

       <property name="brand" value="BMW"></property>

    </bean>

</beans>

测试main:

package cn.jc.spring.factorybean;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {

       ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factorybean.xml");

       Car car = (Car)ctx.getBean("car");

       System.out.println(car.toString());

    }

}

依赖注入的方式

1.属性注入

2.构造器注入

  。可以使用index和type来区分重载的构造器,注意构造器必须要有空的构造方法,因为IOC容器要调动空的构造方法创建对象

属性注入的细节

  。不管是字面值还是引用,赋值都可以使用元素<value/>或者<ref/>元素标签

   。如果字面值中包含特殊字符,可以使用<![CDATA[值]]>

   。可以使用专用的<null/>元素标签作为字符串或者对象的空值

  <property name="name"><null/></property>

  <constructor-arg index="0" type="java.lang.String"><null/></constructor-arg>

  。支持级联属性赋值

<bean id="helloWorld" class="cn.jc.spring.beans.HelloWorld">

                 <property name="name"><value>xtt</value></property>

                 <property name="car">

                       <!-- 内部Bean -->

                      <bean class="cn.jc.spring.beans.Car">

                      <property name="broad" value="laosi"></property>

                      <property name="speed" value="100"></property>

                      </bean>

                 </property>

                      <!-- 级联属性配置 ,前提是先为car属性赋值,否则会报null-->

                 <property name="car.speed" value="200"></property>

</bean> 

  。集合属性的赋值

<bean id="p" class="cn.jc.spring.collections.Person">

    <property name="age" value="10"></property>

    <property name="name" value="cy"></property>

    <!-- 给list集合属性赋值 -->

    <property name="carLists">

         <list>

          <ref bean="car1"/>

          <ref bean="car2"/>

          <!-- 使用内部bean给属性赋值 -->

          <bean class="cn.jc.spring.collections.Car">

            <constructor-arg value="aodi"></constructor-arg>

            <constructor-arg value="0.9"></constructor-arg>

          </bean>

         </list>

    </property>

    <!-- 给数组属性赋值 -->

    <property name="thinks">

      <list>

        <value>t1</value>

        <value>t2</value>

        <value>t3</value>

      </list>

    </property>

    <!-- 给set集合赋值 -->

    <property name="houseLists">

     <set>

       <ref bean="house1"/>

       <ref bean="house2"/>

     </set>

    </property>

  <!--给map集合属性赋值-->

    <property name="moMaps">

      <map>

        <entry key="第一部手机" value-ref="m1"></entry>

        <entry key="第二部手机" value-ref="m2"></entry>

       </map>

     </property>

</bean>

<!-- 配置Properties属性值 -->

<bean id="ds" class="cn.jc.spring.collections.DataSource">

    <property name="properties">

        <props>

         <prop key="user">root</prop>

         <prop key="password">root</prop>

         <prop key="jdbcUrl">jdbc:mysql:///test</prop>

         <prop key="driverClass">com.mysql.jdbc.Driver</prop>

        </props>

    </property>

</bean>

  。配置单例的集合bean

配置单例的集合bean,以供多个bean引用,需要打入util的命名空间

util的命名空间:

xmlns:util=“http://www.springframework.org/schema/util

xsi:schemaLocation=http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-2.5.xsd

例子:

<util:list id="cars">

       <ref bean="car1"/>

       <ref bean="car2"/>

  </util:list>

  <bean id="p2" class="cn.jc.spring.collections.Person">

       <property name="age" value="100"></property>

       <property name="name" value="jack"></property>

<property name="carLists" ref="cars"></property>

</bean>

   。使用P的命名空间

使用p的命名空间给属性赋值需要导入p的命名空间   

xmlns:p="http://www.springframework.org/schema/p"

例子:

<bean id="car1" class="cn.jc.spring.collections.Car">

           <property name="broad" value="jeep"></property>

           <property name="speed" value="100"></property>

      </bean>

      <bean id="car2" class="cn.jc.spring.collections.Car">

           <constructor-arg index="0" type="java.lang.String" value="benchi"></constructor-arg>

              <constructor-arg index="1" type="double" value="250"></constructor-arg>

</bean>

<!—单例的集合bean-->

<util:list id="cars">

<ref bean="car1"/>

<ref bean="car2"/>

</util:list>

<bean id="p3" class="cn.jc.spring.collections.Person" p:age="666" p:name="kkk" p:carLists-ref="cars"></bean>

自动装配

1.类型自动装配

2.名字自动装配

例子:

<bean id="address" class="cn.jc.spring.autowir.Address"
p:city="BJ" p:street="故宫"></bean>

<bean id="car" class="cn.jc.spring.autowir.Car"
p:brand="jackqiongs" p:price="2000"></bean>

<!-- <bean id="person" class="cn.jc.spring.autowir.Person"
p:name="cyang" p:address-ref="address" p:car-ref="car"></bean> -->

<!-- 根据名字自动装配,即该bean下的属性从ioc容器中去找相同的bean的id的名称,如果相同,则装配 -->
<!-- <bean id="person" class="cn.jc.spring.autowir.Person"
p:name="cyang" autowire="byName"></bean> -->

<!-- 根据类型自动装配,即该bean下的属性类型与IOC容器中的bean的class类型如果一致,则装配成功,
如果IOC容器中有多个一样的类型,则会报错 -->
<bean id="person" class="cn.jc.spring.autowir.Person"
p:name="cqian" autowire="byType"></bean>

自动装配的缺点:

                 1.自动装配将会配置bean的所有的属性

                 2.autowire属性要么根据类型,要么根据名称,不可以两者兼而有之

Bean之间的关系

1.继承

继承bean,利用属性parent继承父bean的id值,可以拥有父bean的属性和属性值

<!-- 抽象bean不能被实例化,只能用来被继承 -->

<bean id="address" class="cn.jc.spring.relations.Address" p:street="泓阳广场" p:city="NJ" abstract=true></bean>

<!-- 继承bean的配置 -->

<bean id="address2" parent="address" p:street="百脑汇"></bean>

<!-- 抽象bean不能被实例化,只能用来被继承,如果某一个bean的class属性没有被指定,则该bean必须是一个抽象bean -->

<bean id="address" p:street="泓阳广场" p:city="NJ" abstract="true"></bean>     

<!-- 继承bean的配置 -->

<bean id="address2" class="cn.jc.spring.relations.Address" parent="address" p:street="百脑汇"></bean>

并不是 <bean> 元素里的所有属性都会被继承. 比如: autowire, abstract 等.

2.依赖

依赖bean的配置(依赖bean会在本bean实例化之前创建好,依赖bean需要先配置好此bean。如果依赖于多个bean那么可以用逗号或者空格分开):

<bean id="address" p:street="泓阳广场" p:city="NJ" abstract="true"></bean>

<bean id="address2" class="cn.jc.spring.autowir.Address" parent="address" p:street="百脑汇"></bean>

<bean id="car" class="cn.jc.spring.autowir.Car" p:brand="benchi" p:price="10"></bean>

<bean id="car2" class="cn.jc.spring.autowir.Car" p:brand="benchi2" p:price="102"></bean>

<bean id="person" class="cn.jc.spring.autowir.Person" p:name="张三" p:address-ref="address2" depends-on="car car2"></bean>

bean的作用域

1.单例(singleton)

单例的bean,非延迟加载,当初始化SpringIOC容器的时候就创建了所有的唯一的实例(通过调用默认的构造方法)

2.多例(prototype)

多例的bean,延迟加载,当初始化SpringIOC容器之后,调用getBean方法之后,才会创建相应的实例对象(不唯一)

使用外部属性文件

spEL

SpringIOC容器中bean的生命周期

1.当初始化IOC容器(单例Bean),调用默认的构造函数,容器会创建单例的bean对象,如果scope是多例的,将会延迟加载,在getBean的时候才会创建对象并获取

2.为bean的属性赋值,通过调用setter方法

3.调用bean的初始化方法

4.当关闭spring的IOC容器的时候,调用销毁方法

Spring bean的后置处理器

当在Spring配置文件中配置了Spring bean的后置处理器之后,IOC容器对bean的管理周期将会更加的细致化

bean 的后置处理器允许在调用初始化方法的前后对bean进行额外处理,他是对IOC容器中的所有的bean实例逐一处理,而非处理单一实例,比如检查bean属性的正确性或者是更改bean属性。对bean处理完之后,可以返回最新的bean对象

具体的后置处理器需要实现BeanPostProcessor接口,并且覆盖两个方法,还需要在Spring的配置文件中配置Spring bean的后置处理器

public class MyBeanPostProcessor implements BeanPostProcessor {

      @Override

      public Object postProcessAfterInitialization(Object bean, String beanName)

                 throws BeansException {

           Car car = new Car();

           car.setBrand("fote");

           System.out.println("postProcessAfterInitialization"+car+","+beanName);

           return car;

      }

      @Override

      public Object postProcessBeforeInitialization(Object bean, String beanName)

                 throws BeansException {

           System.out.println("postProcessBeforeInitialization"+bean+","+beanName);

           return bean;

      }

}

<bean id="carcar" class="cn.jc.spring.cycle.Car" init-method="init2" destroy-method="destory" scope="singleton" lazy-init="default">
  <property name="brand" value="#{'audio'}"></property>
</bean>
<bean class="cn.jc.spring.cycle.MyBeanPostProcessor"></bean>

实体类:

package cn.jc.spring.cycle;

public class Car {
private String brand;

public Car(){
System.out.println("cars constructor...");
}
public void setBrand(String brand){
System.out.println("setBrand...");
this.brand = brand;
}
public void init2(){
System.out.println("init....");
}
public void destory(){
System.out.println("destory...");
}

@Override
public String toString() {
return "Car [brand=" + brand + "]";
}
}

测试类:

package cn.jc.spring.cycle;

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

public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");
Car car = (Car)ctx.getBean("carcar");
System.out.println(car);
ConfigurableApplicationContext cac = (ConfigurableApplicationContext)ctx;
cac.close();
}
}

此时Spring IOC容器对bean的生命周期的管理:

  1. 调用默认的构造函数,创建bean对象(scope单例非延迟),如果scope是多例时,将会延迟加载,在getBean时才会调用默认的构造方法,创建bean对象
  2. 为bean的属性赋值或者是对其他bean的引用
  3. 将 Bean 实例传递给 Bean 后置处理器的postProcessBeforeInitialization 方法
  4. 调用bean的初始化方法
  5. 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
  6. 可以使用Bean对象了
  7. 当关闭spring的IOC容器时,调用销毁方法

Spring4.x的新特性

泛型依赖注入

 子类继承父类,子类注入,父类获取的是子类的泛型的对象

举个例子:

BaseService类:

package cn.jc.spring.generic.di;

import org.springframework.beans.factory.annotation.Autowired;

public class BaseService<T> {
@Autowired
protected BaseRepository<T> repository;
    public void add(){
    System.out.println("add ....");
    System.out.println(repository);
  }
}

BaseRespository类:

package cn.jc.spring.generic.di;

  public class BaseRepository<T> {

}

UserService类

package cn.jc.spring.generic.di;

import org.springframework.stereotype.Service;

@Service
public class UserService extends BaseService<User> {

}

UserRepository

package cn.jc.spring.generic.di;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository extends BaseRepository<User> {

}

User类

package cn.jc.spring.generic.di;

  public class User {

}

beans-generic-di.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="cn.jc.spring.generic.di"></context:component-scan>

</beans>

Main 测试类

package cn.jc.spring.generic.di;

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

public class Main {
    public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("beans-generic-di.xml");
    UserService userService = (UserService)ac.getBean("userService");
    userService.add();
  }
}

 

输出的结果:

add ....
cn.jc.spring.generic.di.UserRepository@5684ce7a

 

原文地址:https://www.cnblogs.com/caoyang-q/p/5310229.html