当前位置:   article > 正文

【JAVA】Spring 框架_spring框架代码

spring框架代码

前言

本博文专用于软件创新实验室 Spring 框架课堂,由于课堂时间有限,选取了 Spring 框架中比较重点的几个来介绍。 Spring 框架的诞生是为了使开发更加高效简洁,同时减少耦合程度, 主要还是思想上的一个转变 ,想要深入了解 Spring 框架的,可以查阅Spring 官方文档,也推荐一下狂神老师的视频教学,值得一看。  

初识Spring

简介

2002年,Rod Jahnson首次推出了 Spring 框架雏形 interface21 框架,

2004年3月24日, Spring 框架以 interface21 框架为基础,经过重新设计,发布了1.0正式版,

Spring理念 : 使现有技术更加实用 , 本身就是一个大杂烩 , 整合现有的框架技术,

官网:spring.io/

官方下载地址: repo.spring.io/libs-releas…

GitHub: github.com/spring-proj…

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

组成

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

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

核心容器(Spring Core)

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

应用上下文(Spring Context)

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

Spring面向切面编程(Spring AOP)

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

JDBC和DAO模块(Spring DAO)

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

对象实体映射(Spring ORM)

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

Web模块(Spring Web)

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

MVC模块(Spring Web MVC)

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

创建

新建一个 Maven 项目,在 pom.xml 中进行配置,

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.3</version>
</dependency>
复制代码

目录

先编写一个 User 实体类, User.java ,

package com.idiot.pojo;

public class User{
    private String name;

    public String getName() {
        return name;
    }

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

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

再编写我们的 Spring 文件,这里命名为 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就是java对象 , 由Spring创建和管理-->
    <bean id="helloSpring" class="com.idiot.pojo.User">
        <property name="name" value="Spring"/>
    </bean>
</beans>
复制代码

最后进行测试, MyTest.java ,

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

public class MyTest {
    public static void main(String[] args) {
        //获取Spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        
        //我们的对象现在都在Spring中管理,我们要使用的话,直接从里面取出来
        User helloSpring = (User) context.getBean("helloSpring");
        System.out.println(helloSpring.toString());
    }
}
复制代码

运行结果

配置说明

别名

如果添加了别名,我们也可以使用别名获取这个对象,原名也是可以使用的,在 beans.xml 中进行配置,

<alias name="helloSpring" alias="helloAlias"/>
<bean id="helloSpring" class="com.idiot.pojo.User">
    <property name="name" value="Spring"/>
</bean>
复制代码

Bean的配置

bean 就是 java 对象,由 Spring 创建和管理,

  • id : bean 的唯一标识符,也就相当于对象名,
  • class : bean 对象所对应的全限定名:包名+类型,
  • name :也是别名,而且 name 可以同时取多个别名,可以用逗号,分号,空格隔开,

如果没有配置 id , name 就是默认标识符,

如果配置了 id ,又配置了 name ,那么 name 是别名,

如果不配置 id 和 name ,可以根据 applicationContext.getBean(.class) 获取对象,  

import

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

假设现在项目中有多个人开发,其中三个人负责不同的类的开发,不同的类需要注册在不同的 bean 配置文件中,我们可以利用 import 将所有人的 beans.xml 合并成一个总的,即 applicationContext.xml ,

<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
复制代码

使用的时候直接使用总配置 applicationContext.xml 即可。  

思考

  • User 对象是谁创建的?
    • User 对象是由 Spring 创建的,

      <bean id="helloSpring" class="com.idiot.pojo.User">
          <property name="name" value="Spring"/>
      </bean>
      复制代码
      类型 变量名 = new 类型();
      User user = new User();
      
      id = 变量名
      class = new的对象
      property相当于给对象中的属性设值
      复制代码
  • User 对象的属性是怎么设置的?
    • User 对象的属性是由 Spring 容器设置的,

这个过程就叫控制反转:

  • 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用 Spring 后,对象是由 Spring 来创建的,
  • 反转:程序本身不创建对象,而变成被动的接收对象,

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

IOC 是一种编程思想,由主动的编程变成被动的接收,可以通过 newClassPathXmlApplicationContext 去浏览一下底层源码,  

IOC

本质

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

IoC 是 Spring 框架的核心内容,使用多种方式完美的实现了 IoC ,可以使用 XML 配置,也可以使用注解,新版本的 Spring 也可以零配置实现 IoC 。

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

理论推导

首先创建一个 Maven 项目,然后创建接口和实现类,目录总览如下,

UserDao.java 接口,

package com.idiot.dao;

public interface UserDao {
    public void getUser();
}
复制代码

Dao 的实现类, UserDaoImpl.java ,

package com.idiot.dao;

public class UserDaoImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("获取用户数据");
    }
}
复制代码

UserService 的接口, UserService.java ,

package com.idiot.service;

public interface UserService {
    public void getUser();
}
复制代码

Service 的实现类, UserServiceImpl.java ,

package com.idiot.service;

import com.idiot.dao.UserDao;
import com.idiot.dao.UserDaoImpl;

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();

    @Override
    public void getUser() {
        userDao.getUser();
    }
}
复制代码

测试一下, MyTest.java ,

import com.idiot.service.UserService;
import com.idiot.service.UserServiceImpl;
import org.junit.Test;

public class MyTest {
    @Test
    public void test(){
        //用户实际调用的是业务层,Dao层他们不需要接触!
        UserService service = new UserServiceImpl();
        service.getUser();
    }
}
复制代码

这里要使用 @Test ,需要导入 junit.jar ,

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
复制代码

运行结果如下,

现在增加一个 Dao 的实现类, UserDaoOracleImpl.java ,

package com.idiot.dao;

public class UserDaoOracleImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("Oracle获取用户数据");
    }
}
复制代码

如果用户想去调用 Oracle 这个实现类,则我们必须去源代码去修改代码,即在 service 实现类里面修改对应的实现,

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoOracleImpl();

   @Override
   public void getUser() {
       userDao.getUser();
  }
}
复制代码

再加一个 Mysql 的实现类也是一样的, UserDaoSqlserverImpl.java ,

package com.idiot.dao;

public class UserDaoSqlserverImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("MySql获取用户数据");
    }
}
复制代码

现在我们这个程序的代码少,修改起来是不麻烦的,但假设修改需求非常大 , 修改源码这种方式就根本不适用了, 每次变动都需要修改大量代码,这样子的成本代价是十分昂贵的。

如何解决这样的问题?

我们将使用一个 set 接口实现,使之发生革命性的变化!

UserServiceImpl.java

package com.idiot.service;

import com.idiot.dao.UserDao;

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    // 利用set实现
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        userDao.getUser();
    }
}
复制代码

MyTest.java

@Test
public void test(){
    UserServiceImpl service = new UserServiceImpl();
    //Mysql实现
    service.setUserDao( new UserDaoSqlserverImpl() );
    service.getUser();
    
    //Oracle实现
    service.setUserDao( new UserDaoOracleImpl() );
    service.getUser();
}
复制代码

与使用 set 前的代码进行对比,这已经发生了根本性的变化, 以前所有东西都是由程序主动去进行控制创建 , 而使用了 set 注入之后,程序不再具有主动性,而是变成了被动接受的对象,即把主动权交给了调用者,程序则只负责提供一个接口,

这种思想,从本质上解决了问题,我们程序员不再去管理对象的创建,而是更多的去关注业务的实现,耦合性大大降低,这也就是IOC的原型!  

创建对象方式

1. 使用无参构造创建对象(默认)

目录

User.java

package com.idiot.pojo;

public class User {
    private String name;

    public User() {
        System.out.println("User无参构造");
    }

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

    public void show(){
        System.out.println("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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.idiot.pojo.User">
        <property name="name" value="Hello Spring!"/>
    </bean>
</beans>
复制代码

MyTest.java

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

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

        User user = (User) context.getBean("user");
        user.show();
    }
}
复制代码

运行结果

此时,将无参构造改成有参构造再运行,

public User(String name) {
    System.out.println("User无参构造");
}
复制代码

发现程序报错,没有办法进行初始化, Bean 初始化失败,

2. 使用有参构造创建对象

有参构造器,

public User(String name) {
    this.name = name;
}
复制代码

①下标赋值

<bean id="user" class="com.idiot.pojo.User">
    <constructor-arg index="0" value="idiot"/>
</bean>
复制代码

②类型赋值

如果有两个及以上的相同类型,就无法判断把值给谁了,因此 不建议使用 !

<bean id="user" class="com.idiot.pojo.User">
    <constructor-arg type="java.lang.String" value="idiot.."/>
</bean>
复制代码

③参数名赋值

<bean id="user" class="com.idiot.pojo.User">
    <constructor-arg name="name" value="Idiot"/>
</bean>
复制代码

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

DI依赖注入

构造器注入

中已讲,忘了的话回头看一下。  

Set注入

  • 依赖注入: set 注入,
    bean
    bean
    

【环境搭建】

  1. 复杂类型
  2. 真实测试对象  

复杂类型

Address.java

package com.idiot.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 + '\'' +
                '}';
    }
}
复制代码

如果在 Address 类中不添加 toString 方法,则会导致输出的不是 String 值,而是类似于这样子的 com.idiot.pojo.Address@319b92f3 ,

Student.java

package com.idiot.pojo;

import java.util.*;

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 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 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 + '\'' +
                ", \naddress=" + address +
                ", \nbooks=" + Arrays.toString(books) +
                ", \nhobby=" + hobby +
                ", \ncard=" + card +
                ", \ngames=" + games +
                ", \nwife='" + wife + '\'' +
                ", \ninfo=" + info +
                '}';
    }
}
复制代码

真实测试对象

1. 常量注入

<bean id="student" class="com.idiot.pojo.Student">
    <property name="name" value="idiot"/>
</bean>
复制代码

2. Bean注入

注意:这里的值是一个引用, ref ,

<bean id="addr" class="com.idiot.pojo.Address">
    <property name="address" value="浙江"/>
</bean>

<bean id="student" class="com.idiot.pojo.Student">
    <property name="name" value="idiot"/>
    <property name="address" ref="addr"/>
</bean>
复制代码

3. 数组注入

<bean id="student" class="com.idiot.pojo.Student">
    <property name="name" value="idiot"/>
    <property name="address" ref="addr"/>
    <property name="books">
        <array>
            <value>C++</value>
            <value>Java</value>
            <value>Python</value>
        </array>
    </property>
</bean>
复制代码

4. List注入

<property name="hobby">
    <list>
        <value>sing</value>
        <value>climb</value>
    </list>
</property>
复制代码

5. Map注入

<property name="card">
    <map>
        <entry key="中国电信" value="10000"/>
        <entry key="中国移动" value="10086"/>
    </map>
</property>
复制代码

6. Set注入

<property name="games">
    <set>
        <value>堡垒之夜</value>
        <value>皇室战争</value>
        <value>王者荣耀</value>
    </set>
</property>
复制代码

7. Null注入

<property name="wife"><null/></property>
复制代码

8. Properties注入

<property name="info">
    <props>
        <prop key="学号">123456</prop>
        <prop key="性别">man</prop>
        <prop key="姓名">idiot</prop>
    </props>
</property>
复制代码

下面是完整的 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="addr" class="com.idiot.pojo.Address">
        <property name="address" value="浙江"/>
    </bean>

    <bean id="student" class="com.idiot.pojo.Student">
        <property name="name" value="idiot"/>
        <property name="address" ref="addr"/>

        <property name="books">
            <array>
                <value>C++</value>
                <value>Java</value>
                <value>Python</value>
            </array>
        </property>

        <property name="hobby">
            <list>
                <value>sing</value>
                <value>climb</value>
            </list>
        </property>

        <property name="card">
            <map>
                <entry key="中国电信" value="10000"/>
                <entry key="中国移动" value="10086"/>
            </map>
        </property>

        <property name="games">
            <set>
                <value>堡垒之夜</value>
                <value>皇室战争</value>
                <value>王者荣耀</value>
            </set>
        </property>

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

        <property name="info">
            <props>
                <prop key="学号">123456</prop>
                <prop key="性别">man</prop>
                <prop key="姓名">idiot</prop>
            </props>
        </property>
    </bean>
</beans>
复制代码

以及测试 MyTest.java ,

@Test
public void test01(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    Student student = (Student) context.getBean("student");

    System.out.println(student.toString());
}
复制代码

运行结果

其他注入

P命名空间注入

需要在头文件中加入约束文件 xmlns:p="http://www.springframework.org/schema/p" ,且实体类中要存在无参构造器,

<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="userP" class="com.idiot.pojo.User" p:name="idiot" p:age="3"/>
复制代码

MyTest.java

@Test
public void test02(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
    User user = context.getBean("userP", User.class);
    System.out.println(user);
}
复制代码

C命名空间注入

雷同于P命名空间注入,

需要在头文件中加入约束文件 xmlns:c="http://www.springframework.org/schema/c" ,且实体类中要存在有参构造器,

<!--c命名空间注入,通过构造器注入:construct-args-->
<bean id="userC" class="com.idiot.pojo.User" c:age="3" c:name="idiot"/>
复制代码

AOP

什么是AOP?

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

AOP在Spring中的作用

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

ASPECT
Advice
Target
Proxy
PointCut
JointPoint

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 在不改变原有代码的情况下,去增加新的功能,  

使用Spring实现AOP

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

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>
复制代码

法一:通过Spring API实现

编写业务接口 UserService.java ,

package com.idiot.service;

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void search();
}
复制代码

再编写实现类 UserServiceImpl.java ,

package com.idiot.service;

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("查询用户");
    }
}
复制代码

然后开始 AOP 环节,我们编写两个通知类 , 前置通知 BeforeLog.java , 后置通知 AfterLog.java ,

BeforeLog.java

package com.idiot.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog 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

package com.idiot.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

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

最后编写配置文件进行 Spring 中的注册,并实现 aop 切入实现,注意导入约束, 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.idiot.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.idiot.log.BeforeLog"/>
    <bean id="afterLog" class="com.idiot.log.AfterLog"/>

    <!--aop的配置-->
    <aop:config>
        <!--切入点  expression:表达式匹配要执行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.idiot.service.UserServiceImpl.*(..))"/>
        <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>
复制代码

expression 是一个表达式,其中 expression="execution()" 这是固定的, execution() 的括号里填写的参数是 要执行的位置 ,

这是如何判断的呢?

大概是:修饰词 -> 返回值 -> 类名 -> 方法名 -> 参数值,

* com.idiot.service.UserServiceImpl.*(..) ,第一个 * 表示类名前面可以是任意的修饰词和返回值,第二个 * 则表示任意的方法名, .. 表示任意几个参数,

测试 MyTest.java ,

import com.idiot.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //注意:动态代理代理的是接口
        UserService userService = context.getBean("userService",UserService.class);
        userService.search();
    }
}
复制代码

Spring 的 Aop 就是将公共的业务 (日志,安全等) 和领域业务结合起来,当执行领域业务时,将会把公共业务加进来,实现公共业务的重复利用 ,领域业务更纯粹,程序猿专注领域业务,其本质还是动态代理,  

法二:自定义类实现

目标业务类不变依旧是 userServiceImpl.java ,

  1. 自定义编写切入类 DiyPointcut.java ,
package com.idiot.diy;

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

    public void after(){
        System.out.println("---------方法执行后---------");
    }
}
复制代码
  1. 编写配置文件 applicationContext.xml ,
<!--注册bean-->
<bean id="userService" class="com.idiot.service.UserServiceImpl"/>
<bean id="diy" class="com.idiot.diy.DiyPointcut"/>

<!--aop的配置-->
<aop:config>
    <!--使用AOP的标签实现-->
    <aop:aspect ref="diy">
        <aop:pointcut id="diyPointcut" expression="execution(* com.idiot.service.UserService.*(..))"/>
        <aop:before pointcut-ref="diyPointcut" method="before"/>
        <aop:after pointcut-ref="diyPointcut" method="after"/>
    </aop:aspect>
</aop:config>
复制代码
  1. 测试 MyTest.java ,
import com.idiot.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //注意:动态代理代理的是接口
        UserService userService = context.getBean("userService",UserService.class);
        userService.search();
    }
}
复制代码

法三:使用注解实现

  1. 编写一个注解实现的自定义类 AnnotationPointcut.java ,
package com.idiot.diy;

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

@Aspect
public class AnnotationPointcut {
    @Before("execution(* com.idiot.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("---------方法执行前---------");
    }

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

    @Around("execution(* com.idiot.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");

        //执行目标方法proceed
        Object proceed = jp.proceed();

        System.out.println("环绕后");
    }
}
复制代码
  1. 在 Spring 配置文件中注册 bean ,并增加支持注解的配置,
<bean id="annotationPointcut" class="com.idiot.diy.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
复制代码

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动态代理。  

整合Mybatis

回顾Mybatis

先是导入依赖包,配置仓库,同时不要忘记配置 Maven 静态资源过滤, pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Spring</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-10-mybatis</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <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>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>

        <!-- aspectJ AOP 织入器 -->
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!--配置Maven静态资源过滤问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>
复制代码

编写实体类 User.java ,

package com.idiot.pojo;

import lombok.Data;

@Data
public class User {
    private int id;  //id
    private String name;   //姓名
    private String pwd;   //密码

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

再编写接口 UserMapper.java 及其映射文件 UserMapper.xml ,

UserMapper.java

package com.idiot.mapper;

import com.idiot.pojo.User;

import java.util.List;

public interface UserMapper {
    public List<User> selectUser();
}
复制代码

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.idiot.mapper.UserMapper">

    <select id="selectUser" resultType="User">
        select * from user
    </select>

</mapper>
复制代码

紧接着编写核心配置文件 mybatis-config.xml 以及外部配置文件 db.properties ,

mybatis-config.xml

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--引入外部配置文件-->
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="pwd" value="123456"/>
    </properties>

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

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

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${pwd}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.idiot.mapper.UserMapper"/>
    </mappers>

</configuration>
复制代码

db.properties

driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3307/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
复制代码

最后进行测试,编写测试类 MyTest.java ,这里没有像之前那样特意的去编写工具类了,然是直接在测试类中进行 SqlSession 的一系列操作,

import com.idiot.mapper.UserMapper;
import com.idiot.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {
    @Test
    public void selectUser() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> userList = mapper.selectUser();
        for (User user: userList){
            System.out.println(user);
        }

        sqlSession.close();
    }
}
复制代码

MyBatis-Spring

什么是 MyBatis-Spring ?

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

MyBatis-Spring 需要以下版本:

MyBatis-SpringMyBatisSpring 框架Spring BatchJDK
2.03.5+5.0+4.0+Java 8+
1.33.4+3.2.2+2.1+Java 6+

因此需要在 pom.xml 中加入以下代码:

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-spring</artifactId>
   <version>2.0.2</version>
</dependency>
复制代码

要和 Spring 一起使用 MyBatis ,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。

在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean 来创建 SqlSessionFactory 。要配置这个工厂 bean ,只需要把下面代码放在 Spring 的 XML 配置文件中:

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

注意: SqlSessionFactory 需要一个 DataSource (数据源),且是唯一的必要属性。这可以是任意的 DataSource ,只需要和配置其它 Spring 数据库连接一样配置它就可以了。

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession 。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session 。

一个常用的属性是 configLocation ,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases> 元素。

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置,数据源和 MyBatis 的事务管理器都会被忽略。 SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置( Environment ),并按要求设置自定义环境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession 。

模板( Template )可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
复制代码

现在这个 bean 就可以直接注入到 Mapper bean 中,

package com.idiot.mapper;

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

import java.util.List;

public class UserMapperImpl implements UserMapper{
    //sqlSession不用我们创建,Spring来管理
    private SqlSessionTemplate sqlSession;

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

    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
复制代码

接下来注入 SqlSessionTemplate :

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

整合一

  1. 引入 Spring 的配置文件 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">
</beans>
复制代码
  1. 配置数据源替换 mybaits 的数据源,
<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3307/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</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/idiot/mapper/*.xml"/>
</bean>
复制代码
  1. 注册 sqlSessionTemplate ,关联 sqlSessionFactory ,
<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--利用构造器注入-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
复制代码
  1. 增加 Mapper 接口的实现类 UserMapperImpl.java ,私有化 sqlSessionTemplate ,
package com.idiot.mapper;

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

import java.util.List;

public class UserMapperImpl implements UserMapper{
    //sqlSession不用我们自己创建了,Spring来管理
    private SqlSessionTemplate sqlSession;

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

    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
复制代码
  1. 注册 bean 实现,
<bean id="userMapper" class="com.idiot.mapper.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>
复制代码
  1. 再来看看我们 Mybatis 的配置文件,发现所有内容都可以被 Spring 整合,
<?xml version="1.0" encoding="UTF8" ?>
<!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.idiot.pojo"/>
    </typeAliases>
</configuration>
复制代码
  1. 测试,
@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper mapper = (UserMapper) context.getBean("userMapper");
    List<User> user = mapper.selectUser();
    System.out.println(user);
}
复制代码

整合二

mybatis-spring1.2.3 版以上的才有这个,

dao 继承 Support 类 , 直接利用 getSqlSession() 获得 , 然后直接注入 SqlSessionFactory ,比起方式一,不需要管理 SqlSessionTemplate , 而且对事务的支持更加友好,可跟踪源码查看,

修改 UserMapperImpl.java ,

package com.idiot.mapper;

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

import java.util.List;

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
    public List<User> selectUser() {
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
复制代码

修改 applicationContext.xml 中 bean 的配置,

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

测试,

总结 : 整合到 Spring 以后可以完全不要 mybatis 的配置文件,除了这些方式可以实现整合之外,我们还可以使用注解来实现,

后记

以上内容只是简略的有重点的描述了 Spring 框架,要真正掌握还是需要自己继续深入学习的,谨记面向百度编程!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/461491
推荐阅读
相关标签
  

闽ICP备14008679号