Spring框架(一)--IOC控制反转

相关文章:Spring学习笔记-AOP


慢慢接触spring框架,将所学所用的东西记录下来,点点滴滴,肯定会有所收获。

之所以使用Spring框架,是因为框架可以大量简化程序的开发工作,更加高效稳定的开发大型程序。

IoC(Inversion of Control)控制反转,就是在一般情况下,获取类的实例必须要在程序中new这个类,现在不需要这样了,直接通过配置xml文件,或者对类进行注解,在一个叫做IOC容器的东西里,就存在了类的实例,我们称之为bean,通过在IOC即可获取到要得到的实例。

1、使用spring的先期准备,磨刀不误砍柴工。

在Eclipse上使用Spring框架,首先要安装spring在Eclipse上的插件springsource-tool-suite-3.7.1.RELEASE,为了Eclipse上使用spring。

 下载下来后,在安装软件里安装好。

下载spring的jar包spring-framework-4.2.2.RELEASE-dist.zip,里边lib里有使用spring时的jar包。

下载commons-logging包,spring的日志系统所依赖。

将这些5个jar包配置在项目中:

    Spring框架(一)--IOC控制反转_第1张图片

2、输出Hello World

创建一个HelloWorld类:

package com.cqupt.spring.beans;

public class Helloworld {

	private String name;

	public void setName(String name){
		this.name=name;
	}
	
	public void hello(){
		System.out.println("hello :"+name);
	}
}
***配置xml文件,在src下新建spring Bean Configration File 文件,在beans标签下新建bean标签
    
        
    

注意,使用property配置属性,类中必须要有无参构造函数,因为原理是先使用无参构造函数构造类对象,然后使用set方法配置属性

id代表这个bean的唯一标示,class是映射到的类,property是类里的成员属性配置,name的识别是根据setter函数识别的,如setName2(String ),那么name="name2",value就是给成员属性配置的值。

在main函数中使用spring

	public static void main(String[] args){
		
		//1、创建spring的IOC容器
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		//2、从IOC容器中获取Bean实例
		Helloworld helloworld = (Helloworld) ac.getBean("helloworld");
		helloworld.hello();
	}
注:在执行
ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
这条语句时,其实已将创建了Helloworld的实例,也调用了Set方法。

3、构造注入

getBean有两种方式:根据xml中的id,一种是根据Helloworld.class,区别就是两个bean标签映射到同一类时,getBean(类.class)会出错。

Spring提供两种类型的IOC容器,BeanFactory和ApplicationContext,其中ApplicationContext是对于使用Spring的开发人员的,BeanFactory是Spring的底层,不经常使用。

ApplicationContext是一个接口,有两种IOC的实现类,ClassPathXmlApplicationContext(依据类路径)和FileSystemXmlApplicationContext(依据系统文件路径);

三种注入方式:属性注入,构造器注入,工厂方法注入(不推荐,很少用)

属性注入:依据Set方法,最常用的方式;

构造方法注入:constructor-arg标签

创建一个Car类,构造函数

package com.cqupt.spring.beans;

public class Car {



	private String brand;
	private String corp;
	private int price;
	private int maxSpeed;
	
	public Car(String brand, String corp, int price) {
		super();
		this.brand = brand;
		this.corp = corp;
		this.price = price;
	}

	@Override
	public String toString() {
		return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price
				+ ", maxSpeed=" + maxSpeed + "]";
	}
	
	
}
xml配置

        
        
        
    
index可以不需要,但是写出来时,一定要注意与第几个value值的匹配,否则出错

main函数中使用:

	public static void main(String[] args){
		
		//1、创建spring的IOC容器
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		Car car = (Car) ac.getBean("car");
		System.out.println(car);
	}
注:当出现重载构造函数时,可用type属性进行区分,一个构造函数对应一个bean标签,一个参数对应一个constructor-arg标签

    
        
        
        
    
    
        
        
        
    

4、配置细节

特殊字符配置属性

xml中使用特殊字符配置属性时,需用到,并且使用子节点标注属性

            ]]>
        

bean之间的引用关系

两种方式,使用property的子节点,或者使用property的属性ref=“beanid”

        
        
        
         
        
    

内部bean

在property标签下,建立子节点 其中id可有可无,外部不可引用内部bean
    
        
        
        
             
            
	            
	            
	            
        	
        
    

其实constructor-arg标签中也可以使用name属性,如果标注type属性且与类属性类型不同,则出错
 

null值

可不引用其他bean,而是为null值,使用标签
输出结果:Person [name=lihua, age=23, car=null]

级联属性

在存在bean引用的情况下(即不为null),可以级联属性赋值
    
        
        
        
             
            
	            
	            
	             
        	
        
        
        
    

输出结果:Person [name=lihua, age=23, car=Car [brand=Ford, corp=changan, price=10000.0, maxSpeed=200]]
当为null时,不可以级联赋值
        
        
        
出错

集合属性的配置

在property标签下,使用list、set、map标签

List

Person类有集合属性cars

	public List getCars(){
		return cars;
	}
	
	public void setCars(List cars){
		this.cars=cars;
	}

	private String name;
	private int age;
	
	private List cars;
xml配置
        
            
                
                
            
        

输出结果:Person [name=lihua, age=23, car=[Car [brand=Audi, corp=, price=30000.0, maxSpeed=250], Car [brand=Boma, corp=Beijing, price=0.0, maxSpeed=500]]]

也可以配置内部bean


            
                
                
                
                    
                    
                    
                
            
        

Map 

 使用标签

Person类

	public Map getCars(){
		return cars;
	}
	
	public void setCars(Map cars){
		this.cars=cars;
	}

	private String name;
	private int age;
	
	private Map cars;
Xml


            
                
                
            
        


Properties的配置

在property标签下,使用标签

DataSource类,含有Properties属性

public class DataSource {
	
	public Properties getProperties() {
		return properties;
	}

	public void setProperties(Properties properties) {
		this.properties = properties;
	}

	private Properties properties;
	
}

xml配置

    
        
            
                lihua
                123456
                www.baidu.com
            
        
    

单利集合bean

单独配置一个bean集合,供不同的property使用,需在xml中引入util命名空间

    
    	
    	
    
    
    
        
        
        
    

Person类中cars属性是List集合

p命名空间使用

先导入命名空间p,可以简化配置bean属性,bean引用注意,使用cars-ref=“beanid”


5、自动装配 autowire

自动装配,是通过在bean标签里设置autowire属性,让系统根据名称还是类型进行自动装配。

autowire="byName"是根据引用的bean的名称与类型中的属性名称进行匹配

autowire="byType"是根据引用的bean的类型与类型中的属性类型进行装配,当多个bean指向同类型时,不能根据类型装配,否则异常。、

新建Person类,有Address和Car两个属性,Address和Car均为自定义类型

xml中的配置,先配置address和car的bean,再在person中自动装配这两个bean:

	
	
	
 	
	public static void main(String[] args){
		ApplicationContext ac=new ClassPathXmlApplicationContext("newApplicationContext.xml");
		Person person = (Person)ac.getBean("person");
		System.out.println(person);
	}

输出结果:Person [name=bolt, age=24, address=Address [city=Beijing, road=Daxing], car=Car [brand=Boma, price=30000.0]]

注意,autowire装配是有缺点的:1,只要指定autowire,那么所有引用bean都设置成自动装配;2、要么是byName,要么是byType,不够灵活

一般,开发过程中,不使用自动装配。在整合第三方框架时使用。

6、bean 之间的关系

继承parent

当多个bean之间的配置具有很大相似性时,多次配置相同内容会繁琐,为了简化这种操作,可使用bean的继承关系。
	
	

上述两个bean之间只有个别属性road不同,故可使用bean继承简化操作,在bean标签中添加parent属性:parent="beanid"
	
	
父bean可以使实例化bean,也可以是抽象bean,用作模板使用,bean标签设置属性abstract="true",此时父bean 不能实例化,否则出错
注意,当父bean的class属性不设置的时候,必须把父bean设置为抽象bean,此时父bean只需设置些必要属性即可

依赖 depends-on

依赖关系设置的作用,主要就是,当前bean实例化之前必须实例一个前置bean,这个bean当然不一定在当前bean中引用,起到限制作用:必须先实例化前置依赖bean,才能实例当前bean,所以前置依赖bean不能是抽象bean。在当前bean标签里设置属性:depends-on="beanid"
	
	

如果没有配置过bean car2,那么抛出异常
配置多个依赖bean时,可以用逗号或者空格隔开depends-on="car car2 car3"

7、bean的作用域scope

在spring配置文件中,每个bean代表一个实例,在程序中通过容器的getBean方法获取实例,那么一个bean多次获取实例,这些实例是相同还是不同的呢?这就关系到bean的scope属性,分为四种:singleton  prototype  session request,后两种不经常使用,暂且不讲。bean标签属性scope="singleton"
singleton:默认值,单例模式,某个bean的所有获得的实例均指向同一内存单元,即只存在一个实例,在容器加载时便创建实例;
prototype:每一个bean的所有实例都是不同的,容器初始化时不创建实例,只有在程序运行中第一次获取该实例时,才进行实例化

	public static void main(String[] args) {

		ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext-scope.xml");
		Car car= (Car) ctx.getBean("car");
		Car car2= (Car)ctx.getBean("car");
		System.out.println(car==car2);
	}
输出结果为true
在Car的无参构造函数添加
	System.out.println("Car Constructing...");
public static void main(String[] args) {

		ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext-scope.xml");
	}
此时输出结果为:Car Constructing...

当bean的scope属性设置为prototype时:
	public static void main(String[] args) {

		ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext-scope.xml");
		Car car= (Car) ctx.getBean("car");
		Car car2= (Car)ctx.getBean("car");
		System.out.println(car==car2);
	}

输出结果为:
Car Constructing...
Car Constructing...
false

8、外部属性文件

在开发过程中,经常需要配置各种各样的系统属性,像硬件地址、用户名、密码、网站地址等。spring架构开发中同样需要,其方法就是在类路径下建立properties文件,将配置参数以键值对的形式记录,在spring配置文件中配置bean,通过变量${}的方式获取properties文件中的配置参数。

用连接mysql数据库为例:
建立properties文件db.properties
user=root
password=root
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///test
spring配置文件中配置bean,在spring2.5后使用引入命名空间context的方式加载properties文件:


	
	
	
		
		
		
		
	

程序
	public static void main(String[] args) throws SQLException {

		ApplicationContext ctx= new ClassPathXmlApplicationContext("properties.xml");
		DataSource dataSource= (DataSource) ctx.getBean("dataSource");
		System.out.println(dataSource.getConnection());
	}
输出结果为com.mchange.v2.c3p0.impl.NewProxyConnection@17776a8 [wrapping: com.mysql.jdbc.JDBC4Connection@69a10787]
代表已经连接上本机mysql数据库,在上边properties文件中,用户和密码均设置的是正确数值

9、SpEL的功能与使用

在配置bean中,为了实现更多功能,采用SpEL,可以为属性设置字符值、引用其他类的静态变量、引用其他bean以及其他bean的属性、运算等
value="#{...}" 或者 p:city="#{...}"
	
	
	
	
	
	
	
	
	 

程序:
	public static void main(String[] args){
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-spel.xml");
		
		Address address = (Address) ctx.getBean("address");
		System.out.println(address);
		
		Car car = (Car) ctx.getBean("car");
		System.out.println(car);
		
		Person person = (Person) ctx.getBean("person");
		System.out.println(person);
	}

输出结果:
Address [city=BeiJing, road=DaXing]
Car [brand=Audi, price=500000.0, tyrePerimeter=251.32741228718345]
Person [name=jack, city=BeiJing, car=Car [brand=Audi, price=500000.0, tyrePerimeter=251.32741228718345], info=金领]

10、bean的生命周期

生命周期

每个bean的生命周期可以分成五个部分:
1:利用构造函数或者工厂化方法实例化bean
2:利用set方法对bean的属性进行对实例赋值
3:调用bean的初始化方法
4:bean实例的使用
5:容器关闭,调用bean的销毁方法

通过代码看这五个部分,可以在bean标签中指定属性init-method="init2"和destroy-method="destroy"的方式指定bean的初始化方法和销毁方法
Car类型
public class Car {

	private String brand;
	
	public Car(){
		System.out.println("Car Constructor...");
	}
	
	public void setBrand(String brand){
		System.out.println("Car setter()...");
		this.brand = brand;
	}
	
	public void init2(){
		System.out.println("Car init()...");
	}
	
	public void destroy(){
		System.out.println("Car destroy()...");
	}
}
xml配置
	
主方法:
	public static void main(String[] args){
 
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("cycle.xml");
		Car car = (Car) ctx.getBean("car");
		System.out.println("使用过程 println:"+car);
		ctx.close();
	}
输出结果:
Car Constructor...
Car setter()...
Car init()...
使用过程 println:com.cqupt.spring.cycle.Car@490ab905
Car destroy()...

bean后置处理器

基于生命周期五部分的划分,其实可以利用bean后置处理器对生命周期进行更细粒度的划分。
bean后置处理器实际上是一个继承BeanPostProcessor接口的类,配置到spring xml文件中,可以在容器初始化时自动加载。
功能:对每个bean进行检查,在每个bean的init生命周期前后进行操作。
创建一个后置处理器:MyBeanPostProcessor
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object arg0, String arg1)
			throws BeansException {
		System.out.println("postProessAfterInitialization... "+arg0+" "+arg1);
		return arg0;
	}

	@Override
	public Object postProcessBeforeInitialization(Object arg0, String arg1)
			throws BeansException {
		System.out.println("postProessBeforeInitialization... "+arg0+" "+arg1);
		return arg0;
	}

}
Object arg0 :就是传入进的bean实体
String arg1 : bean的id名字
注意:两个函数一般是返回bean的原型,一个在bean的初始化方法前调用,一个在初始化方法后调用,如果返回为null,则后续的使用阶段getBean将无法使用

后置处理器的bean配置
无需指定id,IOC容器会自动加载该后置处理器的bean,不过指定了也不会出错,其实没必要指定
主函数:
	public static void main(String[] args){
 
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("cycle.xml");
		Car car = (Car) ctx.getBean("car");
		System.out.println("使用过程 println:"+car);
		ctx.close();
	}
输出结果:
Car Constructor...
Car setter()...
postProessBeforeInitialization... com.cqupt.spring.cycle.Car@2f686d1f car
Car init()...
postProessAfterInitialization... com.cqupt.spring.cycle.Car@2f686d1f car
使用过程 println:com.cqupt.spring.cycle.Car@2f686d1f
Car destroy()...
生命周期在bean的初始化方法前后进行细化,分为七个部分。
后置处理器的两个函数,可以对传入的bean实体进行修改、检查等操作,当然也可以返回其他的bean实例,例如新建一个类对象返回,但必须是同类型,否则出错

11、工厂方法配置bean

配置bean,我们前边一直是用的bean对应的全类名,即bean标签的属性class=“全类名”。现在使用工厂方法配置bean,分为两种:静态工厂方法、实例工厂方法。

静态工厂方法:

通过定义一个静态工厂,在配置bean时,让class="静态工厂全类名",和factory-method来配置bean

定义一个Car的静态工厂:

public class StaticFactory {

	private static Map cars= new HashMap();
	
	static{
		cars.put("audi", new Car("audi",300000));
		cars.put("Boma", new Car("Boma",400000));
	}
	public static Car getCar(String brand){
		return cars.get(brand);
	}
}
利用这个静态工厂的静态方法getCar配置bean标签的factory-method属性


		
	
使用constructor-arg子节点为静态工厂方法进行传参。

实例工厂方法:

与静态工厂方法不同得是,必须要配置实例工厂的bean,才能调用工厂的方法

创建非静态工厂:

public class InstanceFactory {
	
	private Map cars;
	public InstanceFactory(){
		cars = new HashMap();
		cars.put("Ford", new Car("Ford",200000));
		cars.put("Boma", new Car("Boma",200000));
	}
	public Car getCar(String brand){
		return cars.get(brand);
	}
}
配置工厂方法的bean,以及Car的bean

	
	
	
		
	
使用factory-bean将工厂的bean引用,factory-method指定方法,constructor-arg传参

12、FactoryBean配置bean

目前配置bean已经详述了 全类名、工厂方法两种方式,还有一种就是利用FactoryBean配置

FactoryBean是一个接口,继承此接口的类,可以用来配置bean

创建一个继承此接口的类:CarFactoryBean

public class CarFactoryBean implements FactoryBean {

	String brand;
	
	public void setBrand(String brand){
		this.brand = brand;
	}
	@Override
	public Car getObject() throws Exception {
		return new Car("BMW",300000);
	}

	@Override
	public Class getObjectType() {
		return Car.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}	
}
继承FactoryBean接口的类都会从接口中继承三个方法:getObject 返回bean原型,getObjectType 返回bean类型,isSingleton 限定是否单利模式。

bean配置,class指向继承接口的类,property子节点时间上是配置的CarFactoryBean的属性,该bean返回的是FactoryBean的getObject方法返回的实例。

	
		
	
目前只是熟悉了工厂方法,FactoryBean 配置bean,但这两种方式的功效具体在哪些方面实现,有待于深入学习,现在是觉得不如全类名配置简单。

13、基于注解的方式配置bean

前边讲的三种配置bean的方式:全类名,工厂方法,FactoryBean都是基于xml文件的方式,基于注解的方式配置bean也是一种常用的方式。

基于注解的方法就是在程序中,在类或者类属性的前边通过添加@形式的注解,spring会依据配置自动扫描这些添加了注解的类进入IOC容器中,IOC中的bean的默认名字就是类名第一个字母小写。

基于注解配置bean

@对类进行注解有四种形式:Component,Repository,Controller,Service,这四种的区别有待于研究。

建立四个包:其他三个包是第一个的子包

com.cqupt.spring.beans.annotation

com.cqupt.spring.beans.annotation.repository

com.cqupt.spring.beans.annotation.service

com.cqupt.spring.beans.annotation.controller

在annotation包下建立一个TestObject类,并通过@Component对类进行注解

@Component
public class TestObject {
	
}

在repository包中建立UserRepository接口,注意,接口是不能进行注解的,因为接口没必要配置bean

public interface UserRepository {

	public void save();
}

在建立这个接口的实现类UserRepositoryImpl,对此接口进行注解,@Repository

@Repository(value="userRepository")
public class UserRepositoryImpl implements UserRepository {
	
	public void save(){
		System.out.println("UserRepository save...");
	}
}
如果bean不使用默认的第一字母小写的类名,可以用上述形式指定名称,也可以去掉value。

同样在service包中创建UserService类,@Service注解

@Service
public class UserService {

	public void add(){
		System.out.println("UserService add...");
	}
}
在controller中创建UserController包,@Controller注解

@Controller
public class UserController {

	public void execute(){
		System.out.println("UserController execute...");
	}
}
当类建立完成并且注解之后,需要在xml中配置,使得spring依据这个配置检测注解的类,并在IOC中生成bean。

使用context:component-scan标签,base-package是指定检测的包及其子包,所以四个包都被IOC进行检测

主函数:

	public static void main(String[] args) {

		ApplicationContext ctx= new ClassPathXmlApplicationContext("beans-annotation.xml");
		
		TestObject to= (TestObject) ctx.getBean("testObject");
		System.out.println(to);
		
		UserService userService = (UserService) ctx.getBean("userService");
		System.out.println(userService);
		userService.add();
		
		UserController userController = (UserController) ctx.getBean("userController");
		System.out.println(userController);
		userController.execute();

		
		UserRepository userRepository = (UserRepository) ctx.getBean("userRepository");
		System.out.println(userRepository);
		userRepository.save();
	}
输出结果:

com.cqupt.spring.annotation.TestObject@727803de
com.cqupt.spring.annotation.service.UserService@704921a5
UserService add...
com.cqupt.spring.annotation.controller.UserController@df27fae
UserController execute...
com.cqupt.spring.annotation.repository.UserRepositoryImpl@24a35978
UserRepository save...

另外:context:component-scan 标签的resource-pattern="repository/*.class"属性指定了IOC检测类的范围限定在repository包下。

	
	

此时在Main中只能获取到UserRepository的对象,因为IOC只创建了名为userRespository的bean

IOC检测的范围限制

通过注解机制,可以使IOC检测程序中被注解的类生产bean,但是如果不希望所以注解的类均生成bean,该如何呢?

使用范围限制:两种:一是使用resource-pattern的属性,另一种就是在context:component-scan 标签子节点context:exclude-filter排除和context:include-filter包含。

context:include-filter经常和use-default-filters="false"结合使用:表示只包含之意。

context:exclude-filter排除和context:include-filter包含这两个节点依据属性type="annotation"和type="assignable“分为两种类型:依据注解组件类型,和依据类的类型

依据组件类型的排除:

	
		
		
	
这样,IOC容器中不会有被Repository注解的bean,注意expression="全类名"

依据类类型的排除:

	
		
	
所有继承接口UserRepository的类均不会在IOC容器中生成bean

依据类类型的包含(只包含)

	
		
	
在IOC中只有继承接口UserRepository的类才会生成bean

当然上述这些范围限定都是在类被注解的基础上的,如果类没有被注解,无论怎么包含,是annotation还是assignable,都不会生成bean

属性基于注解的自动装配

在使用基于注解配置bean时,会使用context:component-scan标签,这个标签还会自动注册一个bean后置处理器AutowiredAnnotationBeanPostProcessor,来实现自动装配bean的属性

使用注解@Autowired @Resource @Inject,最常用的是@Autowired注解

详述@Autowired使用:

使用位置,可以放在普通字段、构造器、一切具有参数的方法前

放在普通字段前:

@Controller
public class UserController {
	@Autowired
	UserService userService;

	public void execute(){
		System.out.println("UserController execute...");
		userService.add();
	}
}

放在方法前:

@Controller
public class UserController {
	
	UserService userService2;
	
	@Autowired
	public void setUserService2(UserService userService2) {
		this.userService2 = userService2;
	}
	public void execute(){
		System.out.println("UserController execute...");
		userService2.add();
	}
}

@Autowired是按照兼容类型自动装配的,如果IOC中已经有生成了类型UserService的bean,不管名字是userService,还是userService2,都可以自动装配进来。

如果IOC中没有生成UserService的bean,那么程序会抛出异常。解决方法是指定@Autowired的属性required为false, @Autowired(required=false)

既然@Autowired是根据类型自动装配的,那么如果IOC中有两个以上同类型的bean的,只是bean名称不一样,就需要注解@Qualifier("beanid"),根据bean名称指定自动装配的bean,实际上在这种情况下,不指定@Qualifier,系统会自动选择名字与属性名一样的bean

@Service
public class UserService {
	@Autowired
	UserRepository userRepository;

	public void add(){
		System.out.println("UserService add...");
		userRepository.save();
	}
}
有两个UserRepository的bean

@Repository("userRepository")
public class UserRepositoryImpl implements UserRepository {
	
	public void save(){
		System.out.println("UserRepository save...");
	}
}
第一个指定bean名称为userRepository
@Repository
public class UserJdbcRepository implements UserRepository {

	@Override
	public void save() {
		System.out.println("UserJdbcRepository save...");
	}

}
第二个没有指定,就是默认的userJdbcRepository

此时@Autowired注解的属性将默认选择名为与属性名相同的bean,即userRepository

如果第一个bean没有指定名字为userRepository

@Repository
public class UserRepositoryImpl implements UserRepository {
	
	public void save(){
		System.out.println("UserRepository save...");
	}
}
那么ICO中两个bean分别是userRepositoryImpl和userJdbcRepository

就会抛出异常:Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.cqupt.spring.annotation.repository.UserRepository] is defined: expected single matching bean but found 2: userJdbcRepository,userRepositoryImpl

此时就必学添加@Qualifier注解:

@Service
public class UserService {
	@Autowired
	@Qualifier("userJdbcRepository")
	UserRepository userRepository;

	public void add(){
		System.out.println("UserService add...");
		userRepository.save();
	}
}
注意:也经常把@Qualifier放在方法的中:

	UserRepository userRepository;
	
	@Autowired
	public void setUserRepository(@Qualifier("userJdbcRepository") UserRepository userRepository) {
		this.userRepository = userRepository;
	}

@Autowired也可以放在数组、集合、java.util.Map上,进行自动装配。

泛型依赖注入

泛型依赖注入与普通注入的区别在于,使用了泛型,在父类之间形成注入依赖,被子类继承,子类可以自动进行依赖注入

创建两个泛型父类:BaseRepository,BaseService,BaseService的属性与BaseRepository形成依赖注入关系

public class BaseRepository {

}

public class BaseService {
	
	@Autowired
	protected BaseRepository repository;
	
	public void add(){
		System.out.println("Service add...");
		System.out.println(repository);
	}
}
创建两个父类的子类UserRepository和UserService,并注解给IOC

@Repository
public class UserRepository extends BaseRepository {

}

@Service
public class UserService extends BaseService {

}
通过测试:

	public static void main(String[] args) {

		ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-module.xml");
		UserService us=(UserService) ctx.getBean("userService");
		us.add();
	}

输出结果:

Service add...
com.cqupt.spring.generic.di.UserRepository@5d11346a

注:注解不可以继承















你可能感兴趣的:(Java,Web后台)