其他分享
首页 > 其他分享> > Spring由浅入深

Spring由浅入深

作者:互联网

文章目录

Spring

1、简介

1.1、什么是Spring

Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。

提供了展现层 SpringMVC 和持久层 Spring JDBCTemplate 以及业务层事务管理等众多的企业级应用技术 ,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

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

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

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

官网 : http://spring.io/

官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/

GitHub : https://github.com/spring-projects

1.2、Spring的优点

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

1.3、Spring的体系结构

image-20211120201222808

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

image-20211120202420523

1.4、扩展

Spring Boot与Spring Cloud

2、什么是容器

3、IOC容器

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

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

3.1、配置元数据

image-20211120212113242

导入jar包:

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

applicationContext.xml配置文件:

一般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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	
    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">  
        <property name="accountDao" ref="accountDao"/>
        <property name="name" value="value"/>
    </bean>
    
    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions go here -->
</beans>

3.2、如何去使用容器

实现类作用
ClassPathXmlApplicationContext它是从类的根路径下加载配置文件,推荐使用这种
FileSystemXmlApplicationContext它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
AnnotationConfigApplicationContext当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解

实例化容器,可以加载多个配置文件

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml", "daos.xml");

getBean():获取容器中的bean

//getBean(String name, Class<T> requiredType)获取实例对象
PetStoreService service = context.getBean("petStore", PetStoreService.class);

4、Spring配置

4.1、Bean标签基本配置

<bean id="fromName" name="toName h2,h3;h4" class="com.kuang.pojo.Hello">
    <property name="name" value="Spring"/>
</bean>

4.2、Bean实例化三种方式

1、构造函数实例化

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

2、工厂静态方法实例化

3、工厂实例方法实例化

4.3、Bean依赖注入

概念

1、构造器注入

<!-- 第一种根据参数名字设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
    <!-- name指参数名 -->
    <constructor-arg name="name" value="value"/>
</bean>

<!-- 第二种根据index参数下标设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
    <!-- index指构造方法 , 下标从0开始 -->
    <constructor-arg index="0" value="value"/>
</bean>

<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
    <constructor-arg type="java.lang.String" value="value"/>
</bean>

2、Setter 注入

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

测试类:

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class Student {

    private String name;//基本类型
    private Address address;//实体属性
    private String[] books;//数组
    private List<String> hobbys;//list集合
    private Map<String,String> card;//map集合
    private Set<String> games;//set集合
    private String wife;//Null,可能没有妻子
    private Properties info;//配置文件注入

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

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

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

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

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

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

    public void setWife(String wife) {
        this.wife = wife;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public void show(){
        System.out.println("name="+ name
                           + ",address="+ address.getAddress()
                           + ",books="
                          );
        for (String book:books){
            System.out.print("<<"+book+">>\t");
        }
        System.out.println("\n爱好:"+hobbys);

        System.out.println("card:"+card);

        System.out.println("games:"+games);

        System.out.println("wife:"+wife);

        System.out.println("info:"+info);

    }
}

1、基本类型注入

	<!-- 
        name:属性名
			name可以设置多个别名,可以用逗号,分号,空格隔开
        ref:指的是另一个bean定义的id名称,引用Spring中创建好的对象
        value:给属性设置基本数据类型的值,底层通过反射获取set方法
	-->
<property name="name" value="pip"/>

2、Bean对象注入

<bean id="address" class="com.pip.pojo.Address">
    <property name="address" value="陕西"/>
</bean>
<bean id="student" class="com.pip.pojo.Student">
    <property name="address" ref="address"/>
</bean>

3、数组注入

<property name="books">
    <array>
        <value>西游记</value>
        <value>红楼梦</value>
        <value>水浒传</value>
    </array>
</property>

4、list集合注入

<property name="hobbys">
    <list>
        <value>听歌</value>
        <value>看电影</value>
        <value>爬山</value>
    </list>
</property>

5、map集合注入

<property name="card">
    <map>
        <entry key="手机号" value="12341234123"/>
        <entry key="id" value="00001"/>
    </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>

<bean class="ExampleBean">
    <property name="email" value=""/>//这里是设为空字符串
</bean>

8、Properties注入

<property name="info">
    <props>
        <prop key="学号">20190604</prop>
        <prop key="性别">男</prop>
        <prop key="姓名">小明</prop>
    </props>
</property>

3、其他注入

p命名和c命名注入

1、P命名空间注入

首先,需要引入P命名空间:

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

P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:

<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.pip.pojo.User" p:name="pip" p:age="18"/>

2、c 命名空间注入

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

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

4.4、Bean自动装配

Spring中bean有三种装配机制:

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

概念

Spring的自动装配是如何实现的:

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

1、byName

autowire byName (按名称自动装配)

Spring 查找与需要自动装配的属性同名的 bean(根据id是否和属性名相同)。例如,如果一个 bean 定义被设置为按名称自动装配并且它包含一个master属性(即它有一个 setMaster(..)方法),Spring 会查找一个名为的 bean 定义master并使用它来设置属性。
<!--
	自动装配byName必须要有相应的set方法
-->
<bean id="address" class="com.pip.pojo.Address"></bean>
<!--用户具有一个address实体类属性-->
<bean id="user" class="com.pip.pojo.User" autowire="byName">
   <property name="u_name" value="pip"/>
</bean>

2、byType

autowire byType (按类型自动装配)

如果容器中恰好存在一个属性类型的 bean,则让属性自动装配。如果存在多个,则会引发致命异常,不能byType为该 bean使用自动装配。如果没有匹配的 bean,则不会发生任何事情(未设置属性)。
<!--这里使用猫和狗来举例子-->
<bean id="dog" class="com.pip.pojo.Dog"/>
<bean id="cat" class="com.pip.pojo.Cat"/>
<bean id="cat2" class="com.pip.pojo.Cat"/>

<bean id="user" class="com.pip.pojo.User" autowire="byType">
   <property name="u_name" value="pip"/>
</bean>

自动装配的优缺点

优点:

4.5、Bean作用域

scope:指对象的作用范围,取值如下:

image-20211121202155802

singleton

单例作用域(单例模式)

image-20211121202540454

<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

Prototype

原型作用域(原型模式)

image-20211121202945671

以下示例将 bean 定义为 XML 中的原型:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

Request

考虑 bean 定义的以下 XML 配置:

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

Spring 容器LoginAction通过loginAction对每个 HTTP 请求使用bean 定义来创建bean 的新实例。也就是说, loginActionbean 的范围在 HTTP 请求级别。您可以根据需要更改所创建实例的内部状态,因为从同一loginActionbean 定义创建的其他实例不会看到这些状态更改。它们是针对个人要求的。当请求完成处理时,该请求范围内的 bean 将被丢弃。

当使用注解驱动的组件或 Java 配置时,@RequestScope注解可用于将组件分配给request作用域。以下示例显示了如何执行此操作:

@RequestScope
@Component
public class LoginAction {
    // ...
}

Session

考虑 bean 定义的以下 XML 配置:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

Spring 容器UserPreferences通过在userPreferences单个 HTTP 的生命周期中使用bean 定义来创建bean 的新实例Session。换句话说,userPreferencesbean 有效地限定在 HTTPSession级别。与请求范围的 bean 一样,您可以根据需要更改所创建实例的内部状态,因为知道Session也使用从相同userPreferencesbean 定义创建的实例的其他 HTTP实例不会看到这些状态更改,因为它们特定于单个 HTTP Session。当 HTTPSession最终被丢弃时,作用域为该特定 HTTP 的 bean Session也将被丢弃。

当使用注解驱动的组件或 Java 配置时,可以使用 @SessionScope注解将组件分配给session作用域。

@SessionScope
@Component
public class UserPreferences {
    // ...
}

Application

考虑 bean 定义的以下 XML 配置:

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

Spring 容器AppPreferences通过appPreferences对整个 Web 应用程序使用一次 bean 定义来创建bean 的新实例。也就是说, appPreferencesbean 的作用域在ServletContext级别并存储为常规 ServletContext属性。这有点类似于 Spring 单例 bean,但在两个重要方面有所不同:它是一个单例 per ServletContext,而不是 per Spring ApplicationContext(在任何给定的 Web 应用程序中可能有多个),并且它实际上是公开的,因此作为一个ServletContext属性可见.

当使用注解驱动的组件或 Java 配置时,可以使用 @ApplicationScope注解将组件分配给application作用域。以下示例显示了如何执行此操作:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}

4.6、Bean别名

在 Bean 定义之外给 Bean 取别名

<!--
	name:bean名
	alias:别名
-->
<alias name="fromName" alias="toName"/>

4.7、引入其他配置文件

团队的合作通过import来实现 ,导入其他的容器xml文件

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

4.8、context标签

使用context标签之前需要引入context约束

开启注解支持:

<context:annotation-config/>

指定注解扫描包:支持自动将包下的类自动创建bean并注册到spring容器中,包含开启注解支持功能)

<context:component-scan base-package="指定要扫描的包"/>

用于加载.properties 文件中的配置:

<context:property-placeholder location="xx.properties"/>

6、基于原始注解

1、简介

概念

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置 文件可以简化配置,提高开发效率。

快速说明

注解说明
@Component使用在类上用于实例化Bean
@Controller使用在web层类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用用于根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

2、注解使用

2.1、配置

引入依赖jar包:spring-aop jar包

image-20211127150321272

引入context约束

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

配置扫描哪些包下的注解

使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean 需要进行扫描以便识别使用注解配置的类、字段和方法。

<!--指定注解扫描包-->
<context:component-scan base-package="指定要扫描的包"/>

@Component

使用@Compont或@Repository标识相应的类需要Spring进行实例化。

在指定包下编写类,增加注解

@Component("user")//id可加可不加
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
   public String name = "pip";
}

@Component三个衍生注解

和Component功能一模一样,只是名字不同

@Autowired

概念

配置**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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启属性注解支持-->
    <context:annotation-config/>
    <!--这里使用猫和狗来举例子-->
    <bean id="dog" class="com.pip.pojo.Dog"/>
    <bean id="cat" class="com.pip.pojo.Cat"/>
    <bean id="cat2" class="com.pip.pojo.Cat"/>

    <bean id="user" class="com.pip.pojo.User" autowire="byType">
        <property name="u_name" value="pip"/>
    </bean>
</beans>

Bean

//说明:false,对象可以为null;true,对象必须存对象,不能为null。
@Autowired(required=false)
private Cat cat;
@Autowired
private Dog dog;
private String u_name;

@Qualifier

@Autowired(required=false)
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
private Dog dog;
private String u_name;

@Resource

@Autowired与@Resource异同:

1、@Autowired@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返),相当于@Autowired+@Qualifier,按照名称进行注入,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

@value

使用注解注入基本属性,可加在set方法中。

@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
    @Value("pip")
    // 相当于配置文件中 <property name="name" value="pip"/>
    public String name;
}

@scope

@Controller("user")
@Scope("prototype")
public class User {
   @Value("pip")
   public String name;
}

7、Spring新注解

1、简介

概念

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下

快速说明

注解说明
@Configuration用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的<context:component-scan base-package="com.pip"/>一样
@Bean使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource用于加载.properties 文件中的配置
@Import用于导入其他配置类

2、注解使用

@Configuration

用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解

@Configuration
public class SpringConfiguration {
}

@ComponentScan

用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的一样

@ComponentScan("com.pip")//等同于<context:component-scan base-package="com.pip"/>
public class SpringConfiguration {
}

@Import

用于导入其他配置类

@Configuration
@ComponentScan("com.pip")
@Import({DataSourceConfiguration.class})//与<import resource="{path}/beans.xml"/>功能一样
public class SpringConfiguration {
}

@Bean

相当于之前的<bean>标签使用在方法上,标注将该方法的返回值存储到 Spring 容器中

@Bean和@Component可以分开使用也可以同时使用,我觉得并无差别

@Configuration
public class AppConfig {

    @Bean("name")//name可加可不加根据需求
    public Bean bean() {
        return new Bean();
    }
}

@PropertySource

用于加载.properties 文件中的配置

8、代理模式

这里简单阐述一下JDK的代理技术

静态代理

静态代理分析

都学到这里了,我就不通过实际例子来说明了,通过代码进行举例

抽象对象:

public interface AbstractSubject {
    void method();
}

真实对象:

public class RealSubject implements AbstractSubject{
    @Override
    public void method() {
        System.out.println("真实对象中的method方法");
    }
}

代理对象:

public class Proxy implements AbstractSubject{
    private RealSubject rs;

    public void setRs(RealSubject rs) {
        this.rs = rs;
    }

    @Override
    public void method() {
        log();
        rs.method();
    }
    
    /**
    * 添加的日志功能
    */
    public static void log(){
        System.out.println(new Date);
    }
}

调用对象:

RealSubject rs = new RealSubject();
Proxy proxy = new Proxy();
//注入真实对象到代理对象中
proxy.setRs(rs);
//调用代理对象的方法
proxy.method();
//结果:
//Thu Nov 25 01:18:24 CST 2021
//真实对象中的method方法

缺点:每一个代理对象都需要我们去编写,非常的耗时耗力,通过动态代理解决。

动态代理

基于类的动态代理–cglib,我没学就不记了

动态代理的对象和静态代理相同

抽象对象:

public interface AbstractSubject {
    void method();
}

真实对象:

public class RealSubject implements AbstractSubject{
    @Override
    public void method() {
        System.out.println("真实对象中的method方法");
    }
}

动态生成代理对象:

核心 : InvocationHandler 和 Proxy

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

public class ProxyInvocationHandler implements InvocationHandler {
    private Object object;

    public void setObject(Object object) {
        this.object = object;
    }
		
    //生成动态代理对象
    //Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);	
    //loader:定义代理类的类加载器
	//interfaces:代理类要实现的接口列表
	//h:指派方法调用的调用处理程序
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                object.getClass().getInterfaces(),this);
    }

     /**
     * 动态代理对象调用接口方法时自动调用该方法
     * @param proxy:调用该方法的代理实例
     * @param method:所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
     * @param args:参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置增强");
        Object o = method.invoke(object, args);
        System.out.println("后置增强");
        return o;
    }
}

调用对象:

RealSubject rs = new RealSubject();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//添加真实对象到代理对象中
pih.setObject(rs);
//动态代理接口中的方法
AbstractSubject proxy = (AbstractSubject) pih.getProxy();
//代理对象调用方法
proxy.method();

9、AOP

1、什么是AOP?

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍 生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序 的可重用性,同时提高了开发的效率。

AOP底层是通过动态代理实现的!在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

1.2、AOP的作用以及优点

1.3、AOP的动态代理技术

常用的动态代理技术

2、AOP 的相关术语

3、配置详解

3.1、导入jar包

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.13</version>
</dependency>

3.2、配置详解

3.2.1、xml配置文件详解

image-20211125165332823

<aop:config>
	<aop:aspect ref=“切面类”>
        <!--通知-->
	</aop:aspect>
</aop:config>
 <aop:pointcut id="diyPonitcut" expression="execution(* com.pip.Service.UserServiceImpl.*(..))"/>
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"></aop:通知类型>     
名称标签说明
前置通知<aop:before>用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知<aop:after-returning>用于配置后置通知。指定增强的方法在切入点方法之后执行
环绕通知<aop:around>用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
异常抛出通知<aop:throwing>用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知<aop:after>用于配置最终通知。无论增强方式执行是否有异常都会执行

3.2.2、切点表达式

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

例如:

execution(public void com.pip.aop.Target.method())
execution(void com.pip.aop.Target.*(..))
execution(* com.pip.aop.*.*(..))
execution(* com.pip.aop..*.*(..))
execution(* *..*.*(..))

4、实现AOP的三种方式

4.1、通过 Spring API 实现

通过API接口实现

编写接口类:

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

实现类:

import org.springframework.stereotype.Component;

@Component()
public class UserServiceImpl implements UserService{

    @Override
    public void add() {
        System.out.println("添加了一个用户");
    }

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

    @Override
    public void uadate() {
        System.out.println("修改了一个用户");
    }

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

编写增强类,这里编写两个,一个前置增强 一个后置增强:

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
    //method : 要执行的目标对象的方法
    //objects : 被调用的方法的参数
    //Object : 目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + "的" + method.getName()+ "方法被执行了");
    }
}

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

编写xml配置文件:

<!--头部文件导入aop约束-->
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置目标类-->
    <bean id="userService" class="com.pip.Service.UserServiceImpl"/>
    <!--配置切面类-->
    <bean id="beforeLog" class="com.pip.log.BeforeLog"/>
    <!--配置切面类-->
    <bean id="afterLog" class="com.pip.log.AfterLog"/>
    <aop:config>
        <!--声明一个切入点 execution():匹配要执行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.pip.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>

测试:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//通过接口中的方法去实现,动态代理原理
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.add();
userService.uadate();
userService.delete();
userService.select();

4.2、自定义类实现

通过切面实现

接口与实现类与第一种方式相同

自定义切入类:

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

编写xml配置文件:

<!--导入AOP约束-->
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置目标类-->
    <bean id="userService" class="com.pip.Service.UserServiceImpl"/>
    <!--配置切面类-->
    <bean id="diy" class="com.pip.log.DiyPointcut"/>
    <aop:config>
        <!--引入DiyPointcut的bean为切面对象-->
        <aop:aspect ref="diy">
            <!--抽取出来的切点表达式,-->
            <aop:pointcut id="diyPonitcut" expression="execution(* com.pip.Service.UserServiceImpl.*(..))"/>
            <!--
				通知配置:
				method=“通知方法名称” 
				pointcut=“切点表达式"
				pointcut-ref="引用的切点"
   			-->
            <aop:before method="before" pointcut-ref="diyPonitcut"/>
            <aop:after method="after" pointcut-ref="diyPonitcut"/>
        </aop:aspect>
    </aop:config>
</beans>

测试:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.add();
userService.uadate();
userService.delete();
userService.select();

4.3、使用注解实现

@Aspect
public class AnnotationPointcut {
    //抽取切入点
    @Pointcut("execution(* com.pip.Service.UserServiceImpl.*(..))")
    public void myPoint(){}
    
    @Before("AnnotationPointcut.myPoint()")
    public void before(){
        System.out.println("---------方法执行前---------");
    }

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

    @Around("execution(* com.pip.Service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        System.out.println("签名:"+jp.getSignature());
        //执行目标方法proceed
        Object proceed = jp.proceed();
        System.out.println("环绕后");
        System.out.println(proceed);
    }
}

编写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 https://www.springframework.org/schema/aop/spring-aop.xsd">
	<!--注册切面类到配置文件,使用@Component实现同理-->
    <bean id="annotationPointcut" class="com.pip.log.AnnotationPointcut"/>
    <!--开启aop自动代理-->
    <aop:aspectj-autoproxy/>
    <!--注册实现类对象-->
    <bean id="userService" class="com.pip.Service.UserServiceImpl"/>
</beans>

测试:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserService userService = applicationContext.getBean(UserService.class);
userService.add();
userService.uadate();
userService.delete();
userService.select();

4.3.1、注解配置 AOP 详解

  1. 使用**@Aspect**标注切面类

  2. 使用@通知注解标注通知方法

    • 通知的配置语法:@通知注解(“切点表达式")
    名称注解说明
    前置通知@Before用于配置前置通知。指定增强的方法在切入点方法之前执行
    后置通知@AfterReturning用于配置后置通知。指定增强的方法在切入点方法之后执行
    环绕通知@Around用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
    异常抛出通知@AfterThrowing用于配置异常抛出通知。指定增强的方法在出现异常时执行
    最终通知@After用于配置最终通知。无论增强方式执行是否有异常都会执行
  3. 抽取切点表达式

@Pointcut("execution(* com.pip.Service.UserServiceImpl.*(..))")
    public void myPoint(){}
@Before("切入类名.myPoint()")
    public void before(){}
  1. 在配置文件中配置aop自动代理

    proxy-target-class:默认为false,设为true表示使用CGLib动态代理技术织入增强,即使proxy-target-class设置为false,但是如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

<aop:aspectj-autoproxy/>

10、MyBatis-Spring

1、简介

什么是MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。

不在需要mybaties-config配置文件,但可以在mybaties-config中保留一些MyBatis 的基础配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--set方法-->
    <settings>
        <setting name="" value=""/>
    </settings>
    <!--别名-->
    <typeAliases>
    </typeAliases>
</configuration>

知识基础

使用MyBatis-Spring之前需要导入各个jar包的版本

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

2、导入依赖

junit测试

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

Spring相关

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.13</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.13</version>
</dependency>

myBatis

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

mySql驱动

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>

mybatis-spring

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

aop织入包

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

3、在spring中配置数据源

将数据源配置移交给Spring容器管理

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456
<!--加载properties文件,也可以不抽取,直接在数据源中赋值-->
<context:property-placeholder location="jdbc.properties"/>
<!--
  配置数据源:
   这里使用Spring自带的数据源,可使用c3p0,durid等等都可
 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

4、在spring配置工厂

将SqlSessionFactory工厂交给spring来管理

<!-- 配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--关联数据源,一个SqlSessionFactory只需要一个数据源-->
    <property name="dataSource" ref="dataSource"/>
    <!--关联myBatis-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath:mapper/userMapper.xml"/>
</bean>

userMapper.xml

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

5、创建SqlSession

注意!!!如果你的接口实现类去继承了SqlSessionDaoSupport,请跳过这一步,直接到6.2的方法二!!!

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

(白话意思是在MyBatis中,可以使用 SqlSessionFactory 来创建 SqlSession,但它线程不安全,在MyBatis-Spring使用SqlSessionTemplate代替SqlSession

当调用 SQL 方法时(包括由 getMapper() 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关。 此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 DataAccessExceptions

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--因为SqlSessionTemplate中没有set方法,所以使用构造器注入关联sqlSessionFactory工厂-->
    <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

6、增加Mapper接口的实现类

6.1、方法一

私有化sqlSessionTemplate

//接口
public interface UserMapper {
    List<Product> query();
}

public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
    }
	
    @Override
    public List<User> query() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.query();
    }
}

在spring中注册接口实现类的bean

<bean id="userMapper" class="com.pip.mapper.UserMapperImpl">
    <!--将创建好的sqlSession注入到实现类的属性-->
    <property name="sqlSession" ref="sqlSession"/>
</bean>

6.2、方法二

接口实现类去继承SqlSessionDaoSupport类

SqlSessionDaoSupport的原理就是方法一

SqlSessionDaoSupport具有getSqlSession() 方法 , 可以将SqlSessionFactory直接注入。比起方式一 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好,可跟踪源码查看。

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
    private SqlSession sqlSession = getSqlSession();
    @Override
    public List<User> query() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.query();
    }
}
<bean id="userMapper" class="com.pip.mapper.UserMapperImpl">
    <!--将创建好的sqlSessionFactory注入到实现类中的getSqlSession()方法-->
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

11、Spring事务

1、什么是事务

事务把一些列动作当成一个独立的工作单元,简单来说,这些动作要么全部完成,要么全部不起作用。

事务四大原则ACID(面试重点)

2、Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

编程式事务管理

声明式事务管理

3、通过xml方式实现事务

1、引入约束

在配置文件中,引入约束:tx

xmlns:tx="http://www.springframework.org/schema/tx"

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

2、配置事务管理器

<!--JDBC事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

3、配置事务通知

<tx:advice>:配置事务通知,绑定事务管理器

<tx:method>

名称作用
REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。默认为这个选择
NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

4、织入事务

<!--织入-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="txPointcut" expression="execution(* com.pip.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

2、Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

编程式事务管理

声明式事务管理

3、通过xml方式实现事务

1、引入约束

在配置文件中,引入约束:tx

xmlns:tx="http://www.springframework.org/schema/tx"

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

2、配置事务管理器

<!--JDBC事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

3、配置事务通知

<tx:advice>:配置事务通知,绑定事务管理器

<tx:method>

名称作用
REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。默认为这个选择
NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

4、织入事务

<!--织入-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="txPointcut" expression="execution(* com.pip.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

标签:由浅入深,对象,Spring,配置,bean,方法,public
来源: https://blog.csdn.net/qq_52799045/article/details/121578618