Spring5

Spring5

1.什么是Spring

  1. 是一个轻量级(jar包少)的开源的JAVAEE框架(容器)

  2. 是为了解决企业应用开发的复杂性

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

  4. 两个核心部分:IOC和AOP

    • IOC:控制反转,把创建对象的过程交给Spring进行管理
    • AOP:面向切面,不修改源代码进行功能增强
  5. Spring的特点

    • 方便解耦(ioc),简化开发
    • Aop编程支持
    • 方便程序测试
    • 方便和其他框架进行整合
    • 方便进行事务操作
    • 降低了API的开发难度

    官网下载地址:https://repo1.maven.org/maven2/org/springframework/spring/

    Github下载地址: GitHub - spring-projects/spring-framework: Spring Framework

    Maven(导Spring web Mvc):

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

    1642993352372

2.IOC理论推导

原来: 在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。

1646986797776

IOC容器:

1646986869377

IOC解耦过程 : “第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心

1646986913835

IOC底层实现:xml,工厂模式,反射

1646988327166

引入:

  1. 首先我们的Dao层和service层

    1643012470603

  2. 当我们业务层Service调用Dao层时,会有一个创建Dao实现类的方法,并且有一个get方法去获取

     public UserDao userDao=new UserDaoImpl();
     //public UserDao userDao=new UserDaoImpl2();  修改的业务
      @Override
        public void getUser() {
            System.out.println(userDao.run());
        }
    
  3. 测试类中

    UserService userService = new UserServiceImpl();
    userService.getUser();
    
  4. 上面的我们就会发现一个弊端,当我们的业务需要修改时,我们必须去修改业务层的代码

  5. 这时就出来了一种解决办法,我们用一个set方法控制创建哪一种业务的对象

    public UserDao userDao;
        public void setUserDao(UserDao userDao){
            this.userDao=userDao;
        }
        @Override
        public void getUser() {
            System.out.println(userDao.run());
        }
    
  6. 测试类时,我们可以先调用set方法来选择不同的业务,这就是IOC的原型

    @Test
        public void test(){
            UserService userService = new UserServiceImpl();
            ((UserServiceImpl)userService).setUserDao(new UserDaoImpl());
            userService.getUser();
        }
    
  7. 我们从主观的去创建对象,变成被动的去接受对象

  8. 这种思想,从本质上解决了问题,我们程序员不需要去管理对象的创建,程序的耦合性降低,可以更加专注在业务的实现上,这就是IOC的原型

3.IOC本质

  • 控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了
  • IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
  • 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

4.HelloSpring

4.1丶实体类

public class Hello {
    private String name;

    public void setName(String name) {   //注意这里set方法必须有(依赖注入 : 就是利用set方法来进行注入的.)
        this.name = name;
    }
    public void show(){
        System.out.println(name+"你好呀!");
    }
}

4.2丶写bean配置文件

<?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">
 <!--
       原来:Hello hello =new Hello();
       现在:bean就代表一个对象,id是对象名,class是映射到对象类的位置(使用完全限定的类名)
            property 设置属性和属性值,一个属性就要对象相对的set
 -->
 <bean id="Hello" class="com.LiuDeHen.pojo.Hello">
     <property name="name" value="spring"/>
     						value=""//具体的值
     						ref=""//使用Spring容器中创建的对象
 </bean>
</beans>

4.3丶测试

Spring提供IOC容器实现的两种方式:(两个接口)

  1. BeanFactory:IOC容器的基本实现,Spring内部接口,不推荐使用

    • 特点:加载配置文件不创建对象,在获取对象时才去创建对象
  2. ApplicationContext:BeanFactory的子接口,提供了更多的方法,推荐

    • 特点:加载配置文件就创建对象,我们web应用时,就需要这种开始就创建好的

    • 常用的实现类:

    • 1646989166972

   public void test(){
//       获取Spring上下文对象!
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//    getBean获取id=Hello的bean对象
        Hello hello = (Hello) context.getBean("Hello");
        hello.show();
    }

4.4丶总结:

  1. 上面的过程 就叫控制反转 :
  2. 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 .
  3. 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
  4. 依赖注入 : 就是利用set方法来进行注入的. (跟上面推导的IOC原型一样)
  5. IOC是一种编程思想 , 由主动的编程变成被动的接收 .
  6. 所谓的IOC就是:Spring去创建,管理,装配对象
  7. Spring提供IOC容器实现的两种方式

5.Bean管理

1.什么是bean管理

  • spring创建对象
  • spring注入属性

2.Bean管理操作的两种方式

  • xml
  • 注解

3.基于xml方式创建对象

  • 使用bean标签

    <bean id="Hello" class="com.LiuDeHen.pojo.Hello">
    
  • bean标签常用属性

    • id:标识
    • class:类全路径(包类路径)
  • 创建对象的时候,默认也是执行无参构造函数

4.基于xml方式注入属性

  • DI注入:依赖注入,就是注入属性

    • 第一种:set方法注入

      • 创建类,定义属性和set方法

      • 之前,就是new对象,调用set方法,注入

      • 现在,xml文件中Bean标签创建对象,属性注入

           <bean id="UserServiceImpl" class="com.LiuDeHen.Service.UserServiceImpl">
        <!--    name:类里的属性名字
                value:属性的值
                ref:属性是对象时用
           -->
               <property name="userDao" ref="UserDao1"/>
           </bean>
        
    • 第二种:有参构造注入

      • 类中创建有参构造

      • 使用子标签

        //第一种方式
        <bean id="Hello" class="com.LiuDeHen.pojo.Hello" name="我是大傻逼 我是小煞笔,我真是傻逼" >
                <constructor-arg name="name" value="spring"/>
                <constructor-arg name="day" value="5"/>
        <!--   当传入的值类型是引用类型,可以用ref     -->
            </bean>
        
        <!-- 第二种方式
         index:表示第几个参数
         -->
            <bean id="Hello" class="com.LiuDeHen.pojo.Hello">
                <constructor-arg index="0" value="Spring"/>
                <constructor-arg index="1" value="5"/>
            </bean>
        

5.p名称注入

6.注入空值和特殊符号

    ```xml

属性中用null标签实现空值


特殊符号

 <value><![CDATA[《南京》]]></value>
   </property>
    ```

7.注入属性--外部Bean,内部Bean和级联赋值

  • 当你从service层类调用Dao层类的方法怎么实现

  • 外部Bean,ref调用其他对象

  • 内部Bean,在bean里的属性里再创建bean

  • 级联:跟外部的区别就是,给调用的那个类赋了值

    外部:
    <bean id="Dao" class="com.LiuDeHen.Dao.Arrecess"/>
    <bean id="service" class="com.LiuDeHen.Dao.Student" >
    <!--  bean注入,ref  -->
        <property name="Dao" ref="Dao"/>
    </bean>
    
    内部:
    <bean id="name" class="com.LiuDeHen.Dao.Student" >
        <property name="arrecess">
            <bean id="a" class="com.LiuDeHen.Dao.Arrecess">
                <property name="age" value="21"/>
            </bean>
        </property>
    </bean>
    
    级联:跟外部的区别就是,给调用的那个类赋了值
    <bean id="service" class="com.LiuDeHen.Dao.Student" >
    <!--  bean注入,ref  -->
        <property name="Dao" ref="Dao"/>
     //第二种方式:需要Dao里有获取另一个对象的get方法
        <property name="Dao.age" value="21"/>
    </bean>
    //第一种方式
    <bean id="Dao" class="com.LiuDeHen.Dao.Arrecess">
     	<property name="age" value="21"/>
    </bean>
    

8.注入集合等

    <bean id="jihe" class="com.LiuDeHen.pojo.Hello">
<!--数组,list,set-->
        <property name="list" >
            <list> <!--set,array -->
                <value>1</value>
                <value>2</value>
                <value>3</value>
            </list>
        </property>
<!--Map-->
        <property name="map" >
            <map>
                <entry key="1" value="1"/>
                <entry key="1" value="1"/>
            </map>
        </property>
        
<!--当插入对象时-->
         <property name="list" >
             <list> <!--set,array -->
               <ref="a"/>
            </list>
        </property>
    </bean>

<bean id="person" class="com.LiuDeHen.pojo.person">
     <property name="age" value="21">
</bean>

注意:

  • 当集合中要注入对象时,我们可以先创建对象,再在里面ref引入

9.Spring内置工厂Bean(FactoryBean)

  • spring有两种Bean:

    • 我们创建的普通Bean

      • 在配置文件中定义Bean类型就是返回的类型
    • 内置的工厂Bean

      • 在配置文件定义Bean类型可以和返回类型不一样

      • 第一步:让某个类作为工厂Bean,实现接口FactoryBean

      • 第二步:实现接口里面实现的方法,在实现的方法中定义返回的Bean类型

        public class User implements FactoryBean<Hello> {
            @Override
            public Hello getObject() throws Exception {
                Hello hello = new Hello();
                return hello;
            }
        }
        

10.bean的作用域

1643076862356

1.我们使用bean创建的对象是单实例对象还是多实例对象

    @Test
    public void test(){
//       获取Spring上下文对象!
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
//    getBean获取id=Hello的bean对象
        Hello hello = (Hello) context.getBean("jihe");
        User user1 = hello.getUser();
        User user2 = hello.getUser();
        System.out.println(user1);
        System.out.println(user2);
    }

2.单实例对象

1647049711510

3.我们可以通过修改bean的属性scope值来修改是单实例还是多实例

  • singleton:单例模式:一加载配置文件就创建对象
    prototype:原型模式:在调用getbean的时候才创建对象
    

11.Bean生命周期

  1. 生命周期:

    从对象创建到销毁的过程

  2. bean的生命周期步骤

    • 通过无参构造创建bean实例
    • 为bean注入属性值或者其他对象的引用
    • 调用bean的初始化方法(需要进行配置初始化的过程)
    • bean可以使用了(获取到对象)
    • 当容器关闭的时候,调用bean的销毁方法(需要配置销毁的方法)
  3. 演示

    public class bean {
        public bean(){
            System.out.println("第一步通过无参构造创建实例");
        }
        private String name;
    
        public void setName(String name) {
            this.name = name;
            System.out.println("第二步,调用了set方法");
        }
    
        public void initMethod(){
            System.out.println("第三步执行初始化方法");
        }
        public void destoryMythod(){
            System.out.println("第五步执行销毁方法");
        }
    }
    
    init-method
    destroy-method:将自己定义的方法配置上去
    <bean id="beans" class="com.LiuDeHen.pojo.bean" init-method="initMethod" destroy-method="destoryMythod">
            <property name="name" value="刘超"/>
        </bean>
    
      @Test
        public void test(){
    //       获取Spring上下文对象!
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
            bean beans = context.getBean("beans", bean.class);
            System.out.println(beans);
            System.out.println("第四步:调用对象");
    
    //        手动销毁
            context.close();
        }
    }
    

    1647051335184

4.后置处理器

  • 会在初始化前后,执行

  • 创建一个类,实现一个接口BeanPostProcessor

  • 具体演示

  • public class myBeanPost implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之前执行");
            return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之后执行");
            return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
    
  •   并在配置文件中创建对象,以后每一个bean就都会执行这个后置处理器
    

5.总结

  • Bean的生命周期一共7步
  • 1647052046105

12.xml自动装配

  • 什么是自动装配

    • 根据你的属性名称或者属性类型,spring会自动将匹配的属性值注入
  • 实例

    <!--
    autowire:会在spring容器上下文自动匹配
    autowire=byname:根据id值到类中匹配来自动装配
    autowire="byType:根据class你的类型来装配
       -->
    <!--    <bean id="Human" class="com.LiuDeHen.Dao.Human" autowire="byName">-->
        <bean id="Human" class="com.LiuDeHen.Dao.Human"  autowire="byName"/>
    <!--当有两个类型一致的时候,就不能用bytype,因为他无法识别是哪一个  -->
        <bean id="dog" class="com.LiuDeHen.Dao.Dog"/>
        <bean id="dog2" class="com.LiuDeHen.Dao.Dog"/>
        <bean id="cat1" class="com.LiuDeHen.Dao.Cat"/>
        <bean id="cat" class="com.LiuDeHen.Dao.Cat"/>
    

13.连接数据库

先导包(Druid德鲁伊)

  • 直接配置

    <!--直接配置连接池-->
        <bean id="datesource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="password" value="123"/>
            <property name="url" value="jdbc:mysql//localhost:3306/mybaties"/>
            <property name="name" value="root"/>
        </bean>
    
  • 引入外部属性文件配置(properties)

    • 先导入约束

       xmlns:context="http://www.springframework.org/schema/context"
      		http://www.springframework.org/schema/context
              https://www.springframework.org/schema/context/spring-context.xsd
      
    
    
  • 配置文件

    prop.driverClassName=com.mysql.jdbc.Driver
    prop.password=123
    prop.url=jdbc:mysql//localhost:3306/mybaties
    prop.name=root
    
  
- 引入
  
    ```xml
    <!--  引入  -->
        <context:property-placeholder location="classpath:1.properties"/>
    <!--直接配置连接池-->
        <bean id="datesource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${prop.driverClassName}"/>
            <property name="password" value="${prop.password}"/>
            <property name="url" value="${prop.url}"/>
            <property name="name" value="${prop.name}"/>
        </bean>

14.Bean创建对象(基于注解)

常用注解:
Spring---基于注解开发 - SegmentFault 思否
使用:
Spring5:@Autowired注解、@Resource注解和@Service注解 - 五月的仓颉 - 博客园 (cnblogs.com)

  • 什么是注解

    • 注解是代码的特殊标记,格式:@注解名(属性名称=值,属性名称2=值2)
    • 可以加在类,方法,属性上
    • 目的:简化xml配置
  • Spring针对Bean管理中创建对象提供的注解

    • @Component
    • @Service:业务层
    • @Controller:控制层
    • @Repository:Dao层
    • 功能一样,都是用来创建Bean实例
  • 基于注解方式实现对象的创建

    • 第一步,引入依赖

      1647070626963

    • 第二步,开启组件扫描(需要开启context依赖)

      <!--  扫描  
      当扫描多个包,可以用逗号隔开
      -->
      <context:component-scan base-package="com.LiuDeHen.Dao"/>
      
    • 第三步,使用注解,创建对象实例

      //value属性值可以省略不写
      //默认就是类名首字母小写
      //Cat----cat
      @Component(value="cat")  //相当于<bean id="cat" class="扫描">
      public class Cat {
          public void shout(){
              System.out.println("猫在叫");
          }
      }
      
    • 测试

      public class myTest {
          @Test
          public void test(){
           ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
          //这个cat就是注解后的value值    
              Cat cat = applicationContext.getBean("cat", Cat.class);
              cat.shout();
          }
      }
      

15.Bean注入属性(注解)

  • @Autowired:根据类型进行自动装配

    • 首先必须得创建这个对象

    • 再使用注解找到这个类型自动装配

      @Repository
      public class UserDao {
          public String run(){
            return "UserDao";
         }
      }
      
      @Repository
      public class UserService {
      
      //    不需要set方法(内部帮我们解决了)
          @Autowired
          UserDao userDao;
      
          public void run(){
              System.out.println("service");
              System.out.println(userDao.run());
          }
      }
      
      
  • @Qualifier:根据属性名称进行注入

    • 跟@Autowired一起使用,因为Autowired是跟据类型,可能有类型相同时,就需要这个注解

          @Autowired
          @Qualifier("Dog")
          UserDao userDao;
      
  • @Resource:可以根据类型,也可以根据名称注入 (不是spring的包,是java的扩展包的注解)

    //    @Resource
          @Resource(name="userDao")
        UserDao userDao;
    
  • @Value:注入普通属性

    @Repository
    public class UserDao {
        @Value(value="你好")
        private String name;
    
        public String run(){
          return name;
        }
    }
    

16.完全注解开发(Springboot常用)

  • 创建配置类,代替xml文件

    @Configuration      //相当于xml文件
    @ComponentScan(basePackages = {"com.LiuDeHen"})  //相当于扫描
    public class more {
    }
    
  • 测试类:AnnotationConfigApplicationContext

 @Test
    public void test() {
        ApplicationContext context = new AnnotationConfigApplicationContext(more.class);
        UserService bean = context.getBean("userService", UserService.class);
        bean.run();
    }

6.阶段总结

1.什么是spring

  - spring是一个开源轻量级框架
  - 为了简化企业开发

2.spring核心

  - **IOC容器**
  - **AOP切面编程**

3.IOC容器的推导以及意义

  - 原本
      - 在业务层创建Dao层的对象
      - 每使用一个对象创建一次
  - IOC容器的由来
      - 在业务层创建一个set方法,参数为Dao的接口,返回一个Dao对象(多态)
      - 在我们的测试类中,不同的对象只需要设置set里的Dao对象即可
- 意义:**由主动的创建对象,变成我们可以被动的管理对象**,降低耦合性
- **IOC:控制反转**

4.怎么用spring使用IOC容器

4.1丶IOC是一种容器(思想),DI是注入(手段)

      - set注入:
          - 首先必须要有这个set方法
          - 再在property属性中就可以了
      - 构造器注入
          - 首先要有相对应的构造器,默认无参
          - 使用构造器的标签constructor-arg
          - 有使用对应参数名(推荐),参数下标,参数类型多种匹配方式 
      - bean作用域:
          - 属性:scope
          - 有单例,所有对象共用:singleton
          - 原型:单对象独享:prototype

4.2丶使用xml的方式创建

      - 创建实体类
      - 创建bean.xml文件,导入spring容器配置
      - 设置xml文件中的bean,id为唯一标识,class为类的路径,一个bean为一个对象
      - bean中标签property为属性
          - 基础类型:name为属性名,value为值
          - 其他类型,list就是又list标签,array就是array标签等
    - 测试类创建spring容器上下文对象 new ClassPathXmlApplicationContext("xml文件")

4.3丶自动装配(隐式创建):

  - xml和使用java都是显示创建

  - 常用有:byName,ByType

  - autowire=byname:根据id值来自动装配

  - autowire="byType:根据class你的类型来装配

4.4丶使用注解创建

  - 在xml文件中

    ```xml
    <!--扫描包    -->    
    <context:component-scan base-package="com.LiuDeHen"/>
    其他的bean都
    ```

  - 在对应类上使用注解**@Component**,就可以**创建bean**对象

  - 使用@value:给属性赋值

  - 自动装配@Autowired

4.5丶使用全java的方式(在springboot很常用)

  - 创建一个实体类
  - 创建一个配置类,有一个get方法,返回的就是实体类的对象
  - get方法上有一个注解**@Bean**,**创建**一个bean对象,类上有一个注解**@Configurable**实现自动装配
  - 这个类就是之前的xml文件,每一个get方法就是一个xml里的bean对象,方法名是id,返回值是class

7.代理模式

什么是代理模式?

  • 实际的例子:我们在登录,注册的时候,验证码是手机短信,不是我们发送,而是移动,电信等公司面向社会提供发送短信的功能
  • 开发中的例子有a,b(代理类),c(目标类)类,a类不能直接(或者不适合)调用c类,但是a类能调用b类,b类能调用c类

为什么要学代理模式?

  • AOP底层就是代理模式
  • 功能增强:在原有的功能上,新增功能
  • 控制访问:代理类不让你访问目标

代理模式的分类

  • 静态代理
  • 动态代理

1643254816638

12.1丶静态代理

角色分析

  • 抽象角色:一般使用接口和抽象类,在这个实例里就是一个出租房接口
  • 真实角色:被代理的角色,这里是房东
  • 代理角色:代理真实角色,代理角色后,会有一些其他操作,这里是中介
  • 客户:访问代理角色的人,这里是客户
抽象角色:
//买卖房接口
public interface Rent {
    void rent();
}

真实角色:
//房东类
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东卖房");
    }
}

代理角色:
//代理类
public class proxy implements Rent{
    private Host H;
    public proxy(Host host){
        H=host;
    }
//继承的父类方法
    @Override
    public void rent() {
        H.rent();
    }
    //自己的方法
    public void sou(){
        System.out.println("收取中介费");
    }
}

客户:
//客户类
public class client{
    public static void main(String[] args) {
        proxy proxy = new proxy(new Host());
        proxy.rent();
    }
}

代理模式的好处:

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

缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍,效率变低

12.2丶加深理解

AOP:面向切面编程,横向开发,再不修改代码的情况下,增加业务

1643273015239

12.3丶动态代理

  • 动态代理角色和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口:----JDK动态代理(我们在这里使用,反射)
      • 什么是动态代理?
        • 使用jdk的反射机制,创建对象的能力,创建的是代理类的对象
        • 动态:在程序执行的过程中,调用jdk提供的方法才能创建代理类的对象
        • 使用jdk反射包中的三个类和接口实现动态代理的功能
        • 反射包java.lang.reflect三个类:InvocationHandler,Method,Proxy
        • InvocationHandler接口就实现一个方法:invoke(),代理类实现的功能就写在里面
        • Method类:Method.invoke()执行目标方法
        • Proxy类:核心对象,创建代理对象
          • 静态方法:newProxyInstance()创建对象
    • 基于类:cglib代理
    • java字节码实现:javasist

实现动态代理的步骤

  1. 创建接口,定义目标类要完成的功能
  2. 创建目标类
  3. 创建InvocationHandler接口的实现类,在Invoke()方法中代理类的功能,并返回代理类
    • 调用目标方法
    • 增强功能
  4. 使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型
卖房接口类
public interface Rent {
public void rent();
}
    
房东类
    public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("我是房东我要卖房");
    }
}

代理类(动态代理就是控制这里的res,会根据传入的接口和参数返回是哪一个类,从而实现动态)
    //1.实现接口InvocationHandler,重写invoke方法实现代理类要做的功能
 public class proxy implements InvocationHandler {
     //2.创建目标类实现的接口
    private Rent rent;
    proxy(Rent rent){
       this.rent=rent;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res=null;
//        三个参数,proxy:代理类,method:代理类实现的方法,args:参数
        res=method.invoke(rent,args);
//        3.可以增加一些额外方法
        if (res!=null){

        }
//        返回这个类
        return res;
    }
}
   
客户类    
    
public class client{
    public static void main(String[] args) {
//        1.创建目标对象
        Rent r = new Host();
//        2.自己写的实现InvocationHandle接口的类
        InvocationHandler handler = new proxy(r);
//        3.创建代理对象,并转成接口
        Rent o = (Rent)Proxy.newProxyInstance(r.getClass().getClassLoader(),
                r.getClass().getInterfaces(), handler);
//        4.通过代理执行方法
        o.rent();
    }
    

好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共也就交给了代理角色,实现了业务的分工
  • 公共业务发生拓展的时候,方便集中管理且不修改原来的代码
  • 一个接口代表的是一类业务,修改功能只需要修改极少代码

8.AOP

1.什么是AOP

  • 面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,降低耦合度,提高程序可重用性,提高开发效率

  • 通俗描述:不修改源代码,就可以在主干功能里添加功能

    1647074471894

2.丶AOP术语:

  • 连接点(JointPoint) :类里面的方法哪些可以被增强,就是连接点

  • 切入点(PointCut) ︰实际被真正增强的方法

  • 通知(Advice) :

    • 实际增强的逻辑部分称为通知(增强)
    • 通知有多种类型
      • 前置通知
      • 后置通知
      • 环绕通知
      • 异常通知
      • 最终通知 finally(不管怎么样都会执行)
  • 切面(ASPECT)︰把通知应用到切入点的过程

    1643509356706

3丶Aop操作准备

  • Spring框架一般都是基于Aspectj实现AOP操作的
    • Aspectj不是Spring的组成部分,是独立的AOP框架,一般把Aspectj和Spring一起使用
  • 基于Aspectj的Aop操作
    • XMl配置文件实现
    • 注解实现
  • 导入依赖
<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
</dependency>
  • 切入点表达式
    • 作用:知道是对哪个类的哪个方法进行增强
    • 语法结构:execution(【权限修饰符】【返回类型】【类全路径【方法名称】【参数列表】)
    • 举例
      • 对com.LiuDeHen.dao.Book的所有方法进行增强
        • execution(* com.LiuDeHen.dao.Book.*(..))
        • *表示所有
        • 返回值类型可省略
        • (..)表示参数列表

4.AOP操作(Aspectj注解)

  1. 导入依赖和导入约束,导入约束时下面两个就修改最后那一点,比如beans改为aop

    • 导入依赖时遇到的问题

      • 导入aspectjweaver包

      • 不要导aspect包,不然会报异常

        nested exception is java.lang.NoClassDefFoundError:org/aspectj/weaver/reflect/ReflectionWorld$Refle
        
           <dependency>
              <groupId> org.aspectj</groupId >
              <artifactId> aspectjweaver</artifactId >
              <version> 1.6.11</version >
            </dependency>
        

1643781073695

  1. 创建普通类和代理类(@Aspect)

    //普通类
    @Component
    public class User {
        public void run(){
            System.out.println("你好");
        }
    }
    
    //创建代理类
    @Component
    @Aspect //配置切面
    //@EnableAspectJAutoProxy(proxyTargetClass = true)跟xml文件里自动找到代理类一样的作用
    //默认false即jdk代理,true即cglib代理,即使默认false,被代理对象没有接口也会采用cglib
    public class UserProxy {
    
        @Before("execution(* com.LiuDeHen.Dao.User.run(..))")
        public void before(){
            System.out.println("Before通知");
        }
    
    //        最终通知(有异常也执行)
        @After("execution(* com.LiuDeHen.Dao.User.run(..))")
        public void After(){
            System.out.println("After通知");
        }
    
    //    后置通知(返回通知,有异常不执行)
        @AfterReturning("execution(* com.LiuDeHen.Dao.User.run(..))")
        public void AfterReturning(){
            System.out.println("AfterReturning通知");
        }
    //    异常通知
        @AfterThrowing("execution(* com.LiuDeHen.Dao.User.run(..))")
        public void AfterThrowing(){
            System.out.println("AfterThrowing通知");
        }
    
    
    //    环绕通知
        @Around("execution(* com.LiuDeHen.Dao.User.run(..))")
        public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕通知前");
    //        增强的方法
            proceedingJoinPoint.proceed();
            System.out.println("环绕通知后");
        }
    }
    
  2. 开启包扫描创建bean和自动创建代理类

    <!--扫描包    -->
        <context:component-scan base-package="com.LiuDeHen.Dao"/>
    <!-- 会自动找到代理类   -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  3. 测试

     @Test
        public void test() {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            User bean = context.getBean("user", User.class);
            bean.run();
        }
    

    1647133065511

  4. 抽取相同切入点

      //提取切入点
    @Pointcut(value = "execution(* com.LiuDeHen.Dao.User.run(..))")
        public void point(){
    
        }
    // 直接调方法
        @Before(value = "point()")
        public void before(){
            System.out.println("Before通知");
        }
    
  5. 当有多个增强类,对同一个方法进行增强,使用注解@Order(1),值越小,优先级越高

    @Component
    @Aspect
    @Order(1)
    public class UserProxy2 {
        //    最终通知(有异常也执行)
        @After("execution(* com.LiuDeHen.Dao.User.run(..))")
        public void After(){
            System.out.println("After2通知");
        }
    }
    

5.AOP操作(Aspectj配置文件)

  • 创建普通类和增强类,及方法
  • 在spring配置文件中创建对象
  • 在spring配置文件中配置切入点
<!--创建对象-->
    <bean id="person" class="com.LiuDeHen.service.Person"/>
    <bean id="personProxy" class="com.LiuDeHen.service.PersonProxy"/>

    <aop:config>
<!--  设置切入点,切到哪个方法 -->
        <aop:pointcut id="p" expression="execution(* com.LiuDeHen.service.Person.run(..))"/>
<!--  设置切面,也就是代理类  -->
        <aop:aspect id="" ref="personProxy">
<!--         a是代理类里的增强方法,p是被代理类的被切入方法   -->
            <aop:before method="a" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>

方式二:自定义类实现

  • 一个切入点类
  • 一个额外类,使用原生就是一个额外方法一个类,然后注册,自定义就是所有额外方法一个类
  • 注册bean
  • 使用aop标签,exection表达式
  • execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
    除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
    <bean id="diy" class="com.LiuDeHen.diy.divPointcut"/>
    <aop:config>
<!--    自定义切面  ref:要引入的类    -->
        <aop:aspect ref="diy">
<!--   切入点 -->
            <aop:pointcut id="point" expression="execution(* com.LiuDeHen.service.userServiceImpl.*(..))"/>
<!--   通知-->
            <aop:after method="After" pointcut-ref="point"/>
            <aop:before method="Before" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

方式三:使用注解实现AOP

无非就是

  • 设置切入面@Aspect
  • 设置需要切入的方法是前是后还是环绕@After,@Before,@Around
  • 设置切入点(用exection表达式):execution(* com.LiuDeHen.service.userServiceImpl.*(..))
//注册bean(使用前,需要bean里必须有扫描包)
@Component
//开启注解支持,有参数设置为false就是默认为jdk代理,ture为cglie代理
@EnableAspectJAutoProxy
//设置切入面
@Aspect
public class divPointcut {
//    设置切入位置和execution表达式(切入点的位置)
    @After("execution(* com.LiuDeHen.service.userServiceImpl.*(..))")
    public void After(){
        System.out.println("方法执行前");
    }
    @Before("execution(* com.LiuDeHen.service.userServiceImpl.*(..))")
    public void Before(){
        System.out.println("方法执行后 ");
    }

//    在环绕增强中,给定一个参数ProceedingJoinPoint,表示我们要获取处理切入的点
    @Around("execution(* com.LiuDeHen.service.userServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        Object proceed = jp.proceed();
        System.out.println("环绕后");
    }
}

9丶整合Mybatis

  • 导入jar包

    • junit
    • mybatis
    • mysql
    • spring
    • aop织入
    • mybatis-spring
  • 配置环境

    1. 导入spring配置

    2. Datesource代替mybatis的数据源,需要类org.springframework.jdbc.datasource.DriverManagerDataSource

    3. SqlSessionFactory获取工厂,并可以设置mybatis里的配置(mapper位置等)需要类org.mybatis.spring.SqlSessionFactoryBean

    4. SqlSessionTemplate相当于获取了SqlSession,需要类org.mybatis.spring.SqlSessionTemplate

    5. Dao接口多了实现类

      <!-- 2.Datesource:使用spring的数据源替换mybatis的配置
           这里我们使用spring提供的org.springframework.jdbc.datasource
         -->
          <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
              <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
              <property name="url" value="jdbc:mysql://localhost:3306/mybaties?useSSL=false&amp;;characterEncoding=UTF-8&amp;;useUnicode=true&amp;;serverTimezone=GMT"/>
              <property name="username" value="root"/>
              <property name="password" value="123"/>
          </bean>
      <!--3.SqlSessionFactory-->
          <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <property name="dataSource" ref="dataSource" />
      <!--    配置的位置    -->
              <property name="configLocation"  value="classpath:mybatisSpring.xml"/>
      <!--     Mapper的位置   -->
              <property name="mapperLocations"  value="classpath:com/LiuDeHen/Dao/UserMapper.xml"/>
          </bean>
      
      <!--  4.SqlSessionTemplate:就是我们使用的Sqlsession  -->
          <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
      <!--只能使用构造器注入SqlSessionTemplate,因为它没有set方法-->
              <constructor-arg index="0" ref="sqlSessionFactory"/>
          </bean>
      
      <!-- 5.实现类   -->
          <bean id="UserMapperImpl" class="com.LiuDeHen.Dao.UserMapperImpl">
              <property name="sqlSession" ref="sqlSession"/>
          </bean>
      
    实现类:第一种方式SqlSessionTemplate
    public class UserMapperImpl implements UserMapper{
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        @Override
        public List<User> select() {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return  mapper.select();
        }
    }
    
    第二种方式:SqlSessionDaoSupport
        继承SqlSessionDaoSupport,获取SqlSessionTemplate,并且此时bean里5的实现类属性变成sqlSessionFactory
    public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
        @Override
        public List<User> select() {
            UserMapperImpl mapper = getSqlSession().getMapper(UserMapperImpl.class);
            return mapper.select();
        }
    }
    
  • 测试

    • 直接获取实现类的bean对象

      @Test
              public void test(){
                  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
                  UserMapperImpl userMapperImpl = applicationContext.getBean("UserMapperImpl", UserMapperImpl.class);
                  for (User user : userMapperImpl.select()) {
                      System.out.println(user);
                  }
      

10.声明式事务

1.回顾事务

  • dml业务要么都成功,要么都失败
  • 典型案例:
    • 银行转账:luck转账给mary100元,
    • luck少100,mary多100
    • 如果中间出现问题,那么luck不能少,mary不能多
  • 确保完整性和一致性

事务的ACID原则(原一隔持):

  • 原子性:确保dml业务,要么都成功,要么都失败
  • 一致性:一旦事务完成,要么都提交,要么失败,资源和状态一直保持一致
  • 隔离性:多个事务操作一个数据,防止数据损坏
  • 持久性: 事务一旦提交,无论系统发生什么问题,结果都不会受到影响,被持久化写到硬盘

2.使用声明创建事务

  • 配置声明事务

    <!--配置声明式事务-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <constructor-arg ref="dataSource" />
        </bean>
    
    
  • 配置事务的通知

    <!--结合AOP实现事务的织入-->
    <!-- 配置事务的通知-->
        <tx:advice id="interceptor" transaction-manager="transactionManager">
    <!--    为哪一个方法创建事务    -->
    <!--    配置事务的传播特性:  propagation=使用 REQUIRED创建,一共有7种 -->
            <tx:attributes>
                <tx:method name="add" propagation="REQUIRED"/>
                <tx:method name="delete" propagation="REQUIRED"/>
                <tx:method name="*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
    
  • 配置事务的切入

    <!--  配置事务的切入  -->
        <aop:config >
    <!-- 设置切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.LiuDeHen.Dao.UserMapperImpl.*(..))"/>
            <aop:advisor advice-ref="interceptor" pointcut-ref="pointcut"/>
        </aop:config>
    
  • 实体类中实现多组dml操作

       @Override
        public int delete(User user) {
            UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
            mapper.add(user);
            mapper.select();
            return mapper.delete(user);
        }
    
  • 当Mapper中的dml操作出现问题

        <select id="select" resultType="User">
            select * from mybaties.user
        </select>
    
        <insert id="add" parameterType="User">
            insert into mybaties.user values (#{id},#{name},#{pwd});
        </insert>
    
        <delete id="delete" parameterType="User">
            这个语句delete加了s,造成错误
            deletes from mybaties.user where id=#{id}
        </delete>
    
  • 测试就会失败,全部失败,而不是部分失败,没有问题则会全部成功

11.总结

  • 什么是spring

    • 是一个整合了很多技术,核心点为控制反转(IOC)和面向切面编程(AOP)的轻量级框架
  • spring核心

    • IOC
      • 什么是IOC?
      • IOC为控制反转,是一种思想,由主动的创建对象,到被动管理对象,创建等操作交给spring容器来实现
      • 基础实现:
        • 1.当我们数据操作层Dao的接口有多个方法,不同的实现类继承这个接口
        • 2.那么在我们测试的时候,每测试一个实现类,都需要去实例化,主动权在我们程序员手里,那么怎么实现IOC呢
        • 3.添加一个业务层Service,实现接口和实体类,并在实体类中,去set和get一个Dao层的接口
        • 4.此时我们测试,只需要new业务层,去调用set来实现Dao层哪一个具体的实现类的方法
        • 5.好处:此时主动权在用户手上,这就是控制反转
    • AOP
      • 什么是AOP?
      • 面向切面编程,底层是动态代理
      • 什么是代理?
        • 1.首先有一共目标类,客户类,客户类不能直接调用或者不适用目标类,此时就需要一共代理类
        • 2.实例:租客,通过中介找房,中介找房东
        • 3.好处:中介可以使用额外的方法,并且不影响到房东和租客(代理类能够在不改变目标类和客户类的情况下,能够添加方法,控制访问)
        • 4.动态代理是AOP的底层
      • 代理分类
        • 静态代理
        • 动态代理
          • 默认JDK代理必须通过接口实现
  • spring怎么用(IOC)

    • 第一种方式:bean创建
      1. 先创建实体类
      2. 构建bean.xml文件,创建bean对象
      3. 测试调用这个对象
    • 第二种方式:bean扫描,在java类中创建
      1. 实体类
      2. bean里扫描包
      3. 实体类上实现Component创建beans,就是一共对象,然后还可以用value注入值,Autowire自动匹配
    • 第三种:纯java创建
      1. 实体类
      2. 然后容器类实现注解confige(容器)和bean(对象)
      3. 测试
  • springAOP的使用

    • 基础概念
      • 切面
      • 切入点
      • 切入位置
    • 具体使用:
      • 第一种方式:原生springAPI的使用
        • 导依赖aop的织入,和aop的约束
        • 创建aop标签,设置切面,切点
      • 第二种方式:
      • 第三种方式:
  • spring-mybatis的连用

posted @   又菜又ai  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
点击右上角即可分享
微信分享提示