一共有四种,singleton,prototype,request,session。
我们设置为单例。
从容器当中获取两次。
我们在对象当中将toString方法注释掉。
toString方法是Object方法。
Object当中的toString方法,默认输出的是内存地址。
从这里就能够看出来,创建的对象是单例的,因为我们打印了两个对象,但是构造方法当中的打印语句,就输出了一次。
如果我们在配置文件当中将scope修改为prototype,再次执行程序,可以看到下面的结果:
如果spring当中有单例模式的bean,在初始化容器的时候,就会创建,这个对象。
杨博超聊自己儿子。
杨博超在老家开了一个母婴店。
进口的奶粉450一罐。纯吃奶粉,不吃母乳的,一星期都要不了,就吃完了,一个月,十罐奶粉。
创建一个普通的类,标注出来生命周期。
在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-销毁");
}
}
为什么,只有三步呢?
这是因为,对象的初始化和销毁的方法,没有被调用。
怎么才能够让他们被调用呢?
非常简单,在我们的配置文件当中,配置bean的时候,绑定上去,就可以了。
我们就指定了对象的初始化方法和销毁方法。
这样就正常了。
没有销毁的原因是,容器关闭的时候,才会销毁bean。
bean的生命周期,默认是有五个步骤。
但是有的程序员还希望在bean初始化前后的时候,做一些其他的操作。
这个就需要bean的后置处理器。
这个接口叫做BeanPostProcesser。
这两个方法,一个是在初始化之后,一个是在初始化之前。
两个参数,一个是Object,一个是String,不知道这两个参数是干什么的。
在java当中,参数名字,都应该是见名知意的。所以这些arg0或者是arg1的参数,不是很好。
我们把实现方法删除了,然后按ctrl+鼠标左键,然后导入一下源码。
导入源码之后,我们再次实现抽象方法,就是下面的效果:
这些传入的参数,是用来干嘛的。
后置处理器是在初始化前后做一些操作。
上面实现方法当中的Object,就是经过你处理之后的,新的bean。
这是为了演示后置处理器。
我们这样写一个后置处理器。
然后我们想办法,将后置处理器,跟spring关联起来。
这样就完成了配置。
比如jdbc。驱动名称,连接地址,用户名和密码放在properties文件中。
数据库连接对象,也可以交给spring管理。
那么spring怎么在xml中配置数据库连接池对象呢?
配置数据库连接池对象。
配置数据库连接池对象的时候,上面的蓝色标注的,就是实现类。
怎么看一下,这个实现类当中的属性呢?通过outline视图。
在这DruidDataSource类当中没有找到对应属性的时候,我们可以去父类当中看看有没有对应的,我们要找的属性。
我们先搜索一下父类:DruidAbstractDataSource。
写一个测试类,测试一下效果:
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是这个版本:
我的connector-java包是这个版本:
mysql-connector-java-5.1.7-bin.jar,这个包,要升级一下。
这里面driver的名字,我们最好是写成jdbc.driver。
这里面property当中,有一个叫做location的name。
用途是,实现页面跳转。
location.href,指定页面跳转的路径。
location.reload,指定页面刷新。
我们就不适用上面的配置方式了。
我们使用下面的写法:
jsp页面的el表达式当中,我们就是使用${},来取值的。作用域当中取值。
在xml当中,是从资源文件当中取值。
mybatis当中,也有${},还有#{},相当于占位符。
测试一下:
没问题的。
导入context的命名空间:
装配是什么意思?
装配就是注入,注入就是赋值。
spring管理的bean就是一个对象,有多个属性和方法,方法是调用,属性是赋值。
spring属性赋值,可以叫做装载,就做注入,就是自动赋值。
自动装配,只针对于非字面量的属性,也就是需要用ref的。
自动装配的属性,都是非字面量的属性。
然后对上面的类,进行封装,创建set和get,重写toString。
正常的。
自动装配就是自动注入。
bean标签当中有一个autowire的属性。
这个就是自动装配的意思。
设置了byName之后,然后重新测试:
效果正常的。
通过name属性,自动装配。
也就是说car和dept属性,通过某种方式,自动赋值。
byName的策略,是什么呢?
就是spring管理的bean当中,bean的id,跟我们的emp的属性名,一致,就能够自动赋值。
只要在spring管理的范围内,有一个bean能够为目标bean的属性赋值,那就自动装配。
比如说:
通过类型自动装配的时候。
第一步:
创建一个car的父类:
目标对象,就是car,修改引用属性:
引用属性的类型,是赋值bean类型的,父类。
测试,是否可以自动装配。
第二步:
创建目标对象,就是car,引用属性类型,对应的dept接口。
目标对象emp当中,car属性类型是CarExtends,是目标bean car的父类。
目标对象emp当中,dept属性类型是DeptI,是目标bean dept的接口。
这个时候,可以自动装配的吗?
测试效果如下:
举例如下:
emp对象有car属性,spring当中有car1和car2,应该自动装配哪个?
byType自动装配时候,spring当中只能出现一个,能赋值的bean。
在,使用byType的过程中,要求spring容器中,只能够有一个,能够为目标对象属性赋值的bean。
byName和byType都不够好。
最大的问题,bean当中添加了autowire属性,作用于bean中所有非字面量属性。
我有时候创建对象,不一定要为所有属性赋值。
问题1:设置autowire属性,会作用域该bean中,所有的非字面量属性。
因此,我们谁都不用。
用过注解吗?Junit的@Test。
servlet的时候@webservlet。web项目,写servlet,要去web.xml中配置,也可以servlet加@webservlet,括号里面写访问路径,相当于web.xml当中配置了servlet。方便。
注解一般以@开头。
今天讲好几个注解。
spring的配置文件,由bean标签组成,组件就是bean。
组件是bean,bean是对象,对象由类产生,是class实例化。
要先确定bean属于哪个class。
四个注解的关系和区别:没有区别,一模一样,名字不一样,标识。
加了注解之后,就相当于在xml当中,自动生成了相对应的bean,这些bean的id就是类的名称的首字母小写为值,class就是类的全限定名。
我们在测试类当中,验证通过id来获取,就是正常的。
通过注解让spring管理bean,第一步加上注解,第二步,spring的xml写context:component-scan,有个属性叫做base-package,里面要写包结构。会在spring的配置文件当中自动生成,相应的bean标签,会有默认的id,以类的首字母小写为值。
这个过程,大家一定要注意。如果说咱们在使用的过程中,只有加了注解,没有扫描,spring不会管理这些类。
这两个步骤是缺一不可的。
所以说,原来在,教学的过程中,有的同学,会问加了注解,会问写了扫描组件标签是什么意思。
这两个东西同时存在才会有意义。
扫描包的时候,basepackage,这个属性里面写的一定是一个包结构。
这个包结构,看着是包,文件系统当中,是多个目录的。
com.atguigu.ioc,当中com就是一个文件夹。
包写越大,扫描到的类也越多,扫描时间长,也很可能做无用功。咱们尽量写的准确一些。
两个包是需要spring管理的,可以具体写出来,用逗号分隔。
如果以后需要管理的包非常多。怎么办呢?
举例:
如果包特别多,单独扫描某些包,或者把某些包排除掉,怎么实现呢?
测试效果是:
这个效果,没有成功。
为什么呢?
如果是要使用include或者exlude的时候,就要设置这个东西。
效果是正常的,成功的。
这是根据类型来进行包含。
这样写,是不行的,一定是要把use-default-filters="false"去除掉,使用exclude-filter的时候。
测试效果是:
context:component-scan:能够写多个包含
context:component-scan:能够写多个排除
不能够包含和排除同时写。
上午的时候,我们写了自动装配,推荐是不要用,也就是说,千万不要在xml当中写自动装配autowire属性。
在控制层写个方法,接收请求。
这里是一个处理,添加用户信息请求的方法。
这里需要调用service。
我们需要通过接口,创建service对象,然后调用service当中的addUser方法:
第二步,我们需要在接口当中定义一个addUser()方法。
第三步,我们需要在serviceImpl当中,实现这个addUser()方法。
在service当中处理完业务逻辑之后,需要将数据,保存到数据库当中的。
这个工作,咱们可以在serviceimpl当中写。但是service都干了,要dao干啥呢。
我们需要在这个serviceimpl类当中,创建userDao类。
第三步,在userDao接口类当中定义一个addUser()方法,然后在userDaoImpl当中实现这个方法。
我们在测试类当中通过uc.addUser()自动调用controller当中的方法。
原来我们通过servlet的dopost方法是自动调用。
因为你后面会大量使用注解来把对象创建工作交给spring,spring会根据注解和组件扫描,自动在xml当中生成bean标签。
但是这些bean标签,我们是看不到的。
所以,我们连bean标签都看不到,我们还使用个鬼的autowire属性。
这个时候,我们怎么实现自动装配呢?
我们可以利用一个组件,叫做@autowired。这个注解,可以加在我们想要自动装配的属性上面。
我们前面讲解自动装配的时候,已经说过了,自动装配是对于对象bean当中的非字面量的属性来进行自动装配的。
我们在我们的controller类当中,我们设置了service属性,这是一个service对象。这是需要自动装配的。
我们在我们的service类当中,我们设置了dao属性,这是一个dao对象。这也是需要自动装配的。
自动装配的意思就是进行赋值,从spring管理的bean当中,找到对应的bean,然后进行赋值。
所以,下面颜色标注的内容,我们是不需要写的。
我们既然就已经写了@autowired的时候,我们是不需要进行new对象的。
这种写法是没有必要的。
我们可以直接写成下面的例子:
在这里,我们只需要创建一个接口对象,就可以了。
这里,是通过什么类型来进行自动装配的呢?
上午的时候,我们只有讲解了byName和byType两种方式的自动装配。
byName是在spring管理范围内寻找到对应的id是等于属性值的bean,然后进行赋值。
我们这里的属性就是userService,这是一个接口类,在spring的管理范围内,不会有接口类对应的bean的。
因为接口类是不能够创建对象的。
byType是在spring管理范围内寻找到唯一的一个,属性类型和bean类型一致的、或者属性类型是赋值bean的父类,或者属性类型是赋值bean的接口的情况,实现自动装配的。
我们这里的userService是一个接口的,它有一个实现类,这个实现类就是,userServiceImpl,这个就是属性userService类型UserService的接口类,是只有一个的,是可以自动赋值和装配的。
所以,@Autowired进行自动装配的时候,是通过byType的方式来实现自动装配的。
我们想一种情况,让spring的@Autowired的byType自动装配模式失效。
我们在xml当中写一个bean。
我们写的这个bean就是所谓的赋值bean,这个bean的类型,是目标bean也就是service属性userDao,属性类型的实现类。
是可以给属性进行自动装配赋值的。
现在我们通过@Repository注解的实现类,也是具有自动装配赋值的。
这个时候,我们进行测试的时候,就会报错了:
这是因为byType进行自动装配的时候,是不能够有2个赋值bean的。
假设,我们这样操作:
我现在来仔细描述一下,现在的情况。
第一,现在在目标bean,也就是userServiceImpl类当中有一个属性,属性的名字叫做userDao,属性的类型是userDao。
第二,现在,在spring管理范围内,类型是userDao实现类UserDaoImpl的,有两个bean,一个是@Autowired自动装配的类,一个是在xml当中手动配置的类。自动装配的类的id叫做userDaoImpl,是类的名字的首字母大小的。手动配置的类的id是,我们手动设置成为了userDao。
第三,这个时候,我们进行测试,byType自动装配类型是没有成功的,但是byName自动装配类型是成功的。
第一,我们在userService当中自动装配了userDao属性。
第二,我们将userDaoImpl上的@Repository注解去掉了。
第三,这个时候,spring管理范围内,没有能够给userDao属性自动装配的bean。
这个时候,的错误,是长这个样子的:
如果这样设置,就算是装配不成功,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属性当中,设置,spring自动生成的bean的id。
默认生成的bean,是使用类名的首字母小写,作为id的。
如果设置了value,就会使用你自定义的aaa。
如果你在小括号当中,写了多个属性,你就必须严格按照,属性名等于属性值,这种格式来进行书写。
如果你只写了一个属性,比如说是value属性,那么,也可以省略属性名。
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.
这个时候userService这个目标bean当中的userDao属性,就有了两个可以进行byType自动装配的赋值bean。
一个就是UserDaoImpl,一个就是UserDaoMybatisImpl。
这就是之前手动创建相同类型赋值bean的情况。
我们还可以通过一个注解,指定,我们使用哪一个赋值bean来进行自动装配的。
@Autowired是要和@Qualifier两个注解,同时使用的。
@Qualifier(value=“bean的id”)
这个注解就是指定的意思。
以后具体的情况,就使用具体的方法。
多说一点,看一下,这个@Qualifier注解的源码:
这里面,Target注解后面表示,这个注解,可以用在属性上面,也可以用在方法上面的。
原来我们,通过手动配置xml的时候,说过,配置的bean,都要求,对应的类当中要有set方法,这是 通过set注入的。
但是你发现,我写了@Autowired之后,自动装配的时候,根本就没有在我们的userDao当中写set方法的。
这里的UserServiceImpl就是目标bean,有一个属性,叫做userDao,我们可以直接,在属性上面,进行自动装配,如果有属性有set方法的话,我们可以在set方法上面进行自动装配。
基于注解的组件化管理:
@Component,@Controller-控制层,@Service-业务层,@Repository-持久层
以上四个注解功能完全相同,
不过在实际开发中,要在实现不同功能的类上,加上相应的注解。
完成组件化管理的过程:
1、在需要被spring管理的类上,添加相应的注解。
2、在配置文件中通过
标签对所设置的包结构进行组件扫描,就会将加上注解的类,作为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的部分就已经结束了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e6kM51IS-1633184495828)(https://programzsximages.oss-cn-beijing.aliyuncs.com/ssm/IOC.svg)]
IOC是通过工厂模式实现的。
AOP是通过代理模式实现的。
大家都学过动态代理吗?
学过的。
代理模式要理解还是简单。
咱们理解的话,拿着静态代理理解。
代理模式有三个角色,三个对象。
原始对象
代理对象
目标对象。
生活的例子很多。
我完成某个事,我不想自己动,我找个代理对象,帮我干。
但是有个原则,保证最终的结果不变。
不管自己完成还是代理对象完成,结果不变。
找房子,我白天上班,没时间,我找中介,中介帮我找。
最终的结果,找到房子,不变。
别房子没找到,找到老婆。
天为被,地为床,旁边有人就行。
尽量别找中介。
2014年5月份来北京。
2015年博超找房子。
当时在唐家岭住。
有小公寓。
特别便宜,一个月800,一个单间。
空调、洗衣机、冰箱、热水器、柜子、桌子。
加上水电费,不超过1000。
特别好。
2015年开始,北京建筑各种非法公寓强拆。
当时,博超去上班,出门了很多营地。
有很多广播。
限定几天之内搬走,不搬走,后果自负。
一看就是美国民主党的人。
一看没啥事。
第二天公寓墙就倒了。
问小伙子,都说没事,说了好几年了。
广播三天之内搬走。
第一天空调。
第二天热水器。
第三天床。
真的是这样,博超都丢了。
不敢住了。
睡觉砸进去,有什么办法。
说理有点难。
晚上就搬走了,找房子。
公寓老板都跑到了法国。
找到一个小区,两居室。
和朋友一起。
一个月4000块。
想要住,押一付三行规。
找了中介,押一付三再交一个月,给中介费。
非常贵。
不住。找了个大院,2个屋子,不是小区。
一个月3300元。
上班之后,各个地方找房子房价。
往后结婚了。
两个人去霍营地铁站。
旁边有个公寓。
35平米一个月三千三。
加上水电费,差不多4000块。
就是35平米。
不敢有什么大动作。
又找了个房子。
在昌平。
有个小区功华新村。
一个月3500,是两居室。
去了上海之后,那就不一样了。
上海的房子有的地方确实是贵。
上班在海边,走路10分钟,开车2分钟。
开窗就能看见海。
当时在地图上的最南边。
一个月1800,三居室
一星期一次台风。
台风大,不让出门,别家待了。
路没路灯。
特别偏,还穷。
晚上要开远光。
那边人喜欢晚上出去遛弯。
而且有人特别过分。
正儿八经躺在路中间睡觉。
房子便宜。
大家以后找房子。
最好不要一个人住。
几个人帮衬。
一个人住太贵。
尽量别找中介。
写一下小案例。
先写一个接口,定义一个基本的功能。
然后将加减乘除都放到这个接口当中。
写完接口之后,下面写一个实现类,功能简单写一下。
java当中的除法就是取整,取模就是取余数。
javascript当中的除法就是正规的除法,该有小数点,就有小数点。
需求里面要求,写日志。
日志功能是项目中,经常会用到的功能,会记录变量的变化情况。
这个东西,很有必要。
需求:日志记录,传参是什么,实现的方法是什么,结果是什么。
假设我们这样写日志:
就是用system.out.println语句来模仿,日志。
我们再次执行一下,就有效果了:
如果调用其他方法,也是一样的效果。
这样写好不好?不好。
这是接口的实现类,是应该实现一些功能的。我这里写了很多的日志信息,代码很乱。
写一行功能,加几行日志,这不是有病吗?
这种写法,是不可以的。
不能将日志功能,写到具体实现某个功能的类中。
业务逻辑的类,只写业务逻辑的代码。
这个时候,就应该怎么办?
大家发现日志格式都是差不多。
能不能把日志功能抽取出来。作为一个类。
业务类当中,通过日志类,就可以解决所有的记录日志的问题。
第一步:将日志代码,都要放在日志类中。
第二步:业务类中需要记录的位置,让日志类生效。
这个就需要用,动态代理。
动态代理的作用:能够为任何需要代理的对象,自动生成代理类。
代理模式中有三个元素:原始对象,代理对象,目标对象。
代理对象是生成的。目标对象就是要去做的事。
什么是动态代理,不管目标对象是什么,都可以通过一个X类,生成相对应的代理对象,让代理对象帮助我去完成功能。
这个X就是动态代理。X跟目标对象没有任何关系。
所以这个X更像是一个工具类。能够帮助程序员生成动态代理对象。
第一步,确定目标对象。
在这里例子当中,目标对象就是MathIImplI。就是代理对象要作用的类。
这里面我们的目标对象,就是MathImplI。
但是根据之前的说法,动态代理,是能够为任何的目标对象,生成代理对象的。
所以,我们的目标对象的类型,不能局限在MathImplI这个类型。
所以,我们应该写成Object。
但是,我们暂时先这样写。
第二步,工具类具有获取代理对象的方法。
现在我们是在动态代理工具类当中,是需要根据目标对象,获取代理对象的。
所以我们的动态代理工具类当中肯定是有一个方法,要返回一个代理对象的。
第三步,代理对象怎么获取呢?
如何创建代理对象呢?
在jdk当中,为我们提供了一个Proxy的类。
Returns an instance of a proxy class for the specified interfaces
that dispatches method invocations to the specified invocation handler.
返回指定接口的代理类的实例
将方法调用分派给指定的调用处理程序。
第三个参数的类型,叫做InvocationHandler,Invocation叫做执行,Handler叫做处理,InvocationHandler叫做执行处理器。
用来干什么?用来设置,代理对象如何实现目标对象的功能。
第二个参数中已经知道了全部的目标对象的功能。
第三个参数就是要如何实现这样的功能呢?
这里要用到一个接口,就是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.
当在代理实例上调用方法时,此方法将在调用处理程序上调用
第一参数,代理对象。
第二个参数,method,方法。
第三个参数,args,参数。
invoke这个方法大家应该用过好多次,在反射当中,大家通过getMethod,getDeclaredMethod,获得一个method对象的时候,method对象当中,就会有一个invoke方法。invoke里面刚好就是两个参数。第一个参数用来指定调用方法的对象。第二个参数用来指定执行方法的时候的形参列表的。因为java的方法之中是存在重载的,方法同名但是不同参,同名不同参,与返回值无关。
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;
}
}