Spring第两天学习笔记

org.springframework spring-webmvc 5.3.9

1、spring

1.1、简介

  • spring:春天 ===>给软件行业带来了春天

  • 2002年,首次推出了spring框架的雏形:interface21

  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。

  • [Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612),Spring Framework创始人,著名作者,它是悉尼大学音乐学的博士

  • Spring是面向切面编程(AOP)和控制反转(IoC)的容器框架。

  • spring设计理念:使现有的技术更加容易使用,本社是一个大杂烩,整合了现有的技术框架

  • SSH:struct2 + spring + hibernate

  • SSM:springmvc + spring + mybatis

官网:https://spring.io/projects/spring-framework

官方下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring

GitHub:https://github.com/spring-projects/spring-framework

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.19</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.19</version>
</dependency>

导webmvc,maven就会帮我们导入其它的所依赖的包

1.2、优点

  • spring是一个开源的免费的框架(容器)
  • spring是一个轻量级的、非入侵式的框架
  • 两大特性:控制反转(IOC)、面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

总结:spring就是一个轻量级的控制反转和面向切面编程的框架

1.3、组成

在这里插入图片描述

1.4、拓展

在spring的官网有这个介绍:现代化的Java开发,就是基于spring的开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BMlLYcNT-1651941805629)(C:/Users/24173/AppData/Roaming/Typora/typora-user-images/image-20220506172444451.png)]

  • Spring Boot

    • 一个快速开发的脚手架;

    • 基于Spring Boot可以快速开发单个微服务;

    • 约定大于配置。

  • Spring Cloud

    • Spring Cloud是基于Spring Boot实现的;

因为现在大多数公司都在使用Spring Boot进行快速开发,学习Spring Boot的前提,需要完全掌握Spring及SpringMVC。承上启下的作用

弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱”。

2、IOC理论推导

原来业务的步骤:

  1. UserDao 接口
  2. UserDaoImpl 实现类
  3. UserService 业务接口
  4. UserServiceImpl 业务实现类

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码。如果程序代码量足够大,修改一次的成本代价很大。

已经发生了革命性的变化!

public class UserServiceImpl implements UserService{
/*
    //使用组合将UserDao引到service里面
    private UserDao userDao = new UserDaoImpl();
*/

    private UserDao userDao;
    //将new UserDaoImpl();隐藏,利用set进行动态实现值的注入  ===>  IOC控制反转
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        //真正是业务层调用dao层的方法
        userDao.getUser();
    }
}
  • 之前,程序是主动创建对象,控制权在程序员手里
  • 现在,利用set进行动态实现值的注入,程序不在具有主动性,而是变成了被动的接收对象

这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建。系统的耦合性大大降低!可以更加专注于业务的实现!!这是IOC的原型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DH6LJtEu-1651941805630)(C:/Users/24173/AppData/Roaming/Typora/typora-user-images/image-20220506181625090.png)]

2.1、IOC本质

IOC是一种思想,而依赖注入(DI)只是它的一种实现。

Spring过程:Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象。XML配置Bean时,Bean的定义信息和实现是分离的,而是采用注解的方式将两者合为一体,最新版的Spring已经可以零配置实现IOC,主要是因为Bean的定义信息以注解的形式定义在实现类中。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式,Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)

img

3、Hello Spring

实体类

public class Hello {
    private String str;

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}

spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
    使用spring来创建我们的对象,在spring中这些都称为bean

    类型 变量名 = new 类型();
    Hello hello = new Hello();

    bean = 对象   new Hello();
    id=变量名
    class=类型/new的对象
    property=给对象中的属性赋值
    -->
    <bean id="hello" class="com.bei.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>

</beans>

测试类

public class MyTest {
    @Test
    public void hello(){
        //获取spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象现在都在spring中管理了,我们要使用直接去里面取出来
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello);
    }
}

思考问题?

  • Hello对象是谁创建的?
    hello对象是由Spring创建的.

  • Hello 对象的属性是怎么设置的?
    hello对象的属性是由Spring容器设置的。

这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的.
反转(ioc):程序本身不创建对象,而变成被动的接收对象.
依赖注入(di):就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收.可以通过new ClassPathXmlApplicationContext()去浏览一下底层源码。

OK,到了现在,我们彻底不用在程序中改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,说白了就是:对象由Spring来创建,管理,装配

4、IOC创建对象的方式

  1. 默认使用无参构造创建对象

  2. 假设我们要使用有参构造创建对象

    • 下标赋值

      <!--第一种:下标赋值-->
      <bean id="user" class="com.bei.pojo.User">
          <constructor-arg index="0" value="一北北"/>
      </bean>
      
    • 类型赋值

      <!--第二种:type类型赋值   不建议使用-->
      <bean id="user" class="com.bei.pojo.User">
         <constructor-arg type="java.lang.String" value="一北北"/>
      </bean>
      
    • 参数名

      <!--第三种:直接通过参数名来设置-->
      <bean id="user" class="com.bei.pojo.User">
         <constructor-arg name="name" value="一北北"/>
      </bean>
      

总结:在配置文件加载时,spring容器中管理的对象就已经初始化了

5、Spring配置

5.1、别名

<!--   别名-->
<alias name="user" alias="alias"/>
@Test
public void test(){
    ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml");
    //User user = (User)Context.getBean("user");
    User user = (User)Context.getBean("alias");
    user.show();
}

5.2、bean的配置

<!--
   id:bean的唯一标识符,对象名
   class:bean对象所对应的全限定名:包名+类型
   name:也是别名,且可以同时取多个别名
-->
<bean id="user" class="com.bei.pojo.User" name="user1,user2;user3 u4">
    <property name="name" value="一北吧"/>
</bean>

5.3、import

这个import,一般用于团队开发使用,它可以将多个配置文件,导入合并为一个

假设,现在项目中有多个人开发,这三个人负责不同的类开发。不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的

  • beans1

  • beans2

  • beans3

  • applicationContext.xml

    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>
    

使用的时候,直接使用总的配置即可

6、依赖注入(DI)

6.1、构造器注入

前面已说,不再赘述

6.2、set方式注入【重点】

  • 依赖注入:set注入
    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象中的所有属性由容器来注入

【环境搭建】

  1. 复杂类型

    public class Address {
        private String address;
    
        public Address(String address) {
            this.address = address;
        }
    
        public Address() {
        }
    }
    
  2. 真实测试对象

    public class Student {
        private String name;
    
        private Address address;
    
        private String[] books;
        private List<String> hobbys;
        private Map<String,String> card;
        private Set<String> games;
        private String wife;
        private Properties info;
    }
    
  3. beans.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--***********************************************-->
        <bean id="address" class="com.bei.pojo.Address"/>
    
        <bean id="student" class="com.bei.pojo.Student">
            <!--第一种,普通值注入,value-->
            <property name="name" value="一北"/>
            <!--第二种,bean注入,ref-->
            <property name="address" ref="address"/>
            <!--数组-->
            <property name="books">
                <array>
                    <value>红楼梦</value>
                    <value>西游记</value>
                    <value>水浒传</value>
                    <value>戏三国</value>
                </array>
            </property>
            <!--list-->
            <property name="hobbys">
                <list>
                    <value>足球</value>
                    <value>篮球</value>
                    <value>排球</value>
                    <value>码码</value>
                </list>
            </property>
            <!--map-->
            <property name="card">
                <map>
                    <entry key="身份证" value="3290231212212122134"/>
                    <entry key="银行卡" value="1910-2121-2129-2121"/>
                </map>
            </property>
            <!--set-->
            <property name="games">
                <set>
                    <value>LOL</value>
                    <value>王者荣耀</value>
                </set>
            </property>
            <!--null-->
            <property name="wife">
                <null/>
            </property>
            <!--properties-->
            <property name="info">
                <props>
                    <prop key="学号">3120002988</prop>
                    <prop key="性别"></prop>
                    <prop key="username">小李</prop>
                    <prop key="password">abc123</prop>
                </props>
            </property>
            
        </bean>
    
    </beans>
    
  4. 测试类

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    
        Student student = (Student) context.getBean("student");
    
        System.out.println("student.getName() = " + student.getName());
    }
    

6.3、其它方式注入

我们可以使用p、c命名空间注入

官方解释:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zYwc3IMZ-1651941805632)(C:/Users/24173/AppData/Roaming/Typora/typora-user-images/image-20220507125823492.png)]

配置使用:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间注入,可以直接注入属性的值:property-->
    <bean id="user" class="com.bei.pojo.User" p:name="一北" p:age="18"/>
    <!--   c命名空间注入,构造器注入:constructor-arg-->
    <bean id="user2" class="com.bei.pojo.User" c:name="一北" c:age="20"/>

</beans>

测试:

@Test
public void test2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
    User user = context.getBean("user2", User.class);
    System.out.println("user = " + user);
}

【注意】p、c命名空间不能直接使用,需要导入xml约束

6.4、bean的作用域(scopes)

ScopeDescription
singleton(Default) Scopes a single bean definition to a single object instance for each Spring IoCcontainer.
prototypeScopes a single bean definition to any number of object instances.
requestScopes a single bean definition to the lifecycle of a single HTTP request. That is,each HTTP request has its own instance of a bean created off the back of a single beandefinition. Only valid in the context of a web-aware SpringApplicationContext.
sessionScopes a single bean definition to the lifecycle of an HTTPSession. Only valid inthe context of a web-aware SpringApplicationContext.
applicationScopes a single bean definition to the lifecycle of aServletContext. Only valid inthe context of a web-aware SpringApplicationContext.
websocketScopes a single bean definition to the lifecycle of aWebSocket. Only valid inthe context of a web-aware SpringApplicationContext.
  1. 单例模式[singleton](Spring的默认机制)

    <bean id="user2" class="com.bei.pojo.User" c:name="一北" c:age="20" scope="singleton"/>
    
  2. 原型模式[prototype]:每次从容器中get的时候都会产生一个新对象

    <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
    
  3. 剩下的request、session、application。只能在web开发中使用到

7、bean的自动装配

  • 自动装配时Spring满足bean依赖的一种方式;

  • Spring会在上下文中自动寻找,并给bean装配属性

    在Spring中有三种装配方式

  1. 在xml中显示地配置
  2. 在java中显示配置
  3. 隐式地自动装配bean【重要】

7.1、测试

  1. 环境搭建:一个人有两个宠物

7.2、byName自动装配

<!--
    byName:自动装配方式之一,将复合类型中的属性名,在Spring容器中查找到与之对应的beanid
    byType:自动装配方式之一,将复合类型中的属性名的类型,在Spring容器中查找到与之对应的beanid的类型
            但byType必须保证该类型全局唯一
-->
    <bean id="person" class="com.bei.pojo.Person" autowire="byName" >
        <property name="name" value="一北宝贝"/>
    </bean>

7.3、byType自动装配

<bean id="cat" class="com.bei.pojo.Cat"/>
<bean id="dog" class="com.bei.pojo.Dog"/>
 <bean id="person" class="com.bei.pojo.Person" autowire="byType" >
        <property name="name" value="一北宝贝"/>
    </bean>

【小结】

  • 在byName的时候,必须保证所有bean的id唯一,且要和自动注入的属性的set方法后的值一致
  • 在byType的时候,必须保证所有bean的class唯一,且要和自动注入的属性的类型一致

7.4、注解实现自动装配

jdk1.5支持注解,Spring2.5就支持注解了

The introduction of annotation-based configuration raised the question of whether thisapproach is “better” than XML.

使用注解之前必做:

  1. 导入约束:context约束

  2. 配置注解的支持:<context:annotation-config/>【重点】

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
    	
        <!--注解的支持-->
        <context:annotation-config/>
    
    </beans>
    

    @Autowired

    直接在用在复合类的属性(或相应的set方法)上即可,使用注解甚至不需要写set方法,因为注解是用反射来实现的。

    @Data
    public class Person {
        private String name;
        @Autowired
        private Cat cat;
        @Autowired
        private Dog dog;
    }
    

    使用@Autowired,我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合相应的名字byName。

    常识:

    @Nullable 若一个字段标记了该注解,则这个字段可以为null
    
    public @interface Autowired {
        boolean required() default true;
    }
    

    测试代码:

    @Data
    public class Person {
        private String name;
    
        //如果显示地定义了Autowired的required属性为false,则说明这个对象可以为null,否则不允许为空
        @Autowired(required = false)
        private Cat cat;
        @Autowired
        private Dog dog;
    
    }
    

    如果@Autowired,自动装配的环境比较复杂,自动装配无法通过一个注解(@Autowired)完成的时候,我们可以使用@Qualifier(“value”)取配合它一起使用,指定一个唯一的bean对象的注入

    @Data
    public class Person {
        private String name;
    
        @Autowired
        @Qualifier("cat")
        private Cat cat;
        @Autowired
        private Dog dog;
    }
    

    @Resource注解

    @Data
    public class Person {
        private String name;
        @Resource(name = "cat")
        private Cat cat;
        @Resource
        private Dog dog;
    }
    

【小结】@Resource和@Autowired的区别

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired 通过byType的方式实现,而且必须要求这个对象存在【常用】
  • @Resource 默认通过byName的方式实现,如果找不到名字,则用byType实现,若两个都找不到,则报错【常用】
  • 执行顺序不同,@Autowired 通过byType的方式实现,类型-名字。@Resource 默认通过byName的方式实现,名字-类型

8、使用注解开发

在Spring 4之后,如果要使用注解开发,必须要先保证AOP的包已经导入

使用注解需要导入context约束,增加注解的支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--注解的支持-->
    <context:annotation-config/>

</beans>
  1. bean

  2. 属性如何注入

    @Component //等价于  <bean id="user" class="com.bei.pojo.User"/>
    public class User {
        //相当于     <property name="name" value="一北宝贝"/>
        @Value("一北宝贝")//优先级更高
        public String name="一北";
    
    }
    

    其中@Value也可以放在相应的set方法上

  3. 衍生的注解 – 都会将修饰的类注册到IOC容器里面

    • pojo 【@Component】 有几个衍生的注解,我们在web开发中,会按照mvc三层架构分层

    • dao 【@Repository】

    • service 【@Service】

    • controller 【@Controller】

    【注】:这四个注解的功能是一样的,都是代表将某个类注册到Spring中,装配bean

  4. 自动装配

    • @Autowired:自动装配类型,名字
      如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(“value”)
    • @Resource:自动装配名字,类型
    • @Nullable: 若一个字段标记了该注解,则这个字段可以为null
  5. 作用域

    @Component //等价于  <bean id="user" class="com.bei.pojo.User"/>
    @Scope("prototype")
    public class User {
        //相当于     <property name="name" value="一北宝贝"/>
        @Value("一北宝贝")//优先级更高
        public String name="一北";
    
    }
    
  6. 小结

    xml与注解:

    • xml具有普适性,适用于任何场合,维护简单方便;
    • 注解不是自己的类使用不了,维护相对复杂;

    xml与注解最佳搭配:

    • xml用来管理bean

    • 注解只负责完成属性的注入

    • 我们在使用的过程中,只需要注意一个问题,若要让注解生效,就需要开启注解的支持,即下面两句话

      <!--注解的支持-->
      <context:annotation-config/>
      <!--指定要扫描的包,这个包下的注解就会生效-->
      <context:component-scan base-package="com.bei"/>
      

9、 使用Java的方式配置Spring

我们现在可以完全不使用Spring的xml配置了,全部交给Java来做

JavaConfig是Spring的一个子项目,在Spring 4之后,它成为了一个核心功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9uJ6YX5-1651941805633)(C:/Users/24173/AppData/Roaming/Typora/typora-user-images/image-20220507180608731.png)]

  • 实体类
@Data
//这个注解就是说明这个类被Spring接管了,已注册容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }
    @Value("一北宝贝")//属性注入值
    public void setName(String name) {
        this.name = name;
    }
}
  • 配置类
@Configuration 
//这个同样会被Spring容器托管,因为它本质上也是一个@Component
//@Configuration代表这个类是一个配置类,和我们之前看的beans.xml等价
@ComponentScan("com.bei.pojo")
@Import(BeiConfig2.class)
public class BeiConfig {
    //注册一个bean,等价于bean标签
    //方法名等价于bean标签中的id属性
    //方法的返回值等价于bean标签中的class属性
    @Bean
    public User myUser(){
        return new User();
    }
}
  • 测试类
public class MyTest {
    @Test
    public void test(){
        //若完全使用注解(配置类)的方式去做,我们就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(BeiConfig.class);

        User user = context.getBean("myUser",User.class);
        System.out.println(user.getName());
    }
}

这种纯Java的配置方式在SpringBoot中随处可见

10、代理模式

为什么需要学习代理模式?

因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】

代理模式的分类:

  • 静态代理:
  • 动态代理:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V1vEa2q6-1651941805634)(C:/Users/24173/AppData/Roaming/Typora/typora-user-images/image-20220507201856188.png)]

10.1、静态代理

角色分析:

  • 抽象角色:一般会用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人!

代码实现:

  1. 抽象接口:

    public interface Rent {
        public void rent();
    }
    
  2. 真实角色:

    public class Host implements Rent{
        private String name;
    
        public Host(String name) {
            this.name = name;
        }
    
        @Override
        public void rent() {
            System.out.println("房东"+name+"出租房子");
        }
    }
    
  3. 代理角色:

    public class Proxy implements Rent{
        private Host host;
        
        public Proxy(Host host) {
            this.host = host;
        }
    
        @Override
        public void rent() {
            System.out.println("代理人操作**************");
            host.rent();
            System.out.println("**********************");
        }
        public void showHouse(){
            System.out.println("中介带你看房");
        }
    }
    
  4. 客户访问代理角色:

    public class Client {
        public static void main(String[] args) {
            Proxy proxy = new Proxy(new Host("贝贝"));
            proxy.showHouse();
            proxy.rent();
        }
    }
    
  5. 测试结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PrCTvYrY-1651941805635)(C:/Users/24173/AppData/Roaming/Typora/typora-user-images/image-20220507205838774.png)]

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共的业务交给了代理角色!实现了业务的分工
  • 公共业务发生拓展的时候,方便集中管理

代理模式的缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍;但也不一定,有可能多个真实角色由同一个代理人代理

10.2、加深代理模式的理解

聊聊AOP:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kqw9hm1g-1651941805636)(C:/Users/24173/AppData/Roaming/Typora/typora-user-images/image-20220507232053997.png)]

代码

  1. 抽象接口:

    public interface UserService {
        public void add();
        public void del();
        public void update();
        public void query();
    }
    
  2. 真实类

    public class UserServiceImpl implements UserService{
    
        @Override
        public void add() {
            System.out.println("增加了一个用户");
        }
    
        @Override
        public void del() {
            System.out.println("删除了一个用户");
        }
    
        @Override
        public void update() {
            System.out.println("修改了一个用户");
        }
    
        @Override
        public void query() {
            System.out.println("查询了一个用户");
        }
    }
    
  3. 代理类

    public class UserServiceProxy implements UserService{
        private UserServiceImpl userService;
    
        //通过set一个组合类的方法来实现代理
        public void setUserService(UserServiceImpl userService) {
            this.userService = userService;
        }
    
        @Override
        public void add() {
            log("add");
            userService.add();
        }
    
        @Override
        public void del() {
            log("del");
            userService.del();
        }
    
        @Override
        public void update() {
            log("update");
            userService.update();
        }
    
        @Override
        public void query() {
            log("query");
            userService.query();
        }
        //日志方法
        public void log(String fun){
            System.out.println("[debug] 使用了"+fun+"方法");
        }
    }
    
  4. 客户访问代理类

    public class Client {
        public static void main(String[] args) {
            //创建一个真实类
            UserServiceImpl userService = new UserServiceImpl();
            //创建一个代理类
            UserServiceProxy userServiceProxy = new UserServiceProxy();
            //中介准备代理房东
            userServiceProxy.setUserService(userService);
            //中介实现代理后执行业务
            userServiceProxy.add();
        }
    }
    

10.3、动态代理

  • 动态代理和静态代理角色都一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理;基于类的动态代理
    • 基于接口: ——JDK 动态代理 【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

需要了解两个类: Proxy:代理、InvocationHandler:调用处理程序

通用万能动态代理代码实现:

  1. 抽象接口:

    public interface UserService {
        public void add();
        public void del();
        public void update();
        public void query();
    }
    
  2. 真实角色:

    public class UserServiceImpl implements UserService{
    
        @Override
        public void add() {
            System.out.println("增加了一个用户");
        }
    
        @Override
        public void del() {
            System.out.println("删除了一个用户");
        }
    
        @Override
        public void update() {
            System.out.println("修改了一个用户");
        }
    
        @Override
        public void query() {
            System.out.println("查询了一个用户");
        }
    }
    
  3. 调用处理程序类,用其生成代理类:

    //用这个类自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //得到代理类
        public Object getProxy() {
                Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
            return o;
        }
    
        @Override//处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log(method.getName());
            //动态代理的本质,就是使用反射的机制来实现的
            Object o = method.invoke(target, args);
            return o;
        }
        public void log(String msg){
            System.out.println("执行了"+msg+"方法");
        }
    }
    
  4. 客户访问代理角色:

    public class Client {
        public static void main(String[] args) {
            //真实角色
            UserServiceImpl userService = new UserServiceImpl();
            //代理角色,不存在
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
            //设置要代理的对象
            pih.setTarget(userService);
            //动态生成代理类
            UserService proxy = (UserService) pih.getProxy();
    
            proxy.add();
    
        }
    }
    

通用动态代理类的具体化:

  1. 抽象接口:

    public interface Rent {
        public void rent();
    }
    
  2. 真实角色:

    public class Host implements Rent {
        private String name;
    
        public Host(String name) {
            this.name = name;
        }
    
        @Override
        public void rent() {
            System.out.println("房东"+name+"出租房子");
        }
    }
    
  3. 调用处理程序类,用其生成代理类:

    //用这个类自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Rent rent;
    
        public void setRent(Rent rent) {
            System.out.println("setRent方法");
            this.rent = rent;
        }
    
        //得到代理类
        public Object getProxy() {
            System.out.println("getProxy方法");
                Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
            return o;
        }
    
        @Override//处理代理实例,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("invoke方法");
            seeHouse();
            //动态代理的本质,就是使用反射的机制来实现的
            Object o = method.invoke(rent, args);
            return o;
        }
        public void seeHouse(){
            System.out.println("中介带客户看房子");
        }
    }
    
  4. 客户访问代理角色:

    public class Client {
        public static void main(String[] args) {
            //真实角色
            Host host = new Host("贝贝宝贝");
            //代理角色:现在没有
            //用代理调用处理程序生成一个代理类
            ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
            //通过调用处理程序角色来处理我们要调用的接口对象
            proxyInvocationHandler.setRent(host);//代理真实角色
    
            //这里的proxy代理实例就是动态生成的,我们并没有写
            Rent proxy = (Rent) proxyInvocationHandler.getProxy();
    
           /*调用顺序
            setRent方法:实现代理
            getProxy方法:获取代理
            invoke方法:调用代理
            */
            proxy.rent();
    
        }
    }
    

动态代理的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务;
  • 公共的业务交给了代理角色!实现了业务的分工;
  • 公共业务发生拓展的时候,方便集中管理;
  • 一个动态代理类代理的是一个接口,对应的是一类业务;
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring是一个开源的Java框架,用于构建企业级应用程序。它提供了一种轻量级的、非侵入式的开发方式,通过依赖注入和面向切面编程等特性,简化了Java应用程序的开发过程。 以下是关于Spring学习的一些笔记: 1. IoC(控制反转):Spring通过IoC容器管理对象的创建和依赖关系的注入。通过配置文件或注解,将对象的创建和依赖关系的维护交给Spring容器来管理,降低了组件之间的耦合度。 2. DI(依赖注入):Spring通过依赖注入将对象之间的依赖关系解耦。通过构造函数、Setter方法或注解,将依赖的对象注入到目标对象中,使得对象之间的关系更加灵活和可维护。 3. AOP(面向切面编程):Spring提供了AOP的支持,可以将与业务逻辑无关的横切关注点(如日志、事务管理等)从业务逻辑中分离出来,提高了代码的可重用性和可维护性。 4. MVC(模型-视图-控制器):Spring提供了一个MVC框架,用于构建Web应用程序。通过DispatcherServlet、Controller、ViewResolver等组件,实现了请求的分发和处理,将业务逻辑和视图展示进行了分离。 5. JDBC和ORM支持:Spring提供了对JDBC和ORM框架(如Hibernate、MyBatis)的集成支持,简化了数据库访问的操作,提高了开发效率。 6. 事务管理:Spring提供了对事务的支持,通过声明式事务管理和编程式事务管理,实现了对数据库事务的控制和管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值