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.1.1.依赖注入基础
重点了解set,构造注入的方式以及单个值如何实现注入。
2.1.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.1.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>
自动装配应用总结
byName 首先根据配置文件里的所有bean对象id去类里面找名字相同的方法,(首字母大小写不管,底层自动换成小写)按属性对应的set方法名从容器中查找名字相同的bean,然后进行注入。假如出现了名字相同,但类型不同的Bean对象时,会注入失败
byType 先从容器中查找属性类型相匹配的值然后找按类中对应的set方法(看参数类型),最后通过set方法为对象的属性注入值。假如容器中出现多个类型相同的对象,就会注入失败。
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
public class TestSysUserDao {
private ClassPathXmlApplicationContext ctx;
@Before
public void init(){
ctx=new ClassPathXmlApplicationContext("ioc.xml");
}
@Test
public void testSysUserDao() {
SysUserDao sys = ctx.getBean("sysUserDao",SysUserDao.class);
sys.insertObject();
}
@After
public void destroy(){
ctx.close();
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实现,具体过程参考
课堂案例,然后总结实践其过程(作业)。
/**使用@Service修饰的对象一般为一个业务层对象
* @Lazy 注解用于定义这个类是否要延迟加载,
* true表示延迟加载,默认为true
* */
@Lazy(false)
@Service
public class SysUserService {
/**@Autowired 注解用于修饰属性或者set方法
* 表示默认按属性类型或对应的set方法参数类型
* 进行自动装配,假如spring容器中有多个类型
* 相同bean对象,此时还会名字进行匹配,假如
* 名字也没有相同的则匹配失败*/
@Autowired
@Qualifier("sysUserDao")
private SysUserDao sysUserDao;
public void setSysUserDao(SysUserDao sysUserDao) {
this.sysUserDao = sysUserDao;
}
public SysUserService() {
System.out.println("SysUserService()");
}
public void saveObject(){
//log..
sysUserDao.insertObject();
//log..
4.Spring 注解工厂应用剖析
4.1.Bean工厂是用于做什么的?
所有的工厂都是用于创建对象的,对象创建一般会基于某种策略
对对象进行存储。
例如Spring 中的Bean工厂:ApplicationContext
1)ClassPathXmlApplicationContext 基于mxl
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的注解名字为Component。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
定义一个用于描述配置的注解名字为ComponentScan。
@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.基于包获取包对应的类路径getClass为得到当前运行的类。getClassLoader为得到类加载器,返回类加载器。getResource()查找具有给定资源的路径。
URL url=
getClass().getClassLoader()
.getResource(dir);
System.out.println("url="+url);
//4.得到路径指定的文件使其变成文件的形式(例如IdGenerator)
File pkgDir=new File(url.getPath());
//4.1获取目录下所有的文件
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)描述构建类对象
//isAnnotationPresent(Component.class)如果指定的类型注释存在此元素上,返回true
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);
}
}
//Object obj=newInstance(cls)的方法
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);
}
}//两个是同一个对象