文章目录
一、配置类
在用@Autowired、@Resource、@Component及其衍生注解进行依赖注入之后,配置文件中就只配置了注解扫描器和命名空间,那么此时写这个配置文件,就显得有点多余
我们使用一个Java类作为配置类来代替这个配置文件
1、@Configuration注解配置类
用于设定当前类是配置类,代替了一堆命名空间和配置信息
2、@ComponentScan扫描包
代替了注解扫描器,扫描多个包的话参数传递数组格式
二、依赖注入
1、注入类
@Component衍生出@Controller、@Service、@Repository
2、给类对象注入属性
2.1 引用数据类型
- @Autowired:自动装配按类型
- @Qualifier与@Autowired配合:按类型,再按名称
- @Resource:精准,可按类型名称同时查
2.2 基本数据类型
@Value:可以看出来赋值int类型,即使带双引号也会自动判断
三、配置Bean
1、设置单例
@Repository
@Scope("singleton") //设置单例
public class BookServiceImp implements BookService {
}
2、设置初始、销毁方法
@PostConstruct
public void init(){
System.out.println("init...");
}
@PreDestroy
public void destory(){
System.out.println("destory...");
}
四、注入第三方Bean
比如要使用druid,无法给人家源代码中去写注解标注接口/类
我们要使用独立的配置类来管理第三方Bean
1、注入步骤
- 定义一个方法返回要管理的对象
- 将返回值定义成这个Bean:@Bean
- 将第三方Bean的配置导入到Spring配置类
@Configuration
@ComponentScan("com.jz")
@PropertySource("classpath:jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfig {
}
2、方法内要使用引用数据类型
直接在方法参数声明,方法内直接用就行
3、加载外部properties文件
上面的例子@Value中的值是写死的,想要从外部文件中加载
- 给spring的配置类上加注解
@PropertySource("classpath:jdbc.properties")
- 在@Value中直接用占位符使用即可
@Value("${jdbc.driverClassName}")
外部文件:
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/yunbook
jdbc.username=root
jdbc.password=123456
五、Aop
1、使用步骤
1.1 导入坐标
可以看到,导入spring-context之后,aop就包含在内了
除此之外,还需要导入aspectj的包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
1.2 启动自动代理
给配置类加上@EnableAspectJAutoProxy
,用于启用基于注解的自动代理功能
1.3 创建通知类,编写通知方法
- 注入容器:
@Component
- 告知Spring容器,这个类是切面,让容器自动代理这个类:
@Aspect
1.4 @Pointcut在通知类中定义切入点
切入点就是空方法体、无返回值的方法,在方法上通过@Pointcut注解指定切入点表达式,描述在哪个类/方法执行通知方法
1.5 描述切面,绑定通知与切入点之间的关系
在切入点之前还是之后执行通知,也就是切面。参数指定切入点方法名
2、AOP工作流程
- spring容器启动
- 读取切面中绑定了通知的切入点
- 初始化bean,匹配切入点:匹配失败,直接创建这个类的对象;匹配成功,创建这个类的代理对象。代理对象可以拦截调用方法,进行操作
- 获取bean/代理bean,执行方法/代理对象增强方法
3、切入点表达式
@PointCut(execution(访问修饰符 返回值 包名.类名/接口名.方法名(参数) 异常))
访问修饰符和异常可以省略,描述切入点通常到接口而不是具体的类
3.1 切入点表达式使用通配符
*
:匹配一个任意符号。
execution(public * com.jz.*.BookService.findBy*(*))
匹配public修饰的,返回值任意,findBy开头的,必须有一个参数的方法
..
:匹配多个任意符号。
execution(public Book com..BookService.findById(..))
匹配public修饰的,返回值是Book类型的,com包下的任意包下的,BookService接口的,任意个参数的findById方法
+
:匹配子类类型
execution(* *..*Service+.*(..))
匹配任意返回值类型的,任意包下的以Service结尾的类的子类的任意参数的方法
例题
匹配这个项目的所有方法:execution(* *..*(..))
4、通知类型
通知描述了抽取的共性内容,根据功能的不同,要加入到不同的位置
4.1 前置通知
@Before
4.2 后置通知
@After
4.3 环绕通知(重点)
@Around
环绕通知要在通知内调用原方法操作,在调用原方法前后增强
既然原方法在通知内由代理调用,也要将方法的返回值返回出去,环绕通知的返回值Object
4.4 返回后通知(了解)
@AfterReturning:方法成功运行完之后才执行通知。如果原方法内部发生异常,这个通知就不会执行;但是后置通知可以执行。
4.5 抛出异常后通知(了解)
@AfterThrowing:原方法内发生了异常才会执行通知
5、通知中获取原方法的数据
如果参数有JoinPoint或者ProeedJointPoint,那么一定是参数表的第一个参数
5.1 获取切入点方法参数:Object[ ] getArgs()
将这两个类对象传进通知的参数表,通知内用他们对象调用getArgs()方法,返回一个Object数组存储原方法参数。
JoinPoint
:除了环绕通知都用这个
ProceedJointPoint
:用于环绕通知
注意:环绕通知可以在通知内调用原方法,那么就可以获取这个参数检查其语法规范,不对的话对其修改再传进原方法执行。可以做自动类型转换这种操作。
5.2 获取切入点方法返回值
返回后通知和环绕通知可以获取
- 环绕通知:ProceedJoinPoint的对象调用proceed方法,执行原方法,返回值就是原方法的返回值
- 返回后通知:在返回后通知的参数表传参Object对象,并在它的注解指定returning为这个对象,就可以在通知内直接获取这个返回值对象。
@AfterReturning(value = "PointCut()", returning = "ret")
public void methodAfterReturn(Object ret){
System.out.println("原方法的返回值是:"+ ret);
System.out.println("图书保存完毕");
}
5.3 获取切入点方法异常信息
抛出异常后通知和环绕通知
- 环绕通知:直接捕获
- 抛出异常后通知:
@AfterThrowing(value = "PointCut()", throwing = "throwable")
public void methodAfterThrow(Throwable throwable){
System.out.println("异常是"+throwable);
System.out.println("抛出了异常,快去处理吧");
}
六、Spring事务
1、注入事务管理器
直接在jdbc配置类中注入事务管理器,并为事务管理器设置数据源
public class JdbcConfig {
@Value("${jdbc.driverClassName}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
2、告诉spring容器使用注解形式做事务管理
@Configuration
@ComponentScan("com")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class})
//启用注解形式做事务管理
@EnableTransactionManagement
public class SpringConfig {
}
3、给相应的业务上添加注解启用事务管理
可以加在类上也可以加在接口上
七、SpringMVC
1、简单使用
1.1 导入spring-webmvc,javax.servlet-api两个依赖
1.2 创建SpringMVC的配置类,扫描控制器所在包
1.3 创建servlet容器的配置类
用于初始化servlet容器,继承抽象类AbstractAnnotationConfigDispatcherServletInitializer,重写三个方法,分别为其加载springmvc的容器、spring容器、设置哪些请求归springmvc处理
代替了传统的web.xml
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
1.4 创建控制器类(等同Servlet的功能)
2、请求
2.1 @RequestMapping设置请求路径
既是方法注解,也是类注解。
定义在类上面:设置当前类中所有请求处理方法的路径前缀
定义在方法上面:设置当前请求处理方法的具体路径
类注解的应用场景:在开发中,不可避免,可能与其他程序员设置的方法名一样,这样访问的时候就会冲突出错
解决方法:在请求路径前加上自己负责模块的名字作为请求路径的前缀
2.1.1 请求路径前缀
2.1.2 post请求中文乱码
解决:在servlet配置类中,设置过滤器
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//乱码处理,通过过滤器
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
return new Filter[]{filter};
}
}
2.1.3 强制请求方式
前端要想访问这个控制方法,就只能使用post请求
@RequestMapping(value = "/checkUser.do", method = RequestMethod.POST)
2.2 @RequestParam绑定请求参数
用于处理表单参数和get方式url后面的查询参数
- 设置接收的参数名,以及是否必须
@RequestMapping("/del")
public String del(@RequestParam(required = false, name = "userId") String id)
对于这些方法参数:普通类型(基本数据类型)、pojo、复杂的pojo、数组
如果请求参数名与方法参数名保持一致,不需要进行以上设计,就不需要写@RequestParam - 绑定集合参数
如果方法参数是集合,前面必须加@RequestParam绑定
请求:http://localhost:8080/listParam?hobby=pingpang&hobby=swim
接收:参数前加@RequestParam绑定
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> hobby){}
2.3 @RequestBody获取请求体中JSON的数据
用于处理非表单数据,通常是json。
如果传递的是json数组,参数传递集合接收;传递的是json对象,参数用Java对象接收。
- 导入坐标:用于JSON和Java对象之间的转换
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
- @EnableWebMvc:给SpringMVC的配置类加上该注解,开启SpringMVC将json数据转为Java对象的功能
- 参数前加@RequestBody
@RequestMapping("/jsonParam")
@ResponseBody
public String jsonParam(@RequestBody 参数){}
2.4 @DateTimeFormat指定参数日期格式
请求:
http://localhost:8080/Springmvc02_war_exploded/param/date?date=2024-07-15 18:25:27
接收:
@RequestMapping("/date")
@ResponseBody
public Date dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")Date date){}
3、响应:@ResponseBody
- @ResponseBody:将返回值序列化为json字符串设置到响应体中,不能转为json字符串就直接作为文本数据响应
- 既是类注解也是方法注解,类注解表示对所有方法有效
- 没有添加@ResponseBody,就会跳转与返回值同名的资源
3.1 返回值中文乱码问题
返回json格式字符串中文可能会乱码,在@RequestMapping注解中设置属性produces:
@RequestMapping(value = "/toJsonPOJO", produces = "application/json;charset=utf-8")
4、REST风格
4.1 REST简介
url表现形式状态转换,支持更多的请求方式,比如put、delete
4.2 优点
传统风格:
http://localhost/user/del?id=1
http://localhost/user/save
rest风格:
http://localhost/users/1
http://localhost/users
- 隐藏资源的访问行为,无法通过地址得知在进行什么操作
- 简洁明了
4.3 REST行为动作
使用请求方式和路径区分对资源进行哪种操作,并且描述模块的行为名要用复数
http://localhost/users、http://localhost/users/1
:
get请求方式是查询:带参数id就是查指定的,不带参数就是查全部
delete请求删除指定用户
post请求添加用户信息
put请求修改用户信息
4.4 路径参数@PathVariable
怎么接收rest风格路径的请求参数?
- @RequestMapping中的参数用{ }括起来占位
- 方法参数前使用注解@PathVariable说明参数来自路径
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@ResponseBody
public User selectOne(@PathVariable Integer id){}
4.5 简化rest
- @ResponseBody做类注解
- @Controller+@ResponseBody=@RestController
- @RequestMapping做类注解提出访问路径前缀
- 将@RequestMapping设置路径和请求方式合一
5、三个接收请求参数的注解区别
@RequestParam:通常用于处理表单参数和查询参数
@RequestBody:通常用于处理非表单数据,接收解析转换json字符串
@PathVariable:用于接收路径参数,提取rest请求中的参数变量
6、表现层响应数据封装
如果后端接口的返回值不统一,那么前端不知道用什么来接收,也比较麻烦
所以在项目中一般都会封装一个返回值类,以确保所有接口都返回固定的格式。应该写在controller包下,因为是表现层的代码
6.1 结果集封装
6.2 状态码封装
6.3 控制层使用
7、异常处理器
可以集中、统一地处理异常
7.1 异常分类
- 业务异常:用户是否规范操作
- 系统异常:项目工作中可预计且无法避免的异常
- 其他异常:未预期到的异常
7.2 异常如何处理
异常要分类处理,在表现层处理,用aop思想处理
7.2.1 自定义项目的系统异常和业务异常
都继承RuntimeExecption,不需要在方法后面throws声明,这两步就是为了做异常分类。
异常类内需要异常码,继续写在Code类中。
7.2.2 在可能触发异常的地方抛出自定义异常
7.2.3 创建异常处理类集中处理异常
两个注解
@RestControllerAdvice/@ControllerAdvice
:类注解声明这个类是做异常处理的,前者用于rest请求的控制器@ExceptionHandler(自定义异常类的字节码文件)
:方法注解,用于声明这个方法处理那种类型的异常,括号内传异常类的字节码文件。
八、SSM整合
Spring、SpringMVC、MyBatis整合在一起工作
1、提前准备工作
- 五个配置类:springconfig、springmvcconfig、servletconfig、jdbcconfig(事务)、mybatisconfig
- 注意pom文件内的jar版本要对应,直接用提供的
- 一个数据源配置文件:jdbc.properties
- 注意修改几个配置文件内的扫描包路径
- 为业务层开启事务