Day 024_Spring

Spring

1. 简介

  • Spring:春天,给软件行业带来的春天
  • 2002年,Rod Johnson首次推出了Spring框架的雏形:interface21框架
  • 2004.3.24发布Spring框架
  • Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
    • SSH:Struct2 + Spring + Hibernate
    • SSM:SpringMvc + Spring + Mybatis

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

官方下载地址:https://repo.spring.io/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.2.8.RELEASE</version>
</dependency>

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

2. 优点

  • Spring是一个开源的免费的框架(容器)

  • Spring是一个轻量级的、非入侵的框架

  • 控制反转(IOC:Inversion of Control),面向切面编程(AOP)

  • 支持事务的处理,对框架整合的支持

    总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

3. 组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJVOKggx-1596717794909)(/Users/hxh/Desktop/hxh/note/picture/Spring七大模块.gif)]

4. 拓展

  • 在Spring的官网有这个介绍:现代化的Java开发,基于Spring的开发

  • SpringBoot:构建一切

    • 一个快速开发的脚手架
    • 基于SpringBoot可以快速的开发单个微服务
    • 约定大于配置(同maven)
  • Spring Cloud:协调一切

    • 基于SpringBoot实现的

    学习SpringBoot的前提,需要完全掌握Spring及SpringMVC

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

IOC理论推导

  1. UserDao 接口

  2. UserDaoImopl 实现类

  3. UserService 业务接口

  4. UserServiceImpl 业务实现类

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

​ 我们使用一个set接口实现:已经发生了革命性的变化。

private UserDao userDao;

//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao) {
    
    
    this.userDao = userDao;
}
  • 之前private UserDao userDao= new UserDaoOracleImpl();程序是主动创建对象,控制权在程序猿手上

  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象

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

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

​ 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

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

HelloSpring

<!--使用Spring来创建对象,在Spring中这些都成为Bean
类型 变量名 = new 类型();
Hello hello = new Hello;

id = 变量名
class = new 的对象
property相当于给对象中的属性设置一个值
bean = 对象  new Hello();-->

<bean id="hello" class="com.hxh.pojo.Hello">
    <property name="str" value="Spring"/>
</bean>

beans.xml(xxx.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="mysqlImpl" class="com.hxh.dao.UserDaoMysqlImpl"/>
    <bean id="oracleImpl" class="com.hxh.dao.UserDaoOracleImpl"/>

    <bean id="UserServiceImpl" class="com.hxh.service.UserServiceImpl">
        <!-- ref:引用Spring容器中创建好的对象
        value:具体的值,基本数据类型
				直接修改下面的内容即可-->
        <property name="userDao" ref="oracleImpl"/>
    </bean>

</beans>
  • hello对象由Spring创建

  • hello对象的属性是由Spring容器设置的

  • 这个过程就叫控制反转

    • 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring创建的
    • 反转:程序本身不创建对象,而是变成被动的接收对象
  • 依赖注入:就是利用set方法来进行注入的

  • IOC是一种编程思想,由主动的编程编程被动的接收

  • 可以通过new ClassPathXmlApplicationContext去浏览一下底层源码

  • 我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改。

IOC:对象由Spring来创建,管理,装配

IOC创建对象的方式

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

public User(){
    
    
    System.out.println("User的无参构造");
}

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

public User(String name){
    this.name=name;
}
  1. 下标赋值

    <!--下标赋值-->
    <bean id="user" class="com.hxh.pojo.User">
        <constructor-arg index="0" value="hxh-下标赋值"/>
    </bean>
    
  2. 通过类型创建,不建议使用(如果有两个String时没办法设置)

    <!--通过类型创建,不建议使用-->
    <bean id="user" class="com.hxh.pojo.User">
        <constructor-arg type="java.lang.String" value="hxh-类型"/>
    </bean>
    
  3. 直接通过参数名来设置

<bean id="user" class="com.hxh.pojo.User">
    <constructor-arg name="name" value="hxh-参数"/>
</bean>

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

//Spring容器就类似于婚介网站
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Spring配置

1. 别名alias

<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="abcd"/>

2. Bean的配置

<!--id:bean的唯一标识符,也就是相当于我们学的对象名
classL:bean对象所对应的权限定名:包名+类型
name:也是别名,name可以同时取多个别名,alias一对一
name可以用逗号分割,也可以通过空格,分号分割-->
<bean id="UserT" class="com.hxh.pojo.UserT" name="user2,u2">
    <property name="name" value="测试name别名"/>
</bean>

3. import

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

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

​ applicationContext.xml

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

​ 使用的时候,直接使用总的配置就可以了

依赖注入

1. 构造器注入

2. Set方式注入(重点)

  • 依赖注入:Set注入

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

    • 复杂类型

      package com.hxh.pojo;
      
      public class Address {
              
              
          private String address;
      
        	//getter setter
          public String getAddress() {
              
              
              return address;
          }
      
          public void setAddress(String address) {
              
              
              this.address = address;
          }
      }
      
    • 真实测试对象

      package com.hxh.pojo;
      
      import java.util.*;
      
      public class Student {
              
              
      
          private String name;
          private Address address;
          private String[] books;
          private List<String> hobbies;
          private Map<String,String> card;
          private Set<String> games;
          private Properties info;
          private String wife;
      //后面继续getter setter overwrite
      }
      
    • 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="student" class="com.hxh.pojo.Student">
              <!--第一种,普通值注入,value-->
              <property name="name" value="hxh"/>
          </bean>
      </beans>
      
    • 测试类

      import com.hxh.pojo.Student;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class MyTest {
          public static void main(String[] args) {
              ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
              Student student = (Student) context.getBean("student");
              System.out.println(student.getName());
          }
      }
      
    • 完善注入信息

      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.hxh.pojo.Address">
              <property name="address" value="xi'an"/>
          </bean>
      
          <bean id="student" class="com.hxh.pojo.Student">
              <!--第一种,普通值注入,value-->
              <property name="name" value="hxh"/>
      
              <!--第二种,Bean注入,ref-->
              <property name="address" ref="address"/>
      
              <!--第三种,数组注入,ref-->
              <property name="books">
                  <array>
                      <value>《红楼梦》</value>
                      <value>《西游记》</value>
                      <value>《水浒传》</value>
                      <value>《三国演义》</value>
                  </array>
              </property>
      
              <!--List-->
              <property name="hobbies">
                  <list>
                      <value>听歌</value>
                      <value>敲代码</value>
                      <value>看电影</value>
                  </list>
              </property>
      
              <!--Map-->
              <property name="card">
                  <map>
                      <entry key="身份证" value="111111"/>
                      <entry key="银行卡" value="666666"/>
                  </map>
              </property>
      
              <!--Set-->
              <property name="games">
                  <set>
                      <value>LOL</value>
                      <value>COC</value>
                      <value>BOB</value>
                  </set>
              </property>
      
              <!--null-->
              <property name="wife">
                  <null/>
              </property>
      
              <!--Properties-->
              <property name="info">
                  <props>
                      <prop key="学号">123456</prop>
                      <prop key="username">小明</prop>
                      <prop key="password">12341234</prop>
                  </props>
              </property>
           </bean>
      </beans>
      

3. 拓展方式

我们可以使用P命名空间和C命名空间进行注入

官方解释:https://docs.spring.io/spring/docs/5.2.8.RELEASE/spring-framework-reference/core.html#beans-p-namespace

​ 1.4.2 XML Shortcut with the p-namespace

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       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">

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

</beans>

测试:

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

注意点:P命名和C命名空间不能直接使用,需要导入xml约束

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

4. bean的作用域

  1. Singleton:Spring默认单例模式

    <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
    
  2. Prototype:原型模式

    每次从容器中get的时候都会产生一个新对象

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

Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并将自动给bean装配属性

在Spring有三种自动装配的方式:

  1. 在xml中显示的配置
  2. 在java中显示配置
  3. 隐式的自动装配bean(重要)

1. 测试

  1. 环境搭建
    • 一个人有两个宠物(三个对象,放进bean里)
package com.hxh.pojo;

public class People {
    
    
    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
    
    
        return cat;
    }

    public void setCat(Cat cat) {
    
    
        this.cat = cat;
    }

    public Dog getDog() {
    
    
        return dog;
    }

    public void setDog(Dog dog) {
    
    
        this.dog = dog;
    }

    public String getName() {
    
    
        return name;
    }

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

    @Override
    public String toString() {
    
    
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

2. 自动装配

  • byName:区分大小写!会自动在容器上下文中自动查找和自己对象set方法后面的值对应的bean id
    <bean id="cat" class="com.hxh.pojo.Cat"/>
    <bean id="dog" class="com.hxh.pojo.Dog"/>

    <!--会自动在容器上下文中自动查找和自己对象set方法后面的值对应的bean id-->
    <bean id="people" class="com.hxh.pojo.People" autowire="byName">
        <property name="name" value="hxh"/>
<!--        <property name="dog" ref="dog"/>-->
<!--        <property name="cat" ref="cat"/>-->
    </bean>

​ 使用了autowire,下面类似的就不用重复写,autowire会自动查找set方面后面的值,名字要对应

<property name="dog" ref="dog"/>
<property name="cat" ref="cat"/>
  • byType:会自动在容器上下文中自动查找和自己属性类型相同的bean 必须保证类型全局唯一,id可以省略

    <bean class="com.hxh.pojo.Cat"/>
    <bean id="dog22" class="com.hxh.pojo.Dog"/>
    
    <!--byName:会自动在容器上下文中自动查找和自己对象set方法后面的值对应的bean id
    byType:会自动在容器上下文中自动查找和自己属性类型相同的bean  必须保证类型全局唯一,id可以省略-->
    <bean id="people" class="com.hxh.pojo.People" autowire="byType">
        <property name="name" value="hxh"/>
    </bean>
    

总结:

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

3. 使用注解实现自动装配

jkd1.5支持的注解,Spring2.5就支持注解的

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

要使用注解须知:

  1. 导入约束:context约束

  2. 配置注解的支持:

    <?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方式上使用
  • 使用Autowired我们可以不用编写set方法,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName
public class People {
    
    
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}

科普:

@Nullable 字段标记了这个注解,说明这个字段可以为null
public @interface Autowired {
    
    

   /**
    * Declares whether the annotated dependency is required.
    * <p>Defaults to {@code true}.
    */
   boolean required() default true;

}

官网定义:

public class SimpleMovieLister {
    
    

    private MovieFinder movieFinder;

    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder) {
    
    
        this.movieFinder = movieFinder;
    }

    // ...
}

@Autowired(required = false):如果定义了required = false说明这个值允许为null,空值时不会报错

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成时,我么你可以通过使用@Qualifier(value=“xxx“)去配置@Autowired的使用,指定一个唯一的bean对象注入

public class People {
    
    
    @Autowired
    @Qualifier(value = "cat1")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog1")
    private Dog dog;
    private String name;
}

@Resource

public class People {
    
    
    @Resource(name = "cat1")//查找cat2
    private Cat cat;
    @Resource
    private Dog dog;
    private String name;
}

先通过id查找,没有的话通过class查找,都没有才会报错

小结:

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

使用注解开发

  • 在Spring4之后,要使用注解开发,必须要保证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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
           https://www.springframework.rg/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:annotation-config/>
    
</beans>
  1. bean

  2. 属性如何注入

@Component
public class User {
    
    
    public String name;

    //相当于<property name="name" value="hxh"/>
    @Value("hxh")
    public void setName(String name) {
    
    
        this.name = name;
    }
}
  1. 衍生的注解

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

    • dao【@Repository】
    • service 【@Service】
    • controller【@Controller】

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

  2. 自动装配

  • @Autowired
  • @Nullable
  • @Resource
  1. 作用域
  • @Component
    @Scope("singleton")
    public class User {
          
          
        public String name;
    
        //相当于<property name="name" value="hxh"/>
        @Value("hxh")
        public void setName(String name) {
          
          
            this.name = name;
        }
    }
    
  1. 小结
  • xml与注解:

    • xml更加方便,适用于任何场合,维护简单方便
    • 注解 不是自己的类使用不了,维护相对复杂
  • xml与注解的最佳实践:

    • xml用来管理Bean

    • 注解只负责属性的注入

    • 我们在使用的过程中,只需要注意一个问题:必须让注解生效就需要开启注解的支持

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

使用Java的方式配置Spring

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

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

实体类

package com.hxh.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component//这个注解的意思就是说明这个类被Spring接管了,注册到了容器中
public class User {
    
    
    private String name;

    public String getName() {
    
    
        return name;
    }

    @Value("hxh")
    public void setName(String name) {
    
    
        this.name = name;
    }

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

配置类

package com.hxh.config;

import com.hxh.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个
// @Component,@Configuration代表这是一个配置类,就和我们之前看的beans.xml是一样的
@ComponentScan("com.hxh")
@Import(MyConfig2.class)
public class MyConfig {
    
    
    //注册一个bean,就相当于我们之前写的一个bean标签
    // 这个方法的名字就相当于bean标签中的id属性
    // 这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User getUser(){
    
    
        return new User();//就是返回要注入到bean的对象
    }
}

测试类

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        //如果完全使用类配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User getUser = (User) context.getBean("user");
        System.out.println(getUser.getName());
    }
}

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

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层。【SpringAOP和SpringMVC】

代理模式的分类:

  • 静态代理
  • 动态代理

静态代理

角色分析:

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

代码步骤:

  1. 接口
package com.hxh.demo01;

//租房
public interface Rent {
    
    
    public void rent();
}
  1. 真实角色
package com.hxh.demo01;

//房东
public class Host implements Rent{
    
    

    public void rent() {
    
    
        System.out.println("房东要出租房子");
    }
}
  1. 代理角色
package com.hxh.demo01;

public class Proxy implements Rent{
    
    
    private Host host;

    public Proxy(){
    
    

    }
    public Proxy(Host host) {
    
    
        this.host = host;
    }

    public void rent() {
    
    
        seeHouse();
        host.rent();
        hetong();
        fare();
    }
    //看房
    public void seeHouse(){
    
    
        System.out.println("中介带你看房");
    }

    //收中介费
    public void fare(){
    
    
        System.out.println("收中介费");
    }

    //签合同
    public void hetong(){
    
    
        System.out.println("签合同");
    }
}
  1. 客户端访问代理角色
package com.hxh.demo01;

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //房东要租房子
        Host host = new Host();
        //代理,中介帮房东租房子,但是代理角色一般会有一些附属操作
        Proxy proxy = new Proxy(host);
        //你不用面对房东,直接找中介租房即可
        proxy.rent();
    }
}

代理模式的好处:

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

缺点:

  • 一个真实角色会产生一个代理角色,代码量会翻倍,开发效率会变低
  • 解决方法:动态代理

动态代理

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

  • 动态代理类是动态生成的,不是我们直接写好的

  • 动态代理分为两大类

    • 基于接口的动态代理:jdk【我们使用jdk】
    • 基于类的动态代理:cglib
    • Java字节码实现:JAVAssist:简单快速,直接使用java编码的形式
  • 需要了解两个类:

    • Proxy:代理
    • InvocationHandler:是由代理实例的调用处理程序实现的接口,invoke方法
package com.hxh.demo04;

import com.hxh.demo03.Rent;

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

//等会会用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    
    

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
    
    
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    

        //动态代理的本质就是使用反射机制实现
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg){
    
    
        System.out.println("执行了"+msg+"方法");
    }
}
package com.hxh.demo04;

import com.hxh.demo02.UserService;
import com.hxh.demo02.UserServiceImpl;

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.delete();
    }
}

总结:

  • 动态代理的好处:除静态代理的优点外
    • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
    • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

AOP

1. 什么是AOP

​ AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效果。

2. AOP在Spring中的作用

提供声明式业务,允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志、安全、缓存、事务等等
  • 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。log类
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。log里面的一个方法
  • 目标(Target):被通知对象。一个接口或者一个方法
  • 代理(Proxy):向目标对象应用通知之后创建的对象。代理类
  • 切入点(PointCut):切面通知执行的“地点”的定义。在哪个地方执行
  • 连接点(JointPoint):与切入点匹配的执行点。

3. 使用Spring实现AOP

【重点】使用AOP导入,需要导入一个依赖包

<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>

方式一:使用Spring的API接口(只要SpringAPI接口实现)

<!--方式一:使用原生Spring API接口-->
<!--配置AOP:需要导入AOP的约束-->
<aop:config>
    <!--切入点 expression:表达式,execution(要执行的位置 * * *)-->
    <aop:pointcut id="pointcut" expression="execution(* com.hxh.service.UserServiceImpl.*(..))"/>
    <!--执行环绕增加,把log类切入到方法中-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

方式二:使用自定义来实现AOP(主要是切面定义)

<!--方式二:自定义 API接口-->
<bean id="diy" class="com.hxh.Diy.DiyPointCut"/>
<aop:config>
    <!--自定义切面,ref要引用的类-->
    <aop:aspect ref="diy">
        <!--切入点 通知 目标-->
        <aop:pointcut id="point" expression="execution(* com.hxh.service.UserServiceImpl.*(..))"/>
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

方式三:使用注解实现

xml文件:

<!--方式三-->
<bean id="annotationPointCut" class="com.hxh.Diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
package com.hxh.Diy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//方式三:使用注解方式实现AOP
@Aspect//标注这个类是一个切面
public class AnnotationPointCut {
    
    
    @Before("execution(* com.hxh.service.UserServiceImpl.*(..))")
    public void before(){
    
    
        System.out.println("方法执行前");
    }
    @After("execution(* com.hxh.service.UserServiceImpl.*(..))")
    public void after(){
    
    
        System.out.println("方法执行后");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.hxh.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
    
    
        System.out.println("环绕前");

//        Signature signature = jp.getSignature();//获得签名
//        System.out.println("signature:"+signature);

        //执行方法
        Object proceed = jp.proceed();
        System.out.println("环绕后");

//        System.out.println(proceed);
    }
}

整合Mybatis

步骤:

  1. 导入相关jar包

    • junit
    • mybatis
    • mysql
    • spring相关的
    • aop织入
    • mybatis-spring 【new】
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-study</artifactId>
            <groupId>com.hxh</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>spring-10-mybatis</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.21</version>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.8.RELEASE</version>
            </dependency>
    
            <!--Spring操作数据库的话还需要一个spring-jdbc-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.8.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.4</version>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.5</version>
            </dependency>
            
        </dependencies>
    
    
    </project>
    
  2. 编写配置文件

1. 回忆Mybatis

  1. 编写实体类
package com.hxh.pojo;

import lombok.Data;

@Data
public class User {
    
    
    private int id;
    private String name;
    private String pwd;
}
  1. 编写核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC
        "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--configuration核心配置文件-->
<configuration>
    <typeAliases>
        <package name="com.hxh.pojo"/>
    </typeAliases>

    <!--连接数据库-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.hxh.mapper.UserMapper"/>
    </mappers>

</configuration>
  1. 编写接口
package com.hxh.mapper;

import com.hxh.pojo.User;

import java.util.List;

public interface UserMapper {
    
    
    public List<User> selectUser();
}
  1. 编写Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--configuration核心配置文件-->
<mapper namespace="com.hxh.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select * from mybatis.user;
    </select>
</mapper>
  1. 测试
import com.hxh.mapper.UserMapper;
import com.hxh.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {
    
    
    @Test
    public void test() throws IOException {
    
    
        String resources = "mybatis-config.xml";
        InputStream in = Resources.getResourceAsStream(resources);

        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sessionFactory.openSession(true);

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.selectUser();

        for (User user : userList) {
    
    
            System.out.println(user);

        }
    }
}

2. Mybatis-Spring

http://mybatis.org/spring/zh/index.html#官方文档

  1. 编写数据源配置
  2. sqlSessionFactory
  3. sqlSessionTemplate
  4. 需要给接口加实现类
  5. 将自己写的实现类注入到Spring中
  6. 测试使用

声明式事务

1. 事务:

  • 把一组业务当成一个业务来做。要么都成功,要么都失败
  • 事务在项目开发中十分的重要,设计到数据的一致性问题,不能马虎
  • 确保完整性和一致性

事务的ACID原则:

  • Atomicity:原子性
  • Consistency:一致性
  • Isolation:隔离性
    • 多个业务可能操作同一个资源,互相隔离,防止数据损坏
  • Durability:持久性
    • 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中
  1. 先导入依赖
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.8.RELEASE</version>
    </dependency>

    <!--Spring操作数据库的话还需要一个spring-jdbc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.8.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.5</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
    </dependency>

</dependencies>
  1. 建包建类

2. Spring中的事务管理

  • 声明式事务:AOP 交由容器管理事务
  • 编程式事务:需要在代码中进行事务的管理
  1. 如果不配置事务,可能存在数据提交不一致的情况
  2. 如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务
  3. 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题

猜你喜欢

转载自blog.csdn.net/sssurprise/article/details/107849330
024