本文已参与「新人创作礼」活动,一起敞开创作之路。

前语

本博文专用于软件立异实验室 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 结构的每个模块(或组件)都能够单独存在,或许与其他一个或多个模块联合完结。每个模块的功用如下:

【JAVA】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>

目录

【JAVA】Spring 框架
先编写一个 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

装备文件可在官方文档中获取,

【JAVA】Spring 框架

<?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());
    }
}

运转成果

【JAVA】Spring 框架

装备阐明

别号

假如添加了别号,咱们也能够运用别号获取这个方针,原名也是能够运用的,在 beans.xml 中进行装备,

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

【JAVA】Spring 框架

Bean的装备

bean 便是 java 方针,由 Spring 创立和办理,

  • idbean 的仅有标识符,也就相当于方针名,
  • classbean 方针所对应的全限定名:包名+类型,
  • name:也是别号,并且 name 能够一起取多个别号,能够用逗号,分号,空格隔开,

假如没有装备 idname 便是默许标识符,

假如装备了 id,又装备了 name,那么 name 是别号,

假如不装备 idname,能够根据 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 的程序中 , 咱们运用面向方针编程 , 方针的创立与方针间的依靠联系完全硬编码在程序中,方针的创立由程序自己操控,操控回转后将方针的创立转移给第三方,个人认为所谓操控回转便是:取得依靠方针的办法回转了。

【JAVA】Spring 框架

IoCSpring 结构的中心内容,运用多种办法完美的完结了 IoC,能够运用 XML 装备,也能够运用注解,新版本的 Spring 也能够零装备完结 IoC

选用 XML 办法装备 Bean 的时分,Bean 的界说信息和完结是分离的,而选用注解的办法能够把两者合为一体,Bean的界说信息直接以注解的办法界说在完结类中,然后达到了零装备的意图。

理论推导

首要创立一个Maven项目,然后创立接口和完结类,目录总览如下,

【JAVA】Spring 框架

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>

运转成果如下,

【JAVA】Spring 框架

现在添加一个 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();
}

【JAVA】Spring 框架

与运用 set 前的代码进行比照,这现已产生了根本性的改变,以前一切东西都是由程序主动去进行操控创立 , 而运用了 set 注入之后,程序不再具有主动性,而是变成了被迫接受的方针,即把主动权交给了调用者,程序则只负责供给一个接口,

【JAVA】Spring 框架
这种思维,从实质上解决了问题,咱们程序员不再去办理方针的创立,而是更多的去重视事务的完结,耦合性大大下降,这也便是IOC的原型!

创立方针办法

1. 运用无参结构创立方针(默许)

目录

【JAVA】Spring 框架

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();
    }
}

运转成果

【JAVA】Spring 框架
此刻,将无参结构改成有参结构再运转,

public User(String name) {
    System.out.println("User无参结构");
}

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

【JAVA】Spring 框架

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依靠注入

结构器注入

初始Spring的创立中已讲,忘了的话回头看一下。

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());
}

运转成果

【JAVA】Spring 框架

其他注入

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?

AOPAspect Oriented Programming)意为:面向切面编程,经过预编译办法和运转期动态署理完结程序功用的一致维护的一种技能。AOP OOP 的延续,是软件开发中的一个热门,也是 Spring 结构中的一个重要内容,是函数式编程的一种衍生范型。运用 AOP 能够对事务逻辑的各个部分进行隔离,然后使得事务逻辑各部分之间的耦合度下降,提高程序的可重用性,一起提高了开发的功率。

【JAVA】Spring 框架

AOP在Spring中的作用

供给声明式事务;允许用户自界说切面

  • 横切重视点:跨越运用程序多个模块的办法或功用。与事务逻辑无关的,可是需求咱们重视的部分,便是横切重视点。如日志 , 安全 , 缓存 , 事务等等…
  • 切面(ASPECT):横切重视点被模块化的特别方针。即它是一个类。
  • 告诉(Advice):切面必需求完结的工作。即它是类中的一个办法。
  • 方针(Target):被告诉方针。
  • 署理(Proxy):向方针方针运用告诉之后创立的方针。
  • 切入点(PointCut):切面告诉履行的“地点”的界说。
  • 连接点(JointPoint):与切入点匹配的履行点。

【JAVA】Spring 框架

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

【JAVA】Spring 框架

编写事务接口 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();
    }
}

SpringAop 便是将公共的事务 (日志,安全等) 和范畴事务结合起来,当履行范畴事务时,将会把公共事务加进来,完结公共事务的重复运用 ,范畴事务更朴实,程序猿专心范畴事务,其实质仍是动态署理,

法二:自界说类完结

方针事务类不变依旧是 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();
    }
}

【JAVA】Spring 框架

法三:运用注解完结

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

【JAVA】Spring 框架

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

【JAVA】Spring 框架

先是导入依靠包,装备库房,一起不要忘记装备 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();
    }
}

【JAVA】Spring 框架

MyBatis-Spring

什么是 MyBatis-Spring

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

MyBatis-Spring 需求以下版本:

MyBatis-Spring MyBatis Spring 结构 Spring Batch JDK
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.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),并按要求设置自界说环境的值。

SqlSessionTemplateMyBatis-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&amp;useUnicode=true&amp;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);
}

【JAVA】Spring 框架

整合二

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

dao 继承 Support 类 , 直接运用 getSqlSession() 取得 , 然后直接注入 SqlSessionFactory,比起办法一,不需求办理 SqlSessionTemplate , 并且对事务的支撑愈加友好,可跟踪源码检查,

【JAVA】Spring 框架

修正 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.xmlbean 的装备,

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

测验,

【JAVA】Spring 框架

总结 : 整合到 Spring 今后能够完全不要 mybatis 的装备文件,除了这些办法能够完结整合之外,咱们还能够运用注解来完结,

跋文

以上内容仅仅简略的有要点的描绘了 Spring 结构,要真实把握仍是需求自己持续深入学习的,谨记面向百度编程!