Spring
- day01
- 查看Markdown文档
- 设计模式之单例模式
- 设计模式之工厂模式
- MVC
- Spring基础
- 使用Spring管理对象
- 使用Spring的细节问题
- Spring管理的对象的作用域
- 关于Spring的单例
- Spring中的bean的生命周期
- Maven异常错误
- 【复习】 异常
- 内存溢出与内存泄露(Leak)
- 如何看待hashCode
- 哪些对象最终需要释放资源
- day02
- 【回顾】 Spring基础
- 模拟MVC
- 使用Spring注入属性的值
- 通过Spring为属性注入集合的值
- 小结
- 创建Java Bean的规范
- 【面试题】 抽象类和接口有什么区别?
- 【了解】 关于Serializable
- day03
- 【回顾】
- 注入属性的值
- Spring的表达式
- Spring的自动装配(AutoWire)
- 使用注解
- 控制反转(IoC)和(依赖注入)DI
- Spring MVC
- SpringMVC - HelloWorld
- 【了解】 关于Serializable接口
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
进行搜索,在搜索结果中,找到Group
为org.springframework
且Artifact
为spring-webmvc
的结果(搜索结果中区分大小写),展开,推荐选择3.2.8
版,如果没有,则选择任何高于或等于3.2且低于5.0的版本,最终确认保存即可。
3.2 在浏览器中打开maven.tedu.cn
(如果在家中则打开maven.aliyun.com,注意还需要替换配置文件),搜索spring-webmvc
,在搜索结果中,找到Group
为org.springframework
且Artifact
为spring-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>
子节点,该节点中需要配置id
和class
属性。这种方式适用于类中存在无参数的构造方法。
另外还有2种情况,使用频率不高。
【一般掌握】 其它配置
通过Spring管理的bean默认都是单例的,可以配置scope
属性配置作用域,默认的单例是singleton
,而取值为prototype
时则不是单例的。
通过配置lazy-init
可以配置其是否是懒汉式加载,默认为false
,表示饿汉式加载。
还可以自定义生命周期方法,然后配置init-method
和destroy-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;
}