1.Spring介绍

官网

  • Spring是一个 IOC(DI) 和 AOP 框架
  • Spring有很多优良特性
    • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
    • 依赖注入:DI(Dependency Injection)是反转控制(IOC)最经典的实现
    • 面向切面编程:Aspect Oriented Programming - AOP
    • 容器:Spring是一个容器,包含并管理应用对象的生命周期
    • 组件化:Spring通过将众多简单的组件配置组合成一个复杂应用。
    • 一站式:Spring提供了一系列框架,解决了应用开发中的众多问题
  • 主要模块
    • Core(核心):IoC容器、事件、资源、国际化、数据校验、数据绑定、类型转换、SpEL、AOP、AOT
    • Testing(测试):对象模拟、测试框架、SpringMVC测试、WebTestClient
    • Data Access(数据访问):事务、DAO 支持、JDBC、R2DBC、对象关系映射、XML转换
    • Web Servlet(Servlet式Web):SpringMVC、WebSocket、SockJS、STOMP 消息

2.Spring-容器

2.1组件和容器

组件:具有一定功能的对象
容器:管理组件(创建、获取、保存、销毁)

2.2IOC和DI

  • IoC:Inversion of Control(控制反转)
    • 控制:资源的控制权(资源的创建、获取、销毁等)
    • 反转:和传统的方式不一样了
  • DI:Dependency Injection(依赖注入)
    • 依赖:组件的依赖关系,如 NewsController 依赖 NewsServices
    • 注入:通过setter方法、构造器、等方式自动的注入(赋值)

2.3容器注册

实验一:@Bean-把组件放到容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootApplication
public class Spring01IocApplication {

public static void main(String[] args) {
//1、跑起一个Spring的应用; ApplicationContext:Spring应用上下文对象; IoC容器
ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);
System.out.println("ioc = " + ioc);

System.out.println("=============================");
//2、获取到容器中所有组件的名字;容器中装了哪些组件; Spring启动会有很多默认组件
String[] names = ioc.getBeanDefinitionNames();
for (String name : names) {
System.out.println("name = " + name);
//3.给容器中注入一个自己的组件
@Bean("组件名字--默认方法名")
public Person person(){
Person person = new Person
return person;
}
}
实验二:从容器中获取组件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@SpringBootApplication
public class Spring01IocApplication {
public static void test01BeanAnnotation(String[] args) {
ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);

//获取容器中的组件对象;精确获取某个组件
// 组件的四大特性:(名字、类型)、对象、作用域
// 组件名字全局唯一;组件名重复了,一定只会给容器中放一个最先声明的哪个。

//小结:
//从容器中获取组件,
// 1)、组件不存在,抛异常:NoSuchBeanDefinitionException
// 2)、组件不唯一,
// 按照类型只要一个:抛异常:NoUniqueBeanDefinitionException
// 按照名字只要一个:精确获取到指定对象
// 按照类型获取多个:返回所有组件的集合(Map)
// 3)、组件唯一存在,正确返回。

//1、按照组件的名字获取对象
Object zhangsan = ioc.getBean("zhangsan");
Person zhangsan = (Person) ioc.getBean("zhangsan");
System.out.println("对象 = " + zhangsan);

//2、按照组件类型获取对象
Person bean = ioc.getBean(Person.class);
System.out.println("bean = " + bean);

//3、按照组件类型获取这种类型的所有对象
Map<String, Person> type = ioc.getBeansOfType(Person.class);
System.out.println("type = " + type);

//4、按照类型+名字
Person bean = ioc.getBean("zhangsan", Person.class);
System.out.println("bean = " + bean);
}
}
实验三:容器创建时机及单例特性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@SpringBootApplication
public class Spring01IocApplication {
public static void main(String[] args) {
//创建时机:容器启动过程中就会创建组件对象
//单实例特性:所有组件默认是单例的,每次获取直接从容器中拿。容器提前会创建组件
ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);
System.out.println("=================ioc容器创建完成===================");

//2、获取组件
Dog bean = ioc.getBean(Dog.class);
System.out.println("bean = " + bean);

Dog bean1 = ioc.getBean(Dog.class);
System.out.println("bean1 = " + bean1);

Dog bean2 = ioc.getBean(Dog.class);
System.out.println("bean2 = " + bean2);
}
}

运行结果:
Dog构造器
ioc容器创建完成
bean = @xxx123
bean1 = @xxx123
bean2 = @xxx123

实验四:@configuration配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//组件:框架的底层配置;
//配置文件:指定配置
//配置类:分类管理组件的配置,配置类也是容器中的一种组件。
@Configuration //告诉Spring容器,这是一个配置类
public class PersonConfig {

@Bean("zhangsan")
public Person haha() {
Person person = new Person();
person.setName("张三2");
person.setAge(20);
person.setGender("男");
return person;
}
@Bean("zhangsan")
public Person zhangsan() {
Person person = new Person();
person.setName("张三1");
person.setAge(20);
person.setGender("男");
return person;
}
@Bean("lisi")
public Person lisi() {
Person person = new Person();
person.setName("李四");
person.setAge(20);
person.setGender("男");
return person;
}

}
实验五:@Controller @Service @Repository @Component

这些组件必须在主程序所在的包及其子包结构下:
控制层@Controller
业务层@Service
持久层@Repository
普通组件@Component

实验六:@ComponentScan批量扫描

@ComponentScan(basePackages = “com.atguigu.spring”)
在main方法上

  • 默认扫描主程序所在的包及其子包
  • 如果标注,则只扫描对应包
实验七:@import导入第三方组件
  • 第三方组件想要导入容器中:没办法快速标注分层注解(不能修改源代码)
    1. @Bean:自己new,注册给容器
    2. @Component 等分层注解
    3. @Import:快速导入组件
      @Import({CoreConstants.class})
      可以在任意位置标注,多个标注只生效一次,因为是单实例的
实验八:@Scope和@Lazy
  • @Scope 调整组件的作用域:
    1. @Scope(“prototype”):非单实例:
      容器启动的时候不会创建非单实例组件的对象。
      什么时候获取,什么时候创建
    2. @Scope(“singleton”):单实例: 默认值
      容器启动的时候会创建单实例组件的对象。
      容器启动完成之前就会创建好
      • @Lazy:懒加载
        容器启动完成之前不会创建懒加载组件的对象
        什么时候获取,什么时候创建
    3. @Scope(“request”):同一个请求单实例
    4. @Scope(“session”):同一次会话单实例
实验九:FactoryBean 利用工厂制作复杂的Bean

不能直接@Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 场景:如果制造某些对象比较复杂的时候,利用工厂方法进行创建。
@Component
public class BYDFactory implements FactoryBean<Car> {


/**
* 调用此方法给容器中制造对象
* @return
* @throws Exception
*/
@Override
public Car getObject() throws Exception {
System.out.println("BYDFactory 正在制造Car对象...");
Car car = new Car();
return car;
}


/**
* 说明造的东西的类型
* @return
*/
@Override
public Class<?> getObjectType() {
return Car.class;
}


/**
* 是单例?
* true:是单例的;
* false:不是单例的;
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}

实验十:@Conditional 条件注册

注解可以放在类上和方法上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 /**
* 条件注册
* @param args
*/
public static void test06(String[] args) {
ConfigurableApplicationContext ioc = SpringApplication.run(Spring01IocApplication.class, args);

Map<String, Person> beans = ioc.getBeansOfType(Person.class);
System.out.println("beans = " + beans);

}

//MacCondition条件判断
public class MacCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();

String property = environment.getProperty("OS");

return property.contains("mac");
}
}

//WindowsCondition条件判断
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//判断环境变量中的OS 包含windows,就是windows系统
Environment environment = context.getEnvironment();
String property = environment.getProperty("OS");
return property.contains("Windows");
}
}

派生注解
code
code

2.4容器注入

实验一:@Autowired
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@ToString
@Data
@Controller
public class UserController {

/**
* 自动装配流程(先按照类型,再按照名称)
* 1、按照类型,找到这个组件;
* 1.0、只有且找到一个,直接注入,名字无所谓
* 1.1、如果找到多个,再按照名称去找; 变量名就是名字(新版)。
* 1.1.1、如果找到: 直接注入。
* 1.1.2、如果找不到,报错
*/
@Autowired //自动装配; 原理:Spring 调用 容器.getBean
UserService abc;

@Autowired
Person bill;

@Autowired //把这个类型的所有组件都拿来
List<Person> personList;

@Autowired
Map<String,Person> personMap;

@Autowired //注入ioc容器自己
ApplicationContext applicationContext;
}
实验二:@Qualifier和@Primary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    /**
* Consider marking one of the beans as @Primary,
* updating the consumer to accept multiple beans,
* or using @Qualifier to identify the bean that should be consumed
*/
// @Qualifier("bill") //精确指定:如果容器中这样的组件存在多个,则使用@Qualifier精确指定组件名

@Qualifier("bill") //精确指定:如果容器中这样的组件存在多个,且有默认组件。我们可以使用 @Qualifier 切换别的组件。
@Autowired
Person atom; // @Primary 一旦存在,改属性名就不能实现组件切换了。


// @Primary加在@Bean上,只要有多个组件,默认是@Primary下的组件


实验三:@Resource
1
2
3
4
5
6
7
8
9
//面试题:@Resource 和 @Autowired 区别?
//1、@Autowired 和 @Resource 都是做bean的注入用的,都可以放在属性上
//2、@Resource 具有更强的通用性
Autowired是spring规定的,
Resource是java规定的,适用于所有组件

@Autowired(required = false)
如果没有找到不报错

实验四:构造器注入和setter注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@ToString
@Repository
public class UserDao {
Dog haha;

/**
* 推荐:构造器注入
* @param dog
*/
//Spring 自动去容器中找到 构造器需要的所有参数的组件值。
public UserDao(Dog dog){
System.out.println("UserDao...有参构造器:"+dog);
this.haha = dog;
}
//setter方法注入
@Autowired
public void setDog(@Qualifier("dog02") Dog dog) {
System.out.println("setDog..."+dog);
this.haha = dog;
}
}
实验五:xxxAware理解感知接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Getter
@ToString
@Service
public class HahaService implements EnvironmentAware, BeanNameAware {

private Environment environment;
private String myName;

@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
//获取操作系统
public String getOsType(){
return environment.getProperty("OS");
}
//获取自己在ioc容器中的名字
@Override
public void setBeanName(String name) {
this.myName = name;
}
}
实验六:@Value和@PropertySource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 1、@Value("字面值"): 直接赋值
* 2、@Value("${key}"):动态从配置文件中取出某一项的值。
* 2.1默认在application.properties文件中获取
* 2.2在类上指定配置文件@PropertySource("classpath:cat.properties")
* 2.2.1 classpath:cat.properties;从自己的项目类路径下找
* 2.2.2 classpath*:Log4j-charsets.properties;从所有包的类路径下找
* 3、@Value("#{SpEL}"):Spring Expression Language;Spring 表达式语言
* 更多写法:https://docs.spring.io/spring-framework/reference/core/expressions.html
*
*/
@Value("旺财")
private String name;
@Value("${dog.age}")
private Integer age;

//取不到,默认为Tom
@Value("${dog.age:Tom}")
private Integer age;

@Value("#{10*20}")
private String color;

//静态获取UUID
@Value("#{T(java.util.UUID).randomUUID().toString()}")
private String id;

//截取Hello
@Value("#{'Hello World!'.substring(0, 5)}")
private String msg;

@Value("#{new String('haha').toUpperCase()}")
private String flag;

@Value("#{new int[] {1, 2, 3}}")
private int[] hahaha;

//以前获取UUID
public Dog() {
String string = UUID.randomUUID().toString();
System.out.println("Dog构造器...");
}
实验七:@Profile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//@Profile("dev") //整体激活
@Configuration
public class DataSourceConfig {

//1、定义环境标识:自定义【dev、test、prod】; 默认【default】
//2、激活环境标识:
// 明确告诉Spring当前处于什么环境。
// 你要不说是啥环境,就是 default 环境
//3、 在配置文件中激活环境spring.profiles.active=dev

//利用条件注解,只在某种环境下激活一个组件。
@Profile({"dev","default"}) // @Profile("环境标识")。当这个环境被激活的时候,才会加入如下组件。
@Bean
public MyDataSource dev(){
MyDataSource myDataSource = new MyDataSource();
myDataSource.setUrl("jdbc:mysql://localhost:3306/dev");
myDataSource.setUsername("dev_user");
myDataSource.setPassword("dev_pwd");

return myDataSource;
}


@Profile("test")
@Bean
public MyDataSource test(){
MyDataSource myDataSource = new MyDataSource();
myDataSource.setUrl("jdbc:mysql://localhost:3306/test");
myDataSource.setUsername("test_user");
myDataSource.setPassword("test_pwd");

return myDataSource;
}


@Profile("prod")
@Bean
public MyDataSource prod(){
MyDataSource myDataSource = new MyDataSource();
myDataSource.setUrl("jdbc:mysql://localhost:3306/prod");
myDataSource.setUsername("prod_user");
myDataSource.setPassword("prod_pwd");

return myDataSource;
}
}

2.5组件的生命周期

实验一:@Bean指定生命周期初始化和销毁方法

定义Bean组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Data
public class User{
private String username;
private String password;

private Car car;

@Autowired
public void setCar(Car car) {
System.out.println("【User】 ==> setter 自动注入:属性值:"+car);
this.car = car;
}

public User(){
System.out.println("【User】 ==> User 构造器...");
}

public void initUser(){
System.out.println("【User】 ==> @Bean 初始化:initUser");
}

public void destoryUser(){
System.out.println("【User】 ==> @Bean 销毁:destoryUser");
}
}

注入Bean组件
1
2
3
4
5
6
7
8
@Configuration
public class UserConfig {

@Bean(initMethod = "initUser",destroyMethod = "destoryUser")
public User user(){
return new User();
}
}

运行结果
1.构造器
2.自动注入
3.initUser
——————-容器运行中——————-
4.destoryUser
code

实验二:InitializingBean 和 DisposableBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Data
public class User implements InitializingBean, DisposableBean {
private String username;
private String password;

private Car car;



@Autowired
public void setCar(Car car) {
System.out.println("【User】 ==> setter 自动注入:属性值:"+car);
this.car = car;
}

public User(){
System.out.println("【User】 ==> User 构造器...");
}


public void initUser(){
System.out.println("【User】 ==> @Bean 初始化:initUser");
}

public void destoryUser(){
System.out.println("【User】 ==> @Bean 销毁:destoryUser");
}


/**
* 属性设置之后进行调用: set赋值完成了
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【User】 ==> 【InitializingBean】 ==== afterPropertiesSet....");
}

@Override
public void destroy() throws Exception {
System.out.println("【User】 ==> 【DisposableBean】 ==== destroy....");
}
}

1.构造器
2.自动注入
3.InitializingBean
4.initUser
——————-容器运行中——————-
5.DisposableBean
6.destory销毁
code

实验三:@PostConstruct 和 @PreDestroy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@Data
public class User implements InitializingBean, DisposableBean {
private String username;
private String password;

private Car car;



@Autowired
public void setCar(Car car) {
System.out.println("【User】 ==> setter 自动注入:属性值:"+car);
this.car = car;
}

public User(){
System.out.println("【User】 ==> User 构造器...");
}


@PostConstruct //构造器之后
public void postConstruct(){
System.out.println("【User】 ==> @PostConstruct....");
}


@PreDestroy
public void preDestroy(){
System.out.println("【User】 ==> @PreDestroy....");
}

public void initUser(){
System.out.println("【User】 ==> @Bean 初始化:initUser");
}

public void destoryUser(){
System.out.println("【User】 ==> @Bean 销毁:destoryUser");
}


/**
* 属性设置之后进行调用: set赋值完成了
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【User】 ==> 【InitializingBean】 ==== afterPropertiesSet....");
}

@Override
public void destroy() throws Exception {
System.out.println("【User】 ==> 【DisposableBean】 ==== destroy....");
}
}

1.构造器
2.自动注入
3.@PostConstruct
4.InitializingBean
5.initUser
——————容器运行中——————-
6.@PreDestroy
7.DisposableBean
8.destory销毁
code

实验四:BeanPostProcessor --Bean外挂修改器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component //拦截所有Bean的后置处理器--有返回值
public class MyTestBeanPostProcessor implements BeanPostProcessor {


@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【postProcessAfterInitialization】:"+beanName);
return bean;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【postProcessBeforeInitialization】:"+beanName);

if(bean instanceof User hello){
hello.setUsername("张三测试");
}
return bean;
}
}

1.构造器
2.自动注入
3.postProcessBeforeInitialization
4.@PostConstruct
5.InitializingBean
6.initUser
7.postProcessAfterInitialization
——————容器运行中——————-
8.@PreDestroy
9.DisposableBean
10.destory销毁
code
code
@Autowired注解是如何实现的?(这里的postProcessBeforeInitialization会在 @Autowired之前)
1.专门有一个处理@Autowired注解的AutowiredAnnotationBeanPostProcessor
2.每个Bean创建以后,会调用BeanPostProcessor的postProcessBeforeInitialization方法
3.postProcessBeforeInitialization里面就会利用反射。得到当前Bean的所有属性,利用反射得到Bean属性上标注的所有注解,看有没有Autowired注解
4.如果有,去容器中找到这个属性对应的组件(按类型,名字)找到。如果没有则报错

3.spring-AOP

3.1日志

  • 场景设计
    • 设计:编写一个计算器接口和实现类,提供加减乘除四则运算
    • 需求:在加减乘除运算的时候需要记录操作日志(运算前参数、运算后结果)
    • 实现:
      • 静态代理
      • 动态代理
      • AOP
硬编码与静态代理
  1. 定义一个方法接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public interface MathCalculator {
    //定义 四则运算
    int add(int i,int j);
    //减法
    int sub(int i,int j);
    //乘法
    int mul(int i,int j) ;
    //除法
    int div(int i,int j);
    }
  2. 实现这个接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    /**
    * 日志:
    * 1、硬编码: 不推荐; 耦合:(通用逻辑 + 专用逻辑)希望不要耦合; 耦合太多就是维护地狱
    * 2、静态代理:
    * 定义:定义一个代理对象,包装这个组件。以后业务的执行,从代理开始,不直接调用组件;
    * 特点:定义期间就指定好了互相代理关系
    */

    @Component
    public class MathCalculatorImpl implements MathCalculator {
    @Override
    public int add(int i, int j) {
    // System.out.println("【日志】add 开始:参数:"+i+","+j);
    int result = i + j;
    System.out.println("结果:"+result);
    // System.out.println("【日志】add 返回:结果:"+result);
    return result;
    }

    @Override
    public int sub(int i, int j) {

    int result = i - j;

    return result;
    }

    @Override
    public int mul(int i, int j) {

    int result = i * j;

    return result;
    }

    @Override
    public int div(int i, int j) {

    System.out.println("目标方法执行....");
    int result = i / j;

    return result;
    }
    }
  3. 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @SpringBootTest
    class Spring02AopApplicationTests {

    //设计模式:依赖倒置:依赖接口,而不是依赖实现。实现可能会经常变。
    @Autowired
    MathCalculator mathCalculator;

    @Test
    void contextLoads() {
    int add = mathCalculator.add(1, 2);
    System.out.println(add);
    }

    }
  4. 实现静态代理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    /**
    * 静态代理: 编码时期间就决定好了代理的关系
    * 定义:代理对象,是目标对象的接口的子类型,代理对象本身并不是目标对象,而是将目标对象作为自己的属性。
    * 优点:同一种类型的所有对象都能代理
    * 缺点:范围太小了,只能负责部分接口代理功能
    * 动态代理: 运行期间才决定好了代理关系(拦截器:拦截所有)
    * 定义:目标对象在执行期间会被动态拦截,插入指定逻辑
    * 优点:可以代理世间万物
    * 缺点:不好写
    *
    */
    @Data
    public class CalculatorStaticProxy implements MathCalculator {

    private MathCalculator target; //目标对象
    //有参构造器
    public CalculatorStaticProxy(MathCalculator mc){
    this.target = mc;
    }


    @Override
    public int add(int i, int j) {
    System.out.println("【日志】add 开始:参数:"+i+","+j);
    int result = target.add(i, j);
    System.out.println("【日志】add 返回:结果:"+result);
    return result;
    }

    @Override
    public int sub(int i, int j) {
    int result = target.sub(i,j);
    return result;
    }

    @Override
    public int mul(int i, int j) {
    int result = target.mul(i,j);
    return result;
    }

    @Override
    public int div(int i, int j) {
    int result = target.div(i,j);
    return result;
    }
    }
  5. 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class MathTest {
    @Test
    void test01() {

    //真正有用的人,目标对象
    MathCalculator target = new MathCalculatorImpl();
    target.add(1, 2);
    System.out.println("============");


    //1、创建静态代理对象
    MathCalculator proxy = new CalculatorStaticProxy(target);
    int add = proxy.add(1, 2);
    System.out.println(add);

    }
    }
动态代理
  1. 定义一个方法接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public interface MathCalculator {
    //定义 四则运算
    int add(int i,int j);
    //减法
    int sub(int i,int j);
    //乘法
    int mul(int i,int j) ;
    //除法
    int div(int i,int j);
    }
  2. 实现这个接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    /**
    * 日志:
    * 1、硬编码: 不推荐; 耦合:(通用逻辑 + 专用逻辑)希望不要耦合; 耦合太多就是维护地狱
    * 2、静态代理:
    * 定义:定义一个代理对象,包装这个组件。以后业务的执行,从代理开始,不直接调用组件;
    * 特点:定义期间就指定好了互相代理关系
    */

    @Component
    public class MathCalculatorImpl implements MathCalculator {
    @Override
    public int add(int i, int j) {
    // System.out.println("【日志】add 开始:参数:"+i+","+j);
    int result = i + j;
    System.out.println("结果:"+result);
    // System.out.println("【日志】add 返回:结果:"+result);
    return result;
    }

    @Override
    public int sub(int i, int j) {

    int result = i - j;

    return result;
    }

    @Override
    public int mul(int i, int j) {

    int result = i * j;

    return result;
    }

    @Override
    public int div(int i, int j) {

    System.out.println("目标方法执行....");
    int result = i / j;

    return result;
    }
    }
  3. 创建日志工具类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class LogUtils {

    public static void logStart(String name,Object... args){
    System.out.println("【日志】:【"+name+"】开始;参数:"+ Arrays.toString(args));
    }
    public static void logEnd(String name){
    System.out.println("【日志】:【"+name+"】结束;");
    }

    public static void logException(String name,Throwable e){
    System.out.println("【日志】:【"+name+"】异常;异常信息:"+e.getCause());
    }

    public static void logReturn(String name,Object result){
    System.out.println("【日志】:【"+name+"】返回;返回值:"+result);
    }

    }
  4. 创建动态代理类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /**
    * 动态代理: JDK动态代理;
    * 强制要求,目标对象必有接口。代理的也只是接口规定的方法。
    *
    */
    public class DynamicProxy {

    //获取目标对象的代理对象
    public static Object getProxyInstance(Object target) {
    return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    (proxy, method, args) -> {
    String name = method.getName();
    //记录开始
    LogUtils.logStart(name, args);
    Object result = null;
    try{
    result = method.invoke(target, args);
    //记录返回值
    LogUtils.logReturn(name, result);
    }catch (Exception e){
    //记录异常
    LogUtils.logException(name, e);
    }finally {
    //记录结束
    LogUtils.logEnd(name);
    }
    return result;
    }
    );
    }
    }
  5. 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Test
    void test03(){
    MathCalculator proxyInstance = (MathCalculator) DynamicProxy.getProxyInstance(new MathCalculatorImpl());

    proxyInstance.add(1, 2);

    System.out.println("===================================");

    proxyInstance.div(10,0);


    }