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的生命周期的管理:
- 调用默认的构造函数,创建bean对象(scope单例非延迟),如果scope是多例时,将会延迟加载,在getBean时才会调用默认的构造方法,创建bean对象
- 为bean的属性赋值或者是对其他bean的引用
- 将 Bean 实例传递给 Bean 后置处理器的postProcessBeforeInitialization 方法
- 调用bean的初始化方法
- 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
- 可以使用Bean对象了
- 当关闭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