Spring基本用法(一)

Spring

Spring可以说是一个框架,也可以说是一家公司,Spring为我们提供了非常庞大的开发生态,到现在它已经深入我们日常工作中,如空气,氧气,那么自然也是那么重要。其产品众多,耳熟能详的,spring frameworkspringmvcspring bootspringcloudspring data JPA等等。本文主要讲spring framework,其包括两个重要组成部分IOC/DI和AOP,即“控制反转/依赖注入”和“面向切面编程”。

一、Spring IOC/DI

Ioc是一个抽象的概念或者思想。通俗的讲:将对象的控制权交给spring容器。对象的新建、使用、销毁被称为对象的生命周期,对象的新建由我们通过new或者其他手段手动创建,使用不用说,销毁由jvm垃圾回收器自动为我们回收。现在有了spring,我们不用关心对象要如何被创建,销毁,这一切都只需交给spring容器,由它全权负责,这样我们就腾出时间一门心思写业务逻辑了。这有点像当初从C语言跨到java,java有垃圾回收器一样。DI,常常和IOC放在一起,因为DI是IOC的具体实现。

1.依赖注入

两种方式:构造器注入和属性注入。

//applicationContext.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"

xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">

      <bean id="person" class="spring01.Person">
      	<!-- 构造器注入:需要有对应的构造方法 -->
      	<!-- <constructor-arg name="id" value="1"></constructor-arg>
      	<constructor-arg name="name" value="wangle"></constructor-arg>
      	<constructor-arg name="food" ref="food"></constructor-arg>
      	 -->
      	 <!-- 属性注入:需要有对用的set方法 -->
      	 <property name="id" value="2"></property>
      	 <property name="name" value="2"></property>
      	 <property name="food" ref="food"></property>
      	 <!-- int数组-->
      	 <property name="array" >
      	 	<array>
      	 		<value>1</value>
      	 		<value>2</value>
      	 		<value>3</value>
      	 	</array>
      	 </property>
      	 <!-- String类型List集合 -->
      	  <property name="list" >
      	 	<list >
      	 		<value>上海</value>
      	 		<value>北京</value>
      	 		<value>成都</value>
      	 	</list>
      	 </property>
      	 <!-- Properties 类型-->
      	 <property name="property">
      	 	<props>
      	 		<prop key="asdf1">asdf</prop>
      	 		<prop key="asdf2">asdf</prop>
      	 		<prop key="asdf3">asdf</prop>
      	 	</props>
      	 	<!-- <value>
      	 		1=ads,
      	 		2=23
      	 	</value> -->
      	 </property>
      	 <!-- String类型map -->
      	 <property name="map">
      	 	<map>
      	 		<entry key="1" value="house"></entry>
      	 		<entry key="2" value="room"></entry>
      	 	</map>
      	 </property>
      	 <!-- Food类型Set集合 -->
      	 <property name="set">
      	 	<set>
      	 		<!--  <bean class="spring01.Food">
  		        	<property name="name" value="苹果"></property>
  		        	<property name="location" value="富士"></property>
  		        </bean>
  		        <bean class="spring01.Food">
  		        	<property name="name" value="西瓜"></property>
  		        	<property name="location" value="攀枝花"></property>
  		        </bean> -->
  		        <!-- 或者ref方式 -->
  		        <ref bean="food1"/>
  		        <ref bean="food2"/>
      	 	</set>
      	 </property>
      </bean>
      <bean class="spring01.Food" id="food1">
      	<property name="name" value="香蕉"></property>
      	<property name="location" value="新疆"></property>
      </bean>
      <bean class="spring01.Food" id="food2">
      	<property name="name" value="荔枝"></property>
      	<property name="location" value="西域"></property>
      </bean>
      
</beans>

//Person
public class Person {

  private int id;
  private String name;
  private Food food;
  private List<String> list;
  private Map<String,String> map;
  private int[] array;
  private Set<Food> set;
  private Properties property;
  public Person() {
  }
  public Person(int id) {
  	this.id = id;
  }
  public Person(int id, String name) {
  	super();
  	this.id = id;
  	this.name = name;
  }
  public Person(int id, String name, Food food) {
  	super();
  	this.id = id;
  	this.name = name;
  	this.food = food;
  }
  //省略getter和setter
}


//Food
package spring01;

public class Food {
private String name;
private String location;
public Food() {
  super();
  // TODO Auto-generated constructor stub
}

public Food(String name) {
  super();
  this.name = name;
}

//省略getter和setter

}

2.applicationContext.xml

既然用到applicationContext.xml,那么就再探讨一下其它用法。在spring中这个配置文件非常重要,名字是约定俗成,约定大于配置嘛。spring框架中既可以用xml也可以用注解的方式进行配置。便于阅读,采用伪代码。

1)depends-on依赖

		<bean id="A" class="com.wangle.A"></bean>
   <!-- 在加载某个类的时候,先加载和初始化其他类。在这里就是在加载类B的时候先去加载并初始化了C -->
        <bean id="B" class="com.wangle.B" depends-on="C"></bean>
        <bean id="C" class="com.wangle.C"></bean>

main:

public static void main(String[] args) {
		ClassPathXmlApplicationContext context = 
		new ClassPathXmlApplicationContext("applicationContext.xml");
	}
	//打印顺序:
	//A
	//C
	//B

总结:
1.depends-on决定配置在xml中类的加载顺序,不配置的话默认是按照配置的先后顺序。
2.应用场景:在初始化一个对象前,需要另一个对象的时候。

2)lazy-init懒加载
配置:

 <!-- 只有懒加载情况 -->
        <bean id="food2" class="spring.Food" lazy-init="true">
   
        </bean>
        
        <!-- depends-on在bean属性配置可以创建bean的加载顺序,默认是按照配置的先后 -->
        <bean id="pet2" class="spring.Pet" lazy-init="true"  >
        	
        </bean>
        
        <!-- 加载food类的时候,先加载Pet类 -->
        <bean id="person2" class="spring.Person" lazy-init="true" >
        </bean>

mian方法:

	public static void main(String[] args) {
		ClassPathXmlApplicationContext beans = 
				new ClassPathXmlApplicationContext("applicationContext.xml");

		new Pet();
		new Person();
		new Food();
	}
	//打印顺序:
	//Pet
	//person
	//food

总结:
1.加载配置文件时,对象不会初始化。
2.new对象或者getBean()时会初始化
3.初始化顺序按照代码的的执行顺序,而不是配置的bean的顺序。

和depends-on互用的情况

		<bean id="food" class="spring.Food">
        	<property name="name" value="荔枝"></property>
        	<property name="location" value="西域"></property>
        </bean>
        
        <!-- depends-on在bean属性配置可以创建bean的加载顺序,默认是按照配置的先后 -->
        <bean id="pet" class="spring.Pet" depends-on="person" >
        	
        </bean>
        
        <!-- 加载food类的时候,先加载Pet类 -->
        <bean id="person" class="spring.Person" lazy-init="true" >
        </bean>

main:

	public static void main(String[] args) {
		ClassPathXmlApplicationContext beans = 
				new ClassPathXmlApplicationContext("applicationContext.xml");

	}
	//打印顺序:
	//food
	//person
	//pet

总结:
1.加载配置文件时,对象会初始化。
2.即使bean是懒加载,但是被其他依赖的话,还是会在xml加载期初始化。

3)循环依赖

		<bean id="Person" class="spring01.Person" scope="prototype">
        	<property name="pet" ref="Pet"></property>
        </bean>
        <bean id="Pet" class="spring01.Pet" scope="prototype">
        	<property name="food" ref="Food"></property>
        </bean>
        <bean id="Food" class="spring01.Food" scope="prototype">
        	<property name="person" ref="Person"></property>
        </bean>

main

	public static void main(String[] args) {
		ClassPathXmlApplicationContext beans = 
				new ClassPathXmlApplicationContext("applicationContext.xml");
		
		Food Food = beans.getBean("Food", Food.class);
	}
	//报错:
	//Requested bean is currently in creation: Is there an unresolvable circular reference?

更改其中一个bean的scope为singleton

		<bean id="Person" class="spring01.Person" scope="prototype">
        	<property name="pet" ref="Pet"></property>
        </bean>
        <bean id="Pet" class="spring01.Pet" scope="singleton">
        	<property name="food" ref="Food"></property>
        </bean>
        <bean id="Food" class="spring01.Food" scope="prototype">
        	<property name="person" ref="Person"></property>
        </bean>
		//main不变,不会报错。

总结:
1.在spring中原型bean的循环引用是被禁止的,目的是防止内存溢出。
2.如果其中一个bean不是原型,就不会出错。
3.prototype修饰的类在初始化前会先做检查,检查是否有引用,有先去初始化引用,发现是循环引用的话就会报异常。而singleton是不管有没引用,先初始化对象再说。
4.singleton可能带来的问题,如下图,如果多个线程同时修改同一个controller对象的num值,就会发生线程安全问题。所以一般我们不会在controller中设置属性,要设置都要使用线程安全的类,如Threadlocal、Collections.synchronizedXXX()等等,即便这样也是会牺牲一部分效率的。

singleton
5.prototype一般使用在web应用的entity类中,比如一个用户User类,它表示一个登陆用户的信息,每个用户的信息都是不同的,如果使用singleton是不合理的。

补充说明:scope对象作用域,有六个值:singleton(单例),prototype(原型)websocket(长连接),request(单次请求)、session(会话期),application(应用程序范围)。前面两个表示实例是否是单例,后面表示的是对象的生命周期,他们都是单例的。

4)静态和实例工厂
applicationContext.xml

		<!-- 静态工厂注入:意思是类.方法().方法是静态的 -->
        <bean id="factory" class="spring01.CarFactory" factory-method="getCar">
        	<!-- 表示方法的参数:type是参数类型(能没有),name是参数名(能没有),index表示参数索引(能没有),value是参数值,不能没有。只能用<constructor-arg>标签,不能用property标签 -->
        	<constructor-arg  type="java.lang.String" name="price"   value="BMW"></constructor-arg>
        	<constructor-arg  type="java.lang.String" name="name"  value="20w"></constructor-arg>
        </bean>
        <!-- 实例工厂注入:通过实例化方法得到bean -->
        <bean id="factory2" class="spring01.CarFactory2" >
        </bean>
        <bean id="car" factory-bean="factory2" factory-method="getCar2">
        	<!-- 表示方法的参数:type是参数类型(能没有),name是参数名(能没有),index表示参数索引(能没有),value是参数值,不能没有。只能用<constructor-arg>标签,不能用property标签 -->
        	<constructor-arg value="BMW"></constructor-arg>
        	<constructor-arg value="20w"></constructor-arg>
        </bean>

CarFactory

public class CarFactory {
	
	public static Car getCar(String name,String price) {
		Car car;
		if(name.equals("BMW")) {
			car = new BMW(name,price);
		}else if(name.equals("BenZ")){
			car = new BenZ(name,price);
		}else {
			car = new Audi(name,price);
		}
		return car;
	}
}

CarFactory2

public class CarFactory2 {
	public  Car getCar2(String name,String price) {
		Car car;
		if(name.equals("BMW")) {
			car = new BMW(name,price);
		}else {
			car = new Audi(name,price);
		}
		
		return car;
	}
}

main

public static void main(String[] args) {
		ClassPathXmlApplicationContext beans = 
				new ClassPathXmlApplicationContext("applicationContext.xml");
		
		//实例
		  CarFactory2 f2 = beans.getBean("factory2", CarFactory2.class);
		  
		  System.out.println(f2.getCar2("Aodi", "110w"));
		 
		 //静态
		  Car car = CarFactory.getCar("BenZ","30W");
		  System.out.println(car);
		
	}

静态工厂和实例工厂是对java中工厂模式的一种实现。

5)自动装配autowire

autowire自动装配:意思是给属性自动初始化值。包含的配置值有:default、no、byName、byType、constructor。
default:默认是no,不自动装配。 no:不自动装配。
byName:按类注册的bean标签上的id和name查找装配。id和name的值要和类的属性名称一致。
byType:按类注册的bean标签上class的属性值查找装配。 constructor:按构造方法装配。

applicationcontext.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"
 
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType"><!-- 全局自动装配 -->
		<!-- 给已有的bean起别名 -->
		<alias name="Person" alias="Person1"/>
		
		<!-- autowire自动装配:意思是给属性自动初始化值。包含的配置值有:default、no、byName、byType、constructor。
		default:默认是no,不自动装配。
		no:不自动装配。
		byName:按类注册的bean标签上的id和name查找装配。id和name的值要和类的属性名称一致。
		byType:按类注册的bean标签上class的属性值查找装配。
		constructor:按构造方法装配。 -->
        <bean id="Person" class="com.wangle.Person" autowire="byName">
        	
        </bean>
        <bean id="pet" class="com.wangle.Pet">
        	<property name="name" value="cat"></property>
        </bean>
        <bean id="pet2" class="com.wangle.Pet">
        	<property name="name" value="pig"></property>
        </bean>
       <!--  自动装配对java自带的引用类型不具备装配功能,采用默认值null -->
        <bean id="name" class="java.lang.String">
        	<constructor-arg value="hh1"></constructor-arg>
        </bean>
        <bean id="name2" class="java.lang.String">
        	<constructor-arg value="hh2"></constructor-arg>
        </bean>
</beans>

Person类

public class Person {

	private int age;
	//bytype autowired String reference same . NO work
	private String name;
	private String name2;
	private boolean sex;
	//byType autowired references are same。throw Exception
	private Pet pet;
	private Pet pet2;
	private int[] array;
	private char cha;

	...省略getter、setter
}

main

	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		Person person = context.getBean("Person1", Person.class);
		System.out.println(ToStringBuilder.reflectionToString(person));
	}
//打印结果:
//com.wangle.Person@13e39c73[age=0,name=<null>,name2=<null>,sex=false,pet=cat,pet2=pig,array=<null>,cha= ]

总结:
1.按名字自动装配是精确查找,按类型查找可能出现发现多个匹配的异常,原因在于在bean容器中配置有多个相同类型的bean。因为这个原因,如果在类中有多个非自带的引用类型相同的属性,就必须使用byName。
2.自动装配如果不赋初始值,默认值和java类加载机制相同使用的都是默认值,如整型为0,char为/u0000空格,boolean为false,String为null。其中String等自带引用类型即使是在bean中带初始值注入都会为null。
3.在beans标签中有个属性default-autowire=“byType”,是全局的自动装配。但是它的优先级比bean上配置要低。

6)空值注入

//表示空串
<value></value>  
//表示null
<null></null>
3.注解注册和注入

1)注册:@component + 类,还需要一个配置才起作用,context:component-scan标签

<!-- 扫描base-package及子包下的类,管理bean的周期。@component只是标记,并不是真正的注册到bean容器,不配置扫描,是不行的。 -->
	<context:component-scan base-package="com.wangle"></context:component-scan>

2)注入:@Autowired + 引用类型属性 ,默认是byType自动装配,注解不像xml,这里是不需要getter,setter的。

具体例子如下:

@Component("person")//可以设定name
@Scope("prototype")//对象的作用域
public class Person {
	@Value("1")
	private int id;
	@Value("zhangsan2")
	private String name;
	@Autowired
	private Food food;
	
}

@Component
public class Food {

}

main

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		Person p1 = context.getBean("person", Person.class);
		Person p2 = context.getBean("person", Person.class);
		System.out.println(p1);
		System.out.println(p2);//
		
//打印结果:
Person@3c9754d8[id=1,name=zhangsan2,food=com.wangle.entity.Food@3fc2959f]
Person@3bf7ca37[id=1,name=zhangsan2,food=com.wangle.entity.Food@3fc2959f]

3)指定@Qualifier(“具体类的id”),当一个接口又多个实现,autowired默认是按byType查找,有多个实现就会报异常,这是需要指明具体实现。

@Component
public class MainService {
	@Autowired
	@Qualifier("oracle")//指定具体实现的类
	private MainDao dao;
}


@Component("mysql")
public class MysqlDaoImpl implements MainDao{

}


@Component("oracle")
public class OracleDaoImpl implements MainDao{
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值