Spring Framework系统架构图
IoC/DI
-
代码书写现状:service接口更换实现类需要重新创建对象,导致代码耦合度偏高。
-
解决方案:IoC(Inversion of Control)控制反转思想
- 使用对象时,由主动new对象转换为外部提供对象
- 对象创建控制权转移给Ioc容器
-
spring技术提供IoC容器对IoC思想进行了实现
- IoC容器负责对象的创建、初始化
- 被创建和管理的对象在IoC容器中称为bean
-
DI(Dependency Injection)依赖注入
- 在IoC容器中建立bean与bean之间的依赖关系
-
IoC实现
-
在pom.xml导入spring坐标
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency>
-
-
在resource目录下创建Spring Config配置文件
-
在applicationContext.xml配置文件中配置bean
-
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl"></bean> <bean id="bookService" name="service" class="com.itheima.service.impl.BookServiceImpl"></bean> <!-- 配置bean:使用<bean>配置 class属性:表示给bean定义类型(最终路径为实现类) id属性:表示给bean起名 name属性:给bean起别名,可以起多个,作用与id相同 scope属性:指定bean单例对象、非单例对象,默认是单例对象 singleton:单例 prototype:非单例 -->
-
-
在启动类的main方法中获取Ioc容器
-
public class App2 { public static void main(String[] args) { //获取容器对象,ApplicationContext是接口 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //调用getBean方法获取对象 BookDao bookDao = (BookDao)ctx.getBean("bookDao"); } }
-
-
-
DI实现
-
删除业务层中使用new的方式创建的dao对象
-
提供dao对象的set方法
-
public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; }
-
-
在applicationContext.xml配置文件中配置service与dao的关系
-
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl"></bean> <bean id="bookService" name="service" class="com.itheima.service.impl.BookServiceImpl"> <property name="bookDao" ref="bookDao"/> <!--dao对象注入service层--> </bean> <!-- 配置service和dao的关系(DI) <property>:配置当前bean的属性 name属性:java程序中对象名 ref属性:参照bean的id -->
-
-
bean实例化四种方式
-
无参构造方法实例化
-
提供无参构造方法
-
public class BookDaoImpl implements BookDao { //无参构造方法 public BookDaoImpl() { System.out.println("book dao constructor is running ...."); } public void save() { System.out.println("book dao save ..."); } }
-
-
在主函数中获取bean
-
//通过无参构造方法获取bean BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save();
-
-
-
静态工厂实例化
-
提供工厂类的静态get方法返回bean
-
//静态工厂创建对象 public class OrderDaoFactory { public static OrderDao getOrderDao(){ System.out.println("factory setup...."); return new OrderDaoImpl(); } }
-
-
在applicationContext.xml配置文件中配置静态工厂bean
-
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/> <!-- 方式二:使用静态工厂(静态方法)实例化bean:需要工厂类和静态get方法 1.class属性:指向工厂类 2.factory-method属性:指向工厂类的静态get方法名 -->
-
-
在主函数中获取bean
-
//通过静态工厂获取bean OrderDao orderDao = OrderDaoFactory.getOrderDao(); orderDao.save();
-
-
-
实例工厂实例化
-
提供实例工厂类的非静态方法返回bean
-
//实例工厂创建对象 public class UserDaoFactory { public UserDao getUserDao(){ return new UserDaoImpl(); } }
-
-
在applicationContext.xml配置文件中配置实例工厂bean
-
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/> <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/> <!-- 方式三:使用实例工厂(非静态方法)实例化bean 1.配置工厂bean a.class属性:指向工厂类 2.配置业务层bean a.factory-method属性:实例工厂的get方法名 b.factory-bean属性:工厂bean -->
-
-
在主函数中获取bean
-
//创建实例工厂对象 UserDaoFactory userDaoFactory = new UserDaoFactory(); //通过实例工厂对象创建对象 UserDao userDao = userDaoFactory.getUserDao(); userDao.save();
-
-
-
FactoryBean实例化
-
提供FactoryBean类的getObject方法和getObjectType方法
-
public class UserDaoFactoryBean implements FactoryBean<UserDao> {//实现FactoryBean接口并指定类型 //代替原始实例工厂中创建对象的方法 public UserDao getObject() throws Exception { return new UserDaoImpl(); } //返回bean对象类型 public Class<?> getObjectType() { return UserDao.class; } }
-
-
在applicationContext.xml配置文件中配置FactoryBean
-
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
-
-
在主函数中获取bean
-
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); OrderDao orderDao = (OrderDao) ctx.getBean("orderDao"); orderDao.save();
-
-
bean生命周期
-
bean生命周期:bean从创建到销毁的整体过程
-
bean初始化和销毁
-
使用配置文件配置初始化和销毁方法
-
//表示bean初始化对应的操作(需要配置) public void init(){ System.out.println("init..."); } public void save() { System.out.println("book dao save ..."); } //表示bean销毁前对应的操作(需要配置) //destory方法关闭容器才能执行 public void destory(){ System.out.println("destory..."); }
-
-
在applicationContext.xml配置文件中配置bean初始化和销毁方法
-
<!-- init-method属性:设置bean初始化生命周期回调函数 destroy-method属性:设置bean销毁生命周期回调函数,仅适用于单例对象,必须关闭容器才能执行 --> <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
-
-
执行销毁方法需要先关闭容器:
-
暴力关闭
-
//关闭容器需要ClassPathXmlApplicationContext接口 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //关闭容器(暴力关闭):暴力关闭后不能获取bean对象 ctx.close();
-
-
注册关闭钩子函数,在虚拟机退出前自动关闭容器
-
//注册关闭钩子函数,动态关闭 ctx.registerShutdownHook();
-
-
-
实现接口重写初始化和销毁方法
-
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean { public void setBookDao(BookDao bookDao) { System.out.println("set ....."); this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } //销毁bean之前执行 public void destroy() throws Exception { System.out.println("service destroy"); } //初始化bean方法,在set方法执行后再执行 public void afterPropertiesSet() throws Exception { System.out.println("service init"); } }
-
-
依赖注入
-
setter注入
-
简单类型
-
public class BookDaoImpl implements BookDao { private String databaseName; private int connectionNum; //setter注入需要提供要注入对象的set方法 public void setConnectionNum(int connectionNum) { this.connectionNum = connectionNum; } //setter注入需要提供要注入对象的set方法 public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public void save() { System.out.println("book dao save ..."+databaseName+","+connectionNum); } }
-
<!-- setter注入简单类型 name属性:设置注入的属性名,实际是set方法对应的名称 value属性:设置注入简单类型数据值 --> <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"> <!--property标签:设置注入属性--> <property name="connectionNum" value="100"/> <property name="databaseName" value="mysql"/> </bean>
-
-
引用类型
-
private BookDao bookDao; private UserDao userDao; //setter注入需要提供要注入对象的set方法 public void setUserDao(UserDao userDao) { this.userDao = userDao; } //setter注入需要提供要注入对象的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; }
-
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"> <!--property标签:设置注入属性--> <property name="bookDao" ref="bookDao"/> <property name="userDao" ref="userDao"/> </bean>
-
-
-
构造器注入
-
简单类型
-
public class BookDaoImpl implements BookDao { private String databaseName; private int connectionNum; public BookDaoImpl(String databaseName, int connectionNum) { this.databaseName = databaseName; this.connectionNum = connectionNum; } public void save() { System.out.println("book dao save ..."+databaseName+","+connectionNum); } }
-
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"> <!--按方法形参名注入,代码耦合度过高--> <constructor-arg name="connectionNum" value="10"/> <constructor-arg name="databaseName" value="mysql"/> </bean> <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"> <!--按类型注入,但类型不能重复--> <constructor-arg type="int" value="10"/> <constructor-arg type="java.lang.String" value="mysql"/> </bean> <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"> <!--根据参数位置注入--> <constructor-arg index="0" value="mysql"/> <constructor-arg index="1" value="100"/> </bean>
-
-
引用类型
-
public class BookServiceImpl implements BookService{ private BookDao bookDao; private UserDao userDao; //使用有参构造方法注入引用类型 public BookServiceImpl(BookDao bookDao, UserDao userDao) { this.bookDao = bookDao; this.userDao = userDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); userDao.save(); } }
-
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"> <!--constructor-arg--> <constructor-arg name="userDao" ref="userDao"/> <constructor-arg name="bookDao" ref="bookDao"/> </bean>
-
-
依赖自动装配
-
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
-
自动装配:只能注入引用类型,优先级低于setter注入和构造器注入。
-
按类型(常用):
-
<!--需要注入的bean也要在spring配置(可以不起名),类型必须唯一--> <bean class="com.itheima.dao.impl.BookDaoImpl"/> <!--自动装配需要提供setter方法--> <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
-
-
按名称
-
<!--按名称装配:需要注入的bean的配置id与java程序一致(耦合)--> <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/> <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
-
-
按构造方法
-
不启用自动装配
-
集合注入
-
在applicationContext.xml配置文件中配置注入类型
-
数组
-
<!--name属性:成员变量名--> <!--数组注入--> <property name="array"> <array> <value>100</value> <value>200</value> <value>300</value> </array> </property>
-
-
List(常用)
-
<!--list集合注入--> <property name="list"> <list> <value>itcast</value> <value>itheima</value> <value>boxuegu</value> <value>chuanzhihui</value> </list> </property>
-
-
Set
-
<!--set集合注入--> <property name="set"> <set> <value>itcast</value> <value>itheima</value> <value>boxuegu</value> <value>boxuegu</value> </set> </property>
-
-
Map(常用)
-
<!-- map集合注入 <map>标签:Z <entry>标签: 1.key属性 2.value属性 --> <property name="map"> <map> <entry key="country" value="china"/> <entry key="province" value="henan"/> <entry key="city" value="kaifeng"/> </map> </property>
-
-
Properties
-
<!-- Properties注入 <props>标签: <prop>标签: 1.key属性 2.value写在标签中 --> <property name="properties"> <props> <prop key="country">china</prop> <prop key="province">henan</prop> <prop key="city">kaifeng</prop> </props> </property>
-
-
-
在代码中测试注入是否成功:
-
public class BookDaoImpl implements BookDao { private int[] array; private List<String> list; private Set<String> set; private Map<String,String> map; private Properties properties; public void setArray(int[] array) { this.array = array; } public void setList(List<String> list) { this.list = list; } public void setSet(Set<String> set) { this.set = set; } public void setMap(Map<String, String> map) { this.map = map; } public void setProperties(Properties properties) { this.properties = properties; } public void save() { System.out.println("book dao save ..."); System.out.println("遍历数组:" + Arrays.toString(array)); System.out.println("遍历List" + list); System.out.println("遍历Set" + set); System.out.println("遍历Map" + map); System.out.println("遍历Properties" + properties); } }
-
数据源对象管理
-
整合druid:
-
导入druid坐标:
-
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
-
-
在applicationContext.xml配置数据源对象:
-
<bean class="com.alibaba.druid.pool.DruidDataSource"> <!--查看DruidDataSource类确定setter注入--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>
-
-
-
整合c3p0:
-
导入c3p0坐标:
-
<dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency>
-
-
在applicationContext.xml配置数据源对象:
-
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/> <property name="user" value="root"/> <property name="password" value="root"/> <property name="maxPoolSize" value="1000"/> </bean>
-
-
加载properties文件
-
在applicationContext.xml的beans标签下开启命名空间:
-
在applicationContext.xml使用context空间加载properties文件:
-
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/> <!--system-properties-mode属性:是否加载系统属性--> <!-- location后可以指定多个配置文件,按,分割 使用*.properties可以加载多个配置文件 classpath:*.properties是标准写法 -->
-
-
使用属性占位符 $(key) 访问properties文件中的属性:
-
<bean class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
-
容器
-
bean加载方式
-
//1.按名称加载(需要强转) BookDao bookDao = (BookDao) ctx.getBean("bookDao"); //2.按名称和类型加载 BookDao bookDao = ctx.getBean("bookDao",BookDao.class); //3.按类型加载 BookDao bookDao = ctx.getBean(BookDao.class);
-
-
容器层次结构图
-
BeanFactory创建完毕后,所有的bean都是延迟加载
-
ApplicationContext创建完毕后,所有的bean都是立即加载
核心容器总结
-
容器总结
-
bean相关
-
依赖注入相关
注解开发定义bean
-
使用@Component注解定义bean
-
//@Component需要按类型来获取bean @Component("bookDao")//按名称来获取bean public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } }
-
-
在applicationContext.xml开启注解扫描:
-
<context:component-scan base-package="com.itheima"/>
-
-
Spring提供@Component注解的三个衍生注解
- @Controller:用于表现层bean定义
- @Service:用于业务层bean定义
- @Repository:用于数据层bean定义
纯注解开发
-
创建配置类:替换applicationContext.xml配置文件
-
@Configuration:声明当前类为Spring配置类
-
@ComponentScan:设置注解扫描路径,多个路径书写为字符串数组格式
-
@Configuration//设置当前类为配置类 @ComponentScan("com.itheima")//扫描需要配置bean的类 public class SpringConfig { }
-
-
public class AppForAnnotation { public static void main(String[] args) { //AnnotationConfigApplicationContext注解开发实现类 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);//加载配置类 //按id获取bean BookDao bookDao = (BookDao) ctx.getBean("bookDao"); System.out.println(bookDao); //按类型获取bean BookService bookService = ctx.getBean(BookService.class); System.out.println(bookService); } }
-
设置单例和非单例对象
- @Scope(“prototype”)非单例
- @Scope(“singleton”)单例
-
设置生命周期方法
- @PostConstruct 初始化方法
- @PreDestroy 销毁前方法
-
设置依赖注入
-
@Autowired:按类型注入引用类型
- 不需要提供set方法,源码底层使用暴力反射直接注入。
-
@Qualifier(“对象名”):按名称注入引用类型
-
@Value:配合配置文件注入简单类型
-
需要在Spring配置类添加@PropertySource注解
-
@Configuration//设置当前类为配置类 @ComponentScan("com.itheima")//扫描需要配置bean的类 //@PropertySource加载properties配置文件{,,,}可以添加多个配置文件,配置文件名允许用* @PropertySource("jdbc.properties") public class SpringConfig { }
-
-
使用@Value(“${}”)注入简单类型
-
@Value("${name}")//@Value:注入简单类型(无需提供set方法) private String name;
-
-
-
-
管理第三方bean
-
创建jdbc的配置类
-
@Configuration public class JdbcConfig { @Value("root") private String username; @Value("root") private String password; @Value("jdbc:mysql://localhost:3306/spring_db") private String url; @Value("com.mysql.jdbc.Driver") private String driverClassName; /** * 配置第三方bean方法 * @param bookDao 参数自动装配注入引用类型 * @return */ @Bean public DataSource dataSource(BookDao bookDao){ System.out.println(bookDao); DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUsername(username); druidDataSource.setPassword(password); druidDataSource.setUrl(url); druidDataSource.setDriverClassName(driverClassName); return druidDataSource; } }
-
-
将第三方bean的配置类加入核心配置
-
方式一:导入式(第三方bean的配置类不需要添加@Configuration)
-
@Configuration @Import(JdbcConfig.class)//导入其他配置类,{,,,}导入多个 public class SpringConfig { }
-
-
方式二:扫描式
-
@Configuration @ComponentScan("com.itheima.config")//扫描式导入(不推荐) public class SpringConfig { }
-
-
-