spring注解
springConfig配置文件中的注解
@Configuration //声明当前类为配置类
@ComponentScan("com.itheima") //组件扫描
@EnableAspectJAutoProxy //启动 AOP功能
@Configuration //添加当前bean到ioc容器
MyAdvice切面中的注解
@Component //IOC配置: 当前bean需要加载到ioc容器中
@Aspect //aop配置 : 声明当前类是切面类 (切面=切入点+通知)
@Pointcut //指定切入点
@Before 编写通知 @AfterReturning 后置通知
@AfterThrowing 异常通知 @After 最终通知
@Around // 环绕通知: 以上四种通知的结合
测试类中的注解
@RunWith(SpringJUnit4ClassRunner.class) //spring整合junit
@ContextConfiguration(classes = xx.class) //加载注解配置类
IOC配置
//IOC配置: 当前bean需要加载到ioc容器中
@Controller //web层
@Repository //dao层
@Service //业务层
@Component //其它(组件)
springMVC中的配置
@RequestMapping("/save") //设置映射路径为/save,即外部访问路径
@ResponseBody //设置当前操作返回结果为字符串
//ResponseBody注解加上类上,表示此类所有方法都有此注解
@RequestParam //同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestBody //在参数前面添加,用于接收json格式参数映射到pojo上
@EnableWebMvc //开启json数据类型自动转换
@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss")
//使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
@PathVariable //会将{id}中的值获取出来映射到对应的参数
@RestController //@RestController = @Controller + @ResponseBody
@RequestMapping("/user") // 提取共同父路径到类上
@GetMapping @PostMapping @PutMapping @DeleteMapping
//简化@RequestMapping的书写
//举例 @RequestMapping(value = "/user",method = RequestMethod.GET)
SpringBoot中的注解
//SpringBoot注解 : 读取配置文件中前缀为enterprise
@ConfigurationProperties(prefix = "enterprise")
@SpringBootTest //设置JUnit加载的SpringBoot启动类
@BeforeAll //测试方法执行之前运行一次,最早运行
@BeforeEach //每个测试方法执行之前运行一次,二早运行
@AfterEach //每个测试方法执行之后运行一次,三早运行
@AfterAll //测试方法执行之后运行一次,四早运行
//推荐 批量加载mapper接口 省略单个接口上的@Mapper
@MapperScan("com.itheima.dao")
配置文件加载顺序
# 按文件类型
properties > yml > yaml
# 按路径分类
当前project/config > 当前project > classpath/config > classpath
# 按命令行设置
jar包外的参数 > jar包内的参数
AOD面向切面编程
概述
切面 = 切入点 + 通知
# 1. 介绍
AOP(Aspect Oriented Programing)面向切面编程,一种编程范式,指导开发者如何组织程序结构
OOP(Object Oriented Programing)面向对象编程
# 2. 作用
在程序运行期间,不修改源码的基础上对已有方法进行增强
(无侵入性: 耦合度低)
# 3. 优势
1). 减少重复代码
2). 提高开发效率
3). 维护方便
# 4. spring的AOP的实现方式
1). 当Bean实现接口时,Spring就会用JDK的动态代理
2). 当Bean没有实现接口时,Spring使用CGLib来实现
JDK8之后, JDK动态代理效率高于CGlib
3). 备注: 开发者可以在spring中强制使用CGLib (了解)
(在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)
大概步骤
TODO:
1. 第一个执行的代码是main方法 -> 运行器中 SpringJUnit4ClassRunner
1). 解析ContextConfiguration注解 : 加载SpringConfig配置类
2. 加载SpringConfig配置类
1). @Configuration : 添加当前bean到ioc容器
2). @ComponentScan("com.itheima") : 组件扫描
com.itheima包下的所有类,spring都知道,注解将可以被识别
3). @PropertySource("classpath:jdbc.properties")
加载外部配置文件
4). @Import(MybatisConfig.class)
将 MybatisConfig放到ioc容器中
5). @Bean方法
创建DruidDataSource连接池对象,放到ioc容器中
3. @ComponentScan("com.itheima") : 组件扫描
1). @Service
AccountServiceImpl类会被创建实例,放到ioc容器中
4. MybatisConfig里中有两个bean放到ioc容器
CGlib
CGlib (Code Generation Library)
0. 也是一个动态代理技术
1. 不基于接口
代理类有无父接口都可以
2. 底层原理是继承
代理类直接继承被代理类,通过重写方法进行增强
3. 动态: 代理类不是静态定义的, 是在运行过程中动态生成的
Spring 的 AOP
0. 思想: 不惊动原始设计前提下, 增强功能
1. 被代理类有父接口, spring底层使用JDK Proxy
2. 被代理类没有接口, spring底层使用 CGlib
JDK8开始, JDK Proxy的效率高于CGlib
CGlib
Code Generation library 代码生成库
1. 导包 : 第三方的库
2. 编写api
切入点表达式
1. 指定切入点
/*
切入点表达式
1. 完整写法:execution(方法的修饰符 方法的返回值 类的全限定名.方法名(参数))
public String com.itheima.service.impl.AccountServiceImpl.findAll()
2. 支持通配符的写法:
1) * 标识任意字符串
2) .. 任意重复次数
3. 规则
1. 方法的修饰符可以省略:
String com.itheima.service.impl.AccountServiceImpl.findAll()
2. 返回值可以使用*号代替:标识任意返回值类型
* com.itheima.service.impl.AccountServiceImpl.findAll()
3. 包名可以使用*号代替,代表任意包(一层包使用一个*)
String com.itheima.*.*.AccountServiceImpl.findAll()
4. .使用.配置包名,标识此包以及此包下的所有子包
String com.itheima..AccountServiceImpl.findAll()
5. 类名可以使用*号代替,标识任意类
String com.itheima..*.findAll()
6. 方法名可以使用*号代替,表示任意方法
String com.itheima..*.*(String)
7. 可以使用..配置参数,任意参数
String com.itheima..*.*(..)
常用:
* com.itheima.service..*(..)
* com.itheima.service..xx(..)
编写通知
. 编写通知
前置通知: 在切入点pt之前,执行xx方法
@Before("pt()")
public void xx(){
System.out.println("111111111111111111");
}
//后置通知: 在切入点之后,执行
@AfterReturning("pt()")
public void afterReturning(){
System.out.println("2222222222222");
}
//异常通知: 在切入点发生异常之后,执行
@AfterThrowing("pt()")
public void afterThrowing(){
System.out.println("333333333333333333");
}
//最终通知: 在切入点执行完毕无论如何最终都会执行的
@After("pt()")
public void after(){
System.out.println("44444444444444444");
}
环绕通知: 以上四种通知的结合
1. 参数 : ProceedingJoinPoint
1). 切入点 : 表达式当前要被增强的方法
2). Object result = pjp.proceed();
切入点执行, 等价于 Object result = method.invoke(目标对象,args);
事务代理小案例
CGlib (Code Generation Library)
0. 也是一个动态代理技术
1. 不基于接口
代理类有无父接口都可以
2. 底层原理是继承
代理类直接继承被代理类,通过重写方法进行增强
3. 动态: 代理类不是静态定义的, 是在运行过程中动态生成的
Spring 的 AOP
0. 思想: 不惊动原始设计前提下, 增强功能
1. 被代理类有父接口, spring底层使用JDK Proxy
2. 被代理类没有接口, spring底层使用 CGlib
JDK8开始, JDK Proxy的效率高于CGlib
CGlib
Code Generation library 代码生成库
1. 导包 : 第三方的库
2. 编写api
代理模式
装饰模式(静态代理)
创建一个代理类对象
装饰模式(静态代理)
I. 操作
1). 创建被代理类对象
2). 创建代理类对象
3). 代理类对象调用方法
II. 公式
1). 角色
a. 代理类
b. 被代理类
c. 共同的父类或父接口
2). 代理类实现被代理类的父接口
3). 代理类中接收被代理类对象(接口类型)
4). 代理类的方法在被代理类的方法基础上进行增强
III. 对比继承
1). 继承只能增强父类UserServiceImpl
2). 而装饰模式的代理类可以增强UserService接口所有实现类
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
br.read();
======================================================================
接口 UserService 中方法 void save();
接口实现类 UserServiceImpl 重写 方法 被代理类
测试类
@Test
public void test02() throws Exception {
// 1). 创建被代理类对象
UserServiceImpl userService = new UserServiceImpl();
// 2). 创建代理类对象
UserServiceProxy proxy = new UserServiceProxy(userService);
// 3). 代理类对象调用方法
proxy.save();
被代理类
package com.itheima.service.impl;
import com.itheima.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void save() {
// System.out.println("开启事务");
System.out.println("UserServiceImpl save...");
// System.out.println("成功提交,失败回滚");
}
@Override
public void insert() {
System.out.println("UserServiceImpl insert...");
}
}
代理类对象
package com.itheima02;
import com.itheima.service.UserService;
// 代理类实现被代理类的父接口implements UserService
public class UserServiceProxy implements UserService {
// 代理类中接收被代理类对象(接口类型)
private UserService userService;
public UserServiceProxy(UserService userService){
this.userService = userService;
}
@Override
public void save() {
// 代理类的方法在被代理类的方法基础上进行增强 事务
System.out.println("开启事务");
userService.save();
System.out.println("成功提交,失败回滚");
}
@Override
public void insert() {
System.out.println("开启事务");
userService.insert();
System.out.println("成功提交,失败回滚");
}
}
打印结果
开启事务
UserServiceImpl save...
成功提交,失败回滚
动态代理
*底层实现一个类去实现接口
I. 操作
1). 创建被代理类对象
2). 创建代理类对象
3). 代理类对象调用方法
II. 公式
1). 角色
a. 代理类
b. 被代理类
c. 共同的父类或父接口
2). 代理类实现被代理类的父接口
3). 代理类中接收被代理类对象(接口类型)
4). 代理类的方法在被代理类的方法基础上进行增强
II. 动态
代理类无需静态定义(写出来),是在运行过程中动态生成 (反射)
III. 对比装饰模式
优势: 代理类无需定义,提高开发效率
反射的作用: 抽取相同规律的代码
=======================================================================测试类
@Test
public void test03(){
// 1). 创建被代理类对象
UserServiceImpl userService = new UserServiceImpl();
// 2). 创建代理类对象
/*
* TODO:
* Proxy.newProxyInstance(loader,interfaces,h);
* 底层原理
* 1. 实现类接口 : 底层会创建一个类实现接口,就会有字节码
* 2. 类加载器 :
* 1). 基本上都是第三方的类,一般是应用类加载器
* 2). 上述的字节码需要类加载器进行加载,产生Class对象
*
* */
Class<?>[] interfaces = userService.getClass().getInterfaces();
// Class<?>[] interfaces = {UserService.class};
ClassLoader loader = userService.getClass().getClassLoader();
// ClassLoader loader = Demo.class.getClassLoader();
InvocationHandler h = new InvocationHandler() {
/*
* 代理类对象调用任何方法,都将执行invoke方法
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行被代理类对象的方法,等价于 userService.xx(args);
System.out.println("开启事务");
Object object = method.invoke(userService,args);
System.out.println("成功提交,失败回滚");
return object;
}
};
UserService proxy = (UserService)Proxy.newProxyInstance(loader,interfaces,h);
// 3). 代理类对象调用方法
proxy.insert();
}
被代理类
public class UserServiceImpl implements UserService {
@Override
public void save() {
// System.out.println("开启事务");
System.out.println("UserServiceImpl save...");
// System.out.println("成功提交,失败回滚");
}
@Override
public void insert() {
System.out.println("UserServiceImpl insert...");
}
}
代理对象 底层创建的 (伪代码 帮助理解)
//模拟动态代理的代理类 !!!
// 代理类实现被代理类的父接口implements UserService
public class JdkProxy implements UserService {
// 代理类中接收被代理类对象(接口类型)
private InvocationHandler h;
public JdkProxy(InvocationHandler h) {
this.h = h;
}
/* @Override
public void save(String name,int age) {
Method save = this.getClass().getMethod("save");
Object[] args = {name,age};
Object object = h.invoke(this,save,args);
return object;
}*/
@Override
public void save() {
try {
Method save = this.getClass().getMethod("save");
Object[] args = null;
h.invoke(this,save,args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void insert() {
// h.invoke();
}