SPRING FRAMWORK
1. 框架回顾
1.1. Spring 概述
1. Spring 是什么?(框架,半成品)
2. Spring 能解决什么问题(面向对象,面向切面,面向服务)
3. Spring 框架核心?(IOC,AOP,MVC,…)
个人认为:Spring 最强大是它的资源整合能力。
1.2. Spring IOC 概述
1. IOC是什么?(控制反转:由spring构建对象,管理对象依赖)
2. IOC 应用优势?(解耦,更好的管理对象,使用系统资源)
3. IOC 的核心?(工厂,配置,依赖注入)
4. IOC 编程步骤?(类,配置,获取)
1.3. Spring IOC 编程
1. Spring Bean对象初始化,作用域,声明周期,延迟加载
1) Bean类型的编写(修饰符,构造方法)
2) Bean 的配置(applicationContext.xml)
3) Bean 的作用域(singleton,prototype)
4) Bean 的生命周期(生命周期方法的使用)
5) Bean 的延迟加载(局部lazy-init,全局 default-lazy-init)
2. Spring Bean依赖(依赖注入,自动装配)
1) 依赖注入(DI)的定义(通过spring为类中的属性注入值)
2) 依赖注入的实现(set注入,构造注入)
3) 依赖注入中的集合值的注入(数组,list,set,map)
4) 依赖注入中的自动装配(未讲)
2. Spring Bean依赖
2.0.1. 依赖注入基础
重点了解set,构造注入的方式以及单个值如何实现注入。
2.0.2. 依赖注入进阶
重点了解集合类型值的注入,例如list,hashmap,properties等
案例1:
定义一个相对复杂的对象
public class ComplexObject {
private String[] names;
private List<String> address;
private Map<String,Integer> map;
private Properties properties;
//set,get
}
在配置文件中配置此对象,并为属性注入值
<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="cObj" class="beans.ComplexObject">
<!-- 为String[]数组属性注入值 -->
<property name="names">
<list>
<!-- #{}为spring中的一个表达式 -->
<value>name-1</value>
<value>name-2</value>
</list>
</property>
<!-- 为list<String>集合注入值 -->
<property name="address">
<list>
<value>北京</value>
<value>深圳</value>
<value>上海</value>
</list>
</property>
<!-- 为map属性注入值 -->
<property name="map">
<map>
<entry key="k1" value="200"/>
<entry key="k2" value="300"/>
</map>
</property>
<!-- 为properties属性注入值 -->
<property name="properties">
<props>
<prop key="pk1">pv1</prop>
<prop key="pk2">pv2</prop>
</props>
</property>
</bean>
</beans>
案例2:spring配置文件中引入properties文件中的数据
step1:类路径的根目录定义properties文件cfg.properties,其内容如下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=root
step2:在spring配置文件中引入此配置文件(借助util标签)
<util:properties
id="cfg"
location="classpath:cfg.properties"/>
step3:在Bean对象中引入配置文件中的值
<bean id="dataSource" class="beans.DataSource">
<property name="driver" value="#{cfg.driver}"/>
<property name="url" value="#{cfg.url}"/>
<property name="username" value="#{cfg.username}"/>
<property name="password" value="#{cfg.password}"/>
</bean>
其中#{}为spring中一个表达式,通过这个表达式可以获取properties文件中的值
例如#{cfg.driver}为获取id为cfg对应的对象中key为driver的值。
2.0.3. 依赖值的自动装配
Spring中为bean对象中的属性提供了自动装配功能,此功能的开启需要借助bean标签中的autowire属性进行指定,此属性的值默认有如下几个:
1. | NO | 自动配置(默认) |
2. | ByName | 按名字自动装配(重点掌握) |
3. | ByType | 按类型自动状态(重点掌握),但有多个类型时会出错 |
4. | Constructor | 与byType类似,不同之处在于它应用于构造器参数。 |
例如:
定义并配置DataSource对象
public class DataSource {
}
<bean id="dataSource" class="beans.DataSource"/>
定义并配置JdbcTemplate对象
public class JdbcTemplate {
private DataSource dataSource;
public JdbcTemplate() {}
public JdbcTemplate(DataSource dataSource) {
this.dataSource=dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
<bean id="jdbcTemplate"
class="beans.JdbcTemplate"
autowire="constructor">
</bean>
自动装配应用总结
v byName 按属性对应的set方法名从容器中查找名字相同的bean,然后进行注入。假如出现了名字相同,但类型不同的Bean对象时,会注入失败
v byType 先从容器中查找属性类型相匹配的值然后找按类中对应的set方法(看参数类型),最后通过set方法为对象的属性注入值。假如容器中出现多个类型相同的对象,就会注入失败。
v constructor 先从容器中查找属性类型相匹配的值,然后找类中对应的构造方法(看参数类型)最后通过构造方法为对象的属性注入值。假如容器中出现多个类型相同的对象,此时再比对参数名字,假如有同名的则直接注入,没有同名的就会注入失败。
3. Spring 注解应用
3.1. Spring 注解概述
Spring中提供了两种方式对Bean进行描述,一种是基于xml方式,一种是基于注解方式。基于注解方式主要是依托于注解对bean以及bean中的属性进行描述
然后spring底层通过反射获取bean上定义的这些注解,通过注解描述初始化对象,管理对象的作用域以及对象与对象之间的依赖。
3.2. Spring 注解基本应用
3.2.1. 常用注解说明
组件应用注解
| 注解名 | 说明 |
1. | @Component | 通用注解 |
2. | @Repository | 持久层组件应用注解 |
3. | @Service | 业务层组件应用注解 |
4. | @Controller | 控制层组件应用注解 |
3.2.2. 注解应用案例
数据层对象
@Repository
public class SysUserDaoImpl implements SysUserDao{
public void saveUser(Object obj) {
System.out.println("dao.save.user");
}
}
业务层对象
@Service
public class SysUserServiceImpl implements SysUserService{
…
}
控制层对象
@Controller
public class UserController {
}
3.2.3. 配置注解扫描
Spring中通过指定一个包路径,由系统自动扫描该包及其子包所有组件类,当发现组件类定义前有特定的注解标记时,就将该组件纳入到Spring容器。
例如:
使用组件扫描,首先需要在XML配置中指定扫描父级package路径,例如
<context:component-scan
base-package=”com.company.spring/>”
在这个配置中,容器会自动扫描org.example包及其子包下所有组件,并实例化bean对象。
3.2.4. 编写测试类获取bean
3.3. Spring 注解应用增强
3.3.1. 作用域及生命周期
@Scope("singleton")
@Repository
public class SysUserDao {
/**@PostConstruct注解修饰方法在对象初始化时执行*/
@PostConstruct
public void init(){
System.out.println("init");
}
public void insertObject(){
System.out.println("insertObject");
}
/**@PreDestroy对象销毁时执行*/
@PreDestroy
public void destory(){
System.out.println("destory");
}
}
3.3.2. 延迟加载配置
@Lazy(false)
@Service
public class SysUserService {
}
3.3.3. 自动装配配置
注解方式的自动装配一般会借助@Autowired和@Resource实现,
具体过程参考课堂案例,然后总结实践其过程(作业)。
注解自动装配使用说明:
应用位置
1)@Autowired/@Qualifier 一般用于修饰属性和构造方法
2)@Resource 一般用于修饰属性和set方法
注入规则:
1)@Autowired 修饰属性,构造方法,set方法时默认按照属性类型
或参数类型进行值的注入。假如容器中有多个类型相同的Bean,
此时还会按名字进行匹配,没有相匹配的则注入失败。假如希望
按名字进行匹配需要再此基础加上@Qualifier注解。
2)@Resource 修饰属性,set方法时默认按属性名或set方法
名进行匹配,假如Resource指定了名称则按指定名称进行匹配,
假如没有找到相匹配的名称,则按类型进行值的注入。
4. Spring 注解工厂应用剖析
4.1. Bean工厂是用于做什么的?
所有的工厂都是用于创建对象的,对象创建一般会基于某中策略
对对象进行存储。
例如Spring 中的Bean工厂:ApplicationContext
1) ClassPathXmlApplicationContext
2) AnnotationApplicationContext
3) …….
4.2. Bean工厂的实现的基本原理?
Spring 中所有的Bean假如需要由Spring管理,此时需要以xml描述的方式或
注解的描述方式告诉spring容器。Spring底层内置了对xml的解析,通过反射
获取注解描述,然后基于这些描述通过反射构建对象。
4.3. Bean工厂理解的拔高?
手写基于注解方式的Spring Bean工厂
1) 包的扫描(将包转换为类所在的路径,然后进行文件的查找)
2) 构建类全名(包名+类名,例如java.util.Date)
3) 基于反射构建类的对象(获取Class对象,构建构造方法对象,构建类的实例对象)
4) 存储类的对象(key是谁)
5) 需要时从工厂中获取bean对象即可
4.4. Bean 工厂手写应用实践
4.4.1. 注解定义实现
定义一个用于修饰bean,描述bean的注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
定义一个用于描述配置的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value() default "";
}
4.4.2. 定义Bean组件
定义一个IdGenerator类,然后使用@Component注解修饰
@Component
public class IdGenerator {}
4.4.3. 定义Bean工厂
这个Bean工厂要实现的功能是基于配置类上的@ComponentScan注解,对注解中定义包进行类的扫描,然后构建类的对象,再将对象存储到map集合,需要时从map获取。其代码如下:
public class AnnotationAppContext{
private Map<String,Object> beanMap=
new HashMap<String,Object>();
public AnnotationAppContext(Class<?> c)throws Exception{
//1.获取class(例如AppConfig)上的@ComponentScan注解
ComponentScan cs=
c.getAnnotation(ComponentScan.class);
//2.获取注解中定义的包名
String pkg=cs.value();
//替换包名中的“.”,改成目录结构
String dir=pkg.replaceAll("\\.", "/");
//3.基于包获取包对应的类路径
URL url=
getClass().getClassLoader()
.getResource(dir);
System.out.println("url="+url);
//4.获取类路径下所有的class文件(例如IdGenerator)
File pkgDir=new File(url.getPath());
//4.1获取目录下所有class文件
File[] fs=pkgDir.listFiles();//class
//4.2遍历所有文件,构建类全名
for(File f:fs){
String fname=
f.getName().substring(0,f.getName().lastIndexOf("."));
String clsName=pkg+"."+fname;
Class<?> cls=Class.forName(clsName);
//5.基于class文件上注解(@Component)描述构建类对象
if(!cls.isAnnotationPresent(Component.class))
continue;
Object obj=newInstance(cls);
//6.将类对象存储到map集合
//6.1获取key的值
Component cp=cls.getAnnotation(Component.class);
String key=cp.value();
if("".equals(key)){
key=String.valueOf(
fname.charAt(0)).toLowerCase()+
fname.substring(1);
}
//6.2存储到map
beanMap.put(key, obj);
}
}
private Object newInstance(Class<?> cls)
throws Exception{
Constructor<?> c=
cls.getDeclaredConstructor();
c.setAccessible(true);
return c.newInstance();
}
public Object getBean(String beanName){
return beanMap.get(beanName);
}
@SuppressWarnings("unchecked")
public <T>T getBean(String beanName,
Class<T> cls){
return (T) beanMap.get(beanName);
}
public static void main(String[] args)
throws Exception{
AnnotationAppContext ctx=
new AnnotationAppContext(AppConfig.class);
IdGenerator obj1=(IdGenerator)
ctx.getBean("idGenerator");
IdGenerator obj2=
ctx.getBean("idGenerator",IdGenerator.class);
System.out.println(obj1);
System.out.println(obj2==obj1);
}
}
5. 总结
5.1. 重点和难点分析
1. Spring 注解的强化理解 2. Spring 注解方式实现自动装配 3. Spring Bean工厂手写方式的实现。 |
5.2. 常见FAQ
4. URL是什么 5. URL的值什么情况下为空 6. 注解是什么,如何自己定义 7. 工厂应用中泛型方法如何实现 |
5.3. 作业
8. 总结spring注解应用
9. 尝试手写spring bean 工厂