什么是SSM
SSM=Spring(Spring Framework)+Spring MVC +mybatis
Spring Framework系统架构
Spring Framework学习线路
IoC(Inversion of Control)和DI(Dependency Injection)
他们解决的问题:代码耦合度高的问题,需要类自己new对象,修改部分代码时,导致"牵一发而动全身"的问题。
Spring提供的IoC容器帮助我们实现控制反转,由IoC容器统一管理我们所要用的容器,需要时只需要从IoC容器获取即可。
我们的业务层类可能需要数据层的实例化对象来实现操作数据库的效果,这时候可以通过依赖注入来建立他们直接的关系,把数据层的实例化对象装配到业务层中。
实现IoC两种配置方式
XML配置文件开发
在xml文件中配置bean
基本配置:id(独有标识) name(别名,用逗号分割) class(类)
bean的作用范围
单例模式下,通过getBean得到的对象唯一,原型模式下,得到的对象不唯一。
bean的生命周期
从这里的命名我们也可以看出来init方法是在bean中所有成员都装配完毕后才调用,而不是创建实例化对象时马上调用。
bean实例化的三种方式
1.构造方法创建实例化对象
spring通过无参构造帮我们创建对象,然后再调用里面的方法。
测试案例
StudentDaoImpl实现StudentDao接口
StudentDao
package com.example.test.dao;
public interface StudentDao {
public void selectAll();
}
StudentDaoImpl
package com.example.test.dao.impl;
import com.example.test.dao.StudentDao;
public class StudentDaoImpl implements StudentDao {
public StudentDaoImpl() {
System.out.println("Spring 通过无参构造帮我们创建对象");
}
@Override
public void selectAll() {
System.out.println("student selectAll");
}
}
在applicationContext.xml中配置
<!-- 1.调用无参构造-->
<!-- name字段用来取别名,多个别名可以用;分割-->
<bean id="studentDao" name="dao;daozz" class="com.example.test.dao.impl.StudentDaoImpl"></bean>
测试代码和运行结果:
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App1 {
public static void main(String[] args) {
//获取Ioc容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注册关闭钩子 在关虚拟机之前先关闭容器
context.registerShutdownHook();
//这里用了别名来获取bean
StudentDao studentDao = context.getBean("daozz", StudentDao.class);
studentDao.selectAll();
}
}
2.通过静态工厂实例化对象
测试案例
静态工厂类(通过静态方法来创建实例化对象)
package com.example.test.factory;
import com.example.test.dao.StudentDao;
import com.example.test.dao.impl.StudentDaoImpl;
//静态工厂创建实例化对象
public class StudentDaoFactory1 {
private static StudentDao getStudentDao(){
System.out.println("工厂的相关配置初始化...");
return new StudentDaoImpl();
}
}
xml中配置(class填写静态工厂类 factory-method里面填写工厂类中用于实例化对象的静态方法 )
<!-- 2.静态工厂实例化对象-->
<bean id="studentDao" class="com.example.test.factory.StudentDaoFactory1" factory-method="getStudentDao"></bean>
测试代码及结果
3.通过工厂类中的非静态方法创建实例化对象
测试案例
工厂类
package com.example.test.factory;
import com.example.test.dao.StudentDao;
import com.example.test.dao.impl.StudentDaoImpl;
//动态工厂创建实例化对象
public class StudentDaoFactory2 {
//注意这是一个非静态方法
public StudentDao getStudentDao(){
return new StudentDaoImpl();
}
}
xml中配置
<!-- 3.通过工厂类中的非静态方法创建实例化对象-->
<!-- 第一步:配置工厂bean 因为不是静态方法,使用的时候要先创建工厂的实例对象-->
<bean id="studentDaoFactory" class="com.example.test.factory.StudentDaoFactory2"></bean>
<!-- 第二步:指定工厂和里面实例化的方法-->
<bean id="studentDao" factory-bean="studentDaoFactory" factory-method="getStudentDao"></bean>
测试代码及结果
4.通过工厂类中的非静态方法创建实例化对象的改进版——实现FactoryBean接口
测试案例
工厂类(顺带测试了下bean的生命周期)
实现了FactoryBean接口 重新getObject方法返回要实例化的对象即可
package com.example.test.factory;
import com.example.test.dao.StudentDao;
import com.example.test.dao.impl.StudentDaoImpl;
import org.springframework.beans.factory.FactoryBean;
//泛型里面填写要实例化的对象
public class StudentDaoFactory3 implements FactoryBean<StudentDao> {
public StudentDaoFactory3() {
System.out.println("工厂的无参构造");
}
@Override
public StudentDao getObject() throws Exception {
return new StudentDaoImpl(); //此乃动态实例化对象的方法
}
@Override
public Class<?> getObjectType() {
return StudentDao.class; //返回StudentDao的字节码
}
@Override
public boolean isSingleton() {
return true; //默认单例模式
}
//设置容器创建和销毁时调用的方法
public void init(){
System.out.println("init...");
}
public void destroy(){
System.out.println("destroy...");
}
}
xml中配置(顺带配置了初始化方法和销毁方法)
<!-- 动态工厂实例化对象的改进版 实现FactoryBean接口-->
<bean id="studentDao" class="com.example.test.factory.StudentDaoFactory3" init-method="init" destroy-method="destroy"></bean>
测试代码及结果(属性注入 在 执行工厂无参构造 和 执行init方法 之间执行)
依赖注入
bean实例化出来了,那么类里面的成员属性和类之间的引用关系要如何实现?Spring提供的依赖注入帮助我们解决这个问题。
1.setter注入
测试案例
bookDaoImpl实现bookDao接口
bookDao
package com.example.test.dao;
public interface BookDao {
public void selectAll();
public void getMsg();
}
bookDaoImpl(注意这里要提供成员属性的setter方法)
package com.example.test.dao.impl;
import com.example.test.dao.BookDao;
import com.example.test.dao.StudentDao;
import com.example.test.dao.TeacherDao;
import lombok.Data;
@Data
public class BookDaoImpl implements BookDao {
private String libName;
private int connectNum;
private TeacherDao teacherDao;
@Override
public void selectAll() {
System.out.println("select all books...");
}
@Override
public void getMsg() {
System.out.println(libName+"--"+connectNum+"--"+teacherDao);
}
}
xml中配置
<bean id="bookDao" class="com.example.test.dao.impl.BookDaoImpl">
<!-- 方式1:setter注入,要提供setter方法-->
<!-- ref是引用类型 value传具体值-->
<property name="connectNum" value="100"/>
<property name="libName" value="图书馆名"/>
<property name="teacherDao" ref="teacherDao"/>
</bean>
测试代码及结果
2.构造器注入
测试案例
bookDaoImpl中需要提供带参数的构造方法(要注入哪个成员就往里面写)
package com.example.test.dao.impl;
import com.example.test.dao.BookDao;
import com.example.test.dao.StudentDao;
import com.example.test.dao.TeacherDao;
import lombok.Data;
@Data
public class BookDaoImpl implements BookDao {
private String libName;
private int connectNum;
private TeacherDao teacherDao;
public BookDaoImpl(String libName, int connectNum, TeacherDao teacherDao) {
this.libName = libName;
this.connectNum = connectNum;
this.teacherDao = teacherDao;
}
@Override
public void selectAll() {
System.out.println("select all books...");
}
@Override
public void getMsg() {
System.out.println(libName+"--"+connectNum+"--"+teacherDao);
}
}
xml中配置
<bean id="bookDao" class="com.example.test.dao.impl.BookDaoImpl">
<!-- 方式1:setter注入,要提供setter方法-->
<!-- ref是引用类型 value传具体值-->
<!-- <property name="connectNum" value="100"/>-->
<!-- <property name="libName" value="图书馆名"/>-->
<!-- <property name="teacherDao" ref="teacherDao"/>-->
<!-- 方式2:构造器注入-->
<constructor-arg name="connectNum" value="100"/>
<constructor-arg name="libName" value="图书馆名"/>
<constructor-arg name="teacherDao" ref="teacherDao"/>
</bean>
代码和测试结果同上
3.自动装配
测试案例
bookDaoImpl
package com.example.test.dao.impl;
import com.example.test.dao.BookDao;
import com.example.test.dao.StudentDao;
import com.example.test.dao.TeacherDao;
import lombok.Data;
@Data
public class BookDaoImpl implements BookDao {
// private String libName;
// private int connectNum;
private TeacherDao teacherDao;
private StudentDao studentDao;
// public BookDaoImpl(String libName, int connectNum, TeacherDao teacherDao) {
// this.libName = libName;
// this.connectNum = connectNum;
// this.teacherDao = teacherDao;
// }
@Override
public void selectAll() {
System.out.println("select all books...");
}
@Override
public void getMsg() {
// System.out.println(libName+"--"+connectNum+"--"+teacherDao);
System.out.println(studentDao+"--"+teacherDao);
}
}
xml中配置
<bean id="teacherDao" class="com.example.test.dao.impl.TeacherDaoImpl"/>
<bean id="studentDao" name="dao;daozz" class="com.example.test.dao.impl.StudentDaoImpl"></bean>
<bean id="bookDao" class="com.example.test.dao.impl.BookDaoImpl" autowire="byName">
<!-- 方式3:自动装配-->
<!-- autowire=byType/byName/constructor-->
</bean>
byType情况下要保证要注入的bean类型唯一
byName情况下要保证要注入的成员的名字和xml文件里面bean的id匹配得上
测试代码及结果
依赖注入方式选择
自动装配要注意byType情况下要注意不能有相同类型的bean byName要注意名字是否匹配,在xml配置文件开发下更推荐使用前面两种。
注入集合对象
采用setter注入
TestCollection类
package com.example.test.service.impl;
import lombok.Data;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@Data
//要提供setter方法
public class TestCollection {
private int[] myArray;
private List<String> myList;
private Set<String> mySet;
private Map<String,Integer> myMap;
private Properties myProperties;
}
xml中配置
<bean id="testCollection" class="com.example.test.service.impl.TestCollection">
<property name="myArray">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
<property name="myList">
<list>
<value>哈哈</value>
<value>"呵呵"</value>
<value>1+六</value>
</list>
</property>
<property name="mySet">
<set>
<!-- 不重复-->
<value>赵</value>
<value>钱</value>
<value>钱</value>
<value>陈</value>
</set>
</property>
<property name="myMap">
<map>
<!-- 被覆盖-->
<entry key="张三" value="123"/>
<entry key="张三" value="456"/>
<entry key="李四" value="789"/>
<entry key="王五" value="666"/>
</map>
</property>
<property name="myProperties">
<props>
<prop key="url">jdbc:mysql://localhost:3306/testspring</prop>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>
测试代码及测试结果
第三方bean的导入
测试代码及结果
优化(加载Properties)
创建context命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载properties文件-->
<context:property-placeholder location="db.properties" system-properties-mode="NEVER"/>
用${param}
<!-- 加载第三方bean-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- driverClassName≠dirver-->
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
创建IoC容器,获取bean的几种方法和懒加载
创建IoC容器
获取bean
懒加载
容器结构层次图
ApplicationContext默认立即加载 BeanFactory默认延迟加载
测试案例
请注意这里的TestLazy没有被其他任何bean引用,否则其他bean在创建的时候会装配TestLazy并创建它的实例化对象
eg1:未开启懒加载,上下文对象被创建时自动调用了无参构造。
eg2:开启懒加载,没有调用这个bean,就不会调用它的无参构造。
总结
注解开发
Spring注解开发-CSDN博客