Spring学习笔记02

1.Bean的自动装配

1.1 Bean的作用域(Bean Scopes)

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为Bean

简单来讲,Bean就是由IoC容器初始化、装配及管理的对象

类别 说明
singleton 在Spring IoC容器中仅存在一个Bean实例,Bean以单例模式存在,默认值
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean(),相当于执行new XxxBean()
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境

几种作用域中,request,session作用域仅在基于Web的应用中使用(不必关心你所采用的是什么Web应用框架),只能用在基于web的Spring ApplicationContext环境

  1. Singletion

当一个Bean的作用域为singletion,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例

Singletion是单例类型,就是在创建容器时就同时自动创建了一个bean的对象,不管你是否使用,它都存在了,每次获取到的对象都是同一个对象。注意:Singletion作用域是Spring中的缺省作用域,要在xml中将bean定义为singletion

Spring学习笔记02 - 图1

  1. <bean id="user" class="com.jcsune.pojo.User" c:age="18" c:name="小明" scope="singleton"/>

测试:

  1. @Test
  2. public void testT(){
  3. ApplicationContext context = new ClassPathXmlApplicationContext("beansuser.xml");
  4. User user=(User)context.getBean("user");
  5. User user2=(User)context.getBean("user");
  6. System.out.println(user==user2);
  7. }

结果:

Spring学习笔记02 - 图2

  1. Prototype

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的Bean实例,Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象,根据经验,对有状态的bean应该使用prototype作用域,对无状态的bean则应该使用singleton作用域,在xml中将bean定义为prototype,可以这样配置:

Spring学习笔记02 - 图3

  1. <bean id="user" class="com.jcsune.pojo.User" p:name="小明" p:age="18" scope="prototype"/>
  2. 或者
  3. <bean id="user" class="com.jcsune.pojo.User" p:name="小明" p:age="18" singleton="false"/>

测试:

  1. @Test
  2. public void testT(){
  3. ApplicationContext context = new ClassPathXmlApplicationContext("beansuser.xml");
  4. User user=(User)context.getBean("user");
  5. User user2=(User)context.getBean("user");
  6. System.out.println(user==user2);
  7. }

结果:

Spring学习笔记02 - 图4

  1. Request

当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例:即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成,该作用域仅在基于web的Spring ApplicationContext情形下有效,考虑下面bean定义:

  1. <bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>

针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的loginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化,当处理请求结束,request作用域的bean实例将被销毁

  1. Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效,考虑下面bean定义:

  1. <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效,与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences 创建的实例,将不会看到这些特定于某个HTTP Session的状态变化,当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉

1.2 自动装配说明

自动装配是使用spring满足bean依赖的一种方法

spring会在应用上下文中为某个bean寻找其依赖的bean

Spring中bean有三种装配机制

  1. 在xml中显式配置
  2. 在java中显式配置
  3. 隐式的bean发现机制和自动装配

这里主要介绍第三种:自动化的装配bean

Spring的自动装配需要从两个角度来实现或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean
  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少

推荐不使用自动装配xml配置,而使用注解

1.3 测试环境搭建

  1. 新建一个项目
  2. 新建两个实体类Cat Dog都有一个叫的方法

Cat.java

  1. package com.jcsune.pojo;
  2. public class Cat {
  3. public void shout(){
  4. System.out.println("miao~");
  5. }
  6. }

Dog.java

  1. package com.jcsune.pojo;
  2. public class Dog {
  3. public void shout(){
  4. System.out.println("wang~");
  5. }
  6. }
  1. 新建一个用户类 User
  1. package com.jcsune.pojo;
  2. public class User {
  3. private Cat cat;
  4. private Dog dog;
  5. private String name;
  6. public Cat getCat() {
  7. return cat;
  8. }
  9. public void setCat(Cat cat) {
  10. this.cat = cat;
  11. }
  12. public Dog getDog() {
  13. return dog;
  14. }
  15. public void setDog(Dog dog) {
  16. this.dog = dog;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. }
  1. 编写spring配置文件
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. https://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="dog" class="com.jcsune.pojo.Dog"/>
  7. <bean id="cat" class="com.jcsune.pojo.Cat"/>
  8. <bean id="user" class="com.jcsune.pojo.User">
  9. <property name="cat" ref="cat"/>
  10. <property name="dog" ref="dog"/>
  11. <property name="name" value="jcsune"/>
  12. </bean>
  13. </beans>
  1. 测试
  1. @Test
  2. public void test(){
  3. ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
  4. User user=(User)context.getBean("user");
  5. user.getCat().shout();
  6. user.getDog().shout();
  7. }
  1. 结果

Spring学习笔记02 - 图5

结果正常输出,环境OK

1.4 byName

autowire by Name(按名称自动装配)

由于在手动配置xml过程中常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发的效率降低

采用自动装配将避免这些错误,并且使配置简单化

测试:

  1. 修改bean配置,增加一个属性 autowire=”byName”
  1. <bean id="user" class="com.jcsune.pojo.User" autowire="byName">
  2. <property name="cat" ref="cat"/>
  3. </bean>
  1. 再次测试,结果依旧成功输出
  2. 再将cat的bean id修改为catXXX
  3. 再次测试,执行时报空指针java.lang.NullPointerException,因为按byName规则找不对应set方法。真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针异常

小结:

当一个bean节点带有autowire byName的属性时

  1. 将查找其类中所有的set方法名。例如setCat,获得将set去掉并且首字母小写的字符串,即cat
  2. 去spring容器中寻找是否有此字符串名称id的对象
  3. 如果有,就取出注入;如果没有,就报空指针异常

1.5 byType

autowire by Type(按类型自动装配)

使用autowire by Type首先需要保证:同一类型的对象,在spring容器中唯一,如果不唯一,会报不唯一的异常

测试:

  1. 将user的bean配置修改一下:autowire=”byType”

  2. 测试,正常输出

  3. 在注册一个cat的bean对象

  1. <bean id="dog" class="com.jcsune.pojo.Dog"/>
  2. <bean id="cat" class="com.jcsune.pojo.Cat"/>
  3. <bean id="cat2" class="com.jcsune.pojo.Cat"/>
  4. <bean id="user" class="com.jcsune.pojo.User" autowire="byType">
  5. <property name="name" value="jcsune"/>
  6. </bean>
  1. 测试,报错:NoUniqueBeanDefinitionException
  2. 删掉cat2,将cat的bean名称改掉!测试,因为是按类型装配,所以并不会报异常,也不影响最后的结果,甚至将id属性去掉,也不影响结果

这就是按照类型自动装配

1.6 注解实现自动装配

jdk1.5开始支持注解, spring2.5开始全面支持注解

准备工作:利用注解的方式注入属性

  1. 在spring配置文件中引入context文件头
  2. 开启属性注解支持
  3. Spring学习笔记02 - 图6

@Autowired

  • @Autowired是按类型自动装配的,不支持id匹配
  • 需要导入spring-aop的包
  • 直接在属性上使用即可,也可以在set方式上使用
  • 使用Autowired我们可以不用编写set方法了,前提是你自动装配的属性在IoC(Spring)容器中存在,且符合名字byName

测试:

  1. 将User类中的set方法去掉,使用@Autowired注解
  1. public class User {
  2. @Autowired
  3. private Cat cat;
  4. @Autowired
  5. private Dog dog;
  6. private String name;
  7. public Cat getCat() {
  8. return cat;
  9. }
  10. public Dog getDog() {
  11. return dog;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. }
  1. 此时配置文件中内容:
  1. <bean id="dog" class="com.jcsune.pojo.Dog"/>
  2. <bean id="cat" class="com.jcsune.pojo.Cat"/>
  3. <bean id="user" class="com.jcsune.pojo.User"/>
  1. 测试,成功输出结果

@Autowired(required=false)说明:false,对象可以为null; true,对象必须存对象,不能为nullfont>

  1. //如果允许对象为null,设置required=false,默认为true
  2. @Autowiredrequired=false
  3. private Cat cat

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用

测试实验步骤:

  1. 配置文件修改内容,保证类型存在对象,且名字不为类的默认名字
  1. <bean id="dog1" class="com.jcsune.pojo.Dog"/>
  2. <bean id="dog2" class="com.jcsune.pojo.Dog"/>
  3. <bean id="cat1" class="com.jcsune.pojo.Cat"/>
  4. <bean id="cat2" class="com.jcsune.pojo.Cat"/>
  1. 没有加Qualifier测试,直接报错
  2. 在属性上添加Qualifier注解
  1. @Autowired
  2. @Qualifier(value = "cat2")
  3. private Cat cat;
  4. @Autowired
  5. @Qualifier(value = "dog2")
  6. private Dog dog;

测试成功!

@Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配
  • 其次再进行默认的byName方式进行装配
  • 如果以上都不成功,则按byType的方式自动装配
  • 都不成功,则报异常

实体类:

  1. @Resource(name= "cat2")
  2. private Cat cat;
  3. @Resource
  4. private Dog dog;
  5. private String name;

beans.xml

  1. <bean id="dog" class="com.jcsune.pojo.Dog"/>
  2. <bean id="cat1" class="com.jcsune.pojo.Cat"/>
  3. <bean id="cat2" class="com.jcsune.pojo.Cat"/>
  4. <bean id="user" class="com.jcsune.pojo.User"/>

测试结果:OK!

配置文件2:beans.xml,删掉cat2

  1. <bean id="dog" class="com.kuang.pojo.Dog"/>
  2. <bean id="cat1" class="com.kuang.pojo.Cat"/>

实体类 上只保留注解

  1. @Resource
  2. private Cat cat;
  3. @Resource
  4. private Dog dog;

测试结果:OK!

结论:先进行byName查找,失败;再进行byType查找,成功

小结:@Autowired与@Resource异同

  1. @Autowired 与@Resource都可以用来装配bean,都可以写在字段上,或写在setter方法上

  2. @Autowired默认按类型分配(属于spring规范)默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,比如@Autowired(required=false),如果我们想使用名称装配可以结合@Qualifier注解进行使用

  3. @Resource(属于J2EE复返) 默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配,当找不到与名称匹配的bean时才按照类型进行装配,但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配

它们的作用相同都是用注解方式注入对象,但执行顺序不同@Autowired先byType@Resource先byName

2.使用注解开发

2.1 说明

在Spring4之后,想要使用注解形式,必须要引入aop的包

Spring学习笔记02 - 图7

在配置文件当中,还得要引入一个context约束

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. </beans>

2.2 Bean的实现

我们之前都是使用bean的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

  1. 配置扫描哪些包下的注解
  1. <!-- 指定要扫描的包,这个包下的注解就会生效-->
  2. <context:component-scan base-package="com.jcsune.pojo"/>
  1. 在指定包下编写类,增加注解
  1. @Component("user")
  2. //相当于配置文件中 <bean id="user" class="当前注解的类"/>
  3. public class User {
  4. public String name="jcsune";
  5. }
  1. 测试
  1. @Test
  2. public void test(){
  3. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  4. User user = (User)context.getBean("user");
  5. System.out.println(user.name);
  6. }
  1. 结果

Spring学习笔记02 - 图8

2.3 属性注入

使用注解注入属性

  1. 可以不用提供set方法,直接在名上添加@value(“值”)
  1. @Component
  2. //相当于配置文件中 <bean id="user" class="当前注解的类"/>
  3. public class User {
  4. @Value("jcsune")
  5. // 相当于配置文件中 <property name="name" value="jcsune"/>
  6. public String name;
  7. }
  1. 如果提供了set方法,在set方法上添加@Value(“值”)
  1. @Component
  2. //相当于配置文件中 <bean id="user" class="当前注解的类"/>
  3. public class User {
  4. public String name;
  5. @Value("jcsune")
  6. public void setName(String name) {
  7. this.name = name;
  8. }
  9. }

2.4 衍生注解

我们这些注解,就是替代了在配置文件当中配置步骤而已,更加德 方便快捷

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其他三个注解,功能一样,目前使用哪一个的功能都一样

  • @Controller: web层
  • @Service:service层
  • @Repository: dao层

写上这些注解,就相当于把这个类交给Spring管理分配了

自动装配注解:见1.6

作用域

@scope

  • singleton: 默认的,Spring会采用单例模式创建这个对象,关闭工厂,所有的对象都会销毁
  • prototype:多例模式,关闭工厂,所有的对象不会销毁,内部的垃圾回收机制会回收
  1. @Controller("user")
  2. @Scope("prototype")
  3. public class User {
  4. @Value("jcsune")
  5. public String name;
  6. }

2.5 小结

xml与注解比较

  • XML可以适用于任何场景,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发:推荐最佳实践

  • XML管理Bean
  • 注解完成属性注入
  • 使用过程中,可以不用扫描,扫描是为了类上的注解

作用

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null

2.6 基于Java类进行配置

JavaConfig原来是Spring的一个子项目,它通过java类的方式提供Bean的定义信息,在Spring4的版本,JavaConfig已正式成为Spring4的核心功能

测试:

  1. 编写一个实体类:Dog
  1. public class Dog {
  2. public String name="dog";
  3. }
  1. 新建一个config配置包,编写一个MyConfig配置类
  1. @Configuration //代表这是一个配置类
  2. public class MyConfig {
  3. @Bean //通过方式注册一个bean,这里的返回值就是Bean的类型,方法名就是bean的id
  4. public Dog dog(){
  5. return new Dog();
  6. }
  7. }
  1. 测试
  1. @Test
  2. public void test(){
  3. ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
  4. Dog dog = (Dog) applicationContext.getBean("dog");
  5. System.out.println(dog.name);
  6. }
  1. 结果:

Spring学习笔记02 - 图9

导入其它配置的方法

  1. 我们再编写一个配置类
  1. @Configuration
  2. public class MyConfig2 {
  3. }
  1. 在之前的配置类中我们来选择导入这个配置类
  1. @Configuration //代表这是一个配置类
  2. @Import(MyConfig2.class)
  3. public class MyConfig {
  4. @Bean //通过方式注册一个bean,这里的返回值就是Bean的类型,方法名就是bean的id
  5. public Dog dog(){
  6. return new Dog();
  7. }
  8. }

关于这种java类的配置方式,我们再之后的SpringBoot和Spring Cloud中还会大量看到,我们需要知道这些注解的作用即可!

3.代理模式

为什么要学习代理模式,因为AOP的底层机制就是动态代理

代理模式:

  • 静态代理
  • 动态代理

3.1 静态代理

静态代理角色分析

  • 抽象角色: 一般使用接口或抽象类来实现
  • 真实角色: 被代理的角色
  • 代理角色:代理真实角色;代理真实角色后,一般会做一些附属的操作
  • 客户: 使用代理角色来进行一些操作

以租房为例:

Rent.java:抽象角色

  1. package com.jcsune.demo1;
  2. //抽象角色:租房
  3. public interface Rent {
  4. public void rent();
  5. }

Host.java 真实角色

  1. package com.jcsune.demo1;
  2. //真实角色:房东,房东要出租房子
  3. public class Host implements Rent{
  4. public void rent(){
  5. System.out.println("房屋出租");
  6. }
  7. }

Proxy.java 代理角色

  1. package com.jcsune.demo1;
  2. //代理角色:中介
  3. public class Proxy implements Rent{
  4. private Host host;
  5. public Proxy(){}
  6. public Proxy(Host host){
  7. this.host=host;
  8. }
  9. //租房
  10. public void rent(){
  11. seeHouse();
  12. host.rent();
  13. fare();
  14. }
  15. //看房
  16. public void seeHouse(){
  17. System.out.println("带房客看房");
  18. }
  19. //收中介费
  20. public void fare(){
  21. System.out.println("收中介费");
  22. }
  23. }

Client.java 客户

  1. package com.jcsune.demo1;
  2. //客户类,一般客户都会去找代理
  3. public class Client {
  4. public static void main(String[] args) {
  5. //房东要出租房
  6. Host host=new Host();
  7. //中介帮助房东
  8. Proxy proxy=new Proxy(host);
  9. //去找中介租房
  10. proxy.rent();
  11. }
  12. }

结果:

Spring学习笔记02 - 图10

分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情

静态代理的好处:

  • 可以使我们的真实角色更加纯粹,不再去关注一些公共的事情
  • 公共的业务由代理来完成,实现了业务的分工
  • 公共业务发生扩展时变得更加集中和方便

缺点:

  • 类多了,多了代理类,工作量变大了,开发效率降低

我们想要静态代理的好处,又不想要静态代理的缺点,所以就有了动态代理

3.2 静态代理再理解

练习步骤:

  1. 创建一个抽象角色,比如平时做的用户业务,抽象起来就是增删改查
  1. //抽象角色:增删改查业务
  2. public interface UserService {
  3. public void add();
  4. public void delete();
  5. public void update();
  6. public void query();
  7. }
  1. 我们需要一个真实对象来完成这些增删改查操作
  1. //真实对象,完成增删改查操作的人
  2. public class UserServiceImpl implements UserService{
  3. public void add() {
  4. System.out.println("增加了一个用户");
  5. }
  6. public void delete() {
  7. System.out.println("删除了一个用户");
  8. }
  9. public void update() {
  10. System.out.println("更新了一个用户");
  11. }
  12. public void query() {
  13. System.out.println("查询了一个用户");
  14. }
  15. }
  1. 需求来了,比如我们需要增加一个日志功能,怎么实现
  • 思路:1:在实现类上增加代码【麻烦!】
  • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能是最好的了!
  1. 设置一个代理类来处理日志!代理角色
  1. public class UserServiceProxy implements UserService {
  2. private UserServiceImpl userService;
  3. public void setUserService(UserServiceImpl userService) {
  4. this.userService = userService;
  5. }
  6. public void add() {
  7. log("add");
  8. userService.add();
  9. }
  10. public void delete() {
  11. log("delete");
  12. userService.delete();
  13. }
  14. public void update() {
  15. log("update");
  16. userService.update();
  17. }
  18. public void query() {
  19. log("query");
  20. userService.query();
  21. }
  22. public void log(String msg){
  23. System.out.println("执行了"+msg+"方法");
  24. }
  25. }
  1. 测试访问类
  1. public class Client {
  2. public static void main(String[] args) {
  3. //真实业务
  4. UserServiceImpl userService=new UserServiceImpl();
  5. //代理类
  6. UserServiceProxy proxy=new UserServiceProxy();
  7. //使用代理类实现日志功能
  8. proxy.setUserService(userService);
  9. proxy.add();
  10. }
  11. }
  1. 结果

Spring学习笔记02 - 图11

OK,到了现在代理模式应该没什么问题了,重点是需要理解其中的思想

做到在不改变原来的代码的情况下,实现对原有功能的增强,这是AOP中最核心的思想

AOP;纵向开发,横向开发

Spring学习笔记02 - 图12

3.3 动态代理

  • 动态代理的角色和静态代理的一样

  • 动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的

  • 动态代理分为两类:一类是基于接口动态代理,一类是基于类的动态代理

    • 基于接口的动态代理——-JDK动态代理
    • 基于类的动态代理——cglib
    • 现在用的比较多的是javasist来生成动态代理
    • 我们这里使用JDK的原生代码来实现,其余额的道理都是一样的

JDK的动态代理需要了解两个类

核心:InvocationHandler 和 Proxy ,打开JDK文档了解一下

[InvocationHandler :调用处理程序]

Spring学习笔记02 - 图13

  1. Object invoke(Object proxy,方法 method,Object[] args);
  2. //参数
  3. //proxy-调用该方法的代理实例
  4. //method-所述方法对应于调用代理实例上的接口方法的实例,方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口
  5. //args-包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数,原始类型的参数包含在适当的原始包装器类的实例中例如java.lang.Integer或java.lang.Boolean

[Proxy: 代理]

Spring学习笔记02 - 图14

Spring学习笔记02 - 图15

Spring学习笔记02 - 图16

  1. //生成代理类
  2. public Object getProxy(){
  3. return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
  4. }

代码实现:

抽象角色和真实角色和之前的一样

  1. Rent.java即抽象角色
  1. //抽象角色:租房
  2. public interface Rent {
  3. public void rent();
  4. }
  1. Host.java即真实角色
  1. //真实角色:房东,房东要出租房子
  2. public class Host implements Rent{
  3. public void rent(){
  4. System.out.println("房屋出租");
  5. }
  6. }
  1. ProxyInvocationHandler.java 即代理角色
  1. public class ProxyInvocationHandler implements InvocationHandler {
  2. private Rent rent;
  3. public void setRent(Rent rent) {
  4. this.rent = rent;
  5. }
  6. //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
  7. public Object getProxy(){
  8. return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
  9. }
  10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11. seeHouse();
  12. //核心:本质利用反射实现
  13. Object result = method.invoke(rent,args);
  14. fare();
  15. return result;
  16. }
  17. //看房
  18. public void seeHouse(){
  19. System.out.println("带房客看房");
  20. }
  21. //收中介费
  22. public void fare(){
  23. System.out.println("收中介费");
  24. }
  25. }
  1. Client.java
  1. public class Client {
  2. public static void main(String[] args) {
  3. //真实角色
  4. Host host=new Host();
  5. //代理实例的调用处理程序
  6. ProxyInvocationHandler pih= new ProxyInvocationHandler();
  7. pih.setRent(host);//将真实角色放置进去
  8. Rent proxy=(Rent)pih.getProxy();//动态生成对应的代理类!
  9. proxy.rent();
  10. }
  11. }
  1. 结果

Spring学习笔记02 - 图17

核心:一个动态代理,一般代理某一类业务,一个动态代理可以代理多个类,代理的是接口

3.4 动态代理的好处

静态代理有的它都有,静态代理没有的,它也有

  • 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情
  • 公共的业务由代理来完成,实现了业务的分工
  • 公共业务发生拓展时变得更加集中和方便
  • 一个动态代理,一般代理某一类业务
  • 一个动态代理代理的是多个类,代理的是接口