ssm-Spring-全部知识点-胜哥
第一章 初识Spring
1.1 Spring简介
-
Spring是一个为简化企业级开发而生的开源框架
- c c++ c#
-
Spring是一个IOC(DI)和AOP容器框架。
- IOC:Inversion of Control【控制反转】
- DI:Dependency Injection【依赖注入】
- AOP:Aspect-Oriented Programming,面向切面编程
-
Spring官网:spring.io
1.2 Spring之Helloworld
-
导入jar包
<!--导入spring-context--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency> <!--导入junit4.12--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
创建Employee对象
public class Employee { private Integer id; private String lastName; private Integer age; private Double salary; //..... }
-
编写配置文件【beans.xml或applicationContext.xml或spring.xml】
-
配置文件位置:resources下
-
配置文件名称:applicationContext.xml
-
示例代码
<?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"> <!-- Spring管理Employee对象 --> <!-- 将Employee交给Spring管理【将Employee注入到容器中】--> <bean id="empCheng" class="com.atguigu.pojo.Employee"> <property name="id" value="1001"></property> <property name="lastName" value="chengcheng"></property> <property name="age" value="18"></property> <property name="salary" value="10.0"></property> </bean> </beans>
-
-
创建并使用容器对象,获取Employee对象
@Test public void testSpringHello(){ //传统方式获取对象 Employee e = new Employee(); //Spring方式获取对象 //1.先创建容器对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.再从容器中获取需要对象【Employee】 Employee empCheng = (Employee)ioc.getBean("empCheng"); System.out.println("empCheng:"+empCheng); }
1.3 Spring中getBean()三种方式
-
getBean(String beanId)
- 不足:需要强转【不方便】
-
getBean(Class clazz)
-
不足:当容器中有多个相同类型bean时,会报如下错:
expected single matching bean but found 2: empCheng,empJing
-
-
getBean(String beanId,Class clazz):推荐使用
day06
1.4 Spring 特性
- 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API。
- 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期。
- 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的JDBCTemplate)。
- 控制反转:IOC——Inversion of Control,指的是将对象的创建权交给Spring去创建。使用Spring之前,对象的创建都是由我们自己在代码中new创建。而使用Spring之后。对象的创建都是由给了Spring框架。
- 依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用setXXX方法去设置,而是通过配置赋值。
- 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上进行功能扩展。
第二章 Spring之IOC底层实现
- 在获取Bean之前,首先需要创建IOC容器。Spring提供了IOC容器的两种实现方式:
2.1 BeanFactory对象工程
- BeanFactory:IOC容器的基本实现,是Spring内部的使用接口,是面向Spring本身的,不是提供给开发人员使用的。
2.2 ApplicationContext容器对象
-
ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
-
ApplicationContext常用实现类及接口
- BeanFactory
- ....
- ApplicationContext【容器对象根接口】
- ConfigurableApplicationContext【容器对象根接口的子接口,提供了刷新关闭容器对象方法】
- .....
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext【容器对象实现类,一般使用该类创建容器对象】
- .....
- ConfigurableApplicationContext【容器对象根接口的子接口,提供了刷新关闭容器对象方法】
- ApplicationContext【容器对象根接口】
- ....
- BeanFactory
2.3 依赖注入方式【重点】
-
set注入
- 语法:property标签
- name属性:设置属性名
- value属性:设置属性值
- 原理
- 调用setXXX()方法,入参
- 语法:property标签
-
构造注入
- 语法:constructor-arg标签
- name:设置构造器中参数名
- value:设置属性值
- 原理
- 调用有参构造器,入参
- 语法:constructor-arg标签
-
p名称空间注入
-
语法:p:
- 名称空间:xmlns:p="http://www.springframework.org/schema/p"
-
原理
- 调用setXXX()方法,入参
-
第三章 Spring中依赖注入数值【重点】
3.1 字面量数值
- 数据类型:基本数据类型+包装类+String
- 注入语法:可以使用value属性或value标签注入数值
3.2 使用CDATA区,解决XML中特殊字符问题
-
语法:<![CDATA[]]>
-
作用:解决XML中特殊字符问题
-
示例代码
<bean id="empCheng" class="com.atguigu.pojo.Employee"> <property name="id" value="1001"></property> <property name="lastName"> <value><![CDATA[<chengcheng>]]></value> </property> <property name="age" value="18"></property> <property name="salary" value="10.0"></property> </bean>
3.3 引用外部已声明的bean
-
示例代码
<bean id="dept1" class="com.atguigu.pojo.Dept"> <property name="deptId" value="101"></property> <property name="deptName" value="程序员鼓励师"></property> </bean> <bean id="empCheng2" class="com.atguigu.pojo.Employee"> <property name="id" value="1001"></property> <property name="lastName" value="chengcheng"></property> <property name="age" value="18"></property> <property name="salary" value="10.0"></property> <property name="dept" ref="dept1"></property> <property name="dept.deptName" value="秘书部门"></property> </bean>
-
注意:修改员工中部门信息,部门信息也会同时改变
3.4 内部bean
-
概念:在一个bean中,完整定义另一个bean,此时里边的bean称之为内部bean。
-
示例代码
<!-- 测试内部bean--> <bean id="empCheng3" class="com.atguigu.pojo.Employee"> <property name="id" value="10086"></property> <property name="lastName" value="chengcheng3"></property> <property name="age" value="18"></property> <property name="salary" value="10.0"></property> <property name="dept"> <bean class="com.atguigu.pojo.Dept"> <property name="deptId" value="102"></property> <property name="deptName" value="小卖部"></property> </bean> </property> </bean>
3.5 依赖注入数值—List
<bean id="dept2" class="com.atguigu.pojo.Dept">
<property name="deptId" value="102"></property>
<property name="deptName" value="研发部门"></property>
<property name="employees">
<list>
<ref bean="empCheng"></ref>
<ref bean="empJing"></ref>
<bean class="com.atguigu.pojo.Employee">
<property name="id" value="1005"></property>
<property name="lastName" value="鑫哥"></property>
<property name="age" value="18"></property>
<property name="salary" value="20.0"></property>
</bean>
</list>
</property>
</bean>
3.6 依赖注入数值—Map
<bean id="dept3" class="com.atguigu.pojo.Dept">
<property name="deptId" value="103"></property>
<property name="deptName" value="测试部门"></property>
<property name="maps">
<map>
<!-- <entry key="" value=""></entry>-->
<entry>
<key>
<value>1001</value>
</key>
<ref bean="empCheng"></ref>
</entry>
<entry>
<key>
<value>1002</value>
</key>
<ref bean="empJing"></ref>
</entry>
</map>
</property>
</bean>
3.7 依赖注入数值—其他【Prop|Array等】
- 提取可重用List
<util:list id="empList">
<ref bean="empCheng"></ref>
<ref bean="empJing"></ref>
<bean class="com.atguigu.pojo.Employee">
<property name="id" value="1005"></property>
<property name="lastName" value="鑫哥"></property>
<property name="age" value="18"></property>
<property name="salary" value="20.0"></property>
</bean>
</util:list>
<!-- 测试List-->
<bean id="dept2" class="com.atguigu.pojo.Dept">
<property name="deptId" value="102"></property>
<property name="deptName" value="研发部门"></property>
<property name="employees" ref="empList"></property>
</bean>
第四章 Spring管理第三方bean【重点】
-
spring管理德鲁伊数据源步骤
-
导入jar包
<!--导入druid的jar包--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--导入mysql的jar包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency>
-
导入db.properties属性文件
#key=value db.username=root db.password=root db.url=jdbc:mysql://localhost:3306/db1216?时区useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true db.driverClassName=com.mysql.jdbc.Driver
-
使用spring框架管理数据源【将DruidDataSource装配到IOC容器中】
<!-- 引入外部db.properties文件 --> <context:property-placeholder location="classpath:db.properties"></context:property-placeholder> <!-- 管理德鲁伊数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${db.driverClassName}"></property> <property name="url" value="${db.url}"></property> <property name="username" value="${db.username}"></property> <property name="password" value="${db.password}"></property> </bean>
-
第五章 FactoryBean【工厂bean】
5.1 FactoryBean概述
- Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。
- 使用FactoryBean可以帮助我们程序员参与bean的创建中
5.2 使用FactoryBean步骤
- 实现接口:FactoryBean
- 重写接口中方法
5.3 示例代码
package com.atguigu.factory;
import com.atguigu.pojo.Employee;
import org.springframework.beans.factory.FactoryBean;
/**
* @author Chunsheng Zhang 尚硅谷
* @create 2022/3/8 15:33
*/
public class MyFactoryBean implements FactoryBean<Employee> {
/**
* 参与Employee对象创建
* @return
* @throws Exception
*/
@Override
public Employee getObject() throws Exception {
Employee employee = new Employee();
employee.setId(1001);
employee.setLastName("zhaolusi");
employee.setSalary(100.0);
return employee;
}
@Override
public Class<?> getObjectType() {
return Employee.class;
}
}
<?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="employee" class="com.atguigu.factory.MyFactoryBean"></bean>
</beans>
@Test
public void testFactoryBean(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext_factoryBean.xml");
Employee employee = context.getBean("employee", Employee.class);
// MyFactoryBean employee = context.getBean("employee", MyFactoryBean.class);
System.out.println("employee = " + employee);
}
第六章 Spring中Bean作用域【重点】
6.1 Bean的四个作用域
- singleton:单例,当前工程中只有一个对象
- 创建容器对象时,spring创建bean对象
- prototype:多例,当前工程中可以有多个对象
- 每次调用getBean()方法时,spring创建bean对象
- request:当前请求有效,离开当前请求失效
- 当前请求:发送请求后,URL不变即为当前请求;URL改变不在当前请求
- session:当前会话有效,离开当前会话失效
- 当前会话:会话指的是浏览器与服务器通信状态,称之为浏览器与服务器会话
- 关闭浏览器或更换浏览器即为结束会话
6.2 设置bean作用域语法
- 在bean标签中添加属性:scope
第七章 Spring中bean的生命周期【重点】
Servlet生命周期
- 默认情况
- 第一次请求Servlet时,服务器创建Servlet对象,并调用init()方法进行初始化,最后调用service()方法处理请求,做出响应。以后再次请求Servlet,只调用service(request,response)处理请求,....。当关闭或重启服务器时,调用destroy()方法,销毁Servlet对象
- 配置情况
7.1 Bean的生命周期
① 通过构造器或工厂方法创建bean实例
② 为bean的属性设置值和对其他bean的引用
③ 调用bean的初始化方法
④ bean可以使用了
⑤ 当容器关闭时,调用bean的销毁方法
7.2 Bean的生命周期示例代码
public class Dept {
//.....
/**
* Dept初始化方法
*/
public void initDept(){
System.out.println("==>3. 初始化方法,initDept()");
}
/**
* Dept销毁方法
*/
public void destroyDept(){
System.out.println("==>5. 销毁方法,destroyDept()");
}
}
<bean id="dept001" class="com.atguigu.pojo.Dept"
init-method="initDept"
destroy-method="destroyDept">
<property name="deptId" value="001"></property>
</bean>
@Test
public void testBeanLc(){
//创建容器对象
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext_beanLifeCycle.xml");
Dept dept001 = context.getBean("dept001", Dept.class);
System.out.println("4. 使用dept001 = " + dept001);
//关闭容器对象
context.close();
}
7.3 Bean的后置处理器
-
创建后置处理器实现类,实现接口:BeanPostProcessor
-
重写接口中两个方法
- postProcessBeforeInitialization:在bean初始化之前执行
- postProcessAfterInitialization:在bean初始化之后执行
-
将实现类装配IOC容器中
-
示例代码
package com.atguigu.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.lang.Nullable; /** * @author Chunsheng Zhang 尚硅谷 * @create 2022/3/8 16:36 */ public class MyPostProcessor implements BeanPostProcessor { /** * 在bean初始化之前执行 * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("**************初始化之前!!!!!"); return bean; } /** * 在bean初始化之后执行 * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("**************初始化之后!!!!!"); return bean; } }
<bean class="com.atguigu.processor.MyPostProcessor"></bean>
7.4 装配后置处理器,bean的生命周期
7.4 添加后置处理器,bean的生命周期
① 通过构造器或工厂方法创建bean实例
② 为bean的属性设置值和对其他bean的引用
- 将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
③ 调用bean的初始化方法
- 将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
④ bean可以使用了
⑤ 当容器关闭时,调用bean的销毁方法
day07
第八章 Spring中自动装配【XML】
8.1 装配方式
- 手动装配:使用value或ref方式为属性注入数值
- 自动装配:无需value或ref方式为属性注入数值,直接在bean标签中添加属性【autowire】即可
8.2 自动装配
-
语法:在bean标签中添加属性【autowire】
-
特点:自动装配只能为非字面量值实现自动装配
-
装配方式
- byName:按照属性名称,与IOC容器中id进行匹配
- byType:按照属性类型,与IOC容器中class进行匹配
-
底层逻辑:使用setXXX()注入方式,实现自动装配
-
总结:自动装配建议使用哪种方式
- 不建议直接使用byName或byType
- byName需要手动设置名称一致
- byType方式不足:在IOC容器中如装配多个类型相同bean对象时,会报错
- 推荐使用注解方式
- 不建议直接使用byName或byType
第九章 Spring中常用注解【重点】
约定>注解>配置>代码
使用注解步骤
- 添加注解支持【jar已经导入了】
- 开启组件扫描
- 使用相关注解
9.1 注解_管理对象【装配对象】
-
位置:在class上面
-
常用注解
- @Component:装配普通组件
- @Repository:装配持久化层组件
- @Service:装配业务逻辑层组件
- @Controller:装配表示层【控制层】组件
-
注意
- 默认类名首字母小写作为bean的id
- 可以使用value属性,标识bean的id
- 注解中如只使用一个value属性时,value关键字可省略
- 上述四个注解本质都是@Component注解,分为四个注解目的只有一个:提高代码可读性
9.2 注解_管理对象中属性【装配对象中属性】
-
常用注解
-
@Autowired
-
作用:为对象中属性自动装配
-
装配原理:反射原理
-
装配方式
-
先按照byType进行匹配
-
匹配0个,【默认情况(required=true)】会报如下错误
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
-
匹配多个,再按照byName进行唯一筛选
- 筛选出一个对象,正常使用
- 未筛选出对象,会报如下错
expected single matching bean but found 2: employeeDao,employeeMapper2
- 匹配1个,正常使用
-
-
-
属性:required,默认值true
- true:表示被@Autowired注解标识的属性,必须装配数值,如未装配数值,会报错
- false:表示被@Autowired注解标识的属性,不必须装配数值,IOC容器中有相应数值则装配,容器中没有相应数值,则不装配【不会报装配错误】
-
-
@Qualifier("employeeDao")【@Autowired兄弟】
- 作用:为需要装配的属性,设置bean的id
-
@Value():为字面量属性装配数值【初始值】
-
第十章 完全注解研发【0配置文件】
-
编写配置类
package com.atguigu.annotation.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author Chunsheng Zhang 尚硅谷 * @create 2022/3/9 14:28 */ @Configuration @ComponentScan(basePackages = "com.atguigu.annotation") public class SpringConfig { }
-
使用容器对象AnnotationConfigApplicationContext
@Test public void testNoXml(){ //创建容器对象 ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class); EmployeeService employeeService = ioc.getBean("employeeService", EmployeeService.class); employeeService.getAllEmps(); }
第十一章 Spring整合Junit4
-
导入相关jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.1</version> </dependency>
-
在测试类上面添加两个注解
@ContextConfiguration(locations = "classpath:applicationContext_annotation.xml") @RunWith(SpringJUnit4ClassRunner.class) public class TestAnnotation { @Test public void testAnnotation2(){ System.out.println("@@@@"+employeeService); employeeService.getAllEmps(); } }
第十二章 Spring中组件扫描
12.1 语法
<context:component-scan base-package="com.atguigu.annotation"></context:component-scan>
- 作用:扫描base-pachage指定的包及其子包下所有注解,被注解标识的类,会将其对象装配到IOC容器中
12.2 包含扫描
- context:include-filter
- type
- annotation:设置包含扫描的,注解的全类名
- assignable:设置包含扫描的,实现类的全类名
<!-- 设置包含扫描包-->
<context:component-scan base-package="com.atguigu.annotation" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<!-- <context:include-filter type="assignable" expression="com.atguigu.annotation.mapper.EmployeeMapperImpl"/>-->
</context:component-scan>
12.3 排除扫描
- context:exclude-filter
- type
- annotation:设置排除扫描的,注解的全类名
- assignable:设置排除扫描的,实现类的全类名
<!-- 假设annotation中一共有100个包,排除扫描mapper包-->
<context:component-scan base-package="com.atguigu.annotation">
<!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>-->
<context:exclude-filter type="assignable" expression="com.atguigu.annotation.service.impl.EmployeeServiceImpl"/>
</context:component-scan>
-
注意:0配置文件,会影响排除扫描【注解>配置】
//@Configuration //@ComponentScan(basePackages = "com.atguigu.annotation") public class SpringConfig { }
第十三章 扩展动态代理模式
13.1 代理模式
-
概述:程序中需要,但不期望在此处实现。此时使用【中介】代理对象实现。
-
底层原理
day08
13.2 为什么使用代理模式
- 以计算器添加日志功能为例,发现如下问题:
- 代码分散【日志功能】
- 代码混乱【业务代码与非业务代码相耦合】
- 解决思路
- 将非核心业务代码【日志功能】,先横向提取,再动态织入到业务代码
- 动态织入:需要动态代理
13.3 手动搭建动态代理模式
- 实现方式
- 基于接口实现动态代理: JDK动态代理
- 基于继承实现动态代理: Cglib、Javassist动态代理
- 实现JDK动态代理关键步骤
- 一个类:Proxy
- 概述:Proxy代理类的基类【类似Object】
- 方法:newProxyInstance():创建代理对象
- 一个接口:InvocationHandler
- 概述:实现动态代理关键【实现动态织入效果】
- 方法:invoke():实现动态织入
- 一个类:Proxy
13.4 手动搭建动态代理步骤及代码
-
创建类【工具类:为实现类创建代理对象】
-
提供属性【目标对象:实现类】
-
提供方法【创建代理对象】
-
提供有参构造器
package com.atguigu.aopbefore; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author Chunsheng Zhang 尚硅谷 * @create 2022/3/11 9:29 */ public class MyProxy { /** * 提供属性【目标对象:实现类】 */ private Object target; /** * 提供有参构造器 */ public MyProxy(Object target){ this.target = target; } /** * 提供方法【创建代理对象】 */ public Object getProxyObject(){ Object proxyObj = null; //类加载器 ClassLoader classLoader = target.getClass().getClassLoader(); //目标对象实现接口Class Class<?>[] interfaces = target.getClass().getInterfaces(); //创建代理对象 proxyObj = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取方法名称 String methodName = method.getName(); //执行方法前日志 MyLogging.methodBefore(methodName,args); //执行目标对象的相应方法【加减乘除】 Object rs = method.invoke(target, args); //执行方法后日志 MyLogging.methodAfter(methodName,rs); return rs; } }); return proxyObj; } }
-
注意:代理对象不能转换为目标对象【代理对象与目标对象是兄弟关系,不能相互转换】
第十四章 Spring之AOP【重点】
14.1 使用AOP框架AspectJ
-
概述:
- Java社区里最完整最流行的AOP框架。
- 在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。
-
使用步骤
-
导入jar包
<!--spirng-aspects的jar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.1</version> </dependency>
-
开启AspectJ注解支持
<!--配置自动扫描的包--> <context:component-scan base-package="com.atguigu.aop"></context:component-scan> <!--开启AspectJ注解支持--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
创建切面类【非核心业务代码提取类】
@Component //将当前类的对象装配到IOC容器中 @Aspect //标识当前类的对象是一个【切面类】 public class MyLogging { /** * 计算之前 */ @Before("execution(public int com.atguigu.aop.CalcImpl.add(int,int))") public void methodBefore(JoinPoint joinPoint){ //获取方法名 String methodName = joinPoint.getSignature().getName(); //获取参数 Object[] args = joinPoint.getArgs(); System.out.println("计算器日志==>"+methodName+"运算,参数:"+ Arrays.toString(args)); } }
-
14.2 AOP概述
- AOP(Aspect-Oriented Programming,面向切面编程)
- 是一种通过动态代理实现程序功能扩展和统一维护的一种技术。
- 横向扩展机制
- 优势:解决代码分散及代码混乱问题
- OOP(Object-Oriented Programming,面向对象编程)
- 纵向继承机制
14.3 AOP相关术语
-
横切关注点:非核心业务称之为横切关注点【日志功能】
-
切面(Aspect):将非核心业务提取到一个类中,这个类称之为切面类【MyLogging切面类】
-
通知(Advice):非核心业务提取到切面后,称之为通知【切面中的日志功能】
-
目标(Target):被通知的对象【CalcImpl】
-
代理(Proxy):向目标对象应用通知之后创建的对象,代理对象如下:
//手动搭建动态代理方式 Calc proxyObject = (Calc)myProxy.getProxyObject(); //使用AOP框架AspectJ Calc calc = ioc.getBean("calcImpl", Calc.class);
-
连接点(Joinpoint):被通知位置,称之为连接点。【通知之前】
-
切入点(pointcut):被通知位置,称之为切入点。【通知之后】
14.4 AspectJ之切入点表达式
-
语法
定义:execution(访问修饰符 返回值类型 方法名全路径(参数列表类型) )
execution(public int com.atguigu.aop.CalcImpl.add(int,int))
-
通配符
【*】
-
可以代表任意【访问修饰符 返回值类型 】
-
可以代表任意包名、类名、方法名
【..】
- 可以代表任意参数数据类型及数量
-
-
重用切入点表达式
@Pointcut("execution(* com.atguigu.aop.*.*(..))") public void myPointCut(){} @Before("myPointCut()") public void methodBefore(JoinPoint joinPoint){}
14.5 JoinPoint对象【连接点对象|切入点对象】
- joinPoint.getArgs():获取参数
- joinPoint.getSignature():获取方法签名
- 方法签名:方法名+参数列表
- JoinPoint子接口:ProceedingJoinPoint对象
- proceedingJoinPoint.proceed():触发目标对象的相应方法
14.6 AspectJ中5种通知
通知作用:将非核心业务代码【以何种形式】动态织入到核心业务代码中
-
前置通知
-
语法:@Before
-
注意:在业务代码执行之前执行,即使核心业务代码报错也会执行
@Pointcut("execution(* com.atguigu.aop.*.*(..))") public void myPointCut(){} /** * 前置通知 */ @Before("myPointCut()") public void methodBefore(JoinPoint joinPoint){ //获取方法名 String methodName = joinPoint.getSignature().getName(); //获取参数 Object[] args = joinPoint.getArgs(); System.out.println("计算器日志==>"+methodName+"运算,参数:"+ Arrays.toString(args)); }
-
-
后置通知
-
语法:@After
-
注意:在业务代码执行之后执行,即使核心业务代码报错也会执行
/** * 后置通知 */ @After("myPointCut()") public void methodAfter(JoinPoint joinPoint){ //获取方法名 String methodName = joinPoint.getSignature().getName(); System.out.println("计算器日志==>"+methodName+"运算,参数:"+Arrays.toString(args)); }
-
-
返回通知
-
语法:@AfterReturning
-
作用:在业务方法返回结果后执行
-
注意:
- 触发到return之后执行【正常:报错不会执行返回通知】
- 要求:returning属性值与接收结果参数名一致
@AfterReturning(value = "myPointCut()",returning = "rs") public void methodAfterReturning(JoinPoint joinPoint,Object rs){ //获取方法名 String methodName = joinPoint.getSignature().getName(); System.out.println("返回通知,计算器日志==>"+methodName+"运算,结果:"+rs); }
-
-
异常通知
-
语法:@AfterThrowing
-
作用:在业务代码中出现异常时执行
-
注意:要求throwing属性值与接收异常参数名一致
/** * 异常通知 * @param joinPoint * @param ex */ @AfterThrowing(value = "myPointCut()",throwing = "ex") public void methodAfterThrowing(JoinPoint joinPoint,Exception ex){ //获取方法名 String methodName = joinPoint.getSignature().getName(); System.out.println("异常通知,计算器日志==>"+methodName+"运算,异常:"+ex); }
-
-
环绕通知
-
语法:@Around
-
作用:环绕通知,相当于所有通知整合【前置通知、后置通知、返回通知、异常通知】
-
注意:
-
入参使用ProceedingJoinPoint对象
-
必须设置环绕通知方法的返回值,如未设置或未接收返回值,会报错:
Null return value from advice does not match primitive return type for: public abstract int com.atguigu.aop.Calc.add(int,int)
@Around("myPointCut()") public Object methodAround(ProceedingJoinPoint pjp){ //获取方法名 String methodName = pjp.getSignature().getName(); //获取参数 Object[] args = pjp.getArgs(); //返回值 Object rs = null; try { //前置通知 后置通知 返回通知 异常通知 System.out.println("前置通知,计算器日志==>"+methodName+"运算,参数:"+ Arrays.toString(args)); //执行目标对象的相应方法【加减乘除】 rs = pjp.proceed(); //返回通知 System.out.println("返回通知,计算器日志==>"+methodName+"运算,结果:"+rs); } catch (Throwable throwable) { throwable.printStackTrace(); //异常通知 System.out.println("异常通知,计算器日志==>"+methodName+"运算,异常:"+throwable); } finally { //后置通知 System.out.println("后置通知,计算器日志==>"+methodName+"运算,参数:"+Arrays.toString(args)); } return rs; }
-
-
14.7 AspectJ之切面优先级
-
语法:@Order(value=index)
- index是int类型,默认值:int可存储最大值
- 一般使用正整数,数值越小优先级越高
-
示例代码
@Component //标识普通组件 @Aspect //标识切面类 @Order(2) public class MyLogging {} @Component @Aspect @Order(1) public class MyValidate {}
14.8 AspectJ之XML配置方式
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置计算器实现类-->
<bean id="calculator" class="com.atguigu.spring.aop.xml.CalculatorImpl"></bean>
<!--配置切面类-->
<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>
<!--AOP配置-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pointCut"
expression="execution(* com.atguigu.spring.aop.xml.Calculator.*(..))"/>
<!--配置切面-->
<aop:aspect ref="loggingAspect">
<!--前置通知-->
<aop:before method="beforeAdvice" pointcut-ref="pointCut"></aop:before>
<!--返回通知-->
<aop:after-returning method="returningAdvice" pointcut-ref="pointCut" returning="result"></aop:after-returning>
<!--异常通知-->
<aop:after-throwing method="throwingAdvice" pointcut-ref="pointCut" throwing="e"></aop:after-throwing>
<!--后置通知-->
<aop:after method="afterAdvice" pointcut-ref="pointCut"></aop:after>
<!--环绕通知-->
<aop:around method="aroundAdvice" pointcut-ref="pointCut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
第十五章 Spring之JdbcTemplate
15.1 JdbcTemplate概述
- JdbcTemplate是一个小型的轻量级持久化层框架
15.2 搭建JdbcTemplate环境
-
导入相关jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring-version}</version> </dependency> <!--导入druid的jar包--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid-version}</version> </dependency> <!--导入mysql的jar包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-version}</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit-version}</version> <scope>test</scope> </dependency>
-
将JdbcTemplate装配到IOC容器中
<!-- 加载外部属性文件--> <context:property-placeholder location="classpath:db.properties"></context:property-placeholder> <!-- 装配DataSource--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${db.driverClassName}"></property> <property name="url" value="${db.url}"></property> <property name="username" value="${db.username}"></property> <property name="password" value="${db.password}"></property> </bean> <!-- 将JdbcTemplate装配到IOC容器中--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
-
使用JdbcTemplate相关API
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext_jdbcTemplate.xml"); JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); System.out.println("jdbcTemplate = " + jdbcTemplate); //添加部门信息 String sql = "INSERT INTO tbl_dept(dept_name) VALUES(?)"; jdbcTemplate.update(sql,"董事部门2");
day09
15.3 JdbcTemplate常用API
- 通用增删改
- update(String sql,Object... param);
- 通用批量增删改
- batchUpdate(String sql,List<Object[]>);
- 通用查询单个值
- queryForObject(String sql,Class clazz,Object... param);
- 通用查询单个对象
- queryForObject(String sql,RowMapper
mapper, Object... param);
- queryForObject(String sql,RowMapper
- 通用查询多个对象
- query(String sql,RowMapper
mapper,Object... param);
- query(String sql,RowMapper
15.4 使用JdbcTemplate封装Dao层
<!-- 将JdbcTemplate装配到IOC容器中-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
@Repository("employeeDao")
public class EmployeeDaoImpl implements EmployeeDao {
@Autowired
@Qualifier("jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Override
public List<Employee> selectAllEmps() {
String sql = "select id,last_name,email,salary from tbl_employee";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
return jdbcTemplate.query(sql,rowMapper);
}
}
@Test
public void testDao(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext_jdbcTemplate.xml");
EmployeeDao employeeDao = context.getBean("employeeDao", EmployeeDaoImpl.class);
System.out.println("***************");
for (Employee employee : employeeDao.selectAllEmps()) {
System.out.println("employee = " + employee);
}
}
第十六章 Spring 中事务管理
事务回顾
- 事务四大特性
- 原子性
- 一致性
- 隔离性
- 持久性
- 事务三种操作
- 开启事务:connection.setAutoCommit(false);
- 提交事务:connection.commit();
- 回滚事务:connection.rollback();
16.1 Spring中支持两种事务管理
-
编程式事务管理【使用原生的JDBC API进行事务管理的步骤:】
-
获取数据库连接Connection对象
-
取消事务的自动提交
-
执行操作【核心业务】
-
正常完成操作时手动提交事务
-
执行失败时回滚事务
-
关闭相关资源
- 不足:事务管理代码【非核心业务代码】与核心业务代码相耦合。【代码分散、代码混乱】
- 解决方案:使用AOP思想解决事务管理问题【先将事务管理代码横向提取,再动态织入回到业务代码中】
-
-
声明式事务管理:使用AOP解决方案解决事务管理方式。
16.2 Spring中声明式事务管理使用步骤
-
添加AspectJ支持
<!--spring-aspects--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring-version}</version> </dependency>
-
配置声明式事务管理
- 配置事务管理器
- 开启声明式事务管理
<!-- 将事务管理器装配IOC容器中--> <!-- <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">--> <!-- <property name="dataSource" ref="dataSource"></property>--> <!-- </bean>--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启声明式事务管理--> <!-- <tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>--> <tx:annotation-driven></tx:annotation-driven>
-
使用@Transactional为需要业务代码中添加事务管理
@Transactional public void purchase(String username, String isbn) { //查询book价格 Integer price = bookShopDao.findBookPriceByIsbn(isbn); //修改库存 bookShopDao.updateBookStock(isbn); //修改余额 bookShopDao.updateUserAccount(username, price); }
16.3 Spring声明式事务管理相关属性
-
事务传播行为【Propagation】
-
概述:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
- 如:执行method1()方法【x事务】后,调用method2()【y事务】,此时需要设置method2()方法事务的传播行为。
-
7种传播行为
传播属性 描述 REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行;否则就启动一个新的事务,并在自己的事务内运行。 REQUIRES_NEW 当前的方法*必须*启动新事务,并在自己的事务内运行;如果有事务正在运行,应该将它挂起。 SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则可以不运行在事务中。 NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务将它挂起 MANDATORY 当前的方法必须运行在事务中,如果没有正在运行的事务就抛出异常。 NEVER 当前的方法不应该运行在事务中,如果有正在运行的事务就抛出异常。 NESTED 如果有事务正在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行。 -
图解事务传播行为
-
REQUIRED
- 事务执行机制:只有一个Tx1事务
-
REQUIRES_NEW
- 事务执行机制:事务1-2-1-3-1
-
-
使用场景
/* 需求1:去结账之前判断余额,如余额不足:所有图书不让购买 @Transactional(propagation = Propagation.REQUIRED) 需求2:去结账之前判断余额,如余额不足:最后导致余额不足那本书不让购买 @Transactional(propagation = Propagation.REQUIRES_NEW) */
-
-
事务隔离级别【Isolation】
-
隔离级别概述:事务与事务之间隔离等级【1,2,4,8】
-
隔离等级
-
读未提交【1】:READ UNCOMMITTED
- 存在问题,可能出现脏读现象:可读到未提交数据
-
读已提交【2】:READ COMMITTED
- 存在问题,可能出现不可重复度现象
-
可重复读【4】:REPEATABLE READ
-
串行化【8】:SERIALIZABLE
-
-
图解
-
-
事务超时:timeout
-
事务只读:readonly
- 一般在查询中使用事务只读属性,可以提高一点性能
-
事务回滚:
- rollbackFor:设置回滚的异常Class
- noRollbackFor:设置不回滚的异常Class
16.4 基于XML方式配置声明式事务管理
<?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"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:druid.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="initialSize" value="${jdbc.initialSize}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
</bean>
<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--配置数据源属性-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置BookShopDaoImpl-->
<bean id="bookShopDao" class="com.atguigu.spring.tx.xml.BookShopDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!--配置BookShopServiceImpl-->
<bean id="bookShopService" class="com.atguigu.spring.tx.xml.BookShopServiceImpl">
<property name="bookShopDao" ref="bookShopDao"></property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--配置数据源属性-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置声明式事务-->
<tx:advice id="tx" transaction-manager="transactionManager">
<!--设置添加事务的方法-->
<tx:attributes>
<!--设置查询的方法的只读属性为true-->
<tx:method name="find*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="purchase" propagation="REQUIRES_NEW" isolation="READ_COMMITTED"></tx:method>
</tx:attributes>
</tx:advice>
<!--AOP配置-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pointCut"
expression="execution(* com.atguigu.spring.tx.xml.BookShopServiceImpl.purchase(..))"/>
<!--将事务方法和切入点表达式关联起来-->
<aop:advisor advice-ref="tx" pointcut-ref="pointCut"></aop:advisor>
</aop:config>
</beans>
第十七章 Spring5新特性
17.1 新添加一些注解
名称 | 含义 | 可标注位置 |
---|---|---|
@Nullable | 可以为空 | @Target({ElementType.*METHOD*, ElementType.*PARAMETER*, ElementType.*FIELD*} |
@NonNull | 不应为空 | @Target({ElementType.*METHOD*, ElementType.*PARAMETER*, ElementType.*FIELD*}) |
@NonNullFields | 在特定包下的字段不应为空 | @Target(ElementType.*PACKAGE*) @TypeQualifierDefault(ElementType.*FIELD*) |
@NonNullApi | 参数和方法返回值不应为空 | @Target(ElementType.*PACKAGE*) @TypeQualifierDefault({ElementType.*METHOD*, ElementType.*PARAMETER*}) |
-
以@Nullable注解为例
@Nullable 注解可以使用在方法上面,属性上面,参数前面,表示方法返回可以为空,属性值可以为空,参数值可以为空。
此注解通常用来消除NullPointerException
17.2 Spring5整合Log4j2
-
导入jar包
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.11.2</version> <scope>test</scope> </dependency>
-
编写配置文件【log4j2.xml】
<?xml version="1.0" encoding="UTF-8"?> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出--> <configuration status="INFO"> <!--先定义所有的appender--> <appenders> <!--输出日志信息到控制台--> <console name="Console" target="SYSTEM_OUT"> <!--控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出--> <loggers> <root level="DEBUG"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
17.3 Spring5整合Junit5
-
导入jar包【注意:将junit4jar包删除】
<!--spring-test--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.1</version> </dependency> <!--junit5--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.7.2</version> <scope>test</scope> </dependency>
-
使用测试类
/** * @author Chunsheng Zhang 尚硅谷 * @create 2022/3/12 16:26 */ //@ContextConfiguration(locations = "classpath:applicationContext_transaction.xml") //@ExtendWith(SpringExtension.class) @SpringJUnitConfig(locations = "classpath:applicationContext_transaction.xml") public class TestSpringAndJunit5 { @Autowired(required = true) @Qualifier("cashierService") private CashierService cashierService; @Autowired private BookShopService bookShopService; @Test public void testJunit5(){ System.out.println("cashierService = " + cashierService); System.out.println("bookShopService = " + bookShopService); } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)