Spring
大约 19 分钟
是企业开发复杂性的一站式解决方案
- 核心是 Ioc 容器 与 AOP 面向切面编程
- Spring Ioc 负责创建与管理系统对象,并在此基础上扩展功能
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
整合 Junit4
Maven 工程依赖
spring-test
利用
@RunWith
和@ContextConfiguration
描述测试用例类@RunWith
:将 Junit 的运行过程交给 Spring 来完成@ContextConfiguration
:说明在初始化 IoC 容器时要加载哪个配置文件
测试用例类从容器获取对象完成曾测试用例执行
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
import com.imooc.spring.ioc.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
//将Junit4的执行权交由Spring Test,在测试用例执行前自动初始化IoC容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringTestor {
@Resource
private UserService userService;
@Test
public void testUserService(){
userService.createUser();
}
}
组成模块
![](https://cdn.jsdelivr.net/gh/sunzhenyang/blog-img/img/20210816144242.png)
Spring IoC 容器
IoC 容器是 Spring 生态的地基,用于统一创建与管理对象依赖
- 对象的控制权交由 第三方 统一管理(IOC 控制反转)
- 控制反转全称
Inverse of Control
,是一种设计理念,是现代程序设计遵循的标准,是宏观目标 - 通过加入 Ioc 容器 将对象统一管理,让对象关联变为弱耦合
- 控制反转全称
- 利用 Java 反射 技术,实现运行时对象创建与关联(DI 依赖注入)
- 依赖注入全称
Dependency Injection
,是具体语言技术实现,是微观实现
- 依赖注入全称
- 基于配置提高应用程序的可维护性与扩展性
![](https://cdn.jsdelivr.net/gh/sunzhenyang/blog-img/img/20210815230430.png)
基于 XML 配置 Bean
基于构造方法实例化对象
<bean>
标签默认通过默认构造方法创建对象id
与name
属性相同点- 都是在设置对象在 IoC 容器中唯一标识
- 两者在同一配置文件中都不允许出现重复
- 两者允许在多个配置文件中出现重复,新对象覆盖旧对象
- 注意
- 两者命名要求有意义,按驼峰命名书写
- 在没有
id
和name
的情况下,默认使用类名全称作为bean
标识
id
与name
属性不同点- (推荐)
id
要求更严格,一次只能定义一个对象标识 name
要求宽松,一次允许定义多个对象标识
- (推荐)
<bean>
标签内部添加<contract-arg>
标签使用带参构造方法实例化对象
<!-- 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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="sweetApple" class="com.spring.ioc.entity.Apple">
<constract-arg name="title" value="红富士"></constract-arg>
<constract-arg name="color" value="红色"></constract-arg>
<constract-arg name="origin" value="欧洲"></constract-arg>
</bean>
<bean id="luna" class="com.spring.ioc.entity.Child">
<constract-arg name="name" value="露娜"/>
<constract-arg name="apple" ref="sweetApple"/>
</bean>
</beans>
// SpringApplication.java
package com.spring.ioc;
import com.spring.ioc.entity.Child;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
Child lily = context.getBean("luna", Child.class);
lily.eat();
// 销毁
((ClassPathXmlApplication) context).registerShutdownHook();
}
}
// Apple.java
package com.spring.ioc.entity;
public class Apple {
private String title;
private String color;
private String origin;
public Apple() {
}
public Apple(String title, String color, String origin) {
this.title = title;
this.color = color;
this.origin = origin;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
}
// Child.java
package com.spring.ioc.entity;
public class Child {
private String name;
private Apple apple;
public Child(){}
public Child(String name, Apple apple) {
this.name = name;
this.apple = apple;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Apple getApple() {
return apple;
}
public void setApple(Apple apple) {
this.apple = apple;
}
public void eat(){
System.out.println(name + "吃到了" + apple.getOrigin() + "种植的" + apple.getTitle());
}
}
基于静态工厂实例化对象
<!-- 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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="sweetApple" class="com.spring.ioc.factory.AppleStaticFactory" factory-method="createSweetApple"></bean>
</beans>
// AppleStaticFactory.java
package com.spring.ioc.factory;
import com.spring.ioc.entity.Apple;
public class AppleStaticFactory {
public static Apple createSweetApple() {
Apple apple = new Apple();
apple.setTitle("红富士");
apple.setOrigin("欧洲");
apple.setColor("红色");
return apple;
}
}
// SpringApplication.java
package com.spring.ioc;
import com.spring.ioc.entity.Apple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
Apple sweetApple = context.getBean("sweetApple", Apple.class);
System.out.println(sweetApple.getTitle());
}
}
基于工厂实例方法实例化对象
IoC 容器对工厂类进行实例化并调用对应的实例方法创建对象的过程
<!-- 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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="factoryInstance" class="com.spring.ioc.factory.AppleFactoryInstance"></bean>
<bean id="apple" factory-bean="factoryInstance" factory-method="createSweetApple"></bean>
</beans>
// SpringApplication.java
package com.spring.ioc.factory;
import com.spring.ioc.entity.Apple;
public class AppleFactoryInstance {
public Apple createSweetApple() {
Apple apple = new Apple();
apple.setTitle("红富士");
apple.setOrigin("欧洲");
apple.setColor("红色");
return apple;
}
}
// AppleStaticFactory.java
package com.spring.ioc;
import com.spring.ioc.entity.Apple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
Apple apple = context.getBean("apple", Apple.class);
System.out.println(apple.getTitle());
}
}
对象依赖注入
指运行时将容器内对象利用 反射 赋给其他对象的操作
基于 setter 方法注入对象
<bean id="sweetApple" class="com.spring.ioc.entity.Apple">
<property name="title" value="红富士"></property>
<property name="color" value="红色"></property>
<property name="origin" value="欧洲"></property>
</bean>
<bean id="luna" class="com.imooc.spring.ioc.entity.Child">
<property name="name" value="露娜"/>
<property name="apple" ref="sweetApple"/>
</bean>
基于构造方法注入对象
<bean id="sweetApple" class="com.spring.ioc.entity.Apple">
<property name="title" value="红富士"></property>
<property name="color" value="红色"></property>
<property name="origin" value="欧洲"></property>
</bean>
<bean id="luna" class="com.imooc.spring.ioc.entity.Child">
<constract-arg name="name" value="露娜"/>
<constract-arg name="apple" ref="sweetApple"/>
</bean>
注入集合对象
<!-- 注入 List -->
<bean id="..." class="...">
<property name="someList">
<list>
<value>具体指</value>
<ref bean="beanld"></ref>
</list>
</property>
</bean>
<!-- 注入 Set -->
<bean id="..." class="...">
<property name="someSet">
<set>
<value>具体指</value>
<ref bean="beanld"></ref>
</set>
</property>
</bean>
<!-- 注入 Map -->
<bean id="..." class="...">
<property name="someMap">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value-ref="beanld"></entry>
</map>
</property>
</bean>
<!-- 注入 Properties -->
<bean id="..." class="...">
<property name="someProperties">
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
</props>
</property>
</bean>
获取容器内对象
//获取容器内所有beanId数组ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");String[] beanNames = context.getBeanDefinitionNames();for (String beanName:beanNames){ System.out.println(beanName); System.out.println("类型:" + context.getBean(beanName).getClass().getName()); System.out.println("内容:" + context.getBean(beanName));}
路径表达式
表达式实例 | 说明 |
---|---|
classpath:config.xml | 扫描 classpath 根路径(不包含 jar)的 config.xml |
classpath:com/imooc/config.xml | 扫描 classpath 下(不包含 jar) com.imooc 包中的 config. xml |
classpath*:com/imooc/config.xml | 扫描 classpath 下(包含 jar) com.mooc 包中的 config. xml |
classpath:config-*.xml | 扫描 classpath 根路径下所有以 config- 开头的 XML 文件 |
classpath:com/**/config.xml | 扫描 com 包下(包含任何子包)的 config. xml |
file:c:/config.xml | 扫描 C 盘根路径 config.xm |
Scope
用于决定对象何时被创建与作用范围
- 将影响容器内对象的数量
<bean id="..." class="..." scope="singleton">
属性 | 说明 |
---|---|
singleton | 单例 (默认值),每一个容器有且只有唯一的实例,实例被全局共享(单例多线程执行,存在线程风险安全) |
prototype | 多例,每次使用时都是创建一个实例(占用更多资源,但不存在线程安全问题) |
request | web 环境下,每一次独立请求存在唯一实例 |
session | web 环境下,每一个 session 存在有唯一实例 |
application | web 环境下,ServletContext 存在唯一实例 |
websocket | 每一次 WebSocket 连接中存在唯一实例 |
--- | singleton | prototype |
---|---|---|
对象数量 | 全局唯一 | 存在多个 |
实例化时机 | IoC 容器启动时 | getBean() 或 对象注入时 |
线程安全问题 | 存在 | 不存在 |
执行效率 | 高 | 低 |
bean 生命周期
![](https://cdn.jsdelivr.net/gh/sunzhenyang/blog-img/img/20210817142629.png)
基于注解配置 Bean
优势
- 摆脱繁琐的 XML 形式的 bean 与依赖注入配置
- 基于 声明式 的原则,更适合轻量级的现代企业应用
- 让代码可读性变得更好,研发人员拥有更好的开发体验
分类
组件类型注解
声明当前类的功能与职责
注解 | 说明 |
---|---|
@Component | 组件注解,通用注解,被该注解描述的类将被 IoC 容器管理并实例化 |
@Controller | 语义注解,说明当前类是 MVC 应用的控制器类 |
@Service | 语义注解,说明当前类是 Service 业务服务类 |
@Repository | 语义注解,说明当前类用于业务持久层,通常描述对应的 Dao 类,bean id 默认为首字母小写的类名,也可通过 @Repository(BeanId) 设置 |
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--通知Spring IoC容器初始化时加载属性文件-->
<context:property-placeholder location="classpath:config.properties"/>
<!-- 在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
@Repository
@Service
@Controller
@Component
-->
<!-- XML 配置开启组件扫描,才能使用注解 -->
<context:component-scan base-package="com.imooc">
<!-- 排除:使一些文件不被扫描 -->
<context:exclude-filter type="regex" expression="com.imooc.exl.*" />
</context:component-scan>
</beans>
# config.properties
metaData=imooc.com
connection.driver=xxxxx
connection.url=xxx
connection.username=xxx
connection.password=xxx
自动装配注解
根据属性特征自动注入对象
- Spring IoC 容器会自动通过反射技术将属性
private
修饰符修改为public
直接进行赋值 - 如果装配注解在属性上将不执行 set 方法
- 如果装配注解在 set 方法上,则自动按类型/名称对 set 方法参数进行注入
@Primary
:如果在 IoC 容器中出现多个相同类型的对象,则默认采取@Primary
注解所描述的对象完成注入@Resource
- 设置 name 属性,则按 name 在 IoC 容器中对 bean 注入
- 未设置 name 属性
- 以属性名作为 bean name 在 IoC 容器中匹配 bean ,如果有匹配则注入
- 未匹配到,则按类型进行匹配,需要加入
@Primary
解决类型冲突
- 使用建议:在使用时推荐设置 name 或保证属性名与 bean 名称一致
@Resource(name="userDao")
分类 | 注解 | 说明 |
---|---|---|
按类型装配 | @Autowired | 按容器内对象类型动态注入属性,由 Spring 机构提供 |
@Inject | 基于 JSR-330(Dependency Injection for Java) 标准,其他同 @Autowired ,但不支持 required 属性 | |
按名称装配 | @Named | 与 @Inject 配合使用,JSR-330 规范,暗属性名自动装配属性 |
@Resource | 基于 JSR-250 规范,优先按名称,再按类型只能匹配 |
元数据注解
更细化的辅助 IoC 容器管理对象的注解
注解 | 说明 |
---|---|
@Primary | 按类型装配时出现多个相同类型对象,拥有此注解对象优先被注入 |
@PostConstruct | 描述方法,相当于 XML 中 init- method 配置的注解版本 |
@PreDestory | 描述方法,相当于 XML 中 destroy- method 配置的注解版本 |
@Scope | 设置对象的 scope 属性 |
@Value | 为属性注入静态数据(可以以 @Value("${metaData}") )的形式访问配置文件加载的 config.properties 文件内的属性 |
基于 Java 代码配置 Bean
- 完全摆脱 XML 的束缚,使用独立的 Java 类管理对象与依赖
- 注解配置相对分散,利用 Java Config 可对配置集中管理
- 可以在编译时进行依赖检查,不容易出错
注解 | 说明 |
---|---|
@Configuration | 描述类,说明当前类是 Java Config 配置类,完全替代 ⅩML 文件 |
@Bean | 描述方法,方法返回对象将被 loC 容器管理,beanId 默认为方法名 |
@ImportResource | 描述类,加载静态文件,可使用 @ Value 注解获取 |
@ComponentScan | 描述类,同 XML 的 < context: compoment-scan> 标签 |
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.controller.UserController;
import com.imooc.spring.ioc.dao.EmployeeDao;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.annotation.*;
@Configuration //当前类是一个配置类,用于替代applicationContext.xml
@ComponentScan(basePackages = "com.imooc")
public class Config {
@Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
public UserDao userDao(){
UserDao userDao = new UserDao();
System.out.println("已创建" + userDao);
return userDao;
}
@Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
@Primary
public UserDao userDao1(){
UserDao userDao = new UserDao();
System.out.println("已创建" + userDao);
return userDao;
}
@Bean
//先按name尝试注入,name不存在则按类型注入
public UserService userService(UserDao udao , EmployeeDao employeeDao){
UserService userService = new UserService();
System.out.println("已创建" + userService);
userService.setUserDao(udao);
System.out.println("调用setUserDao:" + udao);
userService.setEmployeeDao(employeeDao);
return userService;
}
@Bean //<bean id="xxx" clas="xxx">
@Scope("prototype")
public UserController userController(UserService userService){
UserController userController = new UserController();
System.out.println("已创建" + userController);
userController.setUserService(userService);
System.out.println("调用setUserService:" + userService);
return userController;
}
}
package com.imooc.spring.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
//基于Java Config配置IoC容器的初始化
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
System.out.println("=========================");
String[] ids = context.getBeanDefinitionNames();
for(String id : ids){
System.out.println(id + ":" + context.getBean(id));
}
}
}
Spring AOP
面向切面编程:Aspect Oriented Programming
将通用的、与业务无关的功能抽象封装为切面类
在不修改源码的情况下,对程序行为进行扩展
Spring AOP 与 AspectJ 的关系
- Eclipse AspectJ 是一种基于 Java 平台的面向切面编程语言
- Spring AOP 使用 AspectJWeaver 实现类与方法匹配
- Spring AOP 利用 代理模式 实现对象运行时功能扩展
<!-- aspectjweaver 是 Spring AOP 的底层依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- AOP配置 -->
<bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect"></bean>
<aop:config>
<!--
PointCut 切点,使用execution表达式描述切面的作用范围
execution(public * com.imooc..*.*(..)) 说明切面作用在com.imooc包下的所有类的所有方法上
<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut>
-->
<!--只对所有Service类生效-->
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"></aop:pointcut>
<!-- 定义切面类 -->
<aop:aspect ref="methodAspect">
<!--
before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime()
-->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
<aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
<aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
<aop:after method="doAfter" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
package com.imooc.spring.aop.aspect;
import org.aspectj.lang.JoinPoint;
import java.text.SimpleDateFormat;
import java.util.Date;
//切面类
public class MethodAspect {
//切面方法,用于扩展额外功能
//JoinPoint 连接点,通过连接点可以获取目标类/方法的信息
public void printExecutionTime(JoinPoint joinPoint){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
String className = joinPoint.getTarget().getClass().getName();//获取目标类的名称
String methodName = joinPoint.getSignature().getName();//获取目标方法名称
System.out.println("---->" + now + ":" + className + "." + methodName);
Object[] args = joinPoint.getArgs();
System.out.println("---->参数个数:" + args.length);
for(Object arg:args){
System.out.println("---->参数:" + arg);
}
}
public void doAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("<----返回后通知:" + ret);
}
public void doAfterThrowing(JoinPoint joinPoint,Throwable th){
System.out.println("<----异常通知:" + th.getMessage());
}
public void doAfter(JoinPoint joinPoint){
System.out.println("<----触发后置通知");
}
}
注解
注解 | 说明 |
---|---|
@Aspect | 切面,具体的可插拔组件功能类,通常一个切面只实现一个通用功能 |
@Target Class/Method | 目标类、目标方法,指真正要执行的与业务相关对的方法 |
@PointCut | 切入点,使用 execution 表达式说明切面要作用在系统的那些类上 |
@JoinPoint | 连接点,切面运行过程中是包含了 目标类/方法 元数据的对象 |
@Adivce | 通知,说明具体的切面的执行时机,Spring 包含了五种不同类型通知 |
JoinPoint
(返回类型)方法 | 说明 |
---|---|
Object getTarget() | 获取 IoC 容器内目标对象 |
Signature getSignature() | 获取目标方法 |
Object[] getArgs() | 获取目标方法参数 |
PointCut
![](https://cdn.jsdelivr.net/gh/sunzhenyang/blog-img/img/20210818152005.png)
<aop:pointcut expression="execution(* com.imooc..*Service.*(..))" />
<!--只对无返回方法生效-->
<aop:pointcut expression="execution(String com.imooc..*Service.*(..))" />
<!--只对所有返回值为String类型方法生效-->
<aop:pointcut expression="execution(String com.imooc..*Service.*(..))" />
<!--对方法名进行约束 -->
<aop:pointcut expression="execution(* com.imooc..*Service.create*(..))" />
<!-- 对参数进行约束 -->
<aop:pointcut expression="execution(* com.imooc..*Service.*(String,*))" />
public
可以去掉
Advice
通知类型 | 说明 |
---|---|
Before Advice | 前置通知,目标方法运行前执行 |
After Returning Advice | 返回后通知,目标方法返回数据后执行,可以接收目标方法的返回值 |
After Throwing Advice | 异常通知,目标方法抛出异常后执行(无论成功与否都会通知) |
After Advice | 后置通知,目标方法运行后执行(无论成功与否都会通知) |
Around Advice | 最强大通知,自定义通知执行时机,可决定目标方法是否运行 |
后置通知、返回后通知、异常通知
的执行顺序由配置的先后顺序决定- 特殊的“通知” - 引介增强
- 是对类的增强,而非方法
- 允许在运行时为目标类增加新属性或方法
- 允许在运行时改变类的行为,让类随运行环境动态变更
<bean id="methodChecker" class="com.imooc.spring.aop.aspect.MethodChecker"></bean>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*.*(..))"></aop:pointcut>
<aop:aspect ref="methodChecker">
<!--环绕通知-->
<aop:around method="check" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
public class MethodChecker {
//ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
long startTime = new Date().getTime();
Object ret = pjp.proceed();// 执行目标方法
long endTime = new Date().getTime();
long duration = endTime - startTime; // 执行时长
if(duration >= 1000){
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
}
return ret;
} catch (Throwable throwable) {
System.out.println("Exception message:" + throwable.getMessage());
throw throwable;
}
}
}
基于注解
注解 | 说明 |
---|---|
@Around | |
@Before | |
@After | |
@AfterThrowing | |
@AfterReturning |
<?xml version="1.0" encoding="UTF-8"?>
<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="http://www.springframework.org/schema/beans"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--初始化IoC容器-->
<context:component-scan base-package="com.imooc"/>
<!--启用Spring AOP注解模式-->
<aop:aspectj-autoproxy/>
</beans>
package com.imooc.spring.aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component //标记当前类为组件
@Aspect //说明当前类是切面类
public class MethodChecker {
//环绕通知,参数为PointCut切点表达式
@Around("execution(* com.imooc..*Service.*(..))")
//ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
long startTime = new Date().getTime();
Object ret = pjp.proceed();//执行目标方法
long endTime = new Date().getTime();
long duration = endTime - startTime; //执行时长
if(duration >= 1000){
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
}
return ret;
} catch (Throwable throwable) {
System.out.println("Exception message:" + throwable.getMessage());
throw throwable;
}
}
}
实现原理
基于 代理模式 实现功能扩展,包含两种形式
- 目标类拥有接口:通过 JDK 动态代理实现功能扩展
- 目标类没有接口:通过 CGLib 组件实现功能扩展
代理模式
通过代理对象对原对象实现功能扩展
Spring JDBC
是 Spring 框架用于处理关系型数据库的模块
- 对 JDBC API 进行封装,极大简化开发工作量
JdbcTemplate
是 Spring JDBC 核心类,提供数据 CRUD 方法
使用步骤
- Maven 工程引入
spring-jdbc
applicationContext.xml
配置DataSource
数据源- 在
Dao
注入JdbcTemplate
对象,实现数据 CRUD
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--JdbcTemplate提供数据CRUD的API-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="employeeDao" class="com.imooc.spring.jdbc.dao.EmployeeDao">
<!--为Dao注入JdbcTemplate对象-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="employeeService" class="com.imooc.spring.jdbc.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
// EmployeeDao,java
package com.imooc.spring.jdbc.dao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
import java.util.Map;
public class EmployeeDao {
private JdbcTemplate jdbcTemplate;
/**
* jdbcTemplate
* 方法:
* queryForObject 将查询结果中单条记录转换为对应的对象进行返回
* query 将查询结果中多条记录每一条转换为对应的实体对象放入 List 进行返回
* queryForList 返回 Map 完成对数据的封装
* update 泛指所有写入操作(添加、删除、修改)
* 参数
* sql SQL语句
* new Object[]{参数1,参数2,...}
* new BeanPropertyRowMapper<Employee>(Employee.class) 可选,返回数据的类
*/
public Employee findById(Integer eno){
String sql = "select * from employee where eno = ?";
//查询单条数据
Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
return employee;
}
public List<Employee> findByDname(String dname){
String sql = "select * from employee where dname = ?";
//查询复合数据
List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class));
return list;
}
public List<Map<String, Object>> findMapByDname(String dname){
String sql = "select eno as empno , salary as s from employee where dname = ?";
//将查询结果作为Map进行封装
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
return maps;
}
public void insert(Employee employee){
String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
//利用update方法实现数据写入操作
jdbcTemplate.update(sql,new Object[]{
employee.getEno() , employee.getEname(),employee.getSalary(),employee.getDname() , employee.getHiredate()
});
}
public int update(Employee employee){
String sql = "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno = ?";
int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()});
return count;
}
public int delete(Integer eno){
String sql = "delete from employee where eno = ?";
return jdbcTemplate.update(sql, new Object[]{eno});
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
// EmployeeService.java
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.util.Date;
public class EmployeeService {
private EmployeeDao employeeDao;
private DataSourceTransactionManager transactionManager;
public void batchImport(){
//定义了事务默认的标准配置
TransactionDefinition definition = new DefaultTransactionDefinition();
//开始一个事务,返回事务状态,事务状态说明当前事务的执行阶段
TransactionStatus status = transactionManager.getTransaction(definition);
try {
for (int i = 1; i <= 10; i++) {
/*if (i == 3) {
throw new RuntimeException("意料之外的异常");
}*/
Employee employee = new Employee();
employee.setEno(8000 + i);
employee.setEname("员工" + i);
employee.setSalary(4000f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
//提交事务
transactionManager.commit(status);
}catch (RuntimeException e){
//回滚事务
transactionManager.rollback(status);
throw e;
}
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
public DataSourceTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(DataSourceTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
}
// SpringApplication.java
package com.imooc.spring.jdbc;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
EmployeeDao employeeDao = context.getBean("employeeDao", EmployeeDao.class);
Employee employee = employeeDao.findById(3308);
System.out.println(employee);
}
}
声明式事务
- 配置 TransactionManager 事务管理器
- 配置事务通知与事务属性
- 为事务绑定 PointCut 切点
<!-- 1.事务管理器,用于创建事务/提交/回滚 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.事务通知配置,决定哪些方法使用事务,哪些方法不使用事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚 -->
<tx:method name="batchImport" propagation="REQUIRED"/>
<tx:method name="batch*" propagation="REQUIRED"/>
<!-- 设置所有findXXX方法不需要使用事务 -->
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="importJob1" propagation="REQUIRES_NEW"/>
<tx:method name="importJob2" propagation="REQUIRES_NEW"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--3. 定义声明式事务的作用范围-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
Spring MVC
是 Spring 体系的轻量级 Web MVC 框架
核心是 Controller 控制器,用于处理请求产生响应
基于 Spring IOC 容器运行,所有对象被 IOC 管理
Model
:模型View
:视图Controller
:控制器
环境配置
- Maven 依赖
spring-mvc
- web.xml 配置
Dispatcherservlet
- 配置
applicationcontext
的 mvc 标记 - 开发 Controller 控制器
<!-- pom.xml -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--DispatchServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<!--
DispatcherServlet是Spring MVC最核心的对象
DispatcherServlet用于拦截Http请求,
并根据请求的URL调用与之对应的Controller方法,来完成Http请求的处理
-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--applicationContext.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--
在Web应用启动时自动创建Spring IOC容器,
并初始化DispatcherServlet
-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--"/" 代表拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>characterFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>