尚硅谷课件:
分类:尚硅谷Spring6教程 - Lixx Blog - 李晓旭的博客
简介
Java Spring 是一个开源的、全面的企业级应用开发框架,旨在简化企业级应用的开发。Spring 框架最初由 Rod Johnson 在 2002 年发布,并随着时间的推移,它已经成为 Java 开发者中最受欢迎和广泛使用的框架之一。Spring 框架的核心是控制反转(IoC)和面向切面编程(AOP),这两个特性极大地提高了应用程序的模块性和可维护性。
Spring 框架的主要特点:
- 控制反转(IoC):IoC 是一种设计原则,用于减少计算机代码之间的耦合。在 Spring 中,IoC 容器负责管理对象(称为 beans)的生命周期和对象之间的关系。开发者不再需要编写大量的代码来创建对象之间的依赖关系,而是通过配置文件或注解来声明这些关系,由 Spring 容器在运行时动态地注入这些依赖。
- 面向切面编程(AOP):AOP 允许开发者将横切关注点(如日志、事务管理等)从业务逻辑中分离出来,从而提高了代码的可重用性和模块化。Spring AOP 提供了声明式的事务管理、日志记录等功能,使得这些横切关注点可以很容易地应用到多个地方,而无需修改业务代码。
- 模块化:Spring 框架被设计成高度模块化的,它包含多个模块,如 Spring Core、Spring MVC、Spring JDBC、Spring ORM 等,每个模块都提供了特定的功能。开发者可以根据需要选择性地使用这些模块,从而构建出满足自己需求的应用程序。
- 集成性:Spring 框架提供了与多种第三方库和框架的集成支持,如 Hibernate、MyBatis、JPA、Struts 等。这使得开发者可以很容易地将 Spring 与其他流行的技术栈结合使用,从而构建出功能强大的企业级应用。
- 测试支持:Spring 提供了对 JUnit 和 TestNG 等测试框架的集成支持,使得开发者可以很方便地对 Spring 应用进行单元测试、集成测试等。此外,Spring 还提供了 Mock 对象的功能,使得开发者可以在不依赖外部资源的情况下测试应用程序。
Spring 框架的应用场景:
Spring 框架适用于各种规模的企业级应用开发,包括但不限于:
- Web 应用开发(使用 Spring MVC)
- 数据访问(使用 Spring JDBC、Spring ORM)
- 消息传递(使用 Spring Integration)
- 批处理(使用 Spring Batch)
- 云服务(使用 Spring Cloud)
总之,Java Spring 框架以其强大的功能、灵活的架构和广泛的社区支持,成为了 Java 开发者构建企业级应用的首选框架之一。
开发步骤
1.引入spring相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ly.mybatis</groupId>
<artifactId>spring6</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-first</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.3</version>
</dependency>
</dependencies>
</project>
2.创建类,定义属性和方法
package com.ly.spring;
import org.junit.jupiter.api.Test;
/**
* @author 刘宇
*/
public class User {
@Test
public void add(){
System.out.println("add");
}
}
3.按照spring的要求配置文件
<?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">
<!--完成user对象创建
bean标签
id属性:唯一标识
class属性:要创建对象所在类的全路径
-->
<bean id="user" class="com.ly.spring.User"></bean>
</beans>
4.在spring配置文件配置相关信息
5.进行最终测试
package com.ly.spring.test;
import com.ly.spring.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 刘宇
*/
public class testUser {
@Test
public void testUserObject(){
//加载spring配置文件,对象创建
ApplicationContext context= new ClassPathXmlApplicationContext("bean.xml");
//获取创建的对象
User user=(User)context.getBean("user");
System.out.println(user);
//使用对象调用方法进行测试
user.add();
}
}
如何使用反射创建的对象
1.加载bean.xml配置文件
2.对xml文件进行解析操作
3.获取xml文件bean标签属性值id属性和class属性
4.使用反射根据类全路径创建对象
Class clazz=Class.forName("全类名");
Object o=clazz.newInstance();
引
整合log4j2日志框架
导入依赖
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<loggers>
<!--
level指定日志级别,从低到高的优先级:
TRACE < DEBUG < INFO < WARN < ERROR < FATAL
trace:追踪,是最低的日志级别,相当于追踪程序的执行
debug:调试,一般在开发中,都将其设置为最低的日志级别
info:信息,输出重要的信息,使用较多
warn:警告,输出警告的信息
error:错误,输出错误信息
fatal:严重错误
-->
<root level="DEBUG">
<appender-ref ref="spring6log"/>
<appender-ref ref="RollingFile"/>
<appender-ref ref="log"/>
</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>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
<File name="log" fileName="D:\logs\springlog4j2" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出所有的信息,
每次大小超过size,
则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,
作为存档-->
<RollingFile name="RollingFile" fileName="D:\logs"
filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<SizeBasedTriggeringPolicy size="50MB"/>
<!-- DefaultRolloverStrategy属性如不设置,
则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</appenders>
</configuration>
容器:IoC
概述
1.控制反转(IoC)
控制反转是一种思想。
控制反转是为了降低程序耦合度,提高程序扩展力。
控制反转,反转的是什么?
1.将对象的创建权利交出去,交给第三方容器负责。
2.将对象和对象之间关系的维护权交出去,交给第三方容器负责。
控制反转这种思想如何实现呢?
- DI(Dependency Injection):依赖注入
2.依赖注入
- 依赖注入(Dependency Injection, DI)是一种设计模式,它指的是对象的依赖关系(即对象所需的外部资源或服务)不是由对象本身在内部创建或管理,而是由外部容器(如Spring框架的ApplicationContext)在创建对象时将其所需的依赖注入到对象中。
依赖注入常见的实现方式包括两种:
- 第一种:set注入
- 第二种:构造注入
所以结论是:IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。
Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
3、IoC容器在Spring的实现
Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:
①BeanFactory
这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
②ApplicationContext
BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
③ApplicationContext的主要实现类
类型名 简介
ClassPathXmlApplicationContext 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ConfigurableApplicationContext ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
WebApplicationContext 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。
基于XML管理Bean
获取bean
1.id根据id获取bean
2.根据类型获取bean
3.根据id和类型获取
4.注意 当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个
<!--创建user对象-->
<bean id="user" class="com.ly.spring6.iocxml.User"></bean>
package com.ly.spring.test;
import com.ly.spring6.iocxml.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 刘宇
*/
public class TestUser {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
//根据id获取bean
User user1 =(User)context.getBean("user");
System.out.println("根据id获取bean"+ user1);
//根据类型获取bean
User user2 = context.getBean(User.class);
System.out.println("根据类型获取对象"+user2);
//根据id和类型获取
User user3 = context.getBean("user", User.class);
System.out.println("根据id和类型获取bean"+user3);
}
}
扩展知识
如果组件类实现了接口,根据接口类型可以获取 bean 吗?
可以,前提是bean唯一
如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?
不行,因为bean不唯一
结论
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
java中,instanceof运算符用于判断前面的对象是否是后面的类,或其子类、实现类的实例。如果是返回true,否则返回false。也就是说:用instanceof关键字做判断时, instanceof 操作符的左右操作必须有继承或实现关系
依赖注入
1.setter注入
第一步:创建类,定义属性,创建构造器和set方法
第二步:在spring配置文件中进行配置
<!--set方法注入-->
<bean id="book" class="com.ly.spring6.iocxml.di.Book">
<property name="bname" value="后端开发"></property>
<property name="author" value="尚硅谷"></property>
</bean>
2.构造器注入
(1).创建有参构造
(2).配置spring文件
<!--构造器注入-->
<bean id="bookCon" class="com.ly.spring6.iocxml.di.Book">
<constructor-arg name="bname" value="后端开发"></constructor-arg>
<constructor-arg name="author" value="尚硅谷"></constructor-arg>
</bean>
注意:
<!--
constructor-arg标签还有两个属性可以进一步描述构造器参数:
index属性:指定参数所在位置的索引(从0开始)
name属性:指定参数名
3.特殊值处理
四种特殊值:1.字面量赋值 int a = 10 10就是字面量值
2.null值
3.xml实体 如"<>"解决方法1:进行转义处理如<转义为<
4.CDATA实体
<property name="expression">
<!-- 解决方案二:使用CDATA节 -->
<!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
<!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
<!-- 所以CDATA节中写什么符号都随意 -->
<value><![CDATA[a < b]]></value>
</property>
为对象类型属性赋值
方式一:引入外部bean
<bean id="dept" class="com.ly.spring6.iocxml.ditest.Dept">
<property name="dname" value="安保部"></property>
</bean>
<bean id="emp" class="com.ly.spring6.iocxml.ditest.Emp">
<property name="age" value="50"></property>
<property name="ename" value="jack"></property>
<!--注入对象类型
private Dept dept
使用ref标签,放入部门的id
-->
<property name="dept" ref="dept"></property>
</bean>
方式二:内部bean
<!--第二种方式内部bean-->
<bean id="emp2" class="com.ly.spring6.iocxml.ditest.Emp">
<property name="age" value="50"></property>
<property name="ename" value="jack"></property>
<property name="dept">
<bean id="dept2" class="com.ly.spring6.iocxml.ditest.Dept">
<property name="dname" value="财务部"></property>
</bean>
</property>
</bean>
方式三:级联方式
<!--第三种方式级联赋值-->
<bean id="dept3" class="com.ly.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术研发部"></property>
</bean>
<bean id="emp3" class="com.ly.spring6.iocxml.ditest.Emp">
<property name="ename" value="lans"></property>
<property name="age" value="20"></property>
<property name="dept" ref="dept3"></property>
<property name="dept.dname" value="测试部"></property>
</bean>
4.数组类型赋值
<!--注入数组类型的属性-->
<bean id="dept" class="com.ly.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术部"></property>
</bean>
<bean id="emp" class="com.ly.spring6.iocxml.ditest.Emp">
<property name="ename" value="luck"></property>
<property name="age" value="20"></property>
<!--对象类型-->
<property name="dept" ref="dept"></property>
<!--数组类型的属性-->
<property name="loves">
<array>
<value>吃饭</value>
<value>睡觉</value>
<value>敲代码</value>
</array>
</property>
</bean>
5.List类型属性注入
<bean id="empone" class="com.ly.spring6.iocxml.ditest.Emp">
<property name="ename" value="luck"></property>
<property name="age" value="20"></property>
</bean>
<bean id="emptwo" class="com.ly.spring6.iocxml.ditest.Emp">
<property name="ename" value="lu"></property>
<property name="age" value="22"></property>
</bean>
<bean id="dept" class="com.ly.spring6.iocxml.ditest.Dept">
<property name="dname" value="技术部"></property>
<property name="empList">
<list>
<ref bean="empone"></ref>
<ref bean="emptwo"></ref>
</list>
</property>
</bean>
6.map类型属性注入
<property name="teacherMap">
<map>
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacherone"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref bean="teachertwo"></ref>
</entry>
</map>
</property>
7.引入集合bean注入
<!--
创建对象
注入普通类型属性
使用util:类型定义
在学生bean中引入util:类型定义bean,完成list map类型属性注入
-->
<bean id="student" class="com.ly.spring6.iocxml.dimap.Student">
<property name="sid" value="100"></property>
<property name="sname" value="luck"></property>
<!--注入List和map类型属性-->
<property name="lessonList" ref="lessonList"></property>
<property name="teacherMap" ref="teacherMap"></property>
</bean>
<util:list id="lessonList">
<ref bean="lessonone"></ref>
<ref bean="lessontwo"></ref>
</util:list>
<util:map id="teacherMap">
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacherone"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref bean="teachertwo"></ref>
</entry>
</util:map>
<bean id="lessonone" class="com.ly.spring6.iocxml.dimap.Lesson">
<property name="lessonName" value="java开发"></property>
</bean>
<bean id="lessontwo" class="com.ly.spring6.iocxml.dimap.Lesson">
<property name="lessonName" value="前段开发"></property>
</bean>
<bean id="teacherone" class="com.ly.spring6.iocxml.dimap.Teacher">
<property name="teacherName" value="西门"></property>
<property name="teacherId" value="100"></property>
</bean>
<bean id="teachertwo" class="com.ly.spring6.iocxml.dimap.Teacher">
<property name="teacherName" value="东门"></property>
<property name="teacherId" value="200"></property>
</bean>
8.p命名空间
引入p命名空间
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
引入p命名空间后,可以通过以下方式为bean的各个属性赋值
<bean id="studentp" class="com.ly.spring6.iocxml.dimap.Student"
p:sid="100" p:sname="mary" p:lessonList-ref="lessonList" p:teacherMap-ref="teacherMap">
</bean>
9.引入外部属性文件
1.加入依赖
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
2.创建外部属性文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=ly
3.引入属性文件
<?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">
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
</beans>
注意:在使用 context:property-placeholder 元素加载外包配置文件功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。
4.配置bean
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
5.测试
@Test
public void testDataSource() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-datasource.xml");
DataSource dataSource = ac.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
10.bean的作用域
可进行配置
<!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象 -->
<!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
<bean class="com.ly.spring6.bean.User" scope="prototype"></bean>
11.bean的生命周期
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行
创建bean的后置处理器:
package com.ly.spring6.iocxml.life;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author 刘宇
*/
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
try {
System.out.println("3 bean后置处理器,初始化之前执行");
} catch (Exception e) {
throw new RuntimeException(e);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5 bean后置处理器,初始化之后执行");
return bean;
}
}
<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.ly.spring6.iocxml.life.MyBeanPost"/>
12.FactorBean
FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
13.基于xml自动装配
使用bean标签的autowire属性设置自动装配效果
自动装配方式:byType
类型匹配
byType:根据类型匹配IOC容器中的某个兼容类型的bean,为属性自动赋值
若在IOC中,没有任何一个兼容类型的bean能够为属性赋值,则该属性不装配,即值为默认值null
若在IOC中,有多个兼容类型的bean能够为属性赋值,则抛出异常NoUniqueBeanDefinitionException
<bean id="userController" class="com.ly.spring6.iocxml.auto.controller.UserController" autowire="byType"></bean>
<bean id="userService" class="com.ly.spring6.iocxml.auto.service.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.ly.spring6.iocxml.auto.dao.UserDaoImpl"></bean>
自动装配方式:byName
byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值,名称匹配,名字要一致
<bean id="userController" class="com.ly.spring6.iocxml.auto.controller.UserController" autowire="byName"></bean>
<bean id="userService" class="com.ly.spring6.iocxml.auto.service.UserServiceImpl" autowire="byName"></bean>
<bean id="userDao" class="com.ly.spring6.iocxml.auto.dao.UserDaoImpl"></bean>
基于注解管理bean(*)
从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。
Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。
Spring 通过注解实现自动装配的步骤如下:
1.引入依赖
2.开启组件扫描
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描功能-->
<context:component-scan base-package="com.ly.spring6"></context:component-scan>
</beans>
注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。
情况一:最基本的扫描方式
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描功能-->
<context:component-scan base-package="com.ly.spring6"></context:component-scan>
</beans>
情况二:指定要排除的组件
<context:component-scan base-package="com.atguigu.spring6">
<!-- context:exclude-filter标签:指定排除规则 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:exclude-filter type="assignable" expression="com.ly.spring6.controller.UserController"/>-->
</context:component-scan>
情况三:仅扫描指定组件
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable" expression="com.ly.spring6.controller.UserController"/>-->
</context:component-scan>
3.使用注解定义 Bean
4.依赖注入
实验一:@Autowired注入
单独使用@Autowired注解,默认根据类型装配。【默认是byType】
1.属性注入
//第一种方式 属性注入
@Autowired//根据类型找到对应对象,完成注入
private UserService userService;
@Autowired//根据类型找到对应对象,完成注入
private UserDao userDao;
2.set方法注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
3.构造方法注入
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
4.形参注入
public UserController(@Autowired UserService userService) {
this.userService = userService;
}
5.只有一个构造函数,无需写注解
6.@Autowired注解和@Qualifier注解联合
@Autowired
@Qualifier("userDaoImpl") // 指定bean的名字
private UserDao userDao;
总结
- @Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。
- 当带参数的构造方法只有一个,@Autowired注解可以省略。()
- @Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier注解一起使用。\
实验二:Resource注入
@Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?
@Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
@Autowired注解是Spring框架自己的。
@Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
@Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
@Resource注解用在属性上、setter方法上。
@Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
1.根据名称进行注入
指定name注入
2.不指定名字,根据属性名注入
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao myUserDao;
@Override
public void out() {
myUserDao.print();
System.out.println("Service层执行结束");
}
}
@Repository("myUserDao")
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao层执行结束");
}
}
3.属性名和名字都不一样,会根据类型自动注入
总结:
@Resource注解:默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个
全注解开发
全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件。
package com.ly.spring6.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
● @author 刘宇
*/
@Configuration //配置类
@ComponentScan("com.ly.spring6")
public class SpringConfig {
}
手写ioc
回顾反射
Java
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java
语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。
Java(二)中反射部分
实现Spring的Ioc
实现步骤
AOP-面向切面编程
代理模式
代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类,对象,方法
目标:被代理"套用"了非核心逻辑的类,对象,方法
静态代理
新建一个代理类
public class CalculatorStaticProxy implements Calculator {
// 将被代理的目标对象声明为成员变量
private Calculator target;
public CalculatorStaticProxy(Calculator target) {
this.target = target;
}
@Override
public int add(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
// 通过目标对象来实现核心业务逻辑
int addResult = target.add(i, j);
System.out.println("[日志] add 方法结束了,结果是:" + addResult);
return addResult;
}
}
静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。
动态代理
package com.ly.spring6.aop.example;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* @author 刘宇
*/
public class ProxyFactory {
//目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//返回代理对象
public Object getProxy(){
/**
* newProxyInstance里有三个参数
* 1.ClassLoader:加载动态生成代理类的类加载器
* 2.Class[] interfaces:目标对象中实现的所有接口的Class类型数组
* 3.InvocationHandler:设置代理对象实现目标对象方法的过程
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler=new InvocationHandler(){
//第一个参数:代理对象
//第二个参数:需要重写目标对象的方法
//第三个参数:method方法里面参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
result = method.invoke(target, args);
System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
} finally {
System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
AOP
概述
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
相关术语
基于注解的AOP
步骤
1.引入相关依赖
2.创建目标资源 (1)接口 (2)实现类
3.创建切面类 (1)切入点 (2)通知类型
基于注解
基于xml配置文件
【Spring高手之路19——Spring AOP注解指南 - CSDN App】Spring高手之路19——Spring AOP注解指南_aop注解生效时间-CSDN博客
事务
什么是事务
声明式事务