08-Spring笔记

Spring

day01

查看Markdown文档

在Windows或Linux或Mac OS操作系统中,下载MarkdownPad或Typora,可以直接查看.md文档。

其实.md文档的本质就是一个普通的文本文档,使用任何的文字编辑软件都可以打开,例如记事本,VI/VIM,Word,甚至Eclipse。

设计模式之单例模式

基本概念

单例模式是一种生产对象型的设计模式。

单例模式指的是某个类的对象在同一时间只允许存在1个实例(对象)。

实现

假设存在类King:

public class King {
	}

普通的类可以随意的创建对象:

King k1 = new King();
King k2 = new King();
King k3 = new King();

因为当创建一个类,并且没有显式的指定构造方法时,等效于:

public class King {
	public King() {
	}
}

要实现单例模式,首先,就必须不允许随意创建对象!则:

public class King {
	private King() {
	}
}

一旦将构造方法私有化,则在类的外部是不允许调用构造方法的!此时,只有类的内部才可以调用构造方法创建对象:

public class King {
	private King king = new King();

	private King() {
	}
}

在以上代码中,创建的king对象就是唯一被创建出来的对象!那么,当类的外部需要时,提供匹配的get方法即可!

public class King {
	private King k = new King();

	private King() {
	}

	public King getInstance() {
		return k;
	}
}

虽然以上代码看似可行,但是,实际使用时,getInstance()方法是无法被调用的!为了保证在没有King类的对象之前就可以调用getInstance()方法,必须使用static进行修饰:

public class King {
		private King k = new King();
	
		private King() {
		}

		public static King getInstance() {
			return k;
		}
	}

由于被static修饰的成员,只能访问其它static成员(不可以访问其它非static成员),因为一旦使用static后,该成员将会最优先加载到内存中!而其它没有被static修饰的数据此时还没有加载到内存中,所以不可以访问!最终,还需要使用static修饰King的对象:

public class King {
		private static King k = new King();
	
		private King() {
		}

		public static King getInstance() {
			return k;
		}
	}

设计模式之工厂模式

基本概念

工厂模式也是一种生产对象型的设计模式。

有了工厂模式后,当需要某个类的对象时,就不再需要去new指定的类,而是调用工厂中的方法即可。

实现

假设存在某个类Phone:

public class MiPhone {
}

普通的使用:

MiPhone p = new MiPhone();
	p.xx = xx;

但是,出于某些原因,也许不希望在使用时调用构造方法来创建对象,则专门创建一个生产对象的工厂类,并且由这个类中的某个方法来创建对象:

public class PhoneFactory {
		public static Phone newInstance() {
			MiPhone p = new MiPhone();
			p.xx = xx;
			return p;
		}
	}

则:

MiPhone phone = PhoneFactory.newInstance();

在以上实现效果中,当需要Phone类的对象时,直接调用工厂中的方法即可,也就不需要关心对象的创建过程

甚至,有的时候,也许你需要的对象只要是某种大分类的类型的就可以,而不必是某个指定的类型:

public interface Phone {
		void dial();
		void call();
	}

	public class MiPhone implements Phone {
	}

一旦存在这样的实现关系后,如果需要的对象能做到是接口中已经定义方法,则:

Phone phone = PhoneFactory.newInstance();
	phone.dial();
	phone.call();

或者,当MiPhone类已经不足以满足你的需求时,还可以:

public class HuaWeiPhone implements Phone {
	}

然后:

public class PhoneFactory {
		public static Phone newInstance() {
			HuaWeiPhone p = new HuaWeiPhone();
			return p;
		}
	}

然而,经过这些调整,原有具体执行的代码却不需要改变!即:

Phone phone = PhoneFactory.newInstance();
	phone.dial();
	phone.call();

以上代码是不需要调整的!所以,工厂模式还有一个特点就是:你可能不会过度的依赖于某个MiPhone或者HuaWeiPhone这样的类,这些类都是可以易于被替换的!以提高了整个项目的可维护性

另外提一点,这些类可以放在同一个包中:

cn.tedu.factory_demo.Phone
	cn.tedu.factory_demo.MiPhone
	cn.tedu.factory_demo.HuaWeiPhone
	cn.tedu.factory_demo.PhoneFactory

然后,具体使用的类,例如MiPhone或者HuaWeiPhone的类本身可以使用默认的权限修饰符,例如:

class MiPhone implements Phone {
	}

其实,这也是一种封装,是对类的封装!

MVC

什么是MVC

MVC具体指的是Model(模型)、View(视图)、Controller(控制器)。

MVC是一种设计程序的理念,帮助我们为一个项目中的多个类明确它们的分工和任务。

MVC也是OOP思想的具体表现之一!

一个项目中的多个类应该是高内聚、低耦合的!

Spring基础

什么是Spring?解决了什么问题?

再说。

怎么用Spring,有什么效果?

1 从FTP上下载applicationContext.zip并解压,得到applicationContext.xml文件,将该文件放在任何你找得到的位置即可。

2 创建Maven Project,并通过Eclipse自动添加web.xml文件。

3 在Maven的配置文件中添加依赖,操作方式可以是:

3.1 打开pom.xml,选择dependencies选项卡,然后点击add按钮,输入spring-webmvc进行搜索,在搜索结果中,找到Grouporg.springframeworkArtifactspring-webmvc的结果(搜索结果中区分大小写),展开,推荐选择3.2.8版,如果没有,则选择任何高于或等于3.2且低于5.0的版本,最终确认保存即可。

3.2 在浏览器中打开maven.tedu.cn(如果在家中则打开maven.aliyun.com,注意还需要替换配置文件),搜索spring-webmvc,在搜索结果中,找到Grouporg.springframeworkArtifactspring-webmvc的结果(搜索结果中区分大小写),展开,推荐选择3.2.8版,如果没有,则选择任何高于或等于3.2且低于5.0的版本,当选择了版本号,在右下角的XML处会显示配置的源代码,复制这些源代码,回到Eclipse的pom.xml文件中,选择最后一个选项卡,以打开该文件的源代码,在根节点中添加<dependencies>节点,并把刚才从网页中复制的源代码粘贴到该节点之下。

以上无论哪种操作方式,都是为了在pom.xml的源代码的根节点下添加:

<dependencies>
  	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-webmvc</artifactId>
	  <version>3.2.8.RELEASE</version>
	</dependency>
</dependencies>

检验以上操作成功的标准就是在Libraries下的Maven Dependencies下会有9个jar包,如果失败,可以对项目点击鼠标右键,在Maven菜单中选择Update选项,以重新更新!

4 将从FTP上下载的applicationContext.xml复制到项目的src\main\resouces下。

如果编辑该文件时,使用alt + /的组合无法提示,可以访问http://schema.tedu.cn/proxy/进行设置(仅限于在公司内部)。

5 假定目前需要Spring帮助我们创建java.util.Date类的对象,则在applicationContext.xml文件的根节点下添加子级节点:

<bean id="date" class="java.util.Date" />

以上代码中,class的值是需要创建对象的类的全名(包括package),而id的值是自由命名,要求唯一。

6 然后,在程序中:

// 1. 确定配置文件:文件名区分大小写
	String fileName 
			= "applicationContext.xml";
	// 2. 加载配置文件,并获取Spring容器
	ApplicationContext ac
			= new ClassPathXmlApplicationContext(
				fileName);
	// 3. 根据XML配置中的id名称
	//		从Spring容器中获取对象
	Date d = (Date) ac.getBean("date");
	// 4. 测试
	System.out.println(
			"从Spring容器中获取id=date的对象:"
			+ d);

7 通过以上演示,可以发现:Spring就是一个帮助我们创建对象的工具,使得我们在编写程序时,不需要再使用new关键字来创建对象!以实现“解耦”!

为什么叫“Spring容器”

因为Spring会帮助我们创建对象,并且把这些对象管理起来,当我们需要的时候,通过Spring直接获取就可以,所以,Spring就是很多个对象的容器。

使用Spring管理对象

【重点】 情况1

需要创建对象的类存在无参数的构造方法:

<bean id="date" class="java.util.Date" />

【仅了解】 情况2

需要创建对象的类没有无参数的构造方法,但是,在这个类中,有静态的工厂方法:

<bean id="calendar" class="java.util.Calendar" factory-method="getInstance" />

【仅了解】 情况3

需要创建对象的类没有无参数的构造方法,并且,这个类中也没有静态工厂方法,工厂方法是在另一个类中的,先模拟情景:

public class Phone {
	public Phone(String name) {
	}
}

public class PhoneFactory {
	public Phone newInstance() {
		return new Phone("XiaoMi");
	}
}

如果希望通过工厂生产对象:

// 先创建工厂的对象
PhoneFactory factory = new PhoneFactory();
// 再通过工厂对象的方法创建对象 
Phone phone = factory.newInstance();

所以,在配置Spring时,也需要先配置工厂:

<bean id="phoneFactory" class="cn.tedu.spring.PhoneFactory" />

然后,再配置所需的类:

<bean id="phone" class="cn.tedu.spring.Phone" factory-bean="phoneFactory" factory-method="newInstance" />

使用Spring的细节问题

调用getBean()时的强制转换

在ApplicationContext中,将getBean()重载了几次,其中,有一个getBean(String, Class)方法,可以使得获取对象时不必再强制转换:

Date d = ac.getBean("date", Date.class);

关于提示Resource leak: ac is never closed

使用的ApplicationContext对象在使用完毕后,应该调用close()方法以关闭,释放资源。

ApplicationContext接口中并没有声明close()方法,而AbstractApplicationContext抽象类中是有声明这个方法的,所以,将对象声明为抽象类的类型,即可调用close()方法。

Spring管理的对象的作用域

在默认情况下,Spring管理对象的模式使用的是单例模式,也就是说,通过Spring反复获取同一个id的对象,都会是同一个对象!

在配置Spring时,为<bean>节点添加scope属性可以调整作用域,该属性的取值有:singleton(单例的,默认值),prototype(非单例的)。

一般情况下,使用Spring配置的都会是单例的!

关于Spring的单例

关于单例模式,可以区分为“饿汉式”和“懒汉式”,其中,

  • “饿汉式”是:
public class King {
		private static King k = new King();
	
		private King() {
		}

		public static King getInstance() {
			return k;
		}
	}
  • 而“懒汉式”的是:
public class King {
		private static King k;
	
		private King() {
		}

		public static King getInstance() {
			if (k == null) {
				k = new King();
			}
			return k;
		}
	}

在Spring中的单例对象,也可以分区为这2种模式,默认情况下是饿汉式,当加载配置文件时就已经创建了对象!

在Spring配置中,为<bean>节点添加lazy-init属性的配置,取值为true表示“懒汉式”,取值为false表示“饿汉式”,当然,如果需要是“饿汉式”,根本就不需要配置这个属性!

关于这项配置,仅当这个<bean>是单例时才有效!

Spring中的bean的生命周期

由于将类交给Spring管理,由Spring创建对象后,可能不便于确定类在创建时或销毁时应该做什么,可以自行在类中创建相关的方法,用于确定创建时和销毁时需要执行的任何,然后在Spring中将它们配置为初始化方法和销毁方法,例如:

public class Person {
	// 初始化方法
	public void init() { 
	}

	// 销毁方法
	public void destroy() {
	}
}

然后,在Spring的配置文件中:

<bean id="person"
	class="cn.tedu.spring.Person"
	init-method="init"
	destroy-method="destroy" />

**注意:**对于非单例的对象而言,声明周期方法是没有意义的。

Maven异常错误

Maven的工作机制是从Maven服务器中下载对应的jar包到指定的位置,后续使用时,某项目中如果需要对应的jar包,就会从本机的位置中复制到项目中。

如果出现Maven下载的jar包出错(通常是下载的文件出了错,与服务器的不一致),则删除本地缓存的所有jar包文件,然后关闭eclipse,重新启动,并更新项目。

1 找到缓存文件夹:在eclipse的设置中,找到Maven -> User Settings,找到Local Respository,对应的路径就是缓存文件夹

2 删除缓存文件夹,即删除整个.m2文件夹

3 重启eclipse

4 对项目名称点击右键,选择Maven -> Update Project -> Force …

【复习】 异常

异常的体系结构:

Throwable
-- Error
-- -- OutOfMemoryError:使用的内存超出了限制
-- Exception
-- -- RuntimeException
-- -- -- NullPointerException
-- -- -- ClassCastException
-- -- -- ClassNotFoundException
-- -- -- ArithmeticException
-- -- -- IndexOutOfBoundsException
-- -- -- -- ArrayIndexOutOfBoundsException
-- -- IOException
-- -- -- FileNotFoundException  

内存溢出与内存泄露(Leak)

内存泄露出现在:当需要释放某个对象占用的内存资源时,它仍处于引用(使用)状态,导致释放失败,但是,由于已经执行了释放代码,例如变量的作用域已经消失,以后也无法再使用这个对象了,这个对象就是垃圾数据,也称之为内存泄露的表现。

其实,少量的内存泄露是没有危害的!如果这样的数据特别多,会导致可用内存空间越来越少,如果到了极端表现,就会出现内存溢出!

如何看待hashCode

hashCode()方法是Object类中已经完成的方法。

hashCode()方法的返回值是int类型,默认情况下,可以理解为是创建出来的对象在内存中的地址。

hashCode()毕竟只是一个很普通的方法,是可以被重写的,如果被重写,则不可以用于判断多个变量指向的是否是同一个对象!事实上,许多常用类都已经重写了hashCode()方法,例如String、包装类、Date等,只要字面值相同,则hashCode()方法的返回值就是相同的!

哪些对象最终需要释放资源

连接型资源需要释放资源!

连接了内存和内存以外的位置的对象,都是连接型资源,例如IO、JDBC中的Connection等。

day02

【回顾】 Spring基础

Spring的作用

创建并管理对象的容器,使得我们在开发时不必再使用new关键字创建对象,而是直接从容器中获取。

如何使用Spring

必须添加spring-webmvc依赖,并在resources下添加XML配置文件,在配置文件中配置需要管理的类的<bean>节点,当需要对象时,创建ClassPathXmlApplicationContext对象,通过调用getBean(String, Class)方法来获取对象。

如何配置XML文件

在根节点下添加<bean>子节点,该节点中需要配置idclass属性。这种方式适用于类中存在无参数的构造方法。

另外还有2种情况,使用频率不高。

【一般掌握】 其它配置

通过Spring管理的bean默认都是单例的,可以配置scope属性配置作用域,默认的单例是singleton,而取值为prototype时则不是单例的。

通过配置lazy-init可以配置其是否是懒汉式加载,默认为false,表示饿汉式加载。

还可以自定义生命周期方法,然后配置init-methoddestroy-method,以实现创建时和销毁时的自动调用。

模拟MVC

通常,在使用MVC时,会把某些类的名称中使用特定的后缀,以表现它的定位,例如,当开发与用户相关的功能时,可能存在某个类叫作UserDao(Data Access Object),用于访问数据源,还会有UserService,表示处理业务逻辑的类。

在MVC的访问流程中,应该是:

View -> Controller -> Service -> Dao

当Dao处理完毕之后,再反向回传数据。

模拟Service调用Dao,先设计Dao:

public class UserDao {
	/** 
	 * 增加用户
	 * @return 新增的数据的id
	 */
	public int insert(User user) {
		return 0;
	}

	// 根据用户名查询用户
	public User findUserByUsername(String username) {
		return null;
	}
}

然后设计Service

public class UserService {
	// UserDao的对象
	private UserDao userDao = new UserDao();

	/**
	 * 注册
	 * @return 返回注册的用户的id,如果用户名已经被注册,则返回-1
	 */
	public int reg(User user) {
		User u = userDao.findUserByUsername(user.getUsername());
		if (u == null) {
			return userDao.insert(user);
		} else {
			return -1;
		}
	}

	/**
	 * 登录
	 */
	public void login(String username, String password) {
		User u = userDao.findUserByUsername(username);
		if (u == null) {
			// 无此用户名,登录失败!
			throw new UsernameNotExistsException("登录失败,用户名不存在!");
		} else {
			if (u.getPassword().equals(password)) {
				// 登录成功
			} else {
				// 密码不匹配,登录失败!
				throw new PasswordNotMatchException("登录失败,密码不匹配!");
			}
		}
	}
}

使用Spring注入属性的值

应用场景

当通过Spring获取某个对象时,希望对象的一些属性已经被赋值,例如在Service类中通常都有Dao的属性。

【常用】 通过SET方法注入属性的值

1 创建Dao类:

public class UserDao {
}

2 创建Service类:

public class UserService {
}

3 在Service类声明Dao类型的属性,并添加匹配的Set方法:

public class UserService {
	public UserDao userDao; // 此处不需要赋值
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
}
/* 
在以上代码中,SET方法的名称必须与属性的名称能对应,要求是将属性名的首字母变为大写,
然后在左侧添加set后得到的SET方法的名称,例如属性名称是userDao,则SET方法的名称必须是setUserDao,
如果属性名称是dao,则SET方法名称必须是setDao,一般通过eclipse自动生成,可以不用考虑命名的问题,
在这个功能中,只要求属性有SET方法即可,不要求一定存在GET方法。
*/

4 在XML中配置以上2个类的:

<bean id="userDao" class="xx.xx.xx.UserDao" />
<bean id="userService" class="xx.xx.xx.UserService">
	<property name="userDao" ref="userDao" />
</bean>
/** 
以上配置中,<property>节点中的name属性是类中的属性名称,
ref是被赋值时的对象的Spring中配置的<bean>的id,
同一个<bean>可以根据需求添加多个<property>子节点 **/

【不常用】 通过构造方法注入属性的值

1 创建UserDao类,同上

2 创建UserService类,在类中声明UserDao类型的属性,并添加带参数的构造方法,为属性赋值:

public class UserService {
	private UserDao dao; // 使用这种方式时,属性名称没有要求
	
	public UserService(UserDao d) {
		this.dao = d;
	}
}

3 配置XML文件

<!-- 配置UserService -->
<bean id="userService"
	class="cn.tedu.spring.UserService">
	<!-- constructor-arg用于配置构造方法参数的值 -->
	<!-- index:第几个参数 -->
	<!-- ref:引用的bean的id -->
	<!-- value:值 -->
	<!-- constructor-arg节点可以有多个 -->
	<constructor-arg 
		index="0"
		ref="userDao" />
</bean>

其它

为属性注入值时,如果属性的值是一个对象,应该使用ref引用到另一个bean的id,为该属性赋值,例如:

<bean id="userService"
	class="cn.tedu.spring.UserService">
	<!-- ref:引用的bean的id -->
	<constructor-arg 
		index="0"
		ref="userDao" />
</bean>

如果值是另一个对象,却没有<bean>配置,则应该添加<bean>配置。

如果是属性的值是基本值,可以通过value属性完成值的注入!基本值包括8种基本数据类型、String类型。

当使用基本值时,请忽略基本值与包装类的自动装箱与拆箱机制,需要明确的指定数据类型时,在<constructor-args>节点下添加子级<value>节点,并在<value>中指定type属性:

<bean id="date" class="java.util.Date">
	<constructor-arg index="0">
		<value type="long">8000</value>
	</constructor-arg>
</bean>

这种做法也适用于通过SET方式注入属性的值,关键代码例如:

<property name="类的属性名">
	<value type="属性值的类型"></value>
</property>

通过Spring为属性注入集合的值

注入List类型的值

<bean id="sampleBean"
		class="cn.tedu.spring.SampleBean">
		<!-- 注入List值 -->	
		<property name="provinces">
			<list>
				<value>北京</value>
				<value>上海</value>
				<value>广东省</value>
			</list>
		</property>
	</bean>

注入Set类型的值

参考以上注入List类型的值,区别在于将<list>节点替换为<set>节点。

注入的Set集合是LinkedHashSet类型的。

注入Map类型的值

参考以上注入List或Set类型的值,区别在于它使用的是<map>的子级添加多个<entry key="??" value="??" />的配置方式。

注入的Map集合是LinkedHashMap类型的。

注入Properties类型的值

参考以上注入Map类型的值,区别在于数据类型是Properties,在配置文件中使用的是<props>的子级添加多个<prop>,且每项的值是<prop>下的文本节点,例如:

<!-- 注入Properties值 -->
<property name="dbConfig">
	<props>
		<prop key="">???</prop>
	</props>
</property>

但是,这种做法并不实用!因为Properties类型的数据往往来自于.properties配置文件的,这种配置文件格式简单,即使项目交付给没有专业知识的客户也很容易维护,但是,如果把例如数据库配置写在XML配置里,客户可能就不知道如何修改了!

【常用】 最终的解决方案是:仍然使用.properties配置文件,然后,Spring可以直接读取配置文件中的信息,得到Properties,最后,我们再将这个配置注入到类的Propertiese属性中去。

首先,创建db-config.properties文件:

url=jdbc:mysql://localhost:3306/tedu_spring
driver=com.mysql.jdbc.Driver
user=root
password=1234
initSize=5
maxActive=50

然后,在Spring的配置文件中,在根节点下:

<!-- util:properties可以直接读取.properties文件 -->
	<!-- util:properties的本质依然是一个Bean -->
	<!-- 该节点的配置最终会是的一个Properties类型的对象 -->
	<util:properties id="dataSource"
		location="classpath:db-config.properties" />

最在,当需要注入值时,由于<util:properties>的本质依然是一个Bean,所以,通过ref引用它即可:

<!-- 注入Properties值 -->
<property name="dbConfig"
	ref="dataSource" />

小结

今天内容的重点:理解MVC,通过SET方式注入值,注入Properties类型的值。

创建Java Bean的规范

Java Bean指的是描述数据特征的实体类,例如用户类(User)或员工类(Employee)或部门(Department)或订单类(Order)等,根据项目需求来决定,这些类的主要作用是通过定义属性来描述应该具备哪些数据,例如:

public class User {
	String username;
	String password;
}

从规范性的角度出发,所有的属性都应该是私有的(private),所有的属性都应该有GET/SET方法,添加无参数和全参数的构造方法,并且重写equals()和hashCode()方法,为了便于测试,观察对象的属性值,还应该添加toString()方法,简单的说,在Eclipse的Source菜单中相关的Generate系列的操作,除了从父类中添加构造方法以外,都点一次!

除此以外,Java Bean还应该实现Serializable接口,并通过Eclipse自动生成versionUID。

综上所述,在创建每个Java Bean的类时,应该:

1 声明属性,并且所有属性都使用private进行修饰

2 通过eclipse生成无参数和全参数构造方法

3 通过eclipse生成所有属性对应的GET/SET方法

4 通过eclipse生成关联到所有属性的hashCode()和equals()方法

5 通过eclipse生成关联到所有属性的toString()方法

6 实现Serialiazable接口并自动生成versionUID

【面试题】 抽象类和接口有什么区别?

1 这是2个不同的概念,声明时使用的关键字都不相同

2 语法和使用区别

3 类表达的是“类别”的概念,接口表达的是“形为模式、规范、标准”的概念

4 类与类之间是is的关系,而类与接口之间是has关系

【了解】 关于Serializable

day03

【回顾】

1 通过SET方法为属性注入值:为属性添加匹配的SET方法,然后在配置<bean>时添加<property>子节点,以配置值。

2 通过Spring加载.properties文件,然后直接获取到Properties对象。

注入属性的值

1 已经学过的:

注入基本值(8种基本类型和String)、引用其它bean、使用List、Set、Map、Properties。

2 【了解】 补充

在为属性注入值时,还可以注入Array类型的值,语法格式例如:

<property name="xxx">
	<array>
		<value>v1</value>
		<value>v2</value>
		<value>v3</value>
	</array>
</property>

其实,Array的用处并不大,而且,可以和List混为一谈!

除此以外,还可以为某个属性注入null值,一般没有用处!

Spring的表达式

基本概念

通过Spring表达式,可以在配置Spring中的<bean>时,为某个属性注入值时,使用另一个<bean>的属性值,例如存在ValueBean,各属性都已经在Spring中配置了值,另一个SampleBean的值可以直接通过ValueBean中的某个属性来获取。

语法格式

Spring表达式的语法与ONGL表达式极为相似,与EL表达式也很相似。

基本语法格式:

#{值来源的bean的id.属性名称 }

如果属性是List、Array类型的,需要获取其中的某个元素:

#{值来源的bean的id.属性名称[索引或下标] }

如果属性是Map类型的,需要获取其中的某个Key对应的Value:

#{值来源的bean的id.属性名称.Key }

或者,还可以使用以下语法获取Map中的某个Key对应的Value:

#{值来源的bean的id.属性名称['Key'] }

至于Properties类型的数据,通常在Spring中会配置<util:properties>节点直接读取.properties文件,而这个<util:properties>节点本身也就是一个<bean>,所以,需要值时,直接从这个节点获取即可,例如:

<util:properties id="dataSource"
	location="classpath:xxx.properties" />

<bean id="xxx" class="xxx.xxx.xxx">
	<property name="xxx" value="#{dataSource.driver}" />
</bean>

关于Spring表达式,重点掌握以上基本用法。

综上,Spring表达可以从另一个<bean>的配置中读取值,可以是直接获取另一个<bean>的某属性的值,也可以获取另一个<bean>中某个类型是List、Array、Map、Properties(不包含Set)的其中一个值。

Ps:Spring表达式是一种表达式,也可以用于相关的运算功能,但是实用性并不算高,可以业务时间了解。

Spring的自动装配(AutoWire)

Spring的自动装配表现为:无须再显式的配置需要注入的属性值!

在Spring的配置文件中,为<bean>节点配置autowire属性即可配置自动装配的特性,例如:

<bean id="userService"
	class="cn.tedu.spring.UserService"
	autowire="byType" />

通常,该属性可以取值为byName,意思为:根据名称自动装配。表现为:假设在UserService类中存在名为userDao的属性,则Spring会自动在容器中查找id为userDao的对象,如果找到,则用这个对象为属性赋值!

除了byName以外,常用的取值还有byType,意思是:根据类型自动装配。表现为:假设在UserService类中存在名为userDao的属性,且该属性是UserDao类型的,则Spring会自动在容器中查找类型为UserDao的对象,如果找到,则用这个对象为属性赋值!

当使用byType时,对类型的判断,可以是父级或接口类型,也就是说,如果userDao属性被声明为IUserDao接口类型,然后在Spring中配置了UserDao类是实现了IUserDao接口的,也能匹配成功!

注意:如果使用byType来自动装配,可能出现多个对象都归属于同一个类型,则会导致程序崩溃!!!

还有一些其它的装配方式,其实不常用!

理解很重要,实际不怎么用。

使用注解

开发步骤

1 在Spring的配置文件中,在根节点下添加子级节点:

<context:component-scan
	base-package="cn.tedu.spring" />
// 以上base-package属性的值,是希望Spring管理的那些类所在的包的名称,一旦配置后,Spring会扫描该包及其子包下所有的被注解的类,并创建对象!
// 添加了注解扫描后,在Spring的XML文件中就不再需要配置那些类的<bean>节点了,同时,就要求这些类都是有无参数的构造方法的,并且,当Spring创建了这些类的对象后,会根据类名作为bean的id,只不过首字母是小写的,例如UserDao类的bean id就是userDao。

2 为所有需要被Spring管理的类添加注解,使用@Component,例如:

@Component
public class UserDao { ...

3 当需要注入值时,使用@Resource对属性进行注解(使用这个注解必须添加Tomcat运行时环境),例如:

@Component
public class UserService {

	@Resource
	private UserDao userDao;
	
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
}

4 当以上编写完毕之后,就可以直接通过Spring获取UserService的对象,并且,其中的UserDao属性已经被注入值。

组件扫描

在Spring的配置文件中,在根节点添加<context:component-scan base-package="xx.xx.xx" />以开启组件扫描,配置对哪个根包下的类进行扫描。

当开启组件扫描后,不必须再在XML中配置<bean>节点。

被扫描的类,应该添加相关的注解。

常用注解

常用的注解有:

@Component:通用注解,用于对类进行注解

@Named:通用注解,等效于@Component,不通常!

@Service:对项目中的业务逻辑类进行注解,运行效果与@Component相同,区别在于表达语义

@Repository:对项目中的持久层处理的类(例如Dao)进行注解,运行效果与@Component相同,区别在于表达语义

@Controller:对项目中的控制器类进行注解,运行效果与@Component相同,区别在于表达语义

@Resource:对需要注入值的属性进行注解,该注解是在javax包中的,所以,在使用前,必须添加Tomcat运行时环境

注解的具体使用

在Spring中,如果对类进行注解,并且开启了对应的组件扫描后,Spring会自动创建这些类的对象,并使用类名的首字母改为小写作为对象的bean id。

也可以在注解时,明确的指定bean id:

@Component("userDao")
public class UserDaoImpl { ....

使用@Resource可以对属性进行注解,默认情况下,会根据属性的名称去找对应的bean用于对属性赋值,例如属性名是userDao,则会查找bean id=userDao的对象用于对属性赋值,如果属性名与需要使用的bean id不相符,可以:

// 以下代码,会从Spring容器中查找id为userDao的对象,对dao属性赋值
@Resource(name="userDao")
private UserDao dao;

使用@Resource注解后,默认优先按照名称(byName)自动装配,如果装配失败,则尝试按照类型(byType)自动装配。

小结

使用以上注解时,虽然可以自由指定bean id,但是,仍推荐使用类名首字母改小写的bean id,至于是否需要显式的指定名称,可以自由决定。

【非常用注解】 @Scope

用于对类进行注解,配置该类被Spring管理时,是否是单例的,例如:

@Scope("prototype")
@Component
public class XXX { ...

【非常用注解】 @Lazy

用于对类进行注解,配置该类被Spring管理时,是否为懒汉式加载。

【非常用注解】 @PostConstruct和@PreDestroy

用于对方法进行注解,使用@PostConstruct表示被注解的方法是初始化方法,例如:

@PostConstruct
public void init() { ...

还可以使用@PreDestroy对方法注解,用于表示被注解的方法是销毁的方法,例如:

@PreDestroy
public void destroy() { ...

【非常用注解】 @Autowired与@Qualifier

使用@Autowired用于对属性进行注解,表示自动装配。

@Resource不同,@Autowired默认按照类型(byType)自动装配。

如果一定需要@Autowired按照名称完成自动装配,还需要使用@Qualifier注解:

@Autowired
@Qualifier("userDao")
private IUserDao dao;

关于@Qualifier注解,还可以应用于方法的参数名:

@Autowired
private IUserDao dao;

public void setUserDao(
	@Qualifier("userDao") IUserDao dao) {
	this.dao = dao;
}

【非常用注解】 @Value

以上的自动装配等效于在XML配置Spring时使用ref属性确定值,而那些在XML中通过value属性确定值的,应该使用@Value注解。

使用@Value用于对属性进行注解,例如:

@Value("Jack")
private String name;

在使用@Value注解时,还可以使用Spring表达式:

@Value("#{valueBean.provinces[2] }")
private String province;

控制反转(IoC)和(依赖注入)DI

控制反转是解耦的目标。

依赖注入是实现目标的手段。

================= Spring基础(完) =================

Spring MVC

作用

解决了View -> Controller与Controller -> View的问题。

五大组件

DispatcherServlet:分发

HandlerMapping:映射

Controller:控制器,具体处理请求并响应

ModelAndView:控制器中方法默认的返回值类型

ViewResolver:视图解析器

SpringMVC - HelloWorld

开发步骤 - 1

1 创建Maven项目

2 通过Eclipse自动生成web.xml

3 在pom.xml中添加依赖spring-webmvc

4 添加Tomcat运行时环境

5 复制Spring的配置文件到src\main\resources下,推荐将原文件名applicationContext.xml改为spring-mvc.xml

6 在web.xml中添加配置:

<servlet>
	<servlet-name>SpringMVC</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>SpringMVC</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

7 在spring-mvc.xml中添加组件扫描,扫描的根级包是cn.tedu.spring

8 至此,已经完成了一个普通的WEB项目的创建,任何*.do请求都会交给DispatcherServlet,而DispatcherServlet在启动时就会加载Spring的配置!

9 测试
src\main\java中创建cn.tedu.spring.User类,显式的添加无参数构造方法,在构造方法中输出日志,并将项目添加到Tomcat中,启动Tomcat,如果启动过程没有报错,并能够看到日志,表示目前的操作都是成功的!

开发步骤 - 2

1 设计请求
请求路径:http://SERVER:PORT/PROJECT/hello.do

2 在spring-mvc.xml中配置HandlerMapping

<!-- 配置HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="mappings">
		<props>
			<prop key="/hello.do">???</prop>
		</props>
	</property>
</bean>

3 创建控制器类cn.tedu.spring.HelloController,使用@Component对类进行注解,实现Controller接口:

@Component
public class HelloController implements Controller {

	public ModelAndView handleRequest(
			HttpServletRequest request, 
			HttpServletResponse response) 
					throws Exception {
		// 创建返回值对象
		ModelAndView mav
			= new ModelAndView();
		// 确定前端页面
		mav.setViewName("hello");
		// 返回
		return mav;
	}

}

4 完成spring-mvc.xml中的配置:

<!-- 中间的值是HelloController的bean id -->
<prop key="/hello.do">helloController</prop>

5 在spring-mvc.xml中配置ViewResolver

<!-- 配置ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/" />
	<property name="suffix" value=".jsp" />
</bean>

6 在WEB-INF下创建hello.jsp文件,之所以在这文件夹下创建,是因为上述步骤中配置的prefix属性,而文件名必须是hello,因为此前的Controller返回的ModelAndView中设置的是hello,扩展名必须是.jsp,是因为上述步骤中的配置的suffix属性。

7 在浏览器输入网址以测试。

【了解】 关于Serializable接口

Serializable接口是Java中的序列化接口。

public class Student {
	public String name;
	public String from;
	public int age;
}
发布了15 篇原创文章 · 获赞 0 · 访问量 231

猜你喜欢

转载自blog.csdn.net/weixin_44847293/article/details/105111405