从零开始学Spring

240 阅读17分钟

1. 关于Spring

1.1 Spring简介

Spring是一个轻量级**控制反转(IoC)面向切面(AOP)**的容器框架 它整合了现有的技术框架,使现有的技术更加容易使用

至于什么是Ioc和AOP,下面我将根据自己的理解和网上的资料进行说明。

两种流行的框架集,都离不开Spring的融合:

  • 曾经流行的SSH: Struct2 + Spring + Hibernate
  • 当下流行的SSM: SpringMVC + Spring + Mybatis

学习过程中,可以搭配官方文档进行学习: 官方文档网址-5.2.9版本

需要导入的依赖:


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

1.2 Spring的优点

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

什么是非侵入式框架? 非侵入式框架指的是框架与用户代码依赖性较低的框架,易于迁移,但是用户代码与框架互动的方式比较复杂。 与之相反,侵入式框架会让用户代码对框架产生依赖,用户编写的代码不能在框架外使用,不利于代码的复用。不过,侵入式框架可以是用户代码跟框架更好地结合,更加充分地利用框架提供的功能。

1.3 Spring的组成

Spring有七大核心模块,如图所示:

BCf3FA.png

  1. Spring Core(核心容器)

核心容器提供Spring框架的基本功能。

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

  1. Spring Context(应用上下文)

Spring上下文是一个配置文件,向Spring框架提供上下文信息。

Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能

  1. Spring AOP(Spring面向切面编程)

Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务。

  1. Spring DAO(JDBC与DAO模块)

JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息

  1. Spring ORM(对象实体映射)

Spring框架中插入了若干个ORM框架,从而提供了ORM对象的关系工具(包括Hibernate、iBatis SQL Map等)

什么是ORM? 用于实现面向对象编程语言里不同类型系统的数据之间的转换

  1. Spring Web(Web模块)

Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文

  1. Spring Web MVC(MVC模块)

Spring MVC框架是一个全功能的构建Web应用程序的MVC实现

2. 关于IoC

2.1 IoC理论推导

原来的模式:

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

在以上模式中,用户的需求很容易影响我们原来的代码,因此我们常常需要根据用户的需求去修改源代码,修改成本昂贵 于是,我们想到使用一个set接口来实现


private UserDao userDao;

public void setUserDao(UserDao userDao){
    this.userDao = userDao;
}

对比一下使用set注入前后:

之前,程序是主动创建对象,控制权在程序员手中 使用了set注入以后,程序不再具有主动性,而是变成了被动的接收对象 程序员不需要再去管理对象的创建了 这样一来,系统的耦合性大大降低,可以更加专注于业务的实现上 这便是是Ioc的原型

2.2 IoC本质

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

耦合状态下: BFE2uR.png

使用IoC: BFEhE6.png

拿掉IoC: BFET8e.png

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

BFAH00.png

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

2.3 HelloSpring

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

    <!--使用Spring来创建对象,在Spring中这些都称为bean
    也就是说 bean相当于对象

    Hello hello = new Hello()

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

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

</beans>

  1. 测试

public class MyTest {

    public static void main(String[] args) {
        //获取Spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //我们的对象都在Spring中管理了,我们要使用的话直接去里面取出来就可以
        Hello hello = (Hello)context.getBean("hello");
        System.out.println(hello.toString());
    }
}

我们可以看到,我们想要实现不同的操作,只需要在xml配置文件中进行修改。 总之,我们可以通过Spring进行对象的创建、管理和装配

2.4 IoC创建对象的方式

  • 默认使用无参构造创建对象
  • 假如要使用有参构造创建对象: 有三种形式:
  1. 直接通过参数名创建

<bean id="user" class="com.taotao.pojo.User">
    <constructor-arg name="name" value="tao"/>
</bean>

  1. 参数类型匹配:

<bean id="user" class="com.taotao.pojo.User">
    <constructor-arg type="java.lang.String" value="tao"/>
</bean>

  1. 下标赋值:

<bean id="user" class="com.taotao.pojo.User">
    <constructor-arg index="0" value="涛涛学Java"/>
</bean>

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

3. Spring配置

3.1 别名(<alias>)


<alias name="user" alias="user2"/>

3.2 Bean的配置

  • id: bean的唯一标识符,也就是相当于普通新建对象时的对象名
  • class: bean所对应的全类名(包名+类名)
  • name: 别名,而且与alias标签不同,name属性可以同时取多个别名

<bean id="useTwo" class="com.taotao.pojo.UserTwo" name="user222,u2,UTwo">
    <property name="name" value="tao"/>
</bean>

3.3 import标签

一般用于团队开发使用,可以将多个配置文件导入合并为一个 假如项目中有多个人开发,他们负责不同的类的开发,不同的类需要注册在不同的bean中,为了将所有的配置文件(.xml)合并统一为一个总的配置文件 applicationContext.xml,我们可以使用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="beans.xml"/>
    <import resource="beans2.xml"/>

</beans>

使用的时候,直接引用applicationContext.xml即可

4. 依赖注入(DI)

4.1 构造器注入

前文所用的便是构造器注入的方式

4.2 Set方式注入

  • 依赖注入:本质是Set注入
  • 依赖:bean对象的创建依赖于容器
  • 注入:bean对象中的所有属性,由容器来注入

环境搭建:

  1. 创建复杂类型

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

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

  1. 真实测试对象

public class Student {

    private String name;
    private Address address;
    private String[] book;
    private List<String> hobby;
    private Map<String,String> card;
    private Set<String> games;
    private Properties info;
    private String wife;

  1. application.xml

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

    <bean id="student" class="com.taotao.pojo.Student">
        <!--普通值注入-->
        <property name="name" value="涛涛"/>
    </bean>

</beans>

  1. 测试类:

public class MyTest {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.taotao.pojo.Address">
        <property name="address" value="广州"/>
    </bean>

    <bean id="student" class="com.taotao.pojo.Student">
        <!--1. 普通值注入-->
        <property name="name" value="涛涛"/>

        <!--2. Bean注入-->
        <property name="address" ref="address"/>

        <!--3. 数组注入-->
        <property name="book">
            <array>
                <value>红楼梦</value>
                <value>三国演义</value>
                <value>水浒传</value>
                <value>西游记</value>
            </array>
        </property>
        
        <!--4. List-->
        <property name="hobby">
            <list>
                <value>听歌</value>
                <value>写代码</value>
                <value>谈恋爱</value>
                <value>赚钱</value>
            </list>
        </property>
        
        
        <!--5. Map-->
        <property name="card">
            <map>
                <entry key="涛涛" value="1"/>
                <entry key="狗子" value="2"/>
                <entry key="彤彤" value="3"/>
            </map>
        </property>
        
        <!--6. Set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>BOB</value>
                <value>COC</value>
            </set>
        </property>
        
        <!--7. null-->
        <property name="wife">
            <null/>
        </property>
        
        <!--8. Properties-->
        <property name="info">
            <props>
                <prop key="性别"></prop>
                <prop key="年级">大三</prop>
                <prop key="身高">177</prop>
            </props>
        </property>

    </bean>

</beans>

4.3 拓展方式

  • p命名空间(property)注入,可以直接注入属性的值

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

    <bean id="user" class="com.taotao.pojo.User" p:name="tao" p:age="18"/>

</beans>

  • c命名空间(construct-args)注入,通过构造器注入

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

    <bean id="user2" class="com.taotao.pojo.User" c:age="20" c:name="狗子"/>

</beans>

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


<beans xmlns:c="http://www.springframework.org/schema/c"></beans>


<beans xmlns:p="http://www.springframework.org/schema/p"></beans>

5. Bean的作用域

六种作用域

作用域描述
singleton在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在
prototype每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()方法时,相当于都执行了一次new xxxBean()
request每次Http请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session同一个Http Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境

下面就挑两种详细讲:

  1. 单例模式:(Spring默认模式)获取的是同一个对象

<bean id="user2" class="com.taotao.pojo.User" c:age="20" c:name="狗子" scope="singleton"/>

0jtdSg.png

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

<bean id="user2" class="com.taotao.pojo.User" c:age="20" c:name="狗子" scope="prototype"/>

0jtyT0.png

  1. request、session、application作用域只能在web开发中使用

6. Bean的自动装配

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

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

  1. 在xml中显式地配置

  2. 在Java中显式地配置

  3. 隐式地自动装配

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

  • 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean
  • 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI

6.1 测试

6.1.1 环境搭建

(一个人有两只宠物)

  1. 实体类

public class Cat {

    public void sounds(){
        System.out.println("miao~");
    }
}


public class Dog {
    public void sounds(){
        System.out.println("wang!");
    }
}


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

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

    <context:annotation-config/>

    <bean class="com.taotao.pojo.Cat" id="cat"/>
    <bean class="com.taotao.pojo.Dog" id="dog"/>

    <bean id="person" class="com.taotao.pojo.Person">
        <property name = "cat" ref = "cat"/>
        <property name = "dog" ref = "dog"/>
        <property name = "name" value = "taotao"/>
    </bean>

</beans>

  1. 测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        person.getDog().sounds();
        person.getCat().sounds();

    }
}

6.2 byName自动装配

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

  • 弱点:对名字要求比较苛刻,所以需要保证所有的bean的id各不相同

<bean id="person" class="com.taotao.pojo.Person" autowire="byName">
    <property name="name" value="taotao"/>
</bean>

6.3 byType自动装配

会自动在容器上下文中查找和自己对象属性类型相同的bean

  • 优点:可以无视id
  • 弱点:要求该类型的bean全局唯一,所以需要所有的bean的class各不相同

<bean id="person" class="com.taotao.pojo.Person" autowire="byType">
    <property name="name" value="taotao"/>
</bean>

6.4 使用注解实现自动装配

如果想要使用注解,需要:

  1. 导入约束 context约束:

<beans xmlns:context="http://www.springframework.org/schema/context"></bean>

  1. 配置注解的支持

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

6.4.1 @Autowired

  1. 实体类

public class Person {

    @Autowired
    private Cat cat;

    @Autowired
    private Dog dog;

    private String name;

使用@Autowired注解可以省略去set方法

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

    <context:annotation-config/>

    <bean class="com.taotao.pojo.Cat" id="cat"/>
    <bean class="com.taotao.pojo.Dog" id="dog"/>
    <bean id="person" class="com.taotao.pojo.Person"/>

</beans>

@Autowired的源码


@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

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

}

所以我们可以看到,其实@Autowired是可以带参数的:


@Autowired(required = false)

(联系注解@Nullable)

如果显式地定义了@Autowired的required属性为false,说明这个对象可以为null,否则不允许为null

6.4.2 @Qualifier

@Autowired可以和@Qualifier搭配使用

@Qualifier能指定某个bean进行自动装配


public class Person {

    @Autowired
    @Qualifier(value = "cat111")
    private Cat cat;

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


    <bean class="com.taotao.pojo.Cat" id="cat"/>
    <bean class="com.taotao.pojo.Cat" id="cat111"/>
    <bean class="com.taotao.pojo.Dog" id="dog"/>
    <bean class="com.taotao.pojo.Dog" id="dog222"/>
    <bean id="person" class="com.taotao.pojo.Person"/>

6.4.3 @Resource注解

先通过bean的id查找,在通过类型查找,若两个都不符合,才报错

指定bean @Resource(name = "cat2")

小结: @Resource和@Autowired的异同: 相同点:

  • 都用于bean的自动装配,都可以放在属性字段上 不同点:
  • @Autowired 通过byType方式实现
  • @Resource默认通过byName的方式实现,如果通过byName的方式查找失败,则继续通过byType的方式实现

8. Spring注解开发

要使用注解开发,首先要保证aop包的导入 除此之外,使用注解开发还需要导入context约束,增加注解的支持

@Component:


<?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:component-scan base-package="com.taotao.pojo"/>

    <context:annotation-config/>
    
</beans>

  1. bean的生成

//@Component相当于在xml配置了

@Component
public class User {

    public String name = "taotao";
}


  1. 属性的注入

@Component
public class User {
    
    @Value("taotao")
    public String name;
}


  1. 衍生的注解

@Component的三个衍生注解: 我们在web开发中,会按照mvc三层架构分层,在三个不同的层中,有三个不同的衍生注解,他们功能是一样的,都是代表将某个类注册到Spring中,装配Bean

  • dao层 @Repository

  • service @Service

  • controller(servlet) @Controller

  1. 自动装配注解

@Autowired @Resource

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

@Component
@Scope("singleton")
public class User {

    @Value("taotao")
    public String name;
}

  1. 小结

xml与注解:

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

xml与注解的最佳结合:

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

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

9. 使用Java进行配置

通过这种方法,可以完全不使用Spring的xml配置,全权交给Java来做

JavaConfig,Spring推荐用它来进行配置

  1. 实体类:

public class User {

    private String name;
    public String getName() {
        return name;
    }

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

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

  1. 配置类:

@Configuration
//@Configuration是一个复合注解,底层也有一个@Component注解,所以会将类注册到容器中
//@Configuration表明,这个类是一个配置类,作用相当于以前的beans.xml
public class MyConfig {

    @Bean
    //注册一个bean
    //就相当于我们之前在beans.xml中写的一个bean
    @Import(MyConfig2.class)
    //导入另一个配置类
    public User getUser(){
        return new User();
    }
}

  1. 测试类:

public class MyTest {

    public static void main(String[] args) {

        //如果完全使用了配置类方法来配置,那么我们就只能通过AnnotationConfigApplicationContext来获取容器,通过配置类的class对象获取
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User getUser = context.getBean("getUser", User.class);
        System.out.println(getUser.getName());
        System.out.println(getUser.hashCode());

    }
}

9.AOP

9.1 什么是AOP

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

图示:

BklN9I.png

9.2 AOP在Spring中的作用

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

图示:

BklagP.png

SpringAOP中,通过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的部分名词解释:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等

  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

  • 目标(Target):被通知对象。

  • 代理(Proxy):向目标对象应用通知之后创建的对象。

  • 切入点(PointCut):切面通知 执行的“地点”的定义。

*连接点(JointPoint):与切入点匹配的执行点。

9.3 使用Spring实现AOP

  1. 导入依赖:

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

  1. AOP的实现:

方式一:使用Spring的API接口

  1. 编写业务接口和实现类

public interface UserService {

    public void add();

    public void delete();

    public void update();

    public void search();

}


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 search() {
        System.out.println("查询用户");
    }
}

然后编写两个增强类:

public class Log implements MethodBeforeAdvice {

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



public class AfterLog implements AfterReturningAdvice {
    //returnValue 返回值
    //method被调用的方法
    //args 被调用的方法的对象的参数
    //target 被调用的目标对象
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + target.getClass().getName()
        +"的"+method.getName()+"方法,"
        +"返回值:"+returnValue);
    }
}

  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"
       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-->
    <bean id="userService" class="com.taotao.service.UserServiceImpl"/>
    <bean id="log" class="com.taotao.log.Log"/>
    <bean id="afterLog" class="com.taotao.log.AfterLog"/>
    
    <!--配置aop: 需要导入aop的约束-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="pointcut" expression="execution(* com.taotao.service.UserServiceImpl.*(..))"/>

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

    </aop:config>

</beans>

  1. 测试类:

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

        //动态代理代理的是接口
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}   

方式二:使用自定义类来实现AOP

  1. 日志类:

public class MyPointCut {

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

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

  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"
       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="diy" class="com.taotao.diy.MyPointCut"/>

    <aop:config>
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="pointCut" expression="execution(* com.taotao.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="pointCut"/>
            <aop:after method="after" pointcut-ref="pointCut"/>
        </aop:aspect>
    </aop:config>

</beans>

  1. 测试

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.add();
  }
}

方式三:注解实现AOP

  1. 编写一个注解实现的增强类

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

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

    @After("execution(* com.taotao.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("=========方法执行后============");
    }


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

        //执行方法
        Object proceed = proceedingJoinPoint.proceed();

        System.out.println("=======环绕后=====");
    }
}

  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"
       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="annotationPointCut" class="com.taotao.diy.AnnotationPointCut"/>
    <aop:aspectj-autoproxy/>

</beans>

关于aop:aspectj-autoproxy标签:

通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@Aspect切面的bean创建代理,织入切面。 当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了 <aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理

10. 整合Mybatis

什么是Mybatis-spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中

整合Mybatis方式一:

  1. 导入依赖

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

<build>
   <resources>
       <resource>
           <directory>src/main/java</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>true</filtering>
       </resource>
   </resources>
</build>

  1. 编写数据源配置

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

  1. 配置SqlSessionFactory,关联MyBatis

<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!--关联Mybatis-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath:com/kuang/dao/*.xml"/>
</bean>

  1. 注册sqlSessionTemplate,关联sqlSessionFactory

<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--利用构造器注入-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

tips: 所有基础步骤整合在一起形成一个固定的配置文件 当Spring要整合Mybatis的时候,就可以直接用 然后在applicationContext.xml中在进行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"
       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:使用Spring的数据源替换Mybatis的配置
    这里使用Spring提供的JDBC:org.springframework.jdbc.datasource
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="482051025jsZgt"/>
    </bean>

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

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

    <!---->
    <bean id="userMapper" class="com.taotao.mapper.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
    </bean>


</beans>


sqlSessionFactory

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。 SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的

sqlSessionTemplate

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。

  1. 给接口加实现类,私有化sqlSessionTemplate

public class UserMapperImpl implements UserMapper{

    //一切操作,都通过sqlSessionTemplate来进行,相当于在Mybatis中的sqlSession
    private SqlSessionTemplate sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

  1. 注册Bean实现

<!---->
<bean id="userMapper" class="com.taotao.mapper.UserMapperImpl">
    <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>

  1. 测试

public class Mytest {

    public static void main(String[] args) throws IOException {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

        for (User user: userMapper.selectUser()) {
            System.out.println(user);
        }
    }
}

整合Mybatis方式二:


public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}


<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

11. Spring中的事务管理:

11.1 声明式事务:

  • 一般情况下比编程式事务好用。

  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。

  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

  1. 导入约束

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

  1. 配置声明式事务:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="dataSource" />
</bean>

  1. 配置好事务管理器后我们需要去配置事务的通知

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

11.2 编程式事务:需要在代码中进行事务的管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚

  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码