day02-IOC自动装配

时间:2024-03-18 07:39:43

目录

12-回顾

  1. 昨天先先讲了什么框架:封装具有支撑性的半成品项目
    1. springmvc - servlet
    2. mybatis - jdbc
    3. 框架+业务逻辑 = 项目
  2. IOC/DI 和 AOP
    1. 昨天学习的都是IOC
    2. IOC是一种思想:自己对对象的控制权,交给了程序本身。
    3. spring创建的默认就是单例
    4. DI就是依赖注入,依赖就是一种关系,注入就是赋值。
  3. ApplicationContext\ClassPathXmlApplicationContext
  4. applicationContext.xml - 配置文件
  5. SSM当中简单的配置:项目启动的时候,spring 的配置文件自动加载。
  6. set注入、constructor-arg注入、p命名空间
    1. 构造器注入的时候,必须要有相匹配的构造器。
    2. 构造方法是怎么去选择构造器呢,这跟java代码的底层有关系。
  7. value和ref:字面量,引用数据类型
  8. 内部bean:在某个bean的内部,定义的bean,就叫做内部bean
  9. 集合:property标签当中有很多子标签
    1. list
    2. array
    3. set
    4. map:keyset、values、entryset
  10. 集合bean:utils命名空间

老师电脑桌面

image-20211002094533404

image-20211002095108467

13 - bean的作用域

一共有四种,singleton,prototype,request,session。

  • singleton:单例的。
  • prototype:多例的。
  • request:一次请求中有效。
  • session:一次会话中有效。

演示

image-20211002100514971

image-20211002100926401

作用域

image-20211002100953689

我们设置为单例。

怎么验证是不是单例

从容器当中获取两次。

image-20211002101036039

我们在对象当中将toString方法注释掉。

toString方法是Object方法。

image-20211002101113152

Object当中的toString方法,默认输出的是内存地址。

image-20211002101144594

怎么判断两个对象是否是一样

  • 对象使用object的toString方法。
  • 恒等于
  • .equal方法只要没有经过重写,对象就是可以比较。
    • string字符串代表的是值不可以改变的unicode序列,是一个常量。

验证单例和多例是否生效

  1. spring是通过反射获取对象,class.forName,newInstance方法创建新实例,必须要有无参构造。
  2. 怎么能够看起来更明显,看看spring是通过反射来创建对象的呢?
  3. 在对象的无参构造器当中,输出一句话,看看会不会打印。

image-20211002101822824

image-20211002101831961

从这里就能够看出来,创建的对象是单例的,因为我们打印了两个对象,但是构造方法当中的打印语句,就输出了一次。

image-20211002101940512

如果我们在配置文件当中将scope修改为prototype,再次执行程序,可以看到下面的结果:

image-20211002102012757

单例和多例的进一步演示

image-20211002102148703

image-20211002102204440

  • 当我们是prototype的时候,只初始化容器,什么都不打印的。

image-20211002102313373

image-20211002102325567

  • 当我们的bean是singleton的时候,只初始化容器,就会创建对象。
  • 说明:我们的spring管理的对象,是单例的,在容器初始化的时候,就会直接创建。

如果spring当中有单例模式的bean,在初始化容器的时候,就会创建,这个对象。

总结

image-20211002102739350

14-bean的生命周期

笔记

image-20211002102800823

image-20211002103031686

闲聊

杨博超聊自己儿子。

杨博超在老家开了一个母婴店。

进口的奶粉450一罐。纯吃奶粉,不吃母乳的,一星期都要不了,就吃完了,一个月,十罐奶粉。

演示bean的生命周期

创建一个普通的类,标注出来生命周期。

在servlet当中servlet是可以初始化的。这是因为servlet当中集成的httpServlet当中,有init方法。

但是现在是一个普通的类。

package com.atguigu.ioc.life;

public class Person {

	private Integer id;
	
	private String sex;
	
	private String name;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		System.out.println("2-依赖注入");
		this.id = id;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Person() {
		System.out.println("1-创建对象");
	}

	@Override
	public String toString() {
		return "4-使用";
	}
	
	public void init() {
		System.out.println("3-初始化");
	}
	
	public void destory() {
		System.out.println("5-销毁");
	}
}

image-20211002105435420

为什么,只有三步呢?

这是因为,对象的初始化和销毁的方法,没有被调用。

image-20211002105645589

怎么才能够让他们被调用呢?

非常简单,在我们的配置文件当中,配置bean的时候,绑定上去,就可以了。

image-20211002105802503

我们就指定了对象的初始化方法和销毁方法。

image-20211002105922911

这样就正常了。

没有销毁的原因是,容器关闭的时候,才会销毁bean。

image-20211002110746960

image-20211002110839877

15-bean的后置处理器

image-20211002111034853

bean的生命周期,默认是有五个步骤。

但是有的程序员还希望在bean初始化前后的时候,做一些其他的操作。

这个就需要bean的后置处理器。

image-20211002111142598

  • 后置处理器,是放在初始化之前,或者初始化之后。
  • 后置处理器,设置了之后,对spring管理的每一个bean都有效果的。

后置处理器需要实现一个接口

这个接口叫做BeanPostProcesser。

image-20211002111601901

  • 我们有没有见过一个接口,实现了它之后,没有报错,没有需要实现的方法。
  • 有这种接口的,序列化的接口,就是这样的,里面没有定义任何的抽象方法。
  • 就相当于是一个标识,实现了接口,就是实现了序列化。

image-20211002111725134

这两个方法,一个是在初始化之后,一个是在初始化之前。

两个参数,一个是Object,一个是String,不知道这两个参数是干什么的。

在java当中,参数名字,都应该是见名知意的。所以这些arg0或者是arg1的参数,不是很好。

image-20211002112034260

我们把实现方法删除了,然后按ctrl+鼠标左键,然后导入一下源码。

导入源码之后,我们再次实现抽象方法,就是下面的效果:

image-20211002112227054

这些传入的参数,是用来干嘛的。

后置处理器是在初始化前后做一些操作。

上面实现方法当中的Object,就是经过你处理之后的,新的bean。

小代码演示

这是为了演示后置处理器。

image-20211002112743238

我们这样写一个后置处理器。

然后我们想办法,将后置处理器,跟spring关联起来。

xml中配置后置处理器

image-20211002113503942

这样就完成了配置。

测试效果

image-20211002113550438

16-引用外部资源文件

引用外部属性文件

image-20211002113700420

比如jdbc。驱动名称,连接地址,用户名和密码放在properties文件中。

数据库连接对象,也可以交给spring管理。

那么spring怎么在xml中配置数据库连接池对象呢?

image-20211002114027388

创建一个配置文件

image-20211002114058180

配置数据库连接池对象。

image-20211002114332317

配置数据库连接池对象的时候,上面的蓝色标注的,就是实现类。

怎么看一下,这个实现类当中的属性呢?通过outline视图。

image-20211002114524766

image-20211002114701933

在这DruidDataSource类当中没有找到对应属性的时候,我们可以去父类当中看看有没有对应的,我们要找的属性。

我们先搜索一下父类:DruidAbstractDataSource。

image-20211002114814692

image-20211002114948877

image-20211002115123053

闲聊

  1. DNF游戏密码
  2. QQ密码被盗
  3. 验证码的作用防止暴力登录,验证码一般是保存在session。
  4. 12306的验证码。

image-20211002120122774

xml配置连接池对象

image-20211002120209020

写一个测试类,测试一下效果:

image-20211002120720219

获取连接对象

image-20211002121302499

错误记录

image-20211002121230332

create connection SQLException, url: jdbc:mysql://localhost:3306/godemo, errorCode 0, state S1000

java.sql.SQLException: Unknown initial character set index \'255\' received from server. Initial client character set can be forced via the \'characterEncoding\' property.

  • 这个报错信息意思是咱们的MySql和一个叫做mysql-connector-java包不匹配
  • 一般来说5.1匹配的是mysql8.0以下的版本

我是用的mysql是这个版本:

image-20211002121556563

我的connector-java包是这个版本:

image-20211002121629578

mysql-connector-java-5.1.7-bin.jar,这个包,要升级一下。

效果验证

image-20211002122434502

配置db.properties文件

这里面driver的名字,我们最好是写成jdbc.driver。

image-20211002123533081

xml当中配置properties文件加载

image-20211002123522349

image-20211002123633923

这里面property当中,有一个叫做location的name。

js中location对象

用途是,实现页面跳转。

location.href,指定页面跳转的路径。

location.reload,指定页面刷新。

xml配置properties

image-20211002124121986

根据db.properties文件配置连接bean

image-20211002124310636

我们就不适用上面的配置方式了。

我们使用下面的写法:

jsp页面的el表达式当中,我们就是使用${},来取值的。作用域当中取值。

在xml当中,是从资源文件当中取值。

mybatis当中,也有${},还有#{},相当于占位符。

image-20211002133446315

测试一下:

image-20211002133456560

没问题的。

第一种加载资源文件的方法

image-20211002133528872

第二种加载资源文件的方法

导入context的命名空间:

image-20211002133625168

image-20211002133641416

image-20211002133723210

17-自动装配和兼容性

笔记

image-20211002133848999

装配是什么意思?

装配就是注入,注入就是赋值。

spring管理的bean就是一个对象,有多个属性和方法,方法是调用,属性是赋值。

spring属性赋值,可以叫做装载,就做注入,就是自动赋值。

  • 昨天讲解的value和ref,就是手动装配。
  • 还有一种,就是自动装配。

记住一句话

自动装配,只针对于非字面量的属性,也就是需要用ref的。

自动装配的属性,都是非字面量的属性。

写个例子

image-20211002134124291

image-20211002134233463

image-20211002134243688

image-20211002134307270

然后对上面的类,进行封装,创建set和get,重写toString。

image-20211002134353206

创建配置xml文件

image-20211002134450879

  • 首先创建一个emp的bean。
  • 给emp的car或dept属性赋值有两种属性
    • ref属性
    • 内部bean

image-20211002134802457

image-20211002134921822

image-20211002134911838

创建测试类

image-20211002135033401

正常的。

现在进入自动装配的正题

自动装配就是自动注入。

image-20211002135106865

bean标签当中有一个autowire的属性。

这个就是自动装配的意思。

image-20211002135138519

  • autowire
    • byName
    • byType
    • constructor
    • default
    • no

测试byName

image-20211002135248303

设置了byName之后,然后重新测试:

image-20211002135408223

效果正常的。

byName是什么意思

通过name属性,自动装配。

也就是说car和dept属性,通过某种方式,自动赋值。

byName的策略,是什么呢?

就是spring管理的bean当中,bean的id,跟我们的emp的属性名,一致,就能够自动赋值。

byType

image-20211002135753513

只要在spring管理的范围内,有一个bean能够为目标bean的属性赋值,那就自动装配。

比如说:

  • bean的class属性,跟目标bean的属性类型一致,就能够自动装配。
  • bean有一个class属性,目标bean的属性类型是父类,也能够自动装配。
  • 目标bean的属性类型,跟spring管理的bean类型,bean子类,bean的实现类,都可以自动赋值。
  • 这个就叫做兼容性。

总结byType

通过类型自动装配的时候。

  • spring管理的、能够给属性赋值的bean
  • 目标bean的属性,可以是赋值bean的同一类型、父类、接口类。

测试byType

第一步:

创建一个car的父类:

image-20211002140434097

目标对象,就是car,修改引用属性:

image-20211002140516886

引用属性的类型,是赋值bean类型的,父类。

测试,是否可以自动装配。

第二步:

创建目标对象,就是car,引用属性类型,对应的dept接口。

image-20211002140633017

image-20211002140702617

image-20211002140713248

image-20211002140729430

目标对象emp当中,car属性类型是CarExtends,是目标bean car的父类。

目标对象emp当中,dept属性类型是DeptI,是目标bean dept的接口。

这个时候,可以自动装配的吗?

测试效果如下:

image-20211002140941242

18-byName和byType选用建议

说明

  • autowire,会根据我们,设置的策略,为,非字面量的属性,自动赋值。

image-20211002141250263

  • byType类比
    • 实现分层,service层,有接口层,也有实现层。
    • dao层,有接口层,也有实现层。
    • servlet里面创建service对象,service对象当中创建dao对象。
    • servlet里面,创建的service对象,就相当于属性,到时候,可以通过自动装配的时候,为service对象赋值。
    • 咱们在servlet当中,创建service对象的时候,一般都是创建的接口的对象。
      • userService的接口,实现类叫做userServiceImpl
      • private userService userService = new userServiceImple。
  • spring当中管理的对象,都是针对于类的,不能够针对于接口和抽象类。
    • 因为接口,不能够直接创建对象。
    • 因为抽象类,也不能够创建对象。

出现的小问题

  • 昨天getbean方法的时候,通过getbean(class),这时候,容器有两个class对应的bean,报错没有找到唯一的bean
  • byType在spring容器中,找到两个,能够赋值的bean,它该选谁?

举例如下:

image-20211002141912383

emp对象有car属性,spring当中有car1和car2,应该自动装配哪个?

byType自动装配时候,spring当中只能出现一个,能赋值的bean

在,使用byType的过程中,要求spring容器中,只能够有一个,能够为目标对象属性赋值的bean。

笔记

image-20211002142257468

选用建议

image-20211002142315485

byName和byType都不够好。

最大的问题,bean当中添加了autowire属性,作用于bean中所有非字面量属性。

我有时候创建对象,不一定要为所有属性赋值。

问题1:设置autowire属性,会作用域该bean中,所有的非字面量属性。

因此,我们谁都不用。

19-基于xml的自动装配和基于注解的组件扫描

笔记

image-20211002142811720

用过注解吗?Junit的@Test。

servlet的时候@webservlet。web项目,写servlet,要去web.xml中配置,也可以servlet加@webservlet,括号里面写访问路径,相当于web.xml当中配置了servlet。方便。

注解一般以@开头。

今天讲好几个注解。

  • 之前,xml当中配置bean,给spring管理。
  • 项目中有1000个bean,我咋办。
  • 还去xml当中配置,累死你。
  • 如果bean中大部分非字面量属性。不能autowire配置xml。

spring中的组件是什么?

spring的配置文件,由bean标签组成,组件就是bean。

注解

image-20211002143248008

组件是bean,bean是对象,对象由类产生,是class实例化。

要先确定bean属于哪个class。

  • @Component:组件、bean的意思、加在哪里呢?
    • 写bean标签,id可以不加,class必须加。
    • 注解加在类上、方法上、属性上。
    • 这个要加在类上,就可以被spring管理,作为spring的bean。
    • 一个类加上这个注解,spring就会对这个class产生的对象,进行管理。
  • @Repository:持久层
    • servlet:控制层,service:业务逻辑层,dao:数据访问层,持久层,不要念dao。
    • 这个注解,应该加在持久层
  • @Service
    • 加在逻辑层
  • @Controller
    • 加在控制层
      • springmvc的请求响应和处理的层。
      • jsp+servlet的时候,加在servlet

四个注解的关系和区别:没有区别,一模一样,名字不一样,标识。

  • @Component
    • @Repository
    • @Service
    • @Controller
      • 这个东西,springmvc时候再说,有区别。
      • @Controller标识控制层,在web项目中,由servlet容器tomcat管理。
      • web容器怎么找到对应的控制层,要根据注解分辨。
      • 原来你是写servlet继承httpservlet,明显。
      • 但是springmvc之后,控制层,就是普通类,它封装了servlet,不明显。
      • web容器分辨不出来,根据@Controller注解。
      • structs2封装的是过滤器,控制层是需要继承。

演示

image-20211002145721365

image-20211002145734863

image-20211002145746077

image-20211002145757692

image-20211002145830815

测试验证

image-20211002150854104

  • 咱们加了注解。
  • 咱们在配置文件当中,添加了扫描包。加上注解的类,就是组件。

image-20211002151329462

image-20211002151420437

解释

image-20211002151509309

image-20211002151808183

加了注解之后,就相当于在xml当中,自动生成了相对应的bean,这些bean的id就是类的名称的首字母小写为值,class就是类的全限定名。

我们在测试类当中,验证通过id来获取,就是正常的。

image-20211002151755830

image-20211002152120212

  • 英雄联盟当中有个打野,乌迪尔,就叫做ud。

扫描包的小问题

image-20211002152321980

郑罗茜-{NO001}

image-20211002152731245

2019年6月12日 - 上午 - 结束

20-扫描组件之包含和排除

image-20211002152816134

spring框架第二天

通过注解让spring管理bean,第一步加上注解,第二步,spring的xml写context:component-scan,有个属性叫做base-package,里面要写包结构。会在spring的配置文件当中自动生成,相应的bean标签,会有默认的id,以类的首字母小写为值。

这个过程,大家一定要注意。如果说咱们在使用的过程中,只有加了注解,没有扫描,spring不会管理这些类。

这两个步骤是缺一不可的。

所以说,原来在,教学的过程中,有的同学,会问加了注解,会问写了扫描组件标签是什么意思。

这两个东西同时存在才会有意义。

扫描包的时候,basepackage,这个属性里面写的一定是一个包结构。

这个包结构,看着是包,文件系统当中,是多个目录的。

com.atguigu.ioc,当中com就是一个文件夹。

包写越大,扫描到的类也越多,扫描时间长,也很可能做无用功。咱们尽量写的准确一些。

两个包是需要spring管理的,可以具体写出来,用逗号分隔。

如果以后需要管理的包非常多。怎么办呢?

举例:

image-20211002154521279

image-20211002154534422

如果包特别多,单独扫描某些包,或者把某些包排除掉,怎么实现呢?

包含和排除

image-20211002154600292

  • context:exclude-filter
  • context:include-filter
    • image-20211002154627978
    • 假设我们扫描的就是userMod这个包,只要求扫描一个controller,应该怎么写呢?
    • annotation是注解的一i是,type属性当中,注解的类型,就是
    • image-20211002154825083
    • expression是表达式的意思,这里填写:org.springframework.stereotype.Controller。

image-20211002154911614

测试效果是:

image-20211002155006758

这个效果,没有成功。

为什么呢?

use-default-filters属性

image-20211002155042868

如果是要使用include或者exlude的时候,就要设置这个东西。


修改后测试

image-20211002155302350

效果是正常的,成功的。

include的另外的设置方式

image-20211002155652093

这是根据类型来进行包含。

笔记

image-20211002160109698

排除

image-20211002160248645

这样写,是不行的,一定是要把use-default-filters="false"去除掉,使用exclude-filter的时候。

image-20211002160643972

image-20211002160751994

测试效果是:

image-20211002160804565

小总结

image-20211002160946995

包含和排除的用法

context:component-scan:能够写多个包含

context:component-scan:能够写多个排除

不能够包含和排除同时写。

21-基于注解的自动装配

上午的时候,我们写了自动装配,推荐是不要用,也就是说,千万不要在xml当中写自动装配autowire属性。

在控制层写个方法,接收请求。

image-20211002161440356

这里是一个处理,添加用户信息请求的方法。

这里需要调用service。

我们需要通过接口,创建service对象,然后调用service当中的addUser方法:

image-20211002161659474

第二步,我们需要在接口当中定义一个addUser()方法。

image-20211002161752186

第三步,我们需要在serviceImpl当中,实现这个addUser()方法。

image-20211002161807170

在service当中处理完业务逻辑之后,需要将数据,保存到数据库当中的。

这个工作,咱们可以在serviceimpl当中写。但是service都干了,要dao干啥呢。

我们需要在这个serviceimpl类当中,创建userDao类。

image-20211002161934884

第三步,在userDao接口类当中定义一个addUser()方法,然后在userDaoImpl当中实现这个方法。

image-20211002162045202

image-20211002162119858

测试

image-20211002162246537

我们在测试类当中通过uc.addUser()自动调用controller当中的方法。

原来我们通过servlet的dopost方法是自动调用。

image-20211002162332532

不推荐使用bean标签autowire属性的第二个原因

因为你后面会大量使用注解来把对象创建工作交给spring,spring会根据注解和组件扫描,自动在xml当中生成bean标签。

但是这些bean标签,我们是看不到的。

所以,我们连bean标签都看不到,我们还使用个鬼的autowire属性。

这个时候,我们怎么实现自动装配呢?

我们可以利用一个组件,叫做@autowired。这个注解,可以加在我们想要自动装配的属性上面。

我们前面讲解自动装配的时候,已经说过了,自动装配是对于对象bean当中的非字面量的属性来进行自动装配的。

我们在我们的controller类当中,我们设置了service属性,这是一个service对象。这是需要自动装配的。

我们在我们的service类当中,我们设置了dao属性,这是一个dao对象。这也是需要自动装配的。

image-20211002162829844

自动装配的意思就是进行赋值,从spring管理的bean当中,找到对应的bean,然后进行赋值。

所以,下面颜色标注的内容,我们是不需要写的。

我们既然就已经写了@autowired的时候,我们是不需要进行new对象的。

image-20211002163018347

这种写法是没有必要的。

我们可以直接写成下面的例子:

image-20211002163042442

在这里,我们只需要创建一个接口对象,就可以了。

这里,是通过什么类型来进行自动装配的呢?

上午的时候,我们只有讲解了byName和byType两种方式的自动装配。

byName是在spring管理范围内寻找到对应的id是等于属性值的bean,然后进行赋值。

我们这里的属性就是userService,这是一个接口类,在spring的管理范围内,不会有接口类对应的bean的。

因为接口类是不能够创建对象的。

byType是在spring管理范围内寻找到唯一的一个,属性类型和bean类型一致的、或者属性类型是赋值bean的父类,或者属性类型是赋值bean的接口的情况,实现自动装配的。

我们这里的userService是一个接口的,它有一个实现类,这个实现类就是,userServiceImpl,这个就是属性userService类型UserService的接口类,是只有一个的,是可以自动赋值和装配的。

所以,@Autowired进行自动装配的时候,是通过byType的方式来实现自动装配的。

测试效果

image-20211002163604973

22-组件管理总结

个人总结

image-20211002163949435

下面再做一个操作

我们想一种情况,让spring的@Autowired的byType自动装配模式失效。

我们在xml当中写一个bean。

image-20211002164133682

我们写的这个bean就是所谓的赋值bean,这个bean的类型,是目标bean也就是service属性userDao,属性类型的实现类。

是可以给属性进行自动装配赋值的。

现在我们通过@Repository注解的实现类,也是具有自动装配赋值的。

这个时候,我们进行测试的时候,就会报错了:

image-20211002164318444

这是因为byType进行自动装配的时候,是不能够有2个赋值bean的。

疑问:如果autowire,只有byType的这么一种模式的话,用起来也不是很方便

假设,我们这样操作:

image-20211002164519211

我现在来仔细描述一下,现在的情况。

第一,现在在目标bean,也就是userServiceImpl类当中有一个属性,属性的名字叫做userDao,属性的类型是userDao。

第二,现在,在spring管理范围内,类型是userDao实现类UserDaoImpl的,有两个bean,一个是@Autowired自动装配的类,一个是在xml当中手动配置的类。自动装配的类的id叫做userDaoImpl,是类的名字的首字母大小的。手动配置的类的id是,我们手动设置成为了userDao。

第三,这个时候,我们进行测试,byType自动装配类型是没有成功的,但是byName自动装配类型是成功的。

image-20211002164842292

错误记录

第一,我们在userService当中自动装配了userDao属性。

第二,我们将userDaoImpl上的@Repository注解去掉了。

第三,这个时候,spring管理范围内,没有能够给userDao属性自动装配的bean。

这个时候,的错误,是长这个样子的:

image-20211002165116967

image-20211002174322174

@Autowired(required=false)

image-20211002174352000

如果这样设置,就算是装配不成功,spring也不会报错的。

但是可能项目会报空指针异常。

Scarlett Ingrid Johansson (/dʒoʊˈhænsən/; born November 22, 1984) is an American actress. 

斯佳丽·英格丽德·约翰逊(/dʒoʊˈhænsən/;生于1984年11月22日)是美国女演员。

She was the world\'s highest-paid actress in 2018 and 2019, 
and has featured multiple times on the Forbes Celebrity 100 list. 

她是2018年和2019年全球收入最高的女演员,
并多次入选福布斯名人100强。

Her films have grossed over $14.3 billion worldwide, 
making Johansson the ninth-highest-grossing box office star of all time. 

她的电影在全球票房超过143亿美元,
使得约翰逊成为有史以来票房第九高的明星。

She is the recipient of various accolades, 
including a Tony Award for Best Featured Actress in a Play 
and a BAFTA Award for Best Actress, 
as well as nominations for two Academy Awards 
and five Golden Globe Awards.

她获得了各种荣誉,
包括托尼奖最佳戏剧女主角
和英国电影电视艺术学院奖最佳女主角,
以及两项奥斯卡奖
和五项金球奖的提名。

@Controller的value属性

image-20211002174756625

我们可以在@Controller当中的value属性当中,设置,spring自动生成的bean的id。

image-20211002175002019

默认生成的bean,是使用类名的首字母小写,作为id的。

如果设置了value,就会使用你自定义的aaa。

如果你在小括号当中,写了多个属性,你就必须严格按照,属性名等于属性值,这种格式来进行书写。

如果你只写了一个属性,比如说是value属性,那么,也可以省略属性名。

image-20211002175245225

再创建一个dao实现类

image-20211002175447318

Born and raised in Manhattan, New York City, 
Johansson aspired to be an actress from an early age and first appeared on stage in an Off-Broadway play as a child actor. 

在纽约曼哈顿出生长大,
约翰逊从小就渴望成为一名演员,并作为一名儿童演员首次出现在百老汇以外的戏剧舞台上。

She made her film debut in the fantasy comedy North (1994), 
and gained early recognition for her roles in Manny & Lo (1996), The Horse Whisperer (1998), and Ghost World (2001). 

她在奇幻喜剧《北方》(1994)中首次出演电影,
并因在《曼尼&洛》(1996年)、《马语者》(1998年)和《幽灵世界》(2001年)中的角色而获得早期认可。

Johansson shifted to adult roles in 2003 with her performances in Sofia Coppola\'s Lost in Translation, which won her a BAFTA Award for Best Actress, and Girl with a Pearl Earring. 

She was nominated for Golden Globe Awards for these films, and for playing a troubled teenager in the drama A Love Song for Bobby Long (2004), and a seductress in Woody Allen\'s psychological thriller Match Point (2005). 

Other works during this period include Christopher Nolan\'s The Prestige (2006) and Allen\'s Vicky Cristina Barcelona (2008), and the albums Anywhere I Lay My Head (2008) and Break Up (2009), both of which charted on the Billboard 200.

image-20211002175546350

这个时候userService这个目标bean当中的userDao属性,就有了两个可以进行byType自动装配的赋值bean。

一个就是UserDaoImpl,一个就是UserDaoMybatisImpl。

这就是之前手动创建相同类型赋值bean的情况。

我们还可以通过一个注解,指定,我们使用哪一个赋值bean来进行自动装配的。

@Qualifier

@Autowired是要和@Qualifier两个注解,同时使用的。

@Qualifier(value="bean的id")

image-20211002175927039

这个注解就是指定的意思。

以后具体的情况,就使用具体的方法。

多说一点,看一下,这个@Qualifier注解的源码:

image-20211002180254952

这里面,Target注解后面表示,这个注解,可以用在属性上面,也可以用在方法上面的。

小说明

image-20211002180406550

原来我们,通过手动配置xml的时候,说过,配置的bean,都要求,对应的类当中要有set方法,这是 通过set注入的。

但是你发现,我写了@Autowired之后,自动装配的时候,根本就没有在我们的userDao当中写set方法的。

@Qualifier应用在方法上

image-20211002180608944

这里的UserServiceImpl就是目标bean,有一个属性,叫做userDao,我们可以直接,在属性上面,进行自动装配,如果有属性有set方法的话,我们可以在set方法上面进行自动装配。

image-20211002181054928

笔记

基于注解的组件化管理:
@Component,@Controller-控制层,@Service-业务层,@Repository-持久层
以上四个注解功能完全相同,
不过在实际开发中,要在实现不同功能的类上,加上相应的注解。
完成组件化管理的过程:
1、在需要被spring管理的类上,添加相应的注解。
2、在配置文件中通过<context:component-scan>标签对所设置的包结构进行组件扫描,就会将加上注解的类,作为spring的组件进行加载。
base-package属性
user-default-filters属性
context:include-filter标签
context:exclude-filter标签
组件:指spring中管理的bean,
作为spring的组件进行加载:会自动在spring的配置文件中生成相应的bean,
这些bean的id会以类的首字母小写为值。
也可以通过@Component("beanId")为自动生成的bean,指定id。
自动装配:在需要赋值的非字面量属性上,加上@Autowired注解,就可以实现自动装配。
就可以在spring容器中,通过不同的方式,匹配到相对应的bean。
默认使用的方式是byType,此时要求spring容器中只有一个能够为其赋值。
如果byType不行的话,就会自动切换到byName的方式进行自动装配。
当byType实现不了装配的时候,然后会自动切换到byName。
此时要求spring容器中具有一个bean的id是和属性名一致的。
如果自动装配的时候,匹配到了多个能够自动装配的bean,
可以使用@Qualifier(value="beanId")这个注解,
指定使用自动装配的bean。
@Autowired和@Qualifier可以一起作用于,带有形参的方法上面。
注意,这个时候,这个@Qualifier注解所指定的bean,作用于方法的形参。

在java当中也为我们提供了一个自动装配的注解叫做@resource,这个注解是和@Autowired功能是一模一样的,都是为了实现自动装配。
只不过@Autowired是先要byType,不行了然后byName。
@Resource是先要byName,不行了然后byType。

讲解到这里的时候,我们的IOC的部分就已经结束了。

佟丽娅回顾

IOC

23 - AOP前奏

引子

IOC是通过工厂模式实现的。
AOP是通过代理模式实现的。
大家都学过动态代理吗?
学过的。
代理模式要理解还是简单。
咱们理解的话,拿着静态代理理解。
代理模式有三个角色,三个对象。
原始对象
代理对象
目标对象。
生活的例子很多。
我完成某个事,我不想自己动,我找个代理对象,帮我干。
但是有个原则,保证最终的结果不变。
不管自己完成还是代理对象完成,结果不变。
找房子,我白天上班,没时间,我找中介,中介帮我找。
最终的结果,找到房子,不变。
别房子没找到,找到老婆。
天为被,地为床,旁边有人就行。

闲聊

尽量别找中介。
2014年5月份来北京。
2015年博超找房子。
当时在唐家岭住。
有小公寓。
特别便宜,一个月800,一个单间。
空调、洗衣机、冰箱、热水器、柜子、桌子。

加上水电费,不超过1000。
特别好。
2015年开始,北京建筑各种非法公寓强拆。
当时,博超去上班,出门了很多营地。
有很多广播。
限定几天之内搬走,不搬走,后果自负。
一看就是美国*党的人。
一看没啥事。
第二天公寓墙就倒了。
问小伙子,都说没事,说了好几年了。
广播三天之内搬走。
第一天空调。
第二天热水器。
第三天床。
真的是这样,博超都丢了。
不敢住了。
睡觉砸进去,有什么办法。
说理有点难。
晚上就搬走了,找房子。
公寓老板都跑到了法国。
找到一个小区,两居室。
和朋友一起。
一个月4000块。
想要住,押一付三行规。
找了中介,押一付三再交一个月,给中介费。
非常贵。
不住。找了个大院,2个屋子,不是小区。
一个月3300元。
上班之后,各个地方找房子房价。
往后结婚了。
两个人去霍营地铁站。
旁边有个公寓。
35平米一个月三千三。

加上水电费,差不多4000块。
就是35平米。
不敢有什么大动作。
又找了个房子。
在昌平。
有个小区功华新村。

一个月3500,是两居室。
去了上海之后,那就不一样了。
上海的房子有的地方确实是贵。
上班在海边,走路10分钟,开车2分钟。
开窗就能看见海。
当时在地图上的最南边。

一个月1800,三居室
一星期一次台风。
台风大,不让出门,别家待了。
路没路灯。
特别偏,还穷。
晚上要开远光。
那边人喜欢晚上出去遛弯。
而且有人特别过分。
正儿八经躺在路中间睡觉。
房子便宜。
大家以后找房子。
最好不要一个人住。
几个人帮衬。
一个人住太贵。
尽量别找中介。

AOP前奏

image-20211002194959104

写一下小案例。

先写一个接口,定义一个基本的功能。

image-20211002195052218

然后将加减乘除都放到这个接口当中。

image-20211002195212248

写完接口之后,下面写一个实现类,功能简单写一下。

image-20211002195330848

java当中的除法就是取整,取模就是取余数。

javascript当中的除法就是正规的除法,该有小数点,就有小数点。

写一个测试类

image-20211002195608812

image-20211002195626098

需求里面要求,写日志。

日志功能是项目中,经常会用到的功能,会记录变量的变化情况。

这个东西,很有必要。

需求:日志记录,传参是什么,实现的方法是什么,结果是什么。

假设我们这样写日志:

image-20211002195959754

就是用system.out.println语句来模仿,日志。

我们再次执行一下,就有效果了:

image-20211002200117698

如果调用其他方法,也是一样的效果。

思考

这样写好不好?不好。

image-20211002200202289

这是接口的实现类,是应该实现一些功能的。我这里写了很多的日志信息,代码很乱。

写一行功能,加几行日志,这不是有病吗?

这种写法,是不可以的。

不能将日志功能,写到具体实现某个功能的类中。

业务逻辑的类,只写业务逻辑的代码。

这个时候,就应该怎么办?

大家发现日志格式都是差不多。

能不能把日志功能抽取出来。作为一个类。

业务类当中,通过日志类,就可以解决所有的记录日志的问题。

第一步:将日志代码,都要放在日志类中。

第二步:业务类中需要记录的位置,让日志类生效。

这个就需要用,动态代理。

动态代理

动态代理的作用:能够为任何需要代理的对象,自动生成代理类。

代理模式中有三个元素:原始对象,代理对象,目标对象。

代理对象是生成的。目标对象就是要去做的事。

什么是动态代理,不管目标对象是什么,都可以通过一个X类,生成相对应的代理对象,让代理对象帮助我去完成功能。

这个X就是动态代理。X跟目标对象没有任何关系。

image-20211002200845972

所以这个X更像是一个工具类。能够帮助程序员生成动态代理对象。

第一步,确定目标对象。

在这里例子当中,目标对象就是MathIImplI。就是代理对象要作用的类。

image-20211002201033171

这里面我们的目标对象,就是MathImplI。

但是根据之前的说法,动态代理,是能够为任何的目标对象,生成代理对象的。

所以,我们的目标对象的类型,不能局限在MathImplI这个类型。

所以,我们应该写成Object。

但是,我们暂时先这样写。

第二步,工具类具有获取代理对象的方法。

现在我们是在动态代理工具类当中,是需要根据目标对象,获取代理对象的。

所以我们的动态代理工具类当中肯定是有一个方法,要返回一个代理对象的。

image-20211002201316610

第三步,代理对象怎么获取呢?

如何创建代理对象呢?

代理原理

在jdk当中,为我们提供了一个Proxy的类。

image-20211002201526930

  • 这个Proxy的类,是在java.lang.reflect这个包当中。

image-20211002201631780

  • 这个反射包中的Proxy类,有一个方法,叫做newProxyInstance方法,可以生成一个新的代理对象。
  • 所以,我们现在用的方式,叫做jdk动态代理。
  • 我们经常用的动态代理有两种,jdk动态代理,cglib动态代理。
    • jdk动态代理要求,目标对象必须要有接口。
    • cglib动态代理,要求,目标对象必须要有继承关系。

image-20211002202131845

  • 这个newProxyInstance方法中有三个参数
    • ClassLoader:
    • interfaces:
    • InvocationHandler
  • 这三个参数,分别是什么意思。
  • 动态代理要有一个原则,不管动态代理对象完成的事情,还是不用代理完成的事情,结果必须是一样的。
  • 一定要保证结果的一致性。
  • 我给动态代理工具类,传进去了目标对象,生成代理对象。
  • 我不管这个代理对象中间如何添油加醋。
  • 最终我要的结果必须是要一致的。
  • 这个一致性原则,对于项目来说,如何保证结果的一致呢?
  • 代理对象完成的结果,跟目标对象完成的结果,要保持一致,怎么做呢?
  • 让代理对象实现功能的时候,最终去调用目标对象的方法,就可以了
  • 第一个参数:ClassLoader类加载器。
  • 代理对象是我们通过jdk动态代理,通过java.lang.reflect.Proxy.newInstance动态生成的一个代理对象。创建一个代理对象。
Returns an instance of a proxy class for the specified interfaces
that dispatches method invocations to the specified invocation handler.

返回指定接口的代理类的实例
将方法调用分派给指定的调用处理程序。
  • 什么叫做动态生成,就是原来没有。这一段代码执行了之后,才有的。
  • 对象是依赖于类创建的。对象是依赖于类存在的。
  • 如果有一个类,你就把它放在那里,不对它进行任何操作,它会加载和执行吗?
  • 一个类要想被加载和执行,必须要有类加载器。
  • 所以,ClassLoader是用来设置,当前代理对象所属类的类加载器。
  • 只有有了这个类加载器,你的代理对象所属类,才能够被加载。
  • 这个类才能够创建代理对象。
  • 去哪里寻找类加载器呢?
  • 我们的动态代理工具类,ProxyUtil这个工具类,就需要类加载器。
  • 那么ProxyUtil这个工具类,和代理对象所属类,可不可以,共用一个类加载器。
  • 一个类加载器是只能够加载一个类吗?不是的。它能够加载多个类。
  • 所以,我们让代理对象所属类,和当前的动态代理工具类,使用一个类加载器。

image-20211002203629401

  • 我们通过this,表示当前的对象,this.getClass()获取这个动态代理工具类,然后通过getClassLoader()获取动态代理工具类的类加载器。
  • 第二个参数叫做interfaces
  • 这个参数,也很好理解,代理对象要帮助我们完成功能,它最起码要知道,你要实现什么功能。
  • 咱们在代码当中表现的就是目标对象中的方法。
  • 目标对象当中的方法,就是实现类,从接口当中继承过来的。
  • 所以要想知道代理对象最终帮助我们完成什么,很简单。
  • 只需要知道目标对象所实现的所有的接口,就可以了。
  • 我要想让一个类和另外的一个类,具有相同的功能, 就让他们实现相同的接口就可以了。
  • 所以第二个参数,就是,mathImpl.getClass().getInterfaces()。

image-20211002204503457

  • 第三个参数,叫做h

image-20211002204525009

  • 第三个参数的类型,叫做InvocationHandler,Invocation叫做执行,Handler叫做处理,InvocationHandler叫做执行处理器。

  • 用来干什么?用来设置,代理对象如何实现目标对象的功能。

  • 第二个参数中已经知道了全部的目标对象的功能。

  • 第三个参数就是要如何实现这样的功能呢?

  • 这里要用到一个接口,就是InvocationHandler接口。

  • 这个接口里面只有一个方法。

  • 那么在这里,我们怎么写第三个参数呢?

  • 第一种方法,让动态代理工具类,实现InvocationHandler接口。然后重写抽象方法。

image-20211002204924025

  • 然后第三个参数这样写:
  • image-20211002205028009
  • 因为第三个参数是需要一个InvocationHanlder。这个InvocationHandler是一个接口。
  • 第三个参数,我们可以使用一个接口的实现类,来进行赋值。
  • 我们可以将当前工具类,变成InvocationHandler的接口实现类。

image-20211002211346161

  • 第三个参数是InvocationHandler,这个接口或者这个接口的实现类,就是一个作用。
  • 当代理实例,需要进行方法调用的时候,由InvocationHandler来执行方法,并返回结果。
Processes a method invocation on a proxy instance and returns the result. 

处理代理实例上的方法调用并返回结果 

This method will be invoked on an invocation handler 
when a method is invoked on a proxy instance that it is associated with.

当在代理实例上调用方法时,此方法将在调用处理程序上调用

使用匿名内部类

image-20211002212216085

  • 功能在java中表现为:方法。
  • 方法要想执行,在反射中,用的就是invoke方法。
  • 所以上面的invoke,就是定义,方法应该如何执行的。
  • InvocationHandler是用来设置,代理对象实现目标对象功能,是如何实现的。
  • 如何去写invoke呢?

invoke方法

第一参数,代理对象。

第二个参数,method,方法。

第三个参数,args,参数。

invoke这个方法大家应该用过好多次,在反射当中,大家通过getMethod,getDeclaredMethod,获得一个method对象的时候,method对象当中,就会有一个invoke方法。invoke里面刚好就是两个参数。第一个参数用来指定调用方法的对象。第二个参数用来指定执行方法的时候的形参列表的。因为java的方法之中是存在重载的,方法同名但是不同参,同名不同参,与返回值无关。

image-20211002213555147

重新解释

image-20211002214411800

image-20211002220936186

package com.atguigu.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyUtil {

	private MathImpl mathImpl;//定义一个目标对象
	
	//获取代理对象
	public Object getProxy() {
		
		/**
		 * 第一个参数:类加载器。代理对象依赖于代理类。Proxy.newProxyInstance中会为我们生成代理类。
		 * loader: the class loader to define the proxy class - 定义代理类的类加载器。
		 * 这个类加载器加载之后,就是class文件,要来反编译。
		 * 自己上网查查sts怎么装一个反编译的工具。我们使用加载,工具类的类加载器,去加载,代理类。
		 */
		ClassLoader loader = this.getClass().getClassLoader();
		
		/**
		 * 第二个参数:获取一个,接口的class对象,所组成的数组。因为是让代理对象,实现目标对象的功能。目标对象是一个类,类中的方法是,接口继承给它的。
		 * 知道了目标对象所实现的所有的接口,就知道了接口中所有的方法,就知道了目标对象要实现什么功能。
		 * 最终生成的动态代理类,会继承Proxy类,因为java中所有动态生成的代理类,全部都继承Proxy这个类。最终生成的动态代理类,会实现目标对象相同的接口MathI。
		 */
		Class[] interfaces = mathImpl.getClass().getInterfaces();
		

		/**
		 * Proxy.newProxyInstance方法的目的是:返回代理对象。
		 * 这个代理对象是,由指定接口的代理类生成的。
		 * 这个代理对象的方法,是给指定的InvocationHandler执行的。
		 * 动态代理类实现接口后,继承抽象方法,抽象方法要重写,代理对象是如何实现这几个功能。
		 */
		Object proxyObject = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				return method.invoke(mathImpl, args);//根本没有用proxy代理对象,而是直接使用目标对象mathImpl,执行它的方法。
			}
		});
		
		return proxyObject;
	}
}

动态代理总结

动态代理