2、spring注入及自动装配
1. 课程回顾 1-1
1.1. Spring 概述 1-1
1.2. Spring IOC 概述 1-2
1.3. Spring IOC 编程 1-2
2. Spring Bean依赖 2-2
2.1.1. 依赖注入基础 2-2
2.1.2. 依赖注入进阶 2-2
2.1.3. 依赖值的自动装配 2-5
3. Spring 注解应用 3-6
3.1. Spring 注解概述 3-6
3.2. Spring 注解基本应用 3-6
3.2.1. 常用注解说明 3-6
3.2.2. 注解应用案例 3-7
3.2.3. 配置注解扫描 3-7
3.2.4. 编写测试类获取bean 3-8
3.3. Spring 注解应用增强 3-8
3.3.1. 作用域及生命周期 3-8
3.3.2. 延迟加载配置 3-8
3.3.3. 自动装配配置 3-9
4. 总结 4-9
4.1. 重点和难点分析 4-9
4.2. 常见FAQ 4-9
1. 课程回顾
1.1. Spring 概述
- Spring 是什么?(框架,半成品)
- Spring 能解决什么问题(面向对象,面向切面,面向服务)
- Spring 框架核心?(IOC,AOP,MVC,…)
个人认为:Spring 最强大是它的资源整合能力。
1.2. Spring IOC 概述
- IOC是什么?(控制反转:由spring构建对象,管理对象依赖)
- IOC 应用优势?(解耦,更好的管理对象,使用系统资源)
- IOC 的核心?(工厂,配置,依赖注入)
- IOC 编程步骤?(类,配置,获取)
1.3. Spring IOC 编程
- Spring Bean对象初始化,作用域,声明周期,延迟加载
1) Bean类型的编写(修饰符,构造方法)
2) Bean 的配置(applicationContext.xml)
3) Bean 的作用域(singleton,prototype)
4) Bean 的生命周期(生命周期方法的使用)
5) Bean 的延迟加载(局部lazy-init,全局 default-lazy-init)
- 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属性进行指定,此属性的值默认有如下几个:
NO |
自动配置(默认) |
|
ByName |
按名字自动装配(重点掌握) |
|
ByType |
按类型自动状态(重点掌握),但有多个类型时会出错 |
|
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. 常用注解说明
组件应用注解
|
注解名 |
说明 |
@Component |
通用注解 |
|
@Repository |
持久层组件应用注解 |
|
@Service |
业务层组件应用注解 |
|
@Controller |
控制层组件应用注解 |
3.2.2. 注解应用案例
数据层对象
@Repository
业务层对象
@Service
控制层对象
@Controller
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指定了名称则按指定名称进行匹配,假如没有找到相匹配的名称,则按类型进行值的注入。
1. Spring 注解工厂应用剖析
1.1. Bean工厂是用于做什么的?
所有的工厂都是用于创建对象的,对象创建一般会基于某中策略对对象进行存储。
例如Spring 中的Bean工厂:ApplicationContext
1) ClassPathXmlApplicationContext
2) AnnotationApplicationContext
3) …….
1.2. Bean工厂的实现的基本原理?
Spring 中所有的Bean假如需要由Spring管理,此时需要以xml描述的方式或注解的描述方式告诉spring容器。Spring底层内置了对xml的解析,通过反射获取注解描述,然后基于这些描述通过反射构建对象。
1.3. Bean工厂理解的拔高?
手写基于注解方式的Spring Bean工厂
1) 包的扫描(将包转换为类所在的路径,然后进行文件的查找)
2) 构建类全名(包名+类名,例如java.util.Date)
3) 基于反射构建类的对象(获取Class对象,构建构造方法对象,构建类的实例对象)
4) 存储类的对象(key是谁)
5) 需要时从工厂中获取bean对象即可
1.4. Bean 工厂手写应用实践
1.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 "";
}
1.4.2. 定义Bean组件
定义一个IdGenerator类,然后使用@Component注解修饰
@Component
public class IdGenerator {}
1.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); } }
2. 总结
2.1. 重点和难点分析
|
2.2. 常见FAQ
|
2.3. 作业
- 总结spring注解应用
- 尝试手写spring bean 工厂