Spring 笔记

1.认识Spring

1.1简介

  • Spring是一个开源免费的框架 , 容器
  • Spring是一个轻量级的框架 , 非侵入式的
  • 控制反转 IoC , 面向切面 Aop
  • 对事物的支持 , 对框架的支持

一句话概括:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。

1.2组成

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。

  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

1.3拓展

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

  • Spring Boot与Spring Cloud
Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务;
Spring Cloud是基于Spring Boot实现的;
Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;
Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 ,
Spring Cloud很大的一部分是基于Spring Boot来实现,
Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。
SpringBoot在SpringClound中起到了承上启下的作用,如果你要学习SpringCloud必须要学习SpringBoot。

2.IoC(Inversion of Control)

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。

没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,

控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

 使用IoC的目的:解耦

2.1IoC创建对象的方式

2.1.1 搭建环境

User.java

复制代码
public class User {
    private String name;
    public void show(){
        System.out.println("name:"+name);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public User(String name) {
        this.name = name;
    }
    public User() {
    }

}
复制代码

 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">


</beans>

2.1.2通过无参构造器创建对象

复制代码
<!-- 
    id:获取该类对象的名字 
    class:该类的路径
    property:设置变量名的值
    name:变量名
    value:该变量名的值
-->
<bean id="user" class="com.spring.pojo.User">
    <property name="name" value="无参构造器创建"></property>
</bean>
复制代码

注意:如果类中存在有参构造器,那么也要显式的声明无参构造器,因为使用上面的方法创建对象调用的是无参构造器。

2.1.3通过有参构造器创建对象

有三种方式

复制代码
<!--    有参构造器注入的三种方式-->
<!--    第一种:下标-->
    <bean id="user1" class="com.gsy.pojo.User">
        <constructor-arg index="0" value="gsy1"></constructor-arg>
    </bean>
<!--    第二种:通过类型-->
    <bean id="user2" class="com.gsy.pojo.User">
        <constructor-arg type="java.lang.String" value="gsy2"></constructor-arg>
    </bean>
<!-- 第三种,通过参数名-->
<!-- type指参数类型,一般情况下参数类型都不一样,如果一样,按顺序来 -->
<bean id="user3" class="com.gsy.pojo.User"> <constructor-arg name="name" value="gsy3"></constructor-arg> </bean>
复制代码

当配置文件被加载时,bean全都被实例化,即使你没有用getBean()方法去取它,这是由于:

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

 

3 Spring配置 

3.1别名

<alias name="user1" alias="u1"/>

3.2Beans.xml的配置

复制代码
<!--bean就是java对象,由Spring创建和管理-->
<!--
    id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
    如果配置id,又配置了name,那么name是别名
    name可以设置多个别名,可以用逗号,分号,空格隔开
    如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
    class是bean的全限定名=包名+类名
-->
<bean id="user" name="hello2 h2,h3;h4" class="com.kuang.pojo.User">
    <property name="name" value="Spring"/>
</bean>
复制代码

3.3improt

创建了多个beans.xml可以都引入到一个里面,其他被引用的配置文件中的bean也会自动装配。

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

4 DI依赖注入

  • 依赖注入(Dependency Injection,DI)。
  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

4.1构造器依赖注入

见1.1.2/1.1.3

4.2 set注入(重点)

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .

搭建环境:

Address.java

复制代码
package com.gsy.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}
复制代码

Student.java

复制代码
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobby;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public Set<String> getGames() {
        return games;
    }
    public void setGames(Set<String> games) {
        this.games = games;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    public String[] getBooks() {
        return books;
    }
    public void setBooks(String[] books) {
        this.books = books;
    }
    public List<String> getHobby() {
        return hobby;
    }
    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }
    public Map<String, String> getCard() {
        return card;
    }
    public void setCard(Map<String, String> card) {
        this.card = card;
    }
    public String getWife() {
        return wife;
    }
    public void setWife(String wife) {
        this.wife = wife;
    }

    public Properties getInfo() {
        return info;
    }
    public void setInfo(Properties info) {
        this.info = info;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.toString() +
                ", books=" + Arrays.toString(books) +
                ", hobby=" + hobby +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}
复制代码

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.gsy.pojo.Address">
        <property name="address" value="河南"></property>
    </bean>
    <bean id="student" class="com.gsy.pojo.Student">
    </bean>
</beans>
复制代码

set注入的方式

  1. 普通值注入,value
<!--        第一种,普通值注入,value-->
        <property name="name" value="鸡哥"></property>

 2. Bean注入,ref

<!--        第二种,Bean注入,ref-->
        <property name="address" ref="address"></property>

 3.数组

复制代码
<!--        数组-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>三国演义</value>
                <value>水浒传</value>
            </array>
        </property>
复制代码

 4. list

复制代码
<!--        list-->
        <property name="hobby" >
            <list>
                <value></value>
                <value></value>
                <value>rap</value>
                <value>篮球</value>
            </list>
        </property>
复制代码

 5.map

<!--        map-->
        <property name="card">
            <map>
                <entry key="身份证" value="123456"/>
                <entry key="银行卡" value="654321"/>
            </map>
        </property>

 6. Set

<!--        Set-->
        <property name="games">
            <set>
                <value>cf</value>
                <value>lol</value>
                <value>cs</value>
            </set>
        </property>

 7.null

<!--        null-->
        <property name="wife">
            <null/>
        </property>

8.properties

<!--        properties-->
        <property name="info">
            <props>
                <prop key="鸡哥">个人练习生</prop>
                <prop key="时长">两年半</prop>
                <prop key="爱好">你干嘛哎哟</prop>
            </props>
        </property>

这里的key是键,标签中间的是值,相当于value

4.3拓展注入实现

User.java

复制代码
public class User {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
复制代码

1.P命名空间注入 :

需要在头文件中加入约束文件

xmlns:p="http://www.springframework.org/schema/p"
<!--    p:(properties)命名空间,属性依然要设置set方法-->
    <bean id="student2" class="com.gsy.pojo.Student"
          p:name="ikun">
    </bean>

2.C命名空间注入 :

需要在头文件中加入约束文件

xmlns:c="http://www.springframework.org/schema/c" 
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.gsy.pojo.User" c:name="ikunkun" c:age="25"> </bean>

用c命名空间需要有参构造器,参数要写完整。

 4.4 Bean作用域

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

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

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

 4.4.1 singleton 单例模式

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

Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。

注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:

<!--    singleton单例模式 spring默认单例模式-->
    <bean id="student2" class="com.gsy.pojo.Student"
          p:name="ikun" scope="singleton">
    </bean>

 

 4.4.2 prototype 原型模式

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。

Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。

Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。

根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

在XML中将bean定义成prototype,可以这样配置:

<!--    prototype原型模式:每次从容器中get时都会产生一个新的对象-->
    <bean id="user" class="com.gsy.pojo.User"
        c:name="ikunkun" c:age="25" scope="prototype">
    </bean>

5.自动装配

  • 自动装配是使用spring满足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean。

Spring中bean有三种装配机制,分别是:

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

这里我们主要讲第三种:自动化的装配bean。

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

组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使得显式的配置降低到最少。

5.1环境搭建

cat.java

public class Cat {
    public void shout(){
        System.out.println("miao~");
    }
}

 

dog.java

public class Dog {
    public void shout(){
        System.out.println("wang~");
    }
}

 

person.java

复制代码
public class Person {

    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;
    }
}
复制代码

 

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="cat" class="com.gsy.pojo.Cat"/>
    <bean id="dog" class="com.gsy.pojo.Dog"/>
    <bean id="person" class="com.gsy.pojo.Person" >
          <property name="name" value="gsy"/>
     <property name="cat" ref="cat"/>
      <property name="dog" ref="dog"/>
</bean> </beans>
复制代码

 

5.2 byName

autowire byName (按名称自动装配)

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

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

1.修改bean配置,增加一个属性 autowire=”byName”

    <bean id="cat" class="com.gsy.pojo.Cat"/>
    <bean id="dog" class="com.gsy.pojo.Dog"/>
    <bean id="person" class="com.gsy.pojo.Person"  autowired="byName">
        <property name="name" value="gsy"/>
    </bean>

 

2.经测试正常运行。

3.如果将bean 的id改为cat1,再次测试, 执行时报空指针java.lang.NullPointerException。

4.这是因为按byName规则找不到对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。

小结:

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

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

5.3 byType

autowire byType (按类型自动装配)

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

测试:

1.将byName改为byType

2.再注册一个cat的bean id为cat2

3.测试报错NoUniqueBeanDefinitionException

4.删除cat2 正常运行

因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。

5.4 使用注解

jdk 1.5支持注解 spring2.5支持注解

1.配置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"
       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">

....

</beans>
复制代码

 

2.开启属性注解支持

<!--开启属性注解支持-->
    <context:annotation-config/>

 

5.4.1 @Autowired

@Autowired是首先根据byType配置的。

注意,我说的是首先根据byType配置的,为什么是首先呢?因为@Autowired如果发现有冲突,其实会自动转去byName去匹配

只有都两个匹配不到的时候才会报错

Person.java

复制代码
public class Person {
    @Autowiredprivate Cat cat;
    @Autowiredprivate Dog dog;
    private String name;
    public Cat getCat() {
        return cat;
    }
    public Dog getDog() {
        return dog;
    }
    public String getName() {
        return name;
    }
}
复制代码

 

此时可以删掉set方法,因为注解是通过反射实现的。

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

@Autowired配合@Qualifier(value="xxx")指定一个唯一的bean对象注入。

    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;

 

5.4.2 @Resource

  • 如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。
  • @Resource可以指定一个name属性。虽然这个是根据byName来的,但是却和通过xml配置的byName不一样。
  • byName是by类中的setter方法来的。而基于注解的则是根据属性的名字来的,和setter方法没有任何关系。如果name属性不配置上@Resource(name="xxx"),默认就是属性名对应的实现类。

5.4.3 小结

@Autowired与@Resource异同:

@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
@Autowired默认先按类型装配(属于spring规范),找不到时按名字装配。默认情况下必须要求依赖对象必须存在。

如果要允许null 值,可以设置它的required属性为false,

如:@Autowired(required=false) ,如果我们想指定名称装配可以结合@Qualifier注解进行使用
@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,

如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

6.使用注解开发

6.1说明

Spring4之后若要使用注解要引入AOP的包

 6.2 Bean

1.在xml中配置扫描指定的包

<!--扫描指定的包,该包下的注解生效-->
    <context:component-scan base-package="com.gsy.pojo"/>

 

2.在该包下编写类 User.java 添加注解

import org.springframework.stereotype.Component;
//@Component 组件
//该注解等价于在xml中注册了一个bean
//相当于配置文件中 <bean id="user" class="当前注解的类"/>
@Component("user")
public class User {
    public String name="gsy";
}

 

6.3 属性注入

1.直接在属性上加注解 @value("xxx")

2.也可以在set方法上使用该注解

6.4 衍生注解

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

@Component三个衍生注解

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

@Controller:web层
@Service:service层
@Repository:dao层
写上这些注解,就相当于将这个类交给Spring管理装配了!

6.5 自动装配注解

@Autowired:自动装配 先byType再byName 配合@Qualifier(value="xxx")可以指定装配bean id为value的

@Nullable: 字段标记这个注解代表这个字段可以为null

@Resource: 自动装配,先byName再byType

@Component:组件,放在类上,代表该类被Spring管理了,也就是bean。

6.6 作用域

@scope("xxx")

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

6.7 小结

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,维护相对复杂

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

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 要使用注解就要开启注解的支持
<!--开启属性注解支持-->
    <context:annotation-config/>

6.8 基于Java类进行配置

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,

在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

1.User.java

复制代码
@Component
public class User {
    @Value("gsy")
    private String name;

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

}
复制代码

 

2.Myconfig.java 配置类

复制代码
//会被Spring容器托管,注册到容器中 它本来也是一个@Component
//@Configuration 代表这是一个配置类
@Configuration
@ComponentScan("com.gsy.pojo")//可以显式配置也可以不写
@Import(Myconfig2.class)//引入其他配置类
public class Myconfig {
    //注册一个Bean 相当于在xml中的<bean/>
    @Bean
    public User getUser(){//此方法的id就相当于bean标签中的id属性
        return new User();//返回值相当于bean标签中的class属性
    }
}
复制代码

 

3.测试

复制代码
public class Mytest {
    public static void main(String[] args) {
        //完全使用配置类的方式就只能用过AnnotationConfigApplicationContext来获取上下文,通过配置类的Class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
        User user = context.getBean("getUser", User.class);
        System.out.println(user.getName());

    }
}
复制代码

 

7.代理模式

7.1静态代理

静态代理角色分析

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

1.Rent.java 抽象角色

//租房
public interface Rent {
    public void rent();
}

 

2.Host.java 真实角色

public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

 

3.Proxy.java 代理

复制代码
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 hetong(){
        System.out.println("签合同");
    }
    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}
Proxy.java
复制代码

 

4.Client.java 客户

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

 

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


缺点 :

类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

7.2动态代理

动态代理的角色和静态代理的一样 .
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的


动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
基于接口的动态代理——JDK动态代理
基于类的动态代理—cglib
现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist


我们这里使用JDK的原生代码来实现,其余的道理都是一样的!
JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy 

1.Rent.java

2.Host.java

3.ProxyInvocationHandler.java

复制代码
public class ProxyInvocationHandler implements InvocationHandler {
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    // proxy : 代理类
    // method: 代理类的调用处理程序的方法对象.
    // 处理代理实例上的方法调用并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //核心利用反射实现
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }

    public void seeHouse() {
        System.out.println("看房");
    }

    public void fare() {
        System.out.println("收中介费");
    }
}
复制代码

 

4.Client.java

复制代码
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理实例的调用处理程序
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setRent(host);//将真实角色放进去
        Rent proxy = (Rent) pih.getProxy();//动态生成对应的代理类
        proxy.rent();
    }
}
复制代码

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

7.2.1动态代理的好处

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

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

8.AOP面向切面编程

使用Spring实现AOP 要导入包:

pom.xml

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

 

搭建环境:

UserService.java

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

 

UserServiceImpl.java

复制代码
public class UserServiceImpl implements UserService{

    @Override
    public void add() {
        System.out.println("增加用户");
    }

    @Override
    public void delete() {
        System.out.println("增删除用户");
    }

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

    @Override
    public void select() {
        System.out.println("查询用户");
    }
}
复制代码

 

Log.java

复制代码
public class Log implements MethodBeforeAdvice {

    //method:要执行的方法
    //args: 参数
    //target:目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}
复制代码

 

AfterLog.java

public class AfterLog implements AfterReturningAdvice {
    //returnValue:返回值
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

        System.out.println("执行了"+method.getName()+"返回结果为:"+returnValue);
    }
}

 

applicationContext.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"
       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.gsy.service.UserServiceImpl"/>
    <bean id="log" class="com.gsy.log.Log"/>
    <bean id="afterlog" class="com.gsy.log.AfterLog"/>

</beans>
复制代码

8.1方式一:使用原生的Spring API接口

复制代码
    <!--    第一种方式:使用原生的Spring API接口-->
    <!--    配置aop:需要导入aop的约束-->
    <aop:config>
        <!--切入点:expression:表达式,excution(要执行的位置!****)-->
        <aop:pointcut id="pointcut1" expression="execution(* com.gsy.service.UserServiceImpl.*(..))"/>

        <!--    执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut1"/>
        <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut1"/>
    </aop:config>
复制代码

 

8.2方式二:自定义类

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

 

8.3方式三:使用注解

AnnotationPointCut.java

复制代码
@Aspect//标注这个类是一个切面
public class AnnotationPointCut {

    @Before("execution(* com.gsy.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前");
    }
    @After("execution(* com.gsy.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后");
    }
//    在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.gsy.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);
    }
}
复制代码

 

xml配置

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

 

运行结果顺序:环绕前->方法执行前->执行方法->环绕后->方法执行后

9.整合Mybatis

导包

pom.xml

复制代码
   <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.13</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
            <scope>provided</scope>
        </dependency>


<!--        Spring操作数据库还需要spring-jdbc-->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.8</version>
        </dependency>

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

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version> 3.0.1</version>
        </dependency>
复制代码

 

spring6和springBoot3之后只支持JDK17!

User.java  (和8中一样)

 UserMapper.java 

public interface UserMapper {
    public List<User> selectUser();
}

 

UserMapperImpl.java

复制代码
public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;
    public void setSqlSession(SqlSessionTemplate sqlSession){
        this.sqlSession=sqlSession;
    }
    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
复制代码

 

mybatis-config.xml 核心配置文件

复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
    <typeAliases>
        <package name="com.gsy.pojo.User"/>
    </typeAliases>

</configuration>
复制代码

 UserMapper.xml

复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.gsy.mapper.UserMapper">
<select id="selectUser" resultType="com.gsy.pojo.User">
    select * from mybatis.user
</select>
</mapper>
复制代码

spring-dao.xml

  • 数据源
  • sqlSessionFactory
  • sqlSessionTamplate
复制代码
<?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">

<!--    Datasource:使用spring的数据源替换mybatis的配置-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="gsy666"/>
    </bean>

<!--    sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"/>
<!--        绑定Maybtis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/gsy/mapper/*.xml"/>
    </bean>

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


</beans>
复制代码

applicationContext.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">
<!--导入spring-dao.xml-->
    <import resource="spring-dao.xml"/>


    <bean id="userMapper" class="com.gsy.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>
复制代码

 方式二:

UserMapperImpl2.java

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    public List<User> selectUser(){
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

applicationContext.xml

    <bean id="userMapper2" class="com.gsy.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

 10.spring声明事务

使用事务要先添加tx约束

       xmlns:tx="http://www.springframework.org/schema/tx" 
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx">

xml

复制代码
<!--    事务管理器-->
<!--    JDBC事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>
    <!--    结合AOP实现事务的注入-->
    <!--    配合事务通知:-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
<!--    配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.gsy.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
复制代码

 

posted @   ygdgg  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示