欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > Spring基本使用

Spring基本使用

2025/3/12 23:22:52 来源:https://blog.csdn.net/ufosuai555/article/details/146125697  浏览:    关键词:Spring基本使用

Spring是什么?

Spring是一个开源框架,它由Rod Johnson创建,于2003年发布。Spring框架的主要目标是简化Java企业级应用的开发,通过提供一组全面的解决方案,如依赖注入、控制反转(IOC)、面向切面编程(AOP)、事务管理等,使得开发者能够更加专注于业务逻辑的实现,而不是底层的细节。

官网:https://spring.io/projects/spring-framework#learn
GitHub地址:https://github.com/spring-projects/spring-framework

maven依赖:

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

Spring组成

Spring由多个模块组成,主要包括以下几部分:

  • Spring Core: 核心容器,提供依赖注入(DI)和面向切面编程(AOP)等功能。
  • Spring AOP: 面向切面编程,提供面向切面编程的支持。
  • Spring ORM: 对象关系映射,提供对持久层框架的支持,如Hibernate、JPA等。
  • Spring DAO: 数据访问对象,提供对数据库访问的支持。
  • Spring Web: 提供对Web应用开发的支持,包括Spring MVC等。
  • Spring Context: 提供对上下文的支持,包括Bean的创建、配置和管理等。
  • Spring Web MVC: Spring MVC框架,用于构建Web应用。

IOC理论推导

正常我们创建项目需要完成如下步骤:
1、创建dao类, 假设我们这里有两种获取用户的实现方式,一种是从mysql中获取,另一种是从oracle中获取。

package com.myLearning.dao;public interface UserDao {public void getUser();
}package com.myLearning.dao;public class UserDaoMysqlImpl implements UserDao {@Overridepublic void getUser() {System.out.println("mysql 获取用户");}
}package com.myLearning.dao;public class UserDaoOracleImpl implements UserDao{@Overridepublic void getUser() {System.out.println("oracle 获取用户");}
}

2、创建service类, 内部需要创建Dao对象,用于调用dao中的方法。

package com.myLearning.service;public interface UserService {public void getUser();
}package com.myLearning.service;import com.myLearning.dao.UserDao;
import com.myLearning.dao.UserDaoMysqlImpl;public class UserServiceImpl implements UserService {UserDao userDao = new UserDaoMysqlImpl();@Overridepublic void getUser() {userDao.getUser();}
}

3、用户层调用service层实现需求

import com.myLearning.service.UserService;
import com.myLearning.service.UserServiceImpl;public class MyTest {public static void main(String[] args) {UserService userService = new UserServiceImpl();userService.getUser();}
}

以上的代码实现虽然满足了需求,但是如果我们现在需要将mysql改为oracle,那么就需要修改service层,即我们需要修改业务层的代码,这显然是不合理的。
所以我们可以修改UserServiceImpl类,将UserDao对象改为接口类型,然后通过set方法进行注入。

package com.myLearning.service;import com.myLearning.dao.UserDao;public class UserServiceImpl implements UserService {private UserDao userDao;// set方法实现UserDao的注入public void setUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void getUser() {userDao.getUser();}
}

然后在我们需求变化的时候,我们只需要修改客户端代码,即MyTest类,将UserDaoMysqlImpl改为UserDaoOracleImpl即可。

import com.myLearning.dao.UserDaoOracleImpl;
import com.myLearning.service.UserService;
import com.myLearning.service.UserServiceImpl;public class MyTest {public static void main(String[] args) {UserServiceImpl userService = new UserServiceImpl();userService.setUserDao(new UserDaoOracleImpl());userService.getUser();}
}

这样我们就可以在不修改业务层代码的情况下,实现数据库的更换,这就是IOC控制反转的思想。

IOC本质

控制反转是一种思想,就是将对象的创建和对象之间的调用过程交给Spring进行管理,而不是由程序员手动管理,所谓控制反转,就是把创建对象的权利交给Spring,而不是由程序员手动创建对象,即获得对象的方式反转了。
在Spring中,IOC的实现是通过依赖注入(Dependency Injection)来实现的,依赖注入就是将对象之间的依赖关系通过配置文件或者注解的方式,由Spring容器来管理,从而实现对象之间的解耦。

使用Spring进行对象的创建

我们继续修改刚刚的代码,我们现在要使用Spring来创建对象,首先我们需要在pom.xml中添加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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 在这个配置文件中配置对象,相当于我们在java程序中new一个对象 -->
<!-- 这里的id相当于我们new对象时的变量名,class相当于我们准备new的对象类--><bean id="mysqlImpl" class="com.myLearning.dao.UserDaoMysqlImpl"/><bean id="oracleImpl" class="com.myLearning.dao.UserDaoOracleImpl"/><bean id="userServiceImpl" class="com.myLearning.service.UserServiceImpl">
<!-- 这里property内部,name表示属性名,ref引用bean中创建的对象,value表示实际的值--><property name="userDao" ref="mysqlImpl"/></bean></beans>

然后我们就可以直接在Test中获取Spring容器中为我们创建的对象了,而不是自己new对象了

import com.myLearning.dao.UserDaoOracleImpl;
import com.myLearning.service.UserService;
import com.myLearning.service.UserServiceImpl;
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");UserService userService = (UserService) context.getBean("userServiceImpl");userService.getUser();}
}

我们现在只需要修改xml配置文件,就可以切换不同的dao实现类了,而不需要修改程序代码,这就是Spring的IOC思想,即对象交由Spring来创建、管理以及装配。

IOC创建对象的方式

我们先创建一个User类:

package com.myLearning;public class User {private String name;public User(){System.out.println("User 对象被无参构造方式创建了!");}public User(String name){this.name = name;System.out.println("User 对象被有参构造方式创建了!");}public String getName() {return name;}public void setName(String name) {this.name = name;}}

然后再beans.xml中配置:

    <bean id="user" class="com.myLearning.User"><property name="name" value="张三"/></bean>

在默认情况下,Spring会调用类的无参构造方法来创建对象,如果需要调用有参构造方法,一共有三种方法:
1、通过index设置参数位置

    <bean id="user" class="com.myLearning.User"><constructor-arg index="0" value="张三"/></bean>

2、通过type设置参数类型

    <bean id="user" class="com.myLearning.User"><constructor-arg type="java.lang.String" value="张三"/></bean>

3、通过name设置参数名称

    <bean id="user" class="com.myLearning.User"><constructor-arg name="name" value="张三"/></bean>

Spring配置

bean标签

1、id属性:用于表示对象,相当于变量名,唯一标识符,不能包含特殊字符
2、class属性:用于指示创建的对象类名,填类的全限定名
3、name属性:用于创建该对象的别名,可以包含特殊字符,可以设置多个,多个之间用逗号分隔

    <bean id="user" class="com.myLearning.User" name="user1,user2"/>

alias标签

用于设置别名,可以设置多个,多个之间用逗号分隔

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

import标签

用于导入其他配置文件,可以设置多个,多个之间用逗号分隔

    <import resource="beans.xml"/><import resource="beans1.xml"/><import resource="beans2.xml"/>

依赖注入DI

依赖注入(Dependency Injection,DI)是一种设计模式,用于实现控制反转(Inversion of Control,IoC),目的是减少代码间的耦合。
依赖指的是bean对象的创建依赖于容器
注入指的是bean对象中的所有属性,由容器来注入

测试环境搭建

创建实体类

package com.myLearning.pojo;public class Address {private String address;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}
}package com.myLearning.pojo;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> hobbies;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> getHobbies() {return hobbies;}public void setHobbies(List<String> hobbies) {this.hobbies = hobbies;}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;}
}

构造器注入

1、通过index设置参数索引

    <bean id="user" class="com.myLearning.User"><constructor-arg index="0" value="张三"/></bean>

2、通过type设置参数类型

    <bean id="user" class="com.myLearning.User"><constructor-arg type="java.lang.String" value="张三"/></bean>

3、通过name设置参数名称

    <bean id="user" class="com.myLearning.User"><constructor-arg name="name" value="张三"/></bean>

set注入

    <bean id="address" class="com.myLearning.pojo.Address"><property name="address" value="鸠拉·特恩佩斯特联邦国"/></bean><bean id="student" class="com.myLearning.pojo.Student"><!-- 普通值注入     --><property name="name" value="利姆露"/>
<!--       使用bean内对象的引用注入--><property name="address" ref="address"/>
<!--        数组--><property name="books"><array><value>突然穿越了怎么办</value><value>帝王心术</value><value>如何偷偷去酒馆不被发现</value></array></property>
<!--        List--><property name="hobbies"><list><value>吃好吃的</value><value>看好看的</value><value>玩好玩的</value></list></property>
<!--        Map--><property name="card"><map><entry key="id-card" value="10577964"/><entry key="酒馆会员卡" value="100066"/></map></property><!--Set--><property name="games"><set><value>竞技场</value><value>举办宴会</value></set></property>
<!--     设置null值--><property name="wife"><null/></property>
<!--        Properties--><property name="info"><props><prop key="种族">史莱姆</prop></props></property></bean>
</beans>

p注入

使用p命名空间注入属性值需要先导入p命名空间

       xmlns:p="http://www.springframework.org/schema/p"
<bean id="address2" class="com.myLearning.pojo.Address"p:address="朱拉坦派斯特联邦国"/>

使用p命名空间注入可以简化xml配置,相当于之前配置的property

c注入

使用c命名空间注入属性值需要先导入c命名空间

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

同时使用c注入需要先在类中定义有参构造器

    <bean id="user" class="com.myLearning.pojo.User"c:name="张三" c:age="18"/>

使用c命名空间注入可以简化xml配置,相当于之前配置的constructor-arg

bean作用域

bean的作用域包含以下几种

  • singleton:单例(默认):IOC容器中只有一个bean的实例
  • prototype:原型(多例):IOC容器中每次获取bean的时候都会创建一个新的bean
  • request:在web项目中,设置bean的作用域为request表示该bean在一个请求范围内有效
  • session:在web项目中,设置bean的作用域为session表示该bean在一个会话范围内有效
    <bean id="user" class="com.myLearning.pojo.User" scope="singleton"/>

bean自动装配

假设我们有一个人(类),这个人有一只狗

package com.myLearning.pojo;public class Dog {public void wangwang(){System.out.println("汪汪");}
}package com.myLearning.pojo;public class Person {public Dog getDog() {return dog;}public void setDog(Dog dog) {this.dog = dog;}private Dog dog;}

然后我们来编写xml配置文件
一般我们需要手动注入创建对象的属性

    <bean id="dog" class="com.myLearning.pojo.Dog"/><bean id="person" class="com.myLearning.pojo.Person"><property name="dog" ref="dog"/></bean>

但是我们可以设置autowire属性,让spring自动装配,这个属性可以设置为byName和byType,byName表示根据名字自动装配,byType表示根据类型自动装配
根据名字id自动装配,这里搜索的是person类中的dog属性,所以我们需要将小狗类对象的id设置为dog才能被搜索到

    <bean id="dog" class="com.myLearning.pojo.Dog"/><bean id="person" class="com.myLearning.pojo.Person" autowire="byName"/>

根据类型自动装配

    <bean id="dog" class="com.myLearning.pojo.Dog"/><bean id="person" class="com.myLearning.pojo.Person" autowire="byType"/>

使用注解完成自动装配

使用注解完成自动装配需要先开启注解支持,至少保证配置文件包含如下配置:

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

使用@Autowired注解完成自动装配,这个注解可以放在属性上,也可以放在set方法上,也可以放在构造方法上

public class Person {@Autowiredprivate Dog dog;public void shout() {dog.shout();}
}

@Autowired注解默认是先按照类型装配的,如果没有唯一的对应类型,则会试图查找属性名对应的bean, 但如果仍然找不到对应属性名的对象,则会报错,此时需要使用@Qualifier注解指定bean的id
假设我们的配置文件中有如下bean

    <bean id="dog1" class="com.myLearning.pojo.Dog"/><bean id="dog2" class="com.myLearning.pojo.Dog"/>

那么在Person类中,如果使用@Autowired注解,则无法找到唯一的dog对象,此时需要使用@Qualifier注解,它可以指定bean的id

public class Person {@Autowired  @Qualifier("dog1")private Dog dog;public void shout() {dog.shout();}
}

也通过@Ressource注解完成自动装配,这个注解和@Autowired注解类似,使用时放在属性上

使用前需要添加maven依赖

<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version>
</dependency>

它会先按照名称进行装配,如果找不到对应名称的bean,则会按照类型进行装配,如果仍然找不到,则会报错,可以通过(name=“dog1”)指定bean的id

// public class Person {
//     @Resource
//     private Dog dog;//     public void shout() {
//         dog.shout();
//     }
// }public class Person {@Resource(name="dog1")private Dog dog;public void shout() {dog.shout();}
}

使用注解开发

使用注解需要有注解的支持:

<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/></beans>

我们创建一个applicatonContext.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!-- 指定扫描的包,这个包下的注解会生效   --><context:component-scan base-package="com.myLearning.pojo"/><context:annotation-config/></beans>

@Component注解与@Value注解

@Component注解: 将类标识为Spring容器中的一个Bean,Spring会自动扫描并管理这些Bean。相当于在XML配置文件中定义了一个Bean。

@Value注解: 用于为Bean的属性注入值,可以用于字段、方法参数、构造函数参数等。相当于在XML配置文件中为Bean的属性指定了一个值。

package com.myLearning.pojo;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class User {@Value("利姆露")private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

根据mvc三层架构分层,@Component注解在dao、service、controller层有几个作用相同的注解:

  • @Controller: 用于标识一个控制器类,通常用于处理HTTP请求。
  • @Service: 用于标识一个服务类,通常用于处理业务逻辑。
  • @Repository: 用于标识一个数据访问对象类,通常用于访问数据库。

这几个注解的作用和@Component一样,只是名字不同

@Autowired注解 与 @Qualifier注解

用于自动装配,参考前文介绍

@Scope注解

用于指定对应Bean的作用域,有singleton、prototype、request、session、globalSession等选项。

@Component
@Scope("singleton")
public class User {@Value("利姆露")private String name;
}

使用纯Java的方式来配置Spring

我们也可以完全不使用XML配置文件,而是使用纯Java的方式来配置Spring。具体步骤如下:

  1. 创建一个配置类,使用@Configuration注解标识该类为一个配置类,这个注解相当于我们之前xml配置文件中的标签,其中可以包含多个bean.
  2. 在配置类中使用@Bean注解来定义一个Bean,这个注解相当于我们之前xml配置文件中的标签。
// 配置类
package com.myLearning;import com.myLearning.controller.UserController;
import com.myLearning.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration // 这个注解同时也会将这个类注册到Spring容器中,因为它本身也是一个@Component
@ComponentScan("com.myLearning") //这个注解相当于我们之前xml配置文件中的<context:component-scan>标签,它会扫描指定包下的所有类,并将它们注册到Spring容器中
public class AppConfig {@Beanpublic User getUser() {return new User();}@Beanpublic UserController getUserController() {return new UserController();}
}// User类
package com.myLearning.pojo;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class User {@Value("利姆露")private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
  1. 在需要使用该Bean的地方,使用@Autowired注解来自动装配该Bean。
package com.myLearning.controller;import com.myLearning.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {@Autowiredprivate User user;public void printUser() {System.out.println(user.getName());}
}

在我们使用的时候,我们需要使用AnnotationConfigApplicationContext来加载配置文件,然后获取到UserController的实例

import com.myLearning.AppConfig;
import com.myLearning.controller.UserController;
import com.myLearning.pojo.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);//这里既可以使用刚刚注册的getUserController方法用于获取实例,也可以直接使用UserController的类名获取实例// UserController userController = context.getBean(UserController.class);UserController userController = (UserController) context.getBean("getUserController");userController.printUser();}
}

如果我们想将其他配置类导入到一个配置类中,我们可以使用@Import注解,例如:

@Configuration
@Import({AppConfig1.class, AppConfig2.class})
public class AppConfig {@Beanpublic UserController getUserController() {return new UserController();}
}

代理模式

代理模式是一种设计模式,它允许我们通过代理对象来访问目标对象,而不需要直接访问目标对象。在Spring中,代理模式被广泛使用,例如在AOP(面向切面编程)中,代理对象用于在目标方法执行前后添加额外的逻辑。

代理模式的分类

  • 静态代理
  • 动态代理

静态代理

代理模式通常包含三个角色:

  • 抽象主题(Subject):定义了代理类和真实主题的公共接口,这样代理类就可以通过实现这个接口来代理真实主题。
  • 真实主题(Real Subject):实现了抽象主题定义的接口,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理类(Proxy):代理类包含了真实主题的引用,并在调用真实主题的方法前后可以添加一些额外的操作。

假设我们有一个房东要卖房,它们不想直接和购房者打交道,于是请了一个中介来代理它们。房东是真实主题,中介是代理类,购房者是客户端。

我们先定义一个接口,表示房东和中介的公共行为:

package com.myLearning.sellHouse;public interface Sell {void sellHouse();
}

然后我们定义实现这个接口的房东

package com.myLearning.sellHouse;public class Host implements Sell{public void sellHouse() {System.out.println("房屋主人卖出房子!");}
}

但是我们通常不会直接找到房东买房,而是通过中介代理,所以我们定义一个代理类,它不仅负责帮助房东卖房子,还帮助我们签订合同

package com.myLearning.sellHouse;public class Proxy implements Sell{private Host host;public Proxy(Host host) {this.host = host;}private void makeContract(){System.out.println("中介协商签合同");}public void sellHouse() {makeContract();host.sellHouse();}
}

然后我们在客户端就可以使用代理类来买房子和签合同啦

package com.myLearning.sellHouse;public class Client {public static void main(String[] args) {Host host = new Host();// 房主交给中介卖房Proxy proxy = new Proxy(host);// 通过中介签合同、买房proxy.sellHouse();}
}

代理模式的优点

  1. 代理模式能将代理对象与真实对象分离,在一定程度上降低了系统的耦合度。
  2. 代理模式能将一些与真实对象无关的操作,如统计、访问控制等,集中到代理类中完成,这样既可减少真实对象的复杂性,也易维护和扩展。
  3. 代理模式能将真实对象的使用者与真实对象解耦,在一定程度上降低了使用者的复杂性。

动态代理

动态代理是代理模式的一种实现方式,它可以在运行时动态地创建代理类,而不需要提前编写代理类的代码。动态代理通常使用Java的反射机制来实现。

动态代理的原理

动态代理的原理是通过Java的反射机制,在运行时动态地创建代理类,并使用代理类来调用真实对象的方法。具体来说,动态代理的实现步骤如下:

  1. 创建一个实现了InvocationHandler接口的处理器类,该处理器类负责处理代理对象的方法调用。在处理器类中,需要重写invoke方法,该方法会在代理对象的方法被调用时被调用。
  2. 创建一个实现了InvocationHandler接口的处理器对象,并将真实对象作为参数传递给处理器对象的构造方法。
  3. 使用Proxy类的newProxyInstance方法创建代理对象。该方法需要传入三个参数:ClassLoader类加载器,代理对象实现的接口数组,处理器对象。该方法会返回一个代理对象,该代理对象会使用处理器对象来处理方法调用。
  4. 使用代理对象调用真实对象的方法。

动态代理的示例

下面是一个动态代理的示例,该示例使用动态代理来实现一个卖房子的功能。

package com.myLearning.sellHouse;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class DynamicProxy implements InvocationHandler {private Object target;public DynamicProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("中介开始卖房");Object result = method.invoke(target, args);System.out.println("中介卖房结束");return result;}public static void main(String[] args) {Host host = new Host();// 创建代理对象DynamicProxy dynamicProxy = new DynamicProxy(host);ClassLoader classLoader = host.getClass().getClassLoader(); // 获取目标对象的类加载器Class<?>[] interfaces = host.getClass().getInterfaces(); // 获取目标对象实现的接口Sell sellHouseProxy = (Sell) Proxy.newProxyInstance(classLoader, interfaces, dynamicProxy);// 使用代理对象调用真实对象的方法sellHouseProxy.sellHouse();}
}

动态代理的优点

  1. 动态代理可以在运行时创建代理对象,不需要在编译时生成代理类。
  2. 动态代理可以代理任何实现了接口的类,不需要知道具体的实现细节。
  3. 动态代理可以灵活地添加额外的功能,例如日志记录、权限控制等。

Spring AOP

AOP(面向切面编程)是Spring框架中的一个重要特性,它允许我们在不修改原有代码的情况下,通过定义切面(Aspect)来增强或修改原有的功能。

切面(Aspect)

切面是AOP中的一个重要概念,它定义了在哪些地方(Join Points)执行哪些操作(Advice)。切面通常由一个类和一个或多个方法组成,这些方法定义了在特定条件下要执行的操作。

连接点(Join Points)

连接点是程序执行过程中的一个特定点,例如方法调用、异常抛出等。切面可以在这些连接点处执行特定的操作。

通知(Advice)

通知是切面中的一个方法,它定义了在连接点处要执行的操作。通知可以是前置通知(Before Advice)、后置通知(After Advice)、环绕通知(Around Advice)等。

切入点(Pointcut)

切入点是一个表达式,用于定义在哪些连接点处执行通知。切入点可以基于方法名、参数、注解等条件进行匹配。

Spring AOP的使用

万事之前,使用aop我们也需要导入aop相关maven依赖:

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

方式一

首先我们先编写我们的业务类

package com.myLearning.service;public interface UserService {void add();void update();void delete();void select();
}
package com.myLearning.service;public class UserServiceImpl implements UserService{@Overridepublic void add() {System.out.println("执行了添加用户的方法");}@Overridepublic void update() {System.out.println("执行了更新用户的方法");}@Overridepublic void delete() {System.out.println("执行了删除用户的方法");}@Overridepublic void select() {System.out.println("执行了查找用户的方法");}public UserServiceImpl() {}
}

然后现在我们希望在执行这些方法的前后添加记录日志的代码,但是我们又不希望改动我们已经编写好的业务代码,这个时候我们就可以使用Spring AOP来实现这个功能。

我们先编写我们的Log代码,需要分别实现MethodBeforeAdvice 接口以及AfterReturningAdvice 接口,分别用于在业务方法的前后添加日志记录。

package com.myLearning.log;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class BeforeLog implements MethodBeforeAdvice {public void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("Before Method: " + method.getName());}
}
package com.myLearning.log;import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class AfterLog implements AfterReturningAdvice {public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("ReturnedValue: " + returnValue + " Method: " + method + " Args: " + args);}
}

现在我们已经编写好了准备添加到业务前后的Log类,现在我们需要将这两个类通过Spring AOP添加到我们的业务类中,首先我们需要编写一个配置文件applicationContext.xml,也即是我们之前用于配置bean的配置文件,我们需要通过这个配置文件来配置我们的AOP。
类似于我们之前配置的自动装配需要添加配置约束,我们的aop也需要添加配置约束,在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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"></beans>

现在我们在配置文件中添加我们的Log类,业务类,以及配置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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 指定扫描的包,这个包下的注解会生效   -->
<!--    <context:component-scan base-package="com.myLearning.pojo"/>--><context:annotation-config/><bean id="userService" class="com.myLearning.service.UserServiceImpl"/><bean id="beforeLog" class="com.myLearning.log.BeforeLog"/><bean id="afterLog" class="com.myLearning.log.AfterLog"/><!--    配置aop,需要导入aop约束-->
<!--    使用Spring API接口配置--><aop:config>
<!--        切入点 expression用于指定要切入的位置(方法)固定表达式为 execution(返回值 类 方法 参数)--><aop:pointcut id="pointcut" expression="execution(* com.myLearning.service.UserServiceImpl.*(..))"/>
<!--        编写环绕通知,指明要加入的通知类,以及要切入的位置--><aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/><aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/></aop:config></beans>

简单测试一下,发现我们已经在不改动业务代码的情况下,在方法执行前后加入了日志!

import com.myLearning.AppConfig;
import com.myLearning.controller.UserController;
import com.myLearning.pojo.User;
import com.myLearning.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) context.getBean("userService");userService.add();}
}

方式二

以上是通过设置通知来配置aop设置的,我们还可以通过直接编写切面类(切面类用于完成如日志的模块功能的类,通知相当于完成功能的方法,我们前面的设置是直接设置通知advisor的方式完成的)来完成aop的配置:

我们先简单编写一个切面类:

package com.myLearning.log;public class DiyPointCut {public void before(){System.out.println("before method");}public void after(){System.out.println("after method");}
}

然后将这个切面类配置到spring的配置文件中:

    <bean id="userService" class="com.myLearning.service.UserServiceImpl"/><bean id="diyPointCut" class="com.myLearning.log.DiyPointCut"/><aop:config><!--        切入点 expression用于指定要切入的位置(方法)固定表达式为 execution(返回值 类 方法 参数)--><aop:pointcut id="pointcut" expression="execution(* com.myLearning.service.UserServiceImpl.*(..))"/>
<!--      自定义切面  --><aop:aspect ref="diyPointCut"><aop:before method="before" pointcut-ref="pointcut"/><aop:after method="after" pointcut-ref="pointcut"/></aop:aspect></aop:config>

通过这样的方法,我们可以达到与前一种方法同样的效果

方式三

除了以上两种方法,我们还可以通过注解来直接设置切面

首先我们需要写一个切面类,并且在编写时添加注解

package com.myLearning.log;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.myLearning.service.UserServiceImpl.*(..))")public void before(){System.out.println("Before Method");}
//    标识切入点@After("execution(* com.myLearning.service.UserServiceImpl.*(..))")public void after(){System.out.println("After Method");}
//     表示环绕切入点@Around("execution(* com.myLearning.service.UserServiceImpl.*(..))")
//    参数是连接点,用于执行业务实际方法,以及可以显示执行的业务方法的一些信息public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕前");//        执行实际业务代码Object ret = joinPoint.proceed();System.out.println("环绕后");return ret;}
}

然后我们需要再Spring配置文件中配置这个类,并且开启注解支持

    <bean id="userService" class="com.myLearning.service.UserServiceImpl"/><bean id="annotationPointCut" class="com.myLearning.log.AnnotationPointCut"/>
<!--   开启注解支持--><aop:aspectj-autoproxy/>

整合Spring和Mybatis

万事之前,先导包

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.4</version></dependency>

配置基础Mybatis环境

创建实体类,相对于的数据库可能需要自己参考实体类创建一下,或者可以参考我前一篇博客的数据库创建

package com.myLearning.pojo;import org.apache.ibatis.type.Alias;import java.io.Serializable;@Alias("user")
public class User implements Serializable {private int id;private String name;private String pwd;public User() {}public User(int id, String name, String pwd) {this.id = id;this.name = name;this.pwd = pwd;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", pwd='" + pwd + '\'' +'}';}
}

创建Mapper接口

package com.myLearning.mapper;import com.myLearning.pojo.User;import java.util.List;public interface UserMapper {List<User> selectAllUser();
}

创建Mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.mapper.UserMapper"><select id="selectAllUser" resultType="com.myLearning.pojo.User">select * from user</select>
</mapper>

创建mybatis-config.xml配置文件, 注意,我这里并没有配置数据源以及mapper映射,这是因为我们一会后会将Spring与mybatis整合,我们会在Spring的配置文件中配置数据源之类的东西

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><package name="com.myLearning.pojo"/></typeAliases><!--    <settings>-->
<!--        <setting name="" value=""/>-->
<!--    </settings>--></configuration>

设置Spring的配置

在我们创建好了mybatis基础环境后,我们现在可以来编写Spring的配置文件了,在这个文件中,我们主要需要配置
数据源类org.springframework.jdbc.datasource.DriverManagerDataSource,这个就是我们之前在Mybatis中配置的数据源,我们现在在Spring中配置。
工厂类org.mybatis.spring.SqlSessionFactoryBean, 这个是我们之前在Mybatis工具类中创建的工厂类,现在我们将其配置到Spring中。
SqlSession类 org.mybatis.spring.SqlSessionTemplate,这个就是我们之前在Mybatis工具类中创建的SqlSession类,主要用于执行我们的sql语句的,现在我们将其配置到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"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 指定扫描的包,这个包下的注解会生效   --><!--    <context:component-scan base-package="com.myLearning.pojo"/>--><context:annotation-config/><!--首先我们需要配置一个数据源,我们现在使用Spring的数据源代替mybatis的--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/><property name="username" value="root"/><property name="password" value="123456"/></bean><!--我们还需要一个sqlSessionFactory--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="configLocation" value="classpath:mybatis-config.xml"/><property name="mapperLocations" value="classpath:com/myLearning/mapper/*.xml"/></bean><!-- 获得sqlSession对象--><bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg index="0" ref="sqlSessionFactory"/></bean></beans>

现在我们有了sqlSession后,我们就可以创建一个UserMapper的实现类UserMapperImpl,用来通过sqlSession对象来执行sql语句了。

package com.myLearning.mapper;import com.myLearning.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;import java.util.List;public class UserMapperImpl implements UserMapper {private SqlSessionTemplate sqlSession;public void setSqlSession(SqlSessionTemplate sqlSession) {this.sqlSession = sqlSession;}@Overridepublic List<User> selectAllUser() {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);return userMapper.selectAllUser();}
}

同样,我们把它注册到Spring中

<!--    获得UserMapper对象--><bean id="userMapper" class="com.myLearning.mapper.UserMapperImpl"><property name="sqlSession" ref="sqlSession"/></bean>

当然,我们可能需要一个applicationContext.xml来管理所有的Spring配置,在其中导入我们刚刚创建的Spring-dao.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><import resource="spring-dao.xml"/></beans>

简单测试一下

package com.myLearning.dao;import com.myLearning.mapper.UserMapper;
import com.myLearning.pojo.User;
import com.myLearning.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.List;public class UserMapperTest {@Testpublic void selectUserListTest(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper userMapper = (UserMapper)context.getBean("userMapper");List<User> users = userMapper.selectAllUser();for (User user : users) {System.out.println(user);}}}

当然,我们还可以有另一种方式来编写UserMapperImpl类,即让它继承SqlSessionDaoSupport类,这样我们就可以直接使用getSqlSession()方法来获取SqlSession对象,而不需要自己创建SqlSession对象。

package com.myLearning.mapper;import com.myLearning.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;import java.util.List;public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {@Overridepublic List<User> selectAllUser() {UserMapper userMapper = getSqlSession().getMapper(UserMapper.class);return userMapper.selectAllUser();}
}

但要注意,此时我们注册的时候,需要注入的不再是SqlSessionTemplate,而是SqlSessionFactory。

<!--    获得UserMapper对象--><bean id="userMapper" class="com.myLearning.mapper.UserMapperImpl"><property name="sqlSessionFactory" ref="sqlSessionFactory"/></bean>

使用Spring配置声明式事务

首先要确保Spring的配置文件有事务的声明空间,即引入tx命名空间,类似如下内容,主要是包含tx相关的:

<?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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsd"></beans>

首先我们需要再Spring配置文件中配置事务管理器

<!--    配置声明式事务--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><constructor-arg ref="dataSource" /></bean>

然后我们使用aop的通知来实现事务,这里Spring已经为我们写好了事务类型的通知,我们只需要简单配置即可

<!--    配置事务通知--><tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--        给哪些方法配置事务--><tx:attributes><tx:method name="add" propagation="REQUIRED"/><tx:method name="*" propagation="REQUIRED"/></tx:attributes></tx:advice>

然后我们需要使用aop配置将通知和切入点结合起来

<!--    aop配置事务切入--><aop:config>
<!--        设置切入点,给com.myLearning.mapper包内所有类中的所有方法配置事务--><aop:pointcut id="txPointCut" expression="execution(* com.myLearning.mapper.*.*(..))"/>
<!--    设置通知--><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/></aop:config>

以此,我们就为我们的com.myLearning.mapper包内所有类中的所有方法配置了事务管理了

笔记总结于视频:https://www.bilibili.com/video/BV1WE411d7Dv?vd_source=16bf0c507e4a78c3ca31a05dff1bee4e&spm_id_from=333.788.videopod.episodes

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词