Spring详解

1、简介

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

2、优点

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

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

3、组成 

 核心容器(Spring Core)

  核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。

应用上下文(Spring Context)

  Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。

面向切面编程(Spring AOP)

  AOP(Aspect Oriented Programming)

  通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

JDBC和DAO模块(Spring DAO)

  JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。

对象实体映射(Spring ORM)

  ORM(Object Relational Mapping)

  Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。

Web模块(Spring Web)

  Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以Spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

MVC模块(Spring Web MVC)

  MVC(Model View Controller)

  MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。

4、IOC本质

 控制反转(IOC),是一种设计思想,DI(依赖注入)是实现IOC 的一种方法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建有程序自己控制,控制反转后将对象的创建转移给第三方。

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

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

5、Spring配置

5.1、 别名

<?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="user" class="com.zjb.pojo.User">
        <property name="name" value="zjb"/>
    </bean>

    <!--别名-->
    <alias name="user" alias="newUser"/>
</beans>

5.2、 Bean的配置

 <!--
      id:bean的唯一标识符,也就是相当于变量名
      class: bean对象所对应的全限定名:包名+类名
      name: 也是别名,而且name可以同时取多个别名,以","分隔
    -->
    <bean id="user" class="com.zjb.pojo.User" name="u,user2">
        <property name="name" value="zjb"/>
    </bean>

5.3、import

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

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

    <import resource="spring-bean.xml"/>

</beans>

6、依赖注入(DI)

   依赖:bean对象的创建依赖于容器

   注入:bean对象中的属性由容器来注入

  • 构造器注入
  • set注入
  • 拓展注入 

6.1 set注入

环境搭建

1、复杂类型

package com.zjb.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 + '\'' +
                '}';
    }
}

 2、真实测试对象

package com.zjb.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address addr;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }

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

    public Address getAddr() {
        return addr;
    }

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    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 + '\'' +
                ", addr=" + addr.toString() +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}

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

    <bean id="address" class="com.zjb.pojo.Address">
        <property name="address" value="上海"/>
    </bean>

    <bean id="stu" class="com.zjb.pojo.Student">
        <!--普通值,value-->
        <property name="name" value="张三"/>
        <!--bean,ref-->
        <property name="addr" ref="address"/>
        <!--数组-->
        <property name="books">
            <array>
                <value>三国</value>
                <value>水浒</value>
            </array>
        </property>
        <!--map-->
        <property name="card">
            <map>
                <entry key="银行卡号" value="123456"></entry>
            </map>
        </property>
        <!--list-->
        <property name="hobbys">
            <list>
                <value>钓鱼</value>
                <value>打游戏</value>
            </list>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>LoL</value>
                <value>CF</value>
            </set>
        </property>
        <!--null-->
        <property name="wife">
            <null/>
        </property>
        <!--prop-->
        <property name="info">
            <props>
                <prop key="url">www</prop>
                <prop key="username">123</prop>
                <prop key="password">123</prop>
            </props>
        </property>
    </bean>

</beans>

4、测试类

import com.zjb.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student stu = (Student) context.getBean("stu");
        System.out.println(stu.toString());
    }
}

6.2 拓展注入

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

package com.zjb.pojo;

public class User {
    private String name;
    private int age;

    public User() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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


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

 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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间注入 可以直接注入属性的值:property-->
    <bean id="user" class="com.zjb.pojo.User" p:age="18" p:name="张三"/>
    <!--c命名空间注入 通过构造器注入: construct-args-->
    <bean id="user2" class="com.zjb.pojo.User" c:age="17" c:name="李四"/>
</beans>

测试类

import com.zjb.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
        User user = context.getBean("user", User.class);
        User user2 = context.getBean("user2", User.class);

        System.out.println(user.toString());

        System.out.println(user2.toString());
    }
}

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

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

 7、Bean的作用域

作用域描述
singleton(默认)将单个 bean 定义范围限定为每个 Spring IoC 容器的单个对象实例。
prototype将单个 bean 定义范围限定为任意数量的对象实例。
request将单个 bean 定义范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该 bean 实例是在单个 bean 定义的后面创建的。仅在 web-aware Spring 的上下文中有效ApplicationContext
session将单个 bean 定义范围限定为 HTTP 的生命周期Session。仅在 web-aware Spring 的上下文中有效ApplicationContext
application将单个 bean 定义范围限定为ServletContext. 仅在 web-aware Spring 的上下文中有效ApplicationContext
websocket将单个 bean 定义范围限定为WebSocket. 仅在 web-aware Spring 的上下文中有效ApplicationContext

 7.1 、singleton

单例模式:Spring默认的机制

<bean id="user2" class="com.zjb.pojo.User" c:age="17" c:name="李四" scope="singleton"/>

 7.2、prototype

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

<bean id="user2" class="com.zjb.pojo.User" c:age="17" c:name="李四" scope="prototype"/>

8、Bean的自动装配

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

在Spring中有三种装配方式

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

 8.1、byName自动装配

<?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="cat" class="com.zjb.pojo.Cat"/>
    <bean id="dog" class="com.zjb.pojo.Dog"/>
    <!--
      byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid
    -->
    <bean id="person" class="com.zjb.pojo.Person" autowire="byName">
        <property name="name" value="小三"/>
    </bean>

</beans>

 8.2、byType自动装配

<?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="cat" class="com.zjb.pojo.Cat"/>
    <bean id="dog" class="com.zjb.pojo.Dog"/>
    <!--
      byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean
    -->
    <bean id="person" class="com.zjb.pojo.Person" autowire="byType">
        <property name="name" value="小三"/>
    </bean>

</beans>

小结:

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

 8.3、注解实现自动装配

  使用注解实现自动装配的前提

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



</beans>

     2.配置注解的支持

 <context:annotation-config/>

    3.全部配置如下

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

    <context:annotation-config/>

</beans>

8.3.1、@Autowired

直接在属性上使用,也可以在set方法上使用。

使用Autowired我们可以不用编写set方法,前提是这个自动装配的属性在Spring容器中存在

package com.zjb.pojo;

import org.springframework.beans.factory.annotation.Autowired;

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

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

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

        <bean id="cat" class="com.zjb.pojo.Cat"/>
        <bean id="dog" class="com.zjb.pojo.Dog"/>
        <bean id="person" class="com.zjb.pojo.Person">
            <property name="name" value="小三"/>
        </bean>
</beans>

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

package com.zjb.pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class Person {
    @Autowired
    @Qualifier(value = "cat22")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog22")
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

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

        <bean id="cat11" class="com.zjb.pojo.Cat"/>
        <bean id="cat22" class="com.zjb.pojo.Cat"/>
        <bean id="dog11" class="com.zjb.pojo.Dog"/>
        <bean id="dog22" class="com.zjb.pojo.Dog"/>
        <bean id="person" class="com.zjb.pojo.Person">
            <property name="name" value="小三"/>
        </bean>
</beans>

8.3.2、@Resource

package com.zjb.pojo;
import javax.annotation.Resource;

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

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

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

        <bean id="cat11" class="com.zjb.pojo.Cat"/>
        <bean id="cat22" class="com.zjb.pojo.Cat"/>
        <bean id="dog11" class="com.zjb.pojo.Dog"/>
        <bean id="dog22" class="com.zjb.pojo.Dog"/>
        <bean id="person" class="com.zjb.pojo.Person">
            <property name="name" value="小三"/>
        </bean>
</beans>

8.3.3、@Resource和@Autowired的区别

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

9、使用注解开发

   9.1、@Component

package com.zjb.pojo;

import org.springframework.stereotype.Component;

/**
 * @Component : 组件
 * @Component 等价于 <bean id = "user" class="com.zjb.pojo.User"/>
 */
@Component
public class User {
    private String name = "小三";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解支持-->
    <context:annotation-config/>
    <!--component要扫描的包-->
    <context:component-scan base-package="com.zjb.pojo"/>
</beans>

 9.2 @Value

package com.zjb.pojo;

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

/**
 * @Component : 组件
 * @Component 等价于 <bean id = "user" class="com.zjb.pojo.User"/>
 */
@Component
public class User {
    /**
     * @Value 等价于 <property name="name" value="小三"/>
     * @Value 也可以用在set方法上
     */
    @Value("小三")
    private String name;

    public String getName() {
        return name;
    }

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

9.3、@Component衍生的注解

我们在web开发中,会按照mvc三层架构分层。

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

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

9.4、@Scope

package com.zjb.pojo;

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


/**
 * @Scope :作用域
 * @Scope 等价于 <bean id = "user" class="com.zjb.pojo.User" scope="singleton"/>
 */
@Scope("singleton")
@Component
public class User {

    @Value("小三")
    private String name;

    public String getName() {
        return name;
    }

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

 9.5、xml与注解的区别

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

xml与注解的最佳实践

  • xml用来管理bean
  • 注解只负责完成属性的注入

10、使用JavaConfig实现配置

package com.zjb.pojo;

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

@Component
public class User {
    @Value("小三")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.zjb.config;

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

/**
 * @Configuration 代表这是一个配置类,相当于applicationContext.xml文件
 * @Import 相当于 <import resource="xxx.xml"/> 表示 把配置类合并为一个
 * @ComponentScan 相当于  <context:component-scan base-package="com.zjb.pojo"/> 表示要扫描的包路径
 */
@Configuration
@Import(JavaConfig.class)
@ComponentScan("com.zjb.pojo")
public class AppConfig {

    /**
     *   @Bean 注册一个bean,相当于一个bean标签
     *   方法的名字,相当于bean标签的id属性
     *   方法的返回值,相当于bean标签的class属性
     */
    @Bean
    public User user(){
        return new User();
    }

}
import com.zjb.config.AppConfig;
import com.zjb.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = context.getBean("user", User.class);
        System.out.println(user.getName());
    }
}

11、代理模式

 11.1、静态代理

 角色分析

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

实现步骤:

  1、抽象角色

package com.zjb.demo1;

/**
 * 抽象角色
 */
public interface Rent {
    /**
     * 租房子
     */
    public void rent();
}

2、真实角色

package com.zjb.demo1;

/**
 * 真实角色
 */
public class Host implements Rent {
    /**
     * 出租房子
     */
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

3、代理角色

package com.zjb.demo1;

/**
 * 代理角色
 */
public class Proxy implements Rent {

    private Host host;

    public Proxy() {
    }

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

    /**
     * 租房子
     */
    public void rent() {
        seeHourse();
        host.rent();
        heTong();
    }

    public void seeHourse(){
        System.out.println("中介带你去看房子");
    }

    public void heTong(){
        System.out.println("签合同");
    }
}

 4、客户端

package com.zjb.demo1;

/**
 * 客户端访问代理角色
 */
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

静态代理模式的好处:

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

缺点:

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

11.2、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
  1. 基于接口:JDK动态代理
  2. 基于类:cglib
  3. java字节码实现:javassist

   11.2.1 JDK动态代理

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

  代码实现:

package com.zjb.demo2;

public interface UserService {
    void add();
    void update();
    void delete();
    void query();
}
package com.zjb.demo2;

public class UserServiceImpl implements UserService {

    public void add() {
        System.out.println("新增一个用户");
    }

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

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

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

package com.zjb.demo2;

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

public class ProxyInvocationHandler implements InvocationHandler {

    private Object target;

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

    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    /**
     * 处理代理程序
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        printLog(method.getName());
        return method.invoke(target,args);
    }

    public void printLog(String msg){
        System.out.println("执行了"+msg+"方法");
    }
}
package com.zjb.demo2;

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();

        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(userService);

        UserService proxy = (UserService) pih.getProxy();
        proxy.delete();
    }
}

动态代理的好处:

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

12、AOP

12.1、什么是AOP

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

12.2、AOP在Spring中的作用

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

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

SSpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice;

通知类型连接点实现接口
前置通知方法前org.springframework.aop.MethodBeforeAdvice
后置通知方法后org.springframework.aop.AfterReturningAdvice
环境通知方法前后org.aopalliance.intercept.MethodInterceptor
异常抛出通知方法抛出异常org.springframework.aop.ThrowsAdvice
引介通知类中增加新的方法属性org.springframework.aop.IntroductionInterceptor

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

12.3、使用Spring实现AOP

 使用AOP,需要导入一个依赖包

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

 12.3.1 实现方式一:使用Spring 提供的API接口

package com.zjb.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {


    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了");
    }
}
package com.zjb.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法完成,返回值"+returnValue);
    }
}
package com.zjb.service;

public interface UserService {
    void add();
    void update();
    void delete();
    void query();
}
package com.zjb.service;

public class UserServiceImpl implements UserService {

    public void add() {
        System.out.println("新增一个用户");
    }

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

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

    public void query() {
        System.out.println("查询一个用户");
    }
}
import com.zjb.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();

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

    <bean id="userService" class="com.zjb.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.zjb.log.BeforeLog"/>
    <bean id="afterLog" class="com.zjb.log.AfterLog"/>

    <aop:config>
        <aop:pointcut id="logPointCut" expression="execution(* com.zjb.service.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="beforeLog" pointcut-ref="logPointCut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="logPointCut"/>
    </aop:config>
</beans>

12.3.2 实现方式二:自定义类实现AOP

package com.zjb.diy;

public class DiyLog {
    public void before(){
        System.out.println("=========方法执行前=============");
    }

    public void after(){
        System.out.println("=========方法执行后=============");
    }
}
package com.zjb.service;

public interface UserService {
    void add();
    void update();
    void delete();
    void query();
}
package com.zjb.service;

public class UserServiceImpl implements UserService {

    public void add() {
        System.out.println("新增一个用户");
    }

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

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

    public void query() {
        System.out.println("查询一个用户");
    }
}
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

  
    <bean id="userService" class="com.zjb.service.UserServiceImpl"/>
    <bean id="diyLog" class="com.zjb.diy.DiyLog"/>

    <aop:config>
        <aop:aspect id="diy" ref="diyLog">
            <aop:pointcut id="diyPoint" expression="execution(* com.zjb.service.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="diyPoint"/>
            <aop:after method="after" pointcut-ref="diyPoint"/>
        </aop:aspect>
    </aop:config>
</beans>
import com.zjb.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();

    }
}

12.3.3 实现方式三:注解实现AOP

package com.zjb.diy;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnoLog {

    @Before("execution(* com.zjb.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("========方法执行前==========");
    }

    @After("execution(* com.zjb.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("========方法执行后==========");
    }
}
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

   

    <!--开启aop的注解支持-->
    <aop:aspectj-autoproxy/>

    <bean id="annoLog" class="com.zjb.diy.AnnoLog"/>
    <bean id="userService" class="com.zjb.service.UserServiceImpl"/>

</beans>

13、整合Mybatis

1、引入依赖

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

2、mybatis-config.xml 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>

    <settings>
        <!--标准的日志实现-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <typeAliases>
        <package name="com.zjb.pojo"/>
    </typeAliases>

</configuration>

3、User实体类

package com.zjb.pojo;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
    private String pwd;
}

4、UserMapper

package com.zjb.mapper;

import com.zjb.pojo.User;

import java.util.List;

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

5、 UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zjb.mapper.UserMapper">
    <select id="selectUser" resultType="User">
        select * from `user`;
    </select>
</mapper>

 6.测试类

import com.zjb.mapper.UserMapper;
import com.zjb.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

        List<User> users = userMapper.selectUser();

        for (User user : users) {
            System.out.println(user.toString());
        }

    }
}

13.1、方式一:SqlSessionTemplate

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

    <!-- 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/study?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>
    <!-- sqlSession -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 构造注入,因为SqlSessionTemplate这个类没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
</beans>

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

    <import resource="spring-dao.xml"/>


    <bean id="userMapper" class="com.zjb.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>

9、 UserMapperImpl

package com.zjb.mapper;

import com.zjb.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper {

    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    public List<User> selectUser() {
        return sqlSession.getMapper(UserMapper.class).selectUser();
    }
}

13.2、方式二:SqlSessionDaoSupport

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

    <!-- 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/study?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>
</beans>

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

    <import resource="spring-dao.xml"/>


    <bean id="userMapper" class="com.zjb.mapper.UserMapperImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

9、 UserMapperImpl

package com.zjb.mapper;

import com.zjb.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {


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

14、声明式事务

14.1、实现

<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 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/study?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!--配置声明式事务-->
    <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>
            <!--propagation:事务传播特性-->
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="advicePointCut" expression="execution(* com.zjb.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="advicePointCut"/>
    </aop:config>
</beans>

14.2、事务的七种传播特性

 REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 

 SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 

 MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。 

 REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 

 NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 

 NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 

 NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

14.3、事物的实现方式

  • 声明式事务
  • 编程式事务

14.4、事务的特性

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值