Spring笔记

1.Spring

1.1简介

2002.首次推出了Spring框架的雏形:interface21框架

RodJohnson

理念:使现有的技术更加容易使用,整合了现有的技术框架

SSH:Struct2+Spring+Hibernate

SSM:Springmvc+Spring+Mybatis

1.2优点

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

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

  • 控制反转(IOC),面向切面编程(AOP)

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

    方法二:

    去找到你要的包(可以放在任意位置),通过mvn命令的方式,手动把jar加入maven仓库,命令如下:

    安装指定文件到本地仓库命令:mvn install:install-file
    
    -DgroupId=<groupId>       : 设置上传到仓库的包名
    
    -DartifactId=<artifactId> : 设置该包所属的模块名
    
    -Dversion=1.0.0           : 设置该包的版本号
    
    -Dpackaging=jar           : 设置该包的类型(很显然jar包)
    
    -Dfile=<myfile.jar>       : 设置该jar包文件所在的路径与文件名
    

    我的命令:

    mvn install:install-file -DgroupId=org.springframework -DartifactId=spring-webmvc -Dversion=5.1.9.RELEASE -Dpackaging=jar -Dfile=E:\学习资料\spring-webmvc-5.2.9.RELEASE.jar
    

1.3组成

image-20201103172126538

1.4拓展

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

image-20201103172438728

  • Spring Boot

    • 一个快速开发的脚手架,
    • 基于Spring Boot可以快速开发微服务。
    • 约定大于配置

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

  • Spring Cloud

    • SpringCloud是基于Spring Boot实现的。

2.IOC理论推导

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

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

我们使用一个set接口,已经发生了革命性的变化

private UserDao UserDao;
//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao){
    this.userDao=userDao;
}
  • 之前,程序是主动创建对象,控制权在程序员手上
  • 使用set注入后,程序不再具有主动性,而是变成了被动接受的对象

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

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

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

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

3.HellowSpring

控制:谁来控制对象的 创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的,

反转:程序本身不创建对象,而变成被动的接受对象,

依赖注入,就是利用set方法来进行注入的。

IOC是一种编程思想,有主动的编程编程被动的接受。

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

4.IOC创建对象的方式

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

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

  1. 下标赋值

    下标赋值
    <bean id="userServiceImpl" class="com.luobo.service.UserServiceImpl">
            <constructor-arg index="0" value="绝地反击"/>
      </bean>
    
  2. 类型

    通过类创建,不建议使用,在有重复类型时无法使用
    <bean id="userServiceImpl" class="com.luobo.service.UserServiceImpl">
            <constructor-arg type="java.lang.String" value="绝地反击"/>
        </bean>
    

3.参数名

通过参数名来设置
<bean id="userServiceImpl" class="com.luobo.service.UserServiceImpl">
        <constructor-arg name="name" value="绝地反击"/>
    </bean>

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

5.Spring配置

5.1别名

添加别名,使用别名获取对象
<alias name="user" alias="userName"/>

5.2Bean的配置

id:bean的唯一标识符,相当于对象名
class bean对象所对应的全限定名
name  也是别名,而且那么可以同时取多个别名
<bean id="userT" class="com.luobo.pojo.UserT" name="user2 u1,u3;u4">
	<property name="name" value = "西部"/>
</bean>

5.3Import

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

6.依赖注入

6.1构造器注入

6.2set方式注入(重点)

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

【环境搭建】spirng1

1.复杂类型

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = 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 Properties info;
    private String wife;
}

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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="student" class="com.luobo.pojo.Student">
      <!--第一种,普通值注入   value-->
      <property name="name" value="萝卜"/>
   </bean>

</beans>

4.测试类

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

完善注入信息

 <bean id="address" class="com.luobo.pojo.Address"/>
   <bean id="student" class="com.luobo.pojo.Student">
      <!--第一种,普通值注入   value-->
      <property name="name" value="萝卜"/>
      <!--<property name="name">
         <value>萝卜</value>
      </property>-->

      <!--Bean注入 --><!-- -->
      <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>
         </list>
      </property>

      <!--map注入 -->
      <property name="card">
         <map>
            <entry key="u" value="电风扇犯得上"/>
            <entry key="q" value="fdsfdsf"/>
         </map>
      </property>

      <!--set注入 -->
      <property name="games">
         <set>
            <value>英雄联盟</value>
            <value>王者荣耀</value>
         </set>
      </property>

      <!-- Properties:配置-->
      <property name="info">
         <props>
            <prop key="学号">022301190249</prop>
            <prop key="性别">nan</prop>
         </props>
      </property>

      <!-- 空字符串和空值注入-->
      <property name="wife" value=""/>
      <!-- <property name="wife">
         <null/>
      </property>-->

   </bean>

6.3拓展方式注入

​ 我们可以使用p命名空间和c命名空间进行注入

使用

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- p命名空间注入对应的时set方法的注入 property-->
    <bean id="user" class="com.luobo.pojo.User" p:name="经典福克斯附件" p:age="56"/>

    <!--c命名空间注入对应的是构造方法的注入,必须拥有构造方法 construct-args -->
    <bean id="user2" class="com.luobo.pojo.User" c:name="犯得上发生" c:age="58"/>

</beans>

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

6.4bean的作用域

image-20201105173549117

  1. 单例模式:singleton(Spring默认机制)
<bean id="user" class="com.luobo.pojo.User" p:name="经典福克斯附件" p:age="56" scope="singleton"/>

2.原型模式:每次从容器中get的时候,都会产生一个新对象

<bean id="user" class="com.luobo.pojo.User" p:name="经典福克斯附件" p:age="56" scope="prototype"/>

3.其余的request,session,application,这些个只能在web开发中使用到

7.bean的自动装配

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

在Spring中有三种装配的方式

  1. 在xml中显示配置
  2. 在Java中显示配置
  3. 隐式的自动装配bean【重点】

7.1测试(spring2)

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

7.2ByName自动装配

会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean的id

<bean id="people" class="com.luobo.pojo.People" autowire="byName">
        <property name="name" value="yurongji"/>
    </bean>

7.3ByType自动装配

会自动在容器上下文中查找,和自己对象属性类型相同的bean 的id 缺点:必须保证类型唯一

<bean id="people" class="com.luobo.pojo.People" autowire="byType">
        <property name="name" value="yurongji"/>
    </bean>

小结:

  • ByName的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
  • ByType的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致

7.4使用注解实现自动装配(spring3)

基于 Comments 的配置的引入提出了一个问题,即这种方法是否比 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
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:annotation-config/>
    
    </beans>
    

@Autowired(默认按照bean的类型进行装配)

直接在属性上使用即可,使用后可以忽略set方法 ,前提是你这个自动装配的属性在IOC(Spring)容器中存在且符合名字ByType。 也能在set方法上使用,

科普:

@Nullable 字段标记了这个注解,这个字段就可以为空了

@Nullable  字段标记了这个注解,这个字段就可以为空了
    public @interface Autowired{
    boolean required() default true;
}
测试
    pulic class People{
        //如果定义了Autowired的required属性为false,说明这个对象可以为null,,否则不允许为空
        @Autowired(required=false)
        private Cat cat;
    }
}

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

@Autowired会按ByType,ByName找一遍,如果都没有才需要@Qualifier

@Autowired
@Qualifier(value="dog22")
private Dog dog;
@Resource(name="cat2")
private Cat cat;
@Resource
private Dog dog;

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上

  • @Autowired通过ByType的方式实现,多个相同类型下再通过ByName

  • @Resource是先ByName查找,不符合再继续ByType

8.使用注解开发(Spring4)

在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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        <!--指定要扫描的包,这个包下的注解就会生效  component-scan 是扫描@Component这些组件注解的-->
        <context:component-scan base-package="com.luobo"/>
        <context:annotation-config/>
</beans>
  1. bean

  2. 属性如何注入

    @Value可以放在属性上也可以放在set方法上

  3. 衍生的注解

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

    • dao【@Repository】

    • service【@Service】

    • controller【@Controller】

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

  4. 自动装配

    @Autowired:自动装配通过类型,然后名字,如果Autowired不能唯一自动装配上属性。则需要通过@Qualifier(value=“”)来装配

    @Nullable 字段标记了这个注解,说明这个字段可以为null;

    @Resource 自动装配通过名字,然后类型,与第一个相反

    @Component组件,放在类上,说明这个类被Spring管理了,就是bean,它完成了bean的注册,而@Autowired是标注在类中的成员变量或方法上,完成的是bean的注入。

  5. 作用域

    @Scope设置作用域

    @Component
    @Scope("prototype")
    public class User(){
        
    }
    
  6. 小结

    xml与注解

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

    xml与注解的最佳实践

    • xml用来管理bean;

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

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

       <!--指定要扫描的包,这个包下的注解就会生效  component-scan 是扫描@Component这些组件注解的-->
              <context:component-scan base-package="com.luobo.pojo"/>
              <context:annotation-config/>
      

9.使用Java的方式配置Spring(Spring5)

淦:所以xml的那一套白学了。

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

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

User是对象不加@Compnet注解,ioc不管理,ioc管理的是LuoboCofig这个对象,这个对象里有个bean,getUser方法里面用new 创建了一个User对象

第一种

@Configuration
@ComponentScan("com.luobo.pojo")
public class LuoboConfig {
    public User getUser(){
        return new User();
    }
}

@Component
public class User {
    @Value("大师傅似的")
    private String name;

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
 @Test
    public void test(){
        //用@ComponentScan扫描包,
        ApplicationContext context = new AnnotationConfigApplicationContext(LuoboConfig.class);
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }

@Componentscan注解,加@Componentscan扫描就可以不用注册bean了(@Bean)两种选一个就行

第二种

@Configuration
public class LuoboConfig {
    @Bean
    public User getUser(){
        //方法名就是bean的id名,不需要在使用@Componet注解,因为在config里面已经声明了bean
        //@bean就表示将当前方法交给ioc容器管理
        return new User();
    }
}

public class User {
    @Value("大师傅似的")
    private String name;

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

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


@Test
    public void test(){
        ApplicationContext context = new AnnotationConfigApplicationContext(LuoboConfig.class);
        User user = context.getBean("getUser", User.class);
        System.out.println(user);
    }

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

10.注解和反射

注解:Java.Annotation

注解不是程序本身,可以对程序做出解释。可以被其他程序(比如编译器等)读取

10.1内置注解

@Override:声明重写父类方法

@Deprecated:已过时的,不推荐使用但是可以使用,或存在更好的方式

@SuppressWarnings镇压警告

10.2元注解

元注解就是负责注解其他注解的注解,java 定义了4个标准的meta-annotaion类型,他们被用来提供对其他annotation类型作说明

@Target:表示注解使用范围

@Retention:在什么级别

@Documented:文档注释

@Inherited:表示继承

自定义简单注解

去

自定义复杂注解,,使用@Interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

![发](E:\图片\Saved Pictures\发.png)

![hh](E:\图片\Saved Pictures\hh.jpg)

10.3反射机制概述

反射机制:Java.Reflection

![请求](E:\图片\Saved Pictures\请求.jpg)

![全为](E:\图片\Saved Pictures\全为.png)

用反射获得的对象比new出来的对象慢很多

![功能](E:\图片\Saved Pictures\功能.png)

  • 一个类在内存中只有一个Class对象
  • 一个类被加载后,类的整个结构都会被封装在Class对象中。

![儿](E:\图片\Saved Pictures\儿.png)

![十大](E:\图片\Saved Pictures\十大.png)

![脚后跟](E:\图片\Saved Pictures\脚后跟.png)

![vf](E:\图片\Saved Pictures\vf.png)

![反射](E:\图片\Saved Pictures\反射.jpg)

![天涯](E:\图片\Saved Pictures\天涯.jpg)

![规划局](E:\图片\Saved Pictures\规划局.jpg)

![引入](E:\图片\Saved Pictures\引入.png)

![客家话](E:\图片\Saved Pictures\客家话.jpg)

10.4创建运行时类的对象

Java.Reflection

getFields();只能找到public 属性

getDeclaredFields();获得全部的属性

getField("");获得指定public属性值

getDeclaredField("");获得指定的属性值

getMethods();获得本类及其父类的全部public方法

getDeclaredMethod();获得本类的所有方法

getMethod("",type)获得指定的方法,//因为有重载方法所以需要加参数

getConstructors()获得本类public的构造方法

getDeclaredConstructors()获得本类全部构造方法

getDeclaredConstructor(String.class,int.class);获得指定构造器

10.5创建动态对象执行方法

![QQ图片20201108165243](E:\图片\Saved Pictures\QQ图片20201108165243.jpg)

通过反射获取方法并执行

![QQ图片20201108165518](E:\图片\Saved Pictures\QQ图片20201108165518.jpg)

invoke()方法是激活的意思(对象,“方法的值”)

通过反射操作属性

![QQ图片20201108165839](E:\图片\Saved Pictures\QQ图片20201108165839.png)

![QQ图片20201108170051](E:\图片\Saved Pictures\QQ图片20201108170051.jpg)

各种方式执行的效率

![QQ图片20201108170823](E:\图片\Saved Pictures\QQ图片20201108170823.jpg)

反射操作泛型

![QQ图片20201108171056](E:\图片\Saved Pictures\QQ图片20201108171056.png)

反射操作注解(Spring6/fanshe)

//联系反射操作注解
public class fanshe {
    public static void main(String[] args) throws NoSuchFieldException {
        Class<Student> studentClass = Student.class;
        //通过反射获得注解
        Annotation[] annotations = studentClass.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //获得注解value值
        Tableluobo annotation = studentClass.getAnnotation(Tableluobo.class);
        System.out.println(annotation.value());

        //获得类指定的注解
        Field name = studentClass.getDeclaredField("name");
        Fieldluobo annotation1 = name.getAnnotation(Fieldluobo.class);
        System.out.println(annotation1);
    }

}
@Tableluobo("db_student")
class Student{
    @Fieldluobo(columnName = "db_id",type = "int",length = 10)
    private int id;
    @Fieldluobo(columnName = "db_age",type = "int",length = 10)
    private int age;
    @Fieldluobo(columnName = "db_name",type = "varchar",length = 3)
    private String name;

    public Student() {
    }

    public Student(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tableluobo{
    String value();
}


//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldluobo{
    String columnName();
    String type();
    int length();
}

11.代理模式

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

代理模式的分类:

  • 静态代理
  • 动态代理

![QQ图片20201107154736](E:\图片\Saved Pictures\QQ图片20201107154736.png)

11.1静态代理(Spring6/demo1,demo2)

角色分析:

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

代码步骤:

  1. 接口

    //定义一个买家接口
    public interface IBuyer {
        //买家有接收快递的方法
        void process();
    }
    
    
  2. 真实角色

    //定义一个具体的买家,实现接收快递的方法
    public class Buyer implements IBuyer {
        public void process() {
            System.out.println("买家"+Buyer.this+"收到快递");
        }
    }
    
  3. 代理角色

    //定义一个代理类(快递公司),实现买家接口,为买家送货上门,本来需要买家到卖家哪里进行交易,但现在交给快递公司了
    public class BuyerProxy implements  IBuyer {
        private IBuyer target;
    
        public void setTarget(IBuyer target) {
            this.target = target;
        }
    
        public void process() {
            System.out.println("送的路上");
            System.out.println("即将送达");
            target.process();
            System.out.println("完成配送");
        }
    }
    
    
  4. 客户端访问代理角色

    //卖家类,收到订单,委托代理(快递公司)去送快递
    public class Seller {
        @Test
        public void testSave(){
            //模拟一个买家下单
            IBuyer buyer = new Buyer();
            //联系一个快递公司
            BuyerProxy buyerProxy = new BuyerProxy();
            //把这个买家的货物交给快递公司
            buyerProxy.setTarget(buyer);
            //快递公司开始送快递
            buyerProxy.process();
        }
    }
    

代理模式好处:

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

缺点:

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

11.2加深理解(Spring6/demo3)

在不使用代理下:

用户要实现的功能接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}

用户实现类的功能

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("添加一个用户");
    }

    public void delete() {
        System.out.println("删除一个对象");
    }

    public void update() {
        System.out.println("修改一个用户");
    }

    public void select() {
        System.out.println("查询一个用户");
    }
}

测试功能

public class Client {
    @Test
    public void test(){
        //在增加功能时,要不修改原来的代码
        UserServiceImpl userService = new UserServiceImpl();
        userService.add();
    }
}

显示结果

image-20201107210801279

如果要在添加一些功能,在不改变原有代码的情况下用代理实现

例如:在以上代码添加日志功能

添加一个UserServiceProxy代理类,代理UserService的功能并增加日志功能

public class UserServiceProxy implements UserService {
    UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void add() {
        log("添加");
        userService.add();
    }

    public void delete() {
        log("删除");
        userService.delete();
    }

    public void update() {
        log("修改");
        userService.update();
    }

    public void select() {
        log("查找");
        userService.select();
    }

    //日志方法
    public void log(String msg){
        System.out.println("是用了"+msg+"方法");
    }
}

测试

public class Client {
    @Test
    public void test(){
        //在增加功能时,要不修改原来的代码
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.setUserService(userService);
        userServiceProxy.add();
        userServiceProxy.delete();
        userServiceProxy.select();
    }
}

显示结果

image-20201107211936347

OOP编程七大原则

https://www.cnblogs.com/princessd8251/articles/3784949.html

AOP(面向切面编程)(对应Spring6/demo3):如果要添加功能,在不改动原有代码的情况下,横向添加一个功能.

![啊](E:\图片\Saved Pictures\啊.png)

11.3动态代理(底层都是反射)(Spring6/demo4)

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口--JDK动态代理
    • 基于类:cglib
    • Java字节码实现:javassist

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

重写demo3

用户要实现的功能接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}

用户实现类的功能

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("添加一个用户");
    }

    public void delete() {
        System.out.println("删除一个对象");
    }

    public void update() {
        System.out.println("修改一个用户");
    }

    public void select() {
        System.out.println("查询一个用户");
    }
}

使用动态代理 InvocationHandler 和 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 {
        //动态代理的本质,就是使用反射机制实现!这里invoke方法,就是通过反射,执行当前实例的方法,就是静态代理是要做的动作,就是要代理的本来的动作
        log(method.getName());
        Object result = method.invoke(target,args);
        return result;
    }
    public void log(String msg){
        System.out.println("执行了"+msg+"方法");
    }
}

测试

public class Client {
    @Test
    public void test(){
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色,不存在
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        //设置代理对象
        proxyInvocationHandler.setTarget(userService);
        //动态生成代理类
        UserService proxy = (UserService)proxyInvocationHandler.getProxy();

        proxy.delete();
        proxy.add();
    }
}

动态代理的好处:

  • 可以使角色的操作更加纯粹,不用去关注一些公共业务
  • 公共也就交给代理角色,实现了业务分工
  • 公共业务发生拓展的时候,方便集中管理
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

调用了代理实例的方法,就会调用invoke()

调用代理类的每个方法,都会被替换成invoke()

getProxy方法返回代理对象,这个代理对象是代理类的一个实例,代理类也实现了Rent接口,重写了rent方法,多以代理对象proxy调用rent方法就会在代理类的rent方法中调用inxoke方法

12.AOP

1.什么是AOP

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

2.AOP在Spring中的作用

提供声明事务;允许用户自定义切面

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

![QQ图片20201108201245](E:\图片\Saved Pictures\QQ图片20201108201245.png)

即AOP在不改变原有代码的情况下,去增加新功能。

3.使用Spring实现AOP

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

 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
            <scope>runtime</scope>
        </dependency>

方式一:使用Spring的API接口【主要是SpringAPI接口实现】

springaop1

//用户功能接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
//用户功能接口实现类
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一个用户");
    }

    public void delete() {
        System.out.println("删除了一个用户");
    }

    public void update() {
        System.out.println("修改了一个用户");
    }

    public void query() {
        System.out.println("查询了一个用户");
    }
}
//要添加的功能
public class AfterLog implements AfterReturningAdvice {
    //执行后可以有方法返回值
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+o1.getClass().getName()+"类的"+method.getName()+"方法。返回结果为"+o);
    }
}
//要添加的功能
public class Log implements MethodBeforeAdvice {
    //method:要执行的目标对象的方法
    //objects:参数
    //o:目标对象
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"执行了");
    }
}
//配置切入点
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">


   <bean id="userService" class="com.luobo.service.UserServiceImpl"/>
    <bean id="log" class="com.luobo.log.Log"/>
    <bean id="afterLog" class="com.luobo.log.AfterLog"/>
<!--方式一:使用原生的Spring API接口-->
    <!--配置AOP  需要导入aop的约束-->
    <aop:config>
        <!--切入点 expression:表达式,execution(要执行的位置)-->
        <aop:pointcut id="pointcut" expression="execution(* com.luobo.service.UserServiceImpl.*(..))"/>

        <!--执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

测试

public class AopTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
        userService.delete();
        userService.query();
    }
}

方式二:自定义来实现AOP【主要是切面定义】

springaop1

自定义类:要添加的方法

public class DiyPointCut {
    public void before(){
        System.out.println("方法执行前");
    }
    public void after(){
        System.out.println("方法执行后");
    }
}

配置切面

<bean id="userService" class="com.luobo.service.UserServiceImpl"/>
    <bean id="log" class="com.luobo.log.Log"/>
    <bean id="afterLog" class="com.luobo.log.AfterLog"/>   
<bean id="diy" class="com.luobo.diy.DiyPointCut"/>
    <aop:config>
<!--        自定义切面  ref要引用的类-->
        <aop:aspect ref="diy">
<!--            切入点-->
            <aop:pointcut id="point" expression="execution(* com.luobo.service.UserServiceImpl.*(..))"/>
<!--            通知-->
            //此处的method为自定义切面即Log类里的方法名
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

测试

方式三:使用注解实现AOP

springaop1

自定义AnnotationPointCut切面类,实现相关功能

//方式三,使用注解方式实现AOP
@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.luobo.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前");
    }
    @After("execution(* com.luobo.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后");
    }

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

        Signature signature = joinPoint.getSignature();
        System.out.println("signature"+signature);

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

applicationContext.xml将切面类加载到Spring里

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

<!--注册bean-->
   <bean id="userService" class="com.luobo.service.UserServiceImpl"/>
    <bean id="log" class="com.luobo.log.Log"/>
    <bean id="afterLog" class="com.luobo.log.AfterLog"/>

<!--    方式三: id命名一般把类名作为id名   也可以用注解注册annotationPointCut类-->
    <bean id="annotationPointCut" class="com.luobo.diy.AnnotationPointCut"/>
<!--    开启注解支持 autoproxy自动代理  基于接口Jdk(默认jdk)    基于类cglib   proxy-target-class="false"  默认值为false就是默认为jdk实现,为true为cglib实现 -->
    <aop:aspectj-autoproxy />
    

测试

public class AopTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
        userService.delete();
        userService.query();
    }
}

13.整合Mybatis(springmybatis)

1.回忆mybatis

  1. 编写实体类
  2. 编写核心配置文件
  3. 编写接口
  4. 编写Mapper.xml
  5. 测试

整合步骤:

  1. 导入相关jar包

    • junit
    • mybatis
    • mysql数据库
    • spring相关的
    • aop织入
    • mybatis-spring【新包,用来整合mybatis和spring】
  2. 编写配置文件

    pom.xml

    <dependencies>
            <!--mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.46</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.1.9.RELEASE</version>
            </dependency>
    <!--    Spring操作数据库,还需要spring-jdbc-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.1.9.RELEASE</version>
            </dependency>
    <!--        AOP织入包-->
            <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.5</version>
                <scope>runtime</scope>
            </dependency>
    <!--        mybatis与Spring整合的包-->
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.5</version>
            </dependency>
    
        </dependencies>
        <build>
        <!--在build中配置resources,来防止我们资源导出失败的问题-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
        </build>
    
  3. 测试

2.Mybatis-spring

  1. 编写数据源

    <!--整合需要先配置两个东西数据源和sqlSessionFactory以便获得SqlSession-->
    <!--   DataSource:数据源    使用Spring的数据源替换Mybatis的配置  c3p0 dbcp druid   我们这里直接使用Spring提供的Jdbc  依赖于Spring-jdbc的jar包-->
        <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/test?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
    
  2. sqlSessionFactory

    <!--    SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--        配置一下数据源,第二个datasource是数据源的id-->
            <property name="dataSource" ref="datasource"/>
    <!--        绑定Mybatis配置文件 可以省略,Spring可以完全代替 -->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--        绑定Mapper-->
            <property name="mapperLocations" value="classpath:com/luobo/dao/*.xml"/>
        </bean>
    
  3. SqlSessionTemplate

    <!--    SqlSessionTemplate:就是我们使用的sqlSession-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--        只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
    
  4. 需要给接口加实现类

    public class UserMapperImpl implements UserMapper {
        //原来我们的操作都使用SqlSession来执行,现在都是SqlSessionTemplate
    
        private SqlSessionTemplate sqlSessionTemplate;
    
        public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
            this.sqlSessionTemplate = sqlSessionTemplate;
        }
    
        public List<User> getUserList() {
            UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
            return mapper.getUserList();
        }
    }
    
    
  5. 将自己写的实现类,注入到Spring中

    <!--    注册UserMapperImpl类,使用Spring就要不创建对象,所需对象从bean里取,但是不能配置接口,因此增加一个UserMapperImpl类-->
        <bean id="userMapper" class="com.luobo.dao.UserMapperImpl">
            <property name="sqlSessionTemplate" ref="sqlSession"/>
        </bean>
    
  6. 测试使用

    public class ATest {
        @Test
        public void test() throws IOException {
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-dao.xml");
            UserMapper userMapper = classPathXmlApplicationContext.getBean("userMapper2", UserMapper.class);
            List<User> userList = userMapper.getUserList();
            for (User user : userList) {
                System.out.println(user.toString());
            }
        }
        @Test
        public void test1(){
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
            SqlSessionTemplate sqlSession = context.getBean("sqlSession", SqlSessionTemplate.class);
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

14.声明式事务

1.回顾事务

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

事务ACID原则

  • 原子性
  • 一致性
  • 隔离性:多个业务可能操作同一个资源,防止数据损坏
  • 持久性:事务一旦提交,无论系统发生什么问题,结果都不会在被影响,被持久化的写到存储器中

2.Spring中的事务管理

  • 声明式事务:AOP
  • 编程式事务:需要在代码中进行事务的管理
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--整合需要先配置两个东西数据源和sqlSessionFactory以便获得SqlSession-->
    <!--   DataSource:数据源    使用Spring的数据源替换Mybatis的配置  c3p0 dbcp druid   我们这里直接使用Spring提供的Jdbc  依赖于Spring-jdbc的jar包-->
    <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/test?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--    SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--        配置一下数据源,第二个datasource是数据源的id-->
        <property name="dataSource" ref="datasource"/>
        <!--        绑定Mybatis配置文件 可以省略,Spring可以完全代替 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--        绑定Mapper-->
        <property name="mapperLocations" value="classpath:com/luobo/mapper/*.xml"/>
    </bean>

    <!--    SqlSessionTemplate:就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--        只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!--    注册UserMapperImpl类,使用Spring就要不创建对象,所需对象从bean里取,但是不能配置接口,因此增加一个UserMapperImpl类-->
    <bean id="userMapper" class="com.luobo.mapper.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSession"/>
    </bean>
<!--    配置声明事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="datasource"/>
    </bean>

<!--    结合AOP实现事务的织入-->
<!--    配置事物通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--        给那些方法配置事务-->
<!--        配置事务的传播特性:new-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
<!--            read-only:true,query方法不能对数据库进行增删改-->
            <tx:method name="query" read-only="true"/>
        </tx:attributes>
    </tx:advice>
<!--    配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.luobo.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
</beans>
posted @ 2021-04-25 15:34  萝卜i  阅读(95)  评论(0编辑  收藏  举报