什么是控制反转
控制反转:IoC(Inversion of Control)
反转是什么?
反转是两件事:
第一:不在程序中采用硬编码的方式来new对象了。new对象的权利交出去了
第二:不在程序中采用硬编码的方式维护对象的关系了。对象之间关系的维护权,也不管了,交出去了。
控制反转:是一种编程思想
Spring框架
Spring 框架实现了控制反转 IoC 这种思想
Spring 框架可以帮助 new 对象
Spring 框架可以帮助维护对象和对象之间的关系
Spring 是一个实现了 IoC 思想的容器
控制反转的实现方式有多种,比较重要的叫做:依赖注入(Dependency Injection)
依赖注入,包括两种常见的方式:
1、set注入(执行set方法给属性赋值)
2、构造方法注入 (执行构造方法给属性赋值)
Spring的8大模块
Spring core 控制反转
Spring AOP 面向切面编程
Spring Web MVC
Spring DAO 提供了单独支持JDBC操作的API
Spring Webflux
Spring ORM 支持集成常见的ORM框架
Spring Web
Spring Context
第一个Spring程序
写一个User类
设置Spring配置文件
创建测试类
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class FirstSpringTest {
@Test
public void testFirstSpringCode() {
// 1.获取容器对象
// 应用上下文,Spring容器,ApplicationContext是个接口
// ClassPathXmlApplicationContext专门从类路径加载Spring配置文件的Spring上下文对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
// 2.从容器中根据ID获取对象
Object userBean = applicationContext.getBean("userBean");
System.out.println(userBean);
}
}
打印结果
默认情况下Spring会通过反射机制,调用无参数构造方法来实例化对象
测试一下
bean可以配置非自定义类
测试效果
Object类型无法调用,该类型的方法,使用以下代码可以使用指定类型接收,且不需要强制类型转换
public class FirstSpringTest {
@Test
public void testFirstSpringCode() {
// 1.获取容器对象
// 应用上下文,Spring容器,ApplicationContext是个接口
// ClassPathXmlApplicationContext专门从类路径加载Spring配置文件的Spring上下文对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
// 2.从容器中根据ID获取对象
Object userBean = applicationContext.getBean("userBean");
System.out.println(userBean);
Date dateBean = applicationContext.getBean("dateBean", Date.class);
System.out.println(dateBean);
}
}
ApplicationContext的超级父接口是,BeanFactory,Bean工厂就是生产Bean对象的一个工厂对象
BeanFactory是IoC容器的顶级接口
Spring底层的IoC机制是:XML解析 + 工厂模式 + 反射机制
Log4j2日志框架
启用Log4j2:
1、配置依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.23.1</version>
</dependency>
2、编写配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<loggers>
<!--
level指定日志级别,从低到高的顺序是
ALL < TRACE < DEBUG < INFO < ERROR < FATAL < OFF
-->
<root level="DEBUG">
<appender-ref ref="spring6log"/>
</root>
</loggers>
<appenders>
<!-- 输出信息到控制台 -->
<console name="spring6log" target="SYSTEM_OUT">
<!-- 控制日志输出格式 -->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
</appenders>
</configuration>
打印结果如下
自己如何使用Log4j2
//1、创建日志记录对象
//获取FirstSpringTest类的日志记录对象
Logger logger = LoggerFactory.getLogger(FirstSpringTest.class);
//2、记录日志
logger.debug("这是一条调试信息");
打印效果
Spring对IoC的实现
依赖注入实现IoC
set注入
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert() {
logger.info("数据库正在保存用户信息");
}
}
public class UserService {
private UserDao userDao;
//必须要提供一个set方法
//Spring容器会调用这个set方法,来给userDao属性赋值
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser() {
//保存用户信息到数据库
userDao.insert();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置dao-->
<bean id="userDao" class="com.spring6.dao.UserDao"/>
<!--配置Service-->
<bean id="userService" class="com.spring6.service.UserService">
<!--调用对应的set方法需要配置Property标签-->
<!--ref是引用,ref是指定要注入的Bean的ID-->
<property name="userDao" ref="userDao">
</property>
</bean>
</beans>
public class DITest {
@Test
public void testDI() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springDi.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.saveUser();
}
}
通过set方法注入使Service不再有dao对象
set注入内部Bean和外部Bean
public class OrderDao {
private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);
public void generate() {
logger.info("订单正在生成。。。。。。。");
}
}
public class OrderService {
private OrderDao orderDao;
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void generate() {
orderDao.generate();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="orderDaoBean" class="com.spring6.dao.OrderDao">
</bean>
<!--外部Bean-->
<bean id="orderServiceBean" class="com.spring6.service.OrderService">
<property name="orderDao" ref="orderDaoBean">
</property>
</bean>
<!--内部Bean-->
<bean id="orderServiceBean1" class="com.spring6.service.OrderService">
<property name="orderDao">
<bean class="com.spring6.dao.OrderDao"/>
</property>
</bean>
</beans>
public class DITest {
@Test
public void testSetDi() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
OrderService orderService = applicationContext.getBean("orderServiceBean", OrderService.class);
orderService.generate();
OrderService orderService1 = applicationContext.getBean("orderServiceBean1", OrderService.class);
orderService1.generate();
}
}
打印结果
构造方法注入
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert() {
logger.info("数据库正在保存用户信息");
}
}
public class VipDao {
private static final Logger logger = LoggerFactory.getLogger(VipDao.class);
public void insert() {
logger.info("数据库正在保存尊贵的VIP用户信息");
}
}
public class CustomService {
private UserDao userDao;
private VipDao vipDao;
public CustomService(UserDao userDao, VipDao vipDao) {
this.userDao = userDao;
this.vipDao = vipDao;
}
public void save() {
userDao.insert();
vipDao.insert();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoBean" class="com.spring6.dao.UserDao">
</bean>
<bean id="vipDaoBean" class="com.spring6.dao.VipDao">
</bean>
<bean id="customServiceBean" class="com.spring6.service.CustomService">
<!--构造注入-->
<!--构造构造方法的第一个参数,下标index 0-->
<constructor-arg index="0" ref="userDaoBean"/>
<!--构造构造方法的第二个参数,下标index 1-->
<constructor-arg index="1" ref="vipDaoBean"/>
</bean>
</beans>
public class DITest {
@Test
public void testDI() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springDi.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.saveUser();
VipService vipService = applicationContext.getBean("vipServiceDao", VipService.class);
vipService.saveVipUserInfo();
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("construct.xml");
CustomService customService = appContext.getBean("customServiceBean", CustomService.class);
customService.save();
}
}
简单类型的set注入
public class User {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
<!-- 简单类型的set注入 -->
<bean id="userBean" class="com.spring6.bean.User">
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
public class DITest {
@Test
public void testEasy() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
User user = applicationContext.getBean("userBean", User.class);
System.out.println(user);
}
}
打印结果
级联属性注入
需要实现get属性
public class User {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class People {
private String name;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
'}';
}
}
注意此处顺序不能写反,先对象,再属性
<bean id="peopleBean" class="com.spring6.bean.People">
<property name="user" ref="userBean" />
<property name="user.username" value="张三" />
</bean>
<bean id="userBean" class="com.spring6.bean.User">
</bean>
public class DITest {
@Test
public void testEasy() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
User user = applicationContext.getBean("userBean", User.class);
System.out.println(user);
}
}
数组注入
1、简单类型,和视频学的,这段代码我真一个字都不想改(哈哈哈哈)
public class QianDaYe {
private String[] hobbies;
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
@Override
public String toString() {
return "QianDaYe{" +
"hobbies=" + Arrays.toString(hobbies) +
'}';
}
}
<bean id="yuQian" class="com.spring6.bean.QianDaYe">
<!--数组属性当中的类型是String简单类型-->
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
</bean>
public class DITest {
@Test
public void testArray() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("array-di.xml");
QianDaYe yuQian = applicationContext.getBean("yuQian", QianDaYe.class);
System.out.println(yuQian);
}
}
测试
2、复杂类型
array里面嵌套ref,完整配置文件如下
public class Pony {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Pony{" +
"name='" + name + '\'' +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pony1" class="com.spring6.bean.Pony">
<property name="name" value="兔子">
</property>
</bean>
<bean id="pony2" class="com.spring6.bean.Pony">
<property name="name" value="剑齿虎">
</property>
</bean>
<bean id="pony3" class="com.spring6.bean.Pony">
<property name="name" value="烧饼">
</property>
</bean>
<bean id="yuQian" class="com.spring6.bean.QianDaYe">
<!--数组属性当中的类型是String简单类型-->
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<!-- 复杂类型-->
<property name="ponys">
<array>
<ref bean="pony1"/>
<ref bean="pony2"/>
<ref bean="pony3"/>
</array>
</property>
</bean>
</beans>
打印结果
集合类型注入
public class Person {
// 注入List集合类型
private List<String> names;
// 注入Set集合类型
private Set<String> addresses;
public void setNames(List<String> names) {
this.names = names;
}
public void setAddresses(Set<String> addresses) {
this.addresses = addresses;
}
@Override
public String toString() {
return "Person{" +
"names=" + names +
", addresses=" + addresses +
'}';
}
}
<bean id="person" class="com.spring6.bean.Person">
<property name="names">
<!--有序可重复-->
<list>
<value>张三</value>
<value>李四</value>
<value>张李十二</value>
<value>张三</value>
</list>
</property>
<property name="addresses">
<!--无序不可重复-->
<set>
<value>监狱</value>
</set>
</property>
</bean>
打印结果
Map类型注入
public class Person {
// 注入List集合类型
private List<String> names;
// 注入Set集合类型
private Set<String> addresses;
// Map 类型
private Map<Integer, String> phoneNumbers;
public void setPhoneNumbers(Map<Integer, String> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
public void setNames(List<String> names) {
this.names = names;
}
public void setAddresses(Set<String> addresses) {
this.addresses = addresses;
}
@Override
public String toString() {
return "Person{" +
"names=" + names +
", addresses=" + addresses +
", phoneNumbers=" + phoneNumbers +
'}';
}
}
<bean id="person" class="com.spring6.bean.Person">
<property name="phoneNumbers">
<map>
<!--复杂类型-->
<entry key-ref="" value-ref="" />
<!--简单类型-->
<entry key="1" value="110"/>
<entry key="2" value="120"/>
<entry key="3" value="114"/>
</map>
</property>
<property name="names">
<!--有序可重复-->
<list>
<value>张三</value>
<value>李四</value>
<value>张李十二</value>
<value>张三</value>
</list>
</property>
<property name="addresses">
<!--无序不可重复-->
<set>
<value>监狱</value>
</set>
</property>
</bean>
打印结果
p 命名空间注入
public class Phone {
private String chipType;
private String brand;
private String price;
public void setChipType(String chipType) {
this.chipType = chipType;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public String toString() {
return "Phone{" +
"chipType='" + chipType + '\'' +
", brand='" + brand + '\'' +
", price='" + price + '\'' +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入 xmlns:p="http://www.springframework.org/schema/p"-->
<bean id="phoneBean" class="com.spring6.bean.Phone" p:chipType="A22" p:brand="iPhone" p:price="$99999999"/>
</beans>
打印结果
注入属性文件
第一步:引入context命名空间
第二步:使用context:property-placeholder location标签进行查找
location默认从根路径下,开始加载信息
第三步:${key}取值
<?xml version="1.0" encoding="UTF-8"?>
<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命名空间
第二步:使用context:property-placeholder location标签进行查找
Location默认从根路径下,开始加载信息
-->
<context:property-placeholder location="jdbc.properties"/>
<!--配置数据源-->
<bean id="ds" class="com.spring6.jdbc.MyDataSource">
<!-- 第三步${key}取值-->
<property name="driver" value="${driverClass}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
</beans>
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
public void setDriver(String driver) {
this.driver = driver;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public Connection getConnection() throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
打印结果