Spring:春---->给软件行业带来了春天
2002,首次推出了Spring框架的雏形: interface21框架
Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式 版
Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学
spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
SSH : Struct2 + Spring + Hibernate
SSM:SpringMvc + Spring + Mybatis
官网: https://spring.io/projects/spring-framework#overview
官方下载地址: http://repo.spring.io/release/org/springframework/spring
GitHub: https://github.com/spring:projects/spring-framework
org.springframework
spring-webmvc
5.3.11
org.springframework
spring-jdbc
5.3.11
总结一-句话: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP) 的框架!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6jS4VKTG-1656760867323)(Spring.assets/27c714d4c6e88d1a6a5ea8a72ff3281a.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J73r096m-1656760867323)(Spring.assets/5c2586a36ed1cfd9fc4eaefc0a78ce52.png)]
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory
,它是工厂模式的实现。BeanFactory
使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
在Spring的官网有这个介绍:现代化的Java开发!说白就是基于Spring的开发!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M2gdCkqX-1656760867323)(Spring.assets/a42b4e5df07e29b66de4d0d053ba9b73.png)]
Spring Boot
一个快速开发的脚手架
基于Spring Boot可以快速的开发单个微服务
约定大于配置
Spring Cloud
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
新建一个空白的maven项目
我们先用我们原来的方式写一段代码 .
public interface UserDao {
public void getUser();
}
ublic class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
public interface UserService {
public void getUser();
}
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
@Test
public void test(){
UserService service = new UserServiceImpl();
service.getUser();
}
这是我们原来的方式 , 开始大家也都是这么去写的对吧 . 那我们现在修改一下 .
把Userdao的实现类增加一个
public class UserDaoMySqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("MySql获取用户数据");
}
}
紧接着我们要去使用MySql的话 , 我们就需要去service实现类里面修改对应的实现 .
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoMySqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
在假设, 我们再增加一个Userdao的实现类 .
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle获取用户数据");
}
}
那么我们要使用Oracle , 又需要去service实现类里面修改对应的实现 . 假设我们的这种需求非常大 , 这种方式就根本不适用了, 甚至反人类对吧 , 每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身 .
那我们如何去解决呢 ?
我们可以在需要用到他的地方 , 不去实现它 , 而是留出一个接口 , 利用set , 我们去代码里修改下 .
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set实现
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
使用set接口实现
现在去我们的测试类里 , 进行测试 ;
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl();
service.setUserDao( new UserDaoImpl() );
service.getUser();
service.setUserDao( new UserDaoMySqlImpl() );
service.getUser();
//那我们现在又想用Oracle去实现呢
service.setUserDao( new UserDaoOracleImpl() );
service.getUser();
}
大家发现了区别没有 ? 可能很多人说没啥区别 . 但是同学们 , 他们已经发生了根本性的变化 , 很多地方都不一样了 . 仔细去思考一下 , 以前所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 . 程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口 .
这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !
控制反转loC(Inversion of Control),是-种设计思想,DI(依赖注入)是实现loC的一种方法,也有人认为DI只是
IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9CyH5CZ-1656760867324)(Spring.assets/01826c23d18ee93aceaf23a3c73c07c7.png)]
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sZnNZiB8-1656760867324)(Spring.assets/c73f2660c2455df0b11c1c601b01def6.png)]
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为- -体,
Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。 在Spring中实现控制反转
的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)
spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.11version>
dependency>
编写一个Hello实体类
public class Hello {
private String string;
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
@Override
public String toString() {
return "Hello{" +
"string='" + string + '\'' +
'}';
}
}
<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="hello" class="com.study.pojo.Hello">
<property name="string" value="Spring"/>
bean>
beans>
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后 ,对象是由Spring来创建的.
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收.
可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .
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("User:"+name);
}
}
<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">
<bean id="user" class="com.kuang.pojo.User">
<property name="name" value="study"/>
bean>
beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在执行getBean的时候, user已经创建好了 , 通过无参构造
User user = (User) context.getBean("user");
//调用对象的方法 .
user.show();
}
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;
}
public void show(){
System.out.println("User:"+name);
}
}
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg index="0" value="kuangshen2"/>
bean>
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg name="name" value="kuangshen2"/>
bean>
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg type="java.lang.String" value="kuangshen2"/>
bean>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在执行getBean的时候, user已经创建好了 , 通过有参构造
User user = (User) context.getBean("user");
//调用对象的方法 .
user.show();
}
<bean id="user" class="com.study.pojo.User">
<constructor-arg type="java.lang.String" value="root"/>
<constructor-arg type="java.lang.String" value="123456"/>
bean>
<alias name="user" alias="userNew"/>
id:bean的id标识符
class:bean对象所对应的类型(包名+类名)
name:别名,更高级,可以同时取多个别名。
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
bean>
这个import,一般用于团队开发,可以将多个配置文件,导入合并一个
假设,项目中有多个人开发,这三个人负责不同的人开发,不同的类需要注册在不同的bean中,我们可以利用import,将所有人的beans.xml合并为总的
张三
李四
王五
applicationContext.xml
使用的时候使用总的就可以了
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
【环境搭建】
Address
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 + '\'' +
'}';
}
}
Student
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 Properties info;
private String wife;
}
beans.xml
<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">
<bean id="address" class="com.study.pojo.Address">
<property name="address" value="岳阳"/>
bean>
<bean id="student" class="com.study.pojo.Student">
<property name="name" value="小明"/>
bean>
beans>
测试类
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getAddress());
}
}
完善注入信息
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.study.pojo.Address">
<property name="address" value="岳阳"/>
bean>
<bean id="student" class="com.study.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="address"/>
<property name="books">
<array>
<value>红楼梦value>
<value>西游记value>
<value>水浒传value>
<value>三国演义value>
array>
property>
<property name="hobbies">
<list>
<value>听音乐value>
<value>敲代码value>
<value>看电影value>
list>
property>
<property name="card">
<map>
<entry key="身份证" value="24322388967868789809"/>
<entry key="银行卡" value="21321412321321432"/>
map>
property>
<property name="games">
<set>
<value>LOLvalue>
<value>COCvalue>
<value>BOBvalue>
set>
property>
<property name="wife">
<null/>
property>
<property name="info">
<props>
<prop key="学号">201856546233prop>
<prop key="性别">男prop>
<prop key="姓名">小明prop>
props>
property>
bean>
beans>
使用p命名空间和c命名空间注入
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.study.pojo.User" p:name="小明" p:age="18"/>
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明"/>
beans>
测试:
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user2");
System.out.println(user);
System.out.println(user2);
}
注意点:
c命名空间和p命名空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S14BUfR0-1656760867325)(Spring.assets/3bfa22168eb292f9d7577938133d0eec.png)]
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="user" class="com.study.pojo.User" p:name="小明" p:age="18" scope="singleton"/>
测试:
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user.toString() + "," + user.hashCode());
System.out.println(user2.toString() + "," + user2.hashCode());
System.out.println(user==user2);
}
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" scope="prototype"/>
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" singleton="false"/>
测试:
@Test
public void test04(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = (User) context.getBean("user2");
User user2 = (User) context.getBean("user2");
System.out.println(user.toString() + "," + user.hashCode());
System.out.println(user2.toString() + "," + user2.hashCode());
System.out.println(user==user2);
}
当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring
ApplicationContext情形下有效。考虑下面bean定义:
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
Spring中bean有三种装配机制,分别是:
这里我们主要讲第三种:自动化的装配bean。
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
组件扫描和自动装配组合发挥巨大威力,使的显示的配置降低到最少。
推荐不使用自动装配xml配置 , 而使用注解 .
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
public class User {
private Cat cat;
private Dog dog;
private String name;
}
<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">
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="people" class="com.study.pojo.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="张三"/>
bean>
beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
People people = context.getBean("people",People.class);
people.getCat().shout();
people.getDog().shout();
}
autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
测试:
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="people" class="com.study.pojo.People" autowire="byName">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="张三"/>
bean>
再次测试,结果依旧成功输出!
我们将 cat 的bean id修改为 catXXX
再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。
小结:
当一个bean节点带有 autowire byName的属性时。
autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
NoUniqueBeanDefinitionException
测试:
autowire="byType"
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="cat2" class="com.study.pojo.Cat"/>
<bean id="people" class="com.study.pojo.People" autowire="byType">
<property name="name" value="张三"/>
bean>
测试,报错:NoUniqueBeanDefinitionException
删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
这就是按照类型自动装配!
jdk1.5开始支持注解,spring2.5开始全面支持注解。
准备工作: 利用注解的方式注入属性。
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<context:annotation-config/>
测试:
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getStr() {
return str;
}
}
<context:annotation-config/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="people" class="com.study.pojo.People"/>
@Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
测试实验步骤:
1.配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!
<bean id="cat1" class="com.study.pojo.Cat"/>
<bean id="dog1" class="com.study.pojo.Dog"/>
<bean id="cat2" class="com.study.pojo.Cat"/>
<bean id="dog2" class="com.study.pojo.Dog"/>
<bean id="people" class="com.study.pojo.People"/>
没有加Qualifier测试,直接报错
在属性上添加Qualifier注解
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
实体类:
public class People {
@Resource
private Cat cat;
@Resource(name = "dog2")
private Dog dog;
private String name;
}
ApplicationContext.xml
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="dog1" class="com.study.pojo.Dog"/>
<bean id="dog2" class="com.study.pojo.Dog"/>
<bean id="people" class="com.study.pojo.People"/>
测试:结果OK
@Autowired与@Resource异同:
@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
在Spring中有三种自动装配的方式
在spring4之后,必须要保证aop的包导入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x3hDbzmm-1656760867325)(Spring.assets/d4abe373a44a4420e3c23c274359c7e9.png)]
使用注解需要导入context约束, 增加注解的支持!
<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>
我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!
<context:component-scan base-package="com.study.pojo"/>
@Component("user")
// 相当于配置文件中
public class User {
public String name = "张三";
}
@Test
public void test(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("ApplicationContext.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
使用注解注入属性
@Component("user")
// 相当于配置文件中
public class User {
@Value("张三")
// 相当于配置文件中
public String name;
}
@Component("user")
public class User {
public String name;
@Value("张三")
public void setName(String name) {
this.name = name;
}
}
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
写上这些注解,就相当于将这个类交给Spring管理装配了!
在Bean的自动装配已经讲过了,可以回顾!
@scope
@Controller("user")
@Scope("prototype")
public class User {
@Value("张三")
public String name;
}
XML与注解比较
xml与注解整合开发 :推荐最佳实践
<context:annotation-config/>
作用:
JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。
测试:
@Component //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
public String name = "dog";
}
新建一个config配置包,编写一个MyConfig配置类
@Configuration //代表这是一个配置类
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public Dog dog(){
return new Dog();
}
}
@Test
public void test2(){
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) applicationContext.getBean("dog");
System.out.println(dog.name);
}
导入其他配置如何做呢?
@Configuration //代表这是一个配置类
public class MyConfig2 {
/...
}
@Configuration
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
@Bean
public Dog dog(){
return new Dog();
}
}
为什么要学习代理模式?因为这就是SpringAOP的底层! 【SpringAOP 和SpringMVC】
代理模式的分类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3iyrjPQ1-1656760867326)(Spring.assets/4dd369665076f130ef2ff1f31612465d.png)]
角色分析:
1.Rent . java 即抽象角色
//抽象角色:租房
public interface Rent {
public void rent();
}
2.Host . java 即真实角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
3.Proxy . java 即代理角色
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
//租房
public void rent(){
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
4.Client . java 即客户
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮助房东
Proxy proxy = new Proxy(host);
//你去找中介!
proxy.rent();
}
}
分析: 在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。
代理模式的好处: .
缺点:
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
//抽象角色:增删改查业务
public interface UserService {
void add();
void delete();
void update();
void query();
}
我们需要一个真实对象来完成这些增删改查操作
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
设置一个代理类来处理日志! 代理角色
//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
public class Client {
public static void main(String[] args) {
//真实业务
UserServiceImpl userService = new UserServiceImpl();
//代理类
UserServiceProxy proxy = new UserServiceProxy();
//使用代理类实现日志功能!
proxy.setUserService(userService);
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
【聊聊AOP:纵向开发,横向开发】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EIxJLgy1-1656760867326)(Spring.assets/7b69fa91d008ac85b28eac0116780dd4.png)]
动态代理和静态代理一样
动态代理类是动态生成的,不是我们直接写好的!
基于接口的动态代理和基于类的动态代理
需要了解两个类:Proxy:代理、InvocationHandler:调用处理程序
InvocationHandler
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HIfM2E5R-1656760867327)(Spring.assets/7a0edbb5979e61f1d458e6efabd73623.png)]
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
Proxy
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lw8svDuY-1656760867328)(Spring.assets/eeef760d784e1ce6fd99d86f0fce3927.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TY9i8Imt-1656760867328)(Spring.assets/608928c2771e6b9a3f91cd8406763290.png)]
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
代码实现
抽象角色和真实角色和之前的一样!
Rent . java 即抽象角色
//抽象角色:租房
public interface Rent {
public void rent();
}
Host . java 即真实角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
ProxyInvocationHandler. java 即代理角色
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
// proxy : 代理类 method : 代理类的调用处理程序的方法对象.
// 处理代理实例上的方法调用并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//核心:本质利用反射实现!
Object result = method.invoke(rent, args);
fare();
return result;
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
Client . java
//租客
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理实例的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host); //将真实角色放置进去!
Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
proxy.rent();
}
}
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!
我们来使用动态代理实现代理我们后面写的UserService!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// proxy : 代理类
// method : 代理类的调用处理程序的方法对象.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String methodName){
System.out.println("执行了"+methodName+"方法");
}
}
测试!
public class Test {
public static void main(String[] args) {
//真实对象
UserServiceImpl userService = new UserServiceImpl();
//代理对象的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService); //设置要代理的对象
UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
proxy.delete();
}
}
【测试,增删改查,查看结果】
动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!
AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一 -种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKqZn5Je-1656760867329)(Spring.assets/f7a3e475cdac5b7b0bded98df0707ce5.png)]
提供声明式事务:允许用户自定义切面
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ….
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):切面通知 执行的 “地点”的定义。
连接点(JointPoint):与切入点匹配的执行点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YafK5bEo-1656760867329)(Spring.assets/fef216835fd2c9cd191321f09214fa59.png)]
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u0EJSm0b-1656760867330)(Spring.assets/08d2886d9116a7fbe88e07e212b979d1.png)]
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
重点:使用AOP织入,需要导入一个依赖包
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
<scope>runtimescope>
dependency>
方式一:使用Spring的API 接口【主要是接口实现】
首先编写我们的业务接口和实现类
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("更新用户");
}
@Override
public void search() {
System.out.println("查询用户");
}
}
然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}
最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.study.service.UserServiceImpl"/>
<bean id="log" class="com.study.log.Log"/>
<bean id="afterLog" class="com.study.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.study.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
// 动态代理代理代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .
Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .
方式二:自定义来实现AOP【主要是切面定义】
目标业务类不变依旧是userServiceImpl
第一步 : 写我们自己的一个切入类
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
去spring中配置
<bean id="diy" class="com.study.diy.DiyPointcut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="diyPointcut" expression="execution(* com.study.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="diyPointcut"/>
<aop:after method="after" pointcut-ref="diyPointcut"/>
aop:aspect>
aop:config>
测试:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
// 动态代理代理代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
方式三:使用注解实现
第一步:编写一个注解实现的增强类
// 方法三:使用注解方式实现AOP
@Aspect // 标注这个类是一个切面
public class AnnotationPointcut {
@Before("execution(* com.study.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.study.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.study.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前:");
// 获得签名
Signature signature = joinPoint.getSignature();
System.out.println("签名:"+signature);
// 执行方法
Object proceed = joinPoint.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
第二步:在Spring配置文件中,注册bean,并增加支持注解的配置
<bean id="annotationPointcut" class="com.study.diy.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
aop:aspectj-autoproxy:说明
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的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动态代理。
步骤:
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.26version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.11version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.11version>
dependency>
aop织入
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
配置Maven静态资源过滤问题!
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
编写配置文件
测试
public class User {
private int id;
private String name;
private String pwd;
}
public interface UserMapper {
public List<User> selectUser();
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user
select>
mapper>
编写配置文件
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.study.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true" />
<property name="username" value="root" />
<property name="password" value="root" />
dataSource>
environment>
environments>
<mappers>
<package name="com.study.mapper"/>
mappers>
configuration>
测试
@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();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user: userList){
System.out.println(user);
}
sqlSession.close();
}
引入Spring之前需要了解mybatis-spring包中的一些重要类;
http://www.mybatis.org/spring/zh/index.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I0KMkOxc-1656760867330)(Spring.assets/9b30f045d9bc9527b1c12c7c67f4cb2d.png)]
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
知识基础
在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
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。
SqlSessionFactory
有一个唯一的必要属性:用于 JDBC 的 DataSource
。这可以是任意的 DataSource
对象,它的配置方法和其它 Spring 数据库连接是一样的。
一个常用的属性是 configLocation
,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是
或
元素。
需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(
),数据源(
)和 MyBatis 的事务管理器(
)都会被忽略。SqlSessionFactoryBean
会创建它自有的 MyBatis 环境配置(Environment
),并按要求设置自定义环境的值
SqlSessionTemplate
是 MyBatis-Spring 的核心。作为 SqlSession
的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession
。
模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
可以使用 SqlSessionFactory
作为构造方法的参数来创建 SqlSessionTemplate
对象。
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
bean>
现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public User getUser(String userId) {
return sqlSession.getMapper...;
}
}
按下面这样,注入 SqlSessionTemplate
:
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession" />
bean>
<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">
<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&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true" />
<property name="username" value="root" />
<property name="password" value="root" />
bean>
<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/study/mapper/UserMapper.xml"/>
<property name="typeAliasesPackage" value="com.study.pojo"/>
bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
bean>
增加Dao接口的实现类;私有化sqlSessionTemplate
public class UserMapperImpl implements UserMapper{
//我们的所有操作,都使用sqlSession来执行, 在原来,现在都使用sqlSessionTemplate
private SqlSessionTemplate sqlSession;
public SqlSessionTemplate getSqlSession() {
return sqlSession;
}
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
bean id="userMapper" class="com.study.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
bean>
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
configuration>
整合实现二
mybatis-spring1.2.3版以上的才有这个 .
官方文档截图 :
dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ACmVoSJF-1656760867331)(Spring.assets/7f0718f55a70c9a641cc538bbecd2fb6.png)]
测试:
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
<bean id="userMapper2" class="com.study.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
bean>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9hzyCLCg-1656760867332)(Spring.assets/original.png)]
qq_43568728 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uJBrgCZa-1656760867332)(Spring.assets/newCurrentTime2.png)] 于 2021-11-14 13:07:31 发布 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D6huMUTN-1656760867332)(Spring.assets/articleReadEyes2.png)] 191 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wBCBl0mJ-1656760867333)(Spring.assets/tobarCollect2.png)] 收藏 3
文章标签: java spring
版权
官网: https://spring.io/projects/spring-framework#overview
官方下载地址: http://repo.spring.io/release/org/springframework/spring
GitHub: https://github.com/spring:projects/spring-framework
org.springframework
spring-webmvc
5.3.11
org.springframework
spring-jdbc
5.3.11
123456789101112
总结一-句话: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP) 的框架!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-393Ji5yi-1656760867333)(Spring.assets/27c714d4c6e88d1a6a5ea8a72ff3281a-165669008266922.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GLlmf1WY-1656760867333)(Spring.assets/5c2586a36ed1cfd9fc4eaefc0a78ce52-165669008266924.png)]
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
BeanFactory
,它是工厂模式的实现。BeanFactory
使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。在Spring的官网有这个介绍:现代化的Java开发!说白就是基于Spring的开发!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rmkCiRXG-1656760867334)(Spring.assets/a42b4e5df07e29b66de4d0d053ba9b73-165669008266926.png)]
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提 ,需要完全掌握Spring及
SpringMVC!承上启下的作用!
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
新建一个空白的maven项目
我们先用我们原来的方式写一段代码 .
public interface UserDao {
public void getUser();
}
123
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
123456
public interface UserService {
public void getUser();
}
123
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
1234567
@Test
public void test(){
UserService service = new UserServiceImpl();
service.getUser();
}
12345
这是我们原来的方式 , 开始大家也都是这么去写的对吧 . 那我们现在修改一下 .
把Userdao的实现类增加一个 .
public class UserDaoMySqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("MySql获取用户数据");
}
}
123456
紧接着我们要去使用MySql的话 , 我们就需要去service实现类里面修改对应的实现 .
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoMySqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
1234567
在假设, 我们再增加一个Userdao的实现类 .
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle获取用户数据");
}
}
123456
那么我们要使用Oracle , 又需要去service实现类里面修改对应的实现 . 假设我们的这种需求非常大 , 这种方式就根本不适用了, 甚至反人类对吧 , 每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身 .
那我们如何去解决呢 ?
我们可以在需要用到他的地方 , 不去实现它 , 而是留出一个接口 , 利用set , 我们去代码里修改下 .
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set实现
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
1234567891011
使用set接口实现
现在去我们的测试类里 , 进行测试 ;
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl();
service.setUserDao( new UserDaoImpl() );
service.getUser();
service.setUserDao( new UserDaoMySqlImpl() );
service.getUser();
//那我们现在又想用Oracle去实现呢
service.setUserDao( new UserDaoOracleImpl() );
service.getUser();
}
1234567891011
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMxQWd7Z-1636880511138)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114142617269.png)]
大家发现了区别没有 ? 可能很多人说没啥区别 . 但是同学们 , 他们已经发生了根本性的变化 , 很多地方都不一样了 . 仔细去思考一下 , 以前所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 . 程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口 .
这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !
控制反转loC(Inversion of Control),是-种设计思想,DI(依赖注入)是实现loC的一种方法,也有人认为DI只是
IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序
中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖
对象的方式反转了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kNi2XuGz-1656760867334)(Spring.assets/01826c23d18ee93aceaf23a3c73c07c7-165669008266928.png)]
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3kucbUVh-1656760867334)(Spring.assets/c73f2660c2455df0b11c1c601b01def6-165669008266930.png)]
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为- -体,
Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。 在Spring中实现控制反转
的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.11version>
dependency>
123456
编写一个Hello实体类
public class Hello {
private String string;
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
@Override
public String toString() {
return "Hello{" +
"string='" + string + '\'' +
'}';
}
}
123456789101112131415161718
编写我们的spring文件 , 这里我们命名为beans.xml
<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="hello" class="com.study.pojo.Hello">
<property name="string" value="Spring"/>
bean>
beans>
1234567891011
我们可以去进行测试了 .
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
12345678
测试结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKE4IYMv-1636880511142)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114143113904.png)]
这个过程就叫控制反转:
依赖注入:就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收.
可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
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("User:"+name);
}
}
12345678910111213141516171819
<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">
<bean id="user" class="com.kuang.pojo.User">
<property name="name" value="study"/>
bean>
beans>
123456789
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在执行getBean的时候, user已经创建好了 , 通过无参构造
User user = (User) context.getBean("user");
//调用对象的方法 .
user.show();
}
12345678
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3WAsXYu-1636880511144)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114143748966.png)]
结果可以发现,在调用show方法之前,User对象已经通过无参构造初始化了!
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;
}
public void show(){
System.out.println("User:"+name);
}
}
123456789101112131415161718192021222324
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg index="0" value="kuangshen2"/>
bean>
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg name="name" value="kuangshen2"/>
bean>
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg type="java.lang.String" value="kuangshen2"/>
bean>
1234567891011121314
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在执行getBean的时候, user已经创建好了 , 通过有参构造
User user = (User) context.getBean("user");
//调用对象的方法 .
user.show();
}
12345678
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RUjDdYEG-1636880511145)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114144000657.png)]
结论:在配置文件加载的时候。其中管理的对象都已经初始化了!
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
<bean id="user" class="com.study.pojo.User">
<constructor-arg type="java.lang.String" value="root"/>
<constructor-arg type="java.lang.String" value="123456"/>
bean>
<alias name="user" alias="userNew"/>
123456
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
bean>
1234567891011
这个import,一般用于团队开发,可以将多个配置文件,导入合并一个
假设,项目中有多个人开发,这三个人负责不同的人开发,不同的类需要注册在不同的bean中,我们可以利用import,将所有人的beans.xml合并为总的
使用的时候使用总的就可以了
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
123
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
【环境搭建】
Address
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 + '\'' +
'}';
}
}
123456789101112131415161718
Student
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 Properties info;
private String wife;
}
12345678910
beans.xml
<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">
<bean id="address" class="com.study.pojo.Address">
<property name="address" value="岳阳"/>
bean>
<bean id="student" class="com.study.pojo.Student">
<property name="name" value="小明"/>
bean>
beans>
12345678910111213
测试类
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getAddress());
}
}
1234567
完善注入信息
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.study.pojo.Address">
<property name="address" value="岳阳"/>
bean>
<bean id="student" class="com.study.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="address"/>
<property name="books">
<array>
<value>红楼梦value>
<value>西游记value>
<value>水浒传value>
<value>三国演义value>
array>
property>
<property name="hobbies">
<list>
<value>听音乐value>
<value>敲代码value>
<value>看电影value>
list>
property>
<property name="card">
<map>
<entry key="身份证" value="24322388967868789809"/>
<entry key="银行卡" value="21321412321321432"/>
map>
property>
<property name="games">
<set>
<value>LOLvalue>
<value>COCvalue>
<value>BOBvalue>
set>
property>
<property name="wife">
<null/>
property>
<property name="info">
<props>
<prop key="学号">201856546233prop>
<prop key="性别">男prop>
<prop key="姓名">小明prop>
props>
property>
bean>
beans>
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
使用p命名空间和c命名空间注入
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.study.pojo.User" p:name="小明" p:age="18"/>
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明"/>
beans>
123456789101112
测试:
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user2");
System.out.println(user);
System.out.println(user2);
}
12345678
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u0e74xWM-1636880511147)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114145739542.png)]
注意点:
c命名空间和p命名空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
12
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CsMcT1lF-1656760867335)(Spring.assets/3bfa22168eb292f9d7577938133d0eec-165669008266932.png)]
单例模式(Spring默认机制)(每次从容器中get的时候,都是一个对象)
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="user" class="com.study.pojo.User" p:name="小明" p:age="18" scope="singleton"/>
1
测试:
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user.toString() + "," + user.hashCode());
System.out.println(user2.toString() + "," + user2.hashCode());
System.out.println(user==user2);
}
123456789
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HwVv7vXW-1636880511149)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114150205945.png)]
原型模式(每次从容器中get的时候,都会产生新对象)
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" scope="prototype"/>
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" singleton="false"/>
123
测试:
@Test
public void test04(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = (User) context.getBean("user2");
User user2 = (User) context.getBean("user2");
System.out.println(user.toString() + "," + user.hashCode());
System.out.println(user2.toString() + "," + user2.hashCode());
System.out.println(user==user2);
}
123456789
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flyPTxGG-1636880511151)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114150554648.png)]
Request
当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring
ApplicationContext情形下有效。考虑下面bean定义:
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" scope="request"/>
1
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
Session
当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="user2" class="com.study.pojo.User" c:age="18" c:name="李明" scope="session"/>
1
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
Spring中bean有三种装配机制,分别是:
这里我们主要讲第三种:自动化的装配bean。
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
组件扫描和自动装配组合发挥巨大威力,使的显示的配置降低到最少。
推荐不使用自动装配xml配置 , 而使用注解 .
新建一个项目
新建两个实体类,Cat Dog 都有一个叫的方法
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
12345
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
12345
新建一个用户类 User
public class User {
private Cat cat;
private Dog dog;
private String name;
}
12345
编写Spring配置文件
<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">
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="people" class="com.study.pojo.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="张三"/>
bean>
beans>
12345678910111213
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
People people = context.getBean("people",People.class);
people.getCat().shout();
people.getDog().shout();
}
1234567
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FV65Jqzc-1636880511152)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114151955555.png)]
结果正常输出,环境OK
autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
测试:
修改bean配置,增加一个属性 autowire=”byName”
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="people" class="com.study.pojo.People" autowire="byName">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="张三"/>
bean>
1234567
再次测试,结果依旧成功输出!
我们将 cat 的bean id修改为 catXXX
再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。
小结:
当一个bean节点带有 autowire byName的属性时。
autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
NoUniqueBeanDefinitionException
1
测试:
将user的bean配置修改一下 : autowire="byType"
测试,正常输出
在注册一个cat 的bean对象!
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="cat2" class="com.study.pojo.Cat"/>
<bean id="people" class="com.study.pojo.People" autowire="byType">
<property name="name" value="张三"/>
bean>
123456
测试,报错:NoUniqueBeanDefinitionException
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRe7oeGI-1636880511154)(C:\Users\彭浩\AppData\Roaming\Typora\typora-user-images\image-20211114153253119.png)]
删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
这就是按照类型自动装配!
jdk1.5开始支持注解,spring2.5开始全面支持注解。
准备工作: 利用注解的方式注入属性。
在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
123
开启属性注解支持!
<context:annotation-config/>
1
测试:
将People类中的set方法去掉,使用@Autowired注解
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getStr() {
return str;
}
}
12345678910111213141516
此时配置文件内容
<context:annotation-config/>
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="dog" class="com.study.pojo.Dog"/>
<bean id="people" class="com.study.pojo.People"/>
1234
测试,成功输出结果!
@Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
123
测试实验步骤:
配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!
<bean id="cat1" class="com.study.pojo.Cat"/>
<bean id="dog1" class="com.study.pojo.Dog"/>
<bean id="cat2" class="com.study.pojo.Cat"/>
<bean id="dog2" class="com.study.pojo.Dog"/>
<bean id="people" class="com.study.pojo.People"/>
12345
没有加Qualifier测试,直接报错
在属性上添加Qualifier注解
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
123456
测试,成功输出!
实体类:
public class People {
@Resource
private Cat cat;
@Resource(name = "dog2")
private Dog dog;
private String name;
}
1234567
ApplicationContext.xml
<bean id="cat" class="com.study.pojo.Cat"/>
<bean id="dog1" class="com.study.pojo.Dog"/>
<bean id="dog2" class="com.study.pojo.Dog"/>
<bean id="people" class="com.study.pojo.People"/>
1234
测试:结果OK
@Autowired与@Resource异同:
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
在Spring中有三种自动装配的方式
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
在spring4之后,必须要保证aop的包导入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uleWtTRK-1656760867335)(Spring.assets/d4abe373a44a4420e3c23c274359c7e9-165669008266934.png)]
使用注解需要导入context约束, 增加注解的支持!
<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>
123456789
我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!
配置扫描哪些包下的注解
<context:component-scan base-package="com.study.pojo"/>
12
在指定包下编写类,增加注解
@Component("user")
// 相当于配置文件中
public class User {
public String name = "张三";
}
12345
测试
@Test
public void test(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("ApplicationContext.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
1234567
使用注解注入属性
可以不用提供set方法,直接在直接名上添加@value(“值”)
@Component("user")
// 相当于配置文件中
public class User {
@Value("张三")
// 相当于配置文件中
public String name;
}
1234567
如果提供了set方法,在set方法上添加@value(“值”);
@Component("user")
public class User {
public String name;
@Value("张三")
public void setName(String name) {
this.name = name;
}
}
12345678
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
写上这些注解,就相当于将这个类交给Spring管理装配了!
在Bean的自动装配已经讲过了,可以回顾!
@scope
@Controller("user")
@Scope("prototype")
public class User {
@Value("张三")
public String name;
}
123456
XML与注解比较
xml与注解整合开发 :推荐最佳实践
<context:annotation-config/>
1
作用:
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。
测试:
编写一个实体类,Dog
@Component //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
public String name = "dog";
}
1234
新建一个config配置包,编写一个MyConfig配置类
@Configuration //代表这是一个配置类
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public Dog dog(){
return new Dog();
}
}
1234567
测试
@Test
public void test2(){
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) applicationContext.getBean("dog");
System.out.println(dog.name);
}
1234567
成功输出结果!
导入其他配置如何做呢?
我们再编写一个配置类!
@Configuration //代表这是一个配置类
public class MyConfig2 {
/...
}
1234
在之前的配置类中我们来选择导入这个配置类
@Configuration
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
@Bean
public Dog dog(){
return new Dog();
}
}
12345678
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
为什么要学习代理模式?因为这就是SpringAOP的底层! 【SpringAOP 和SpringMVC】
代理模式的分类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ruEK9R04-1656760867336)(Spring.assets/4dd369665076f130ef2ff1f31612465d-165669008266936.png)]
角色分析:
1.Rent . java 即抽象角色
//抽象角色:租房
public interface Rent {
public void rent();
}
1234
2.Host . java 即真实角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
123456
3.Proxy . java 即代理角色
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
//租房
public void rent(){
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
12345678910111213141516171819202122
4.Client . java 即客户
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮助房东
Proxy proxy = new Proxy(host);
//你去找中介!
proxy.rent();
}
}
1234567891011
分析: 在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。
代理模式的好处: .
缺点:
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!
//抽象角色:增删改查业务
public interface UserService {
void add();
void delete();
void update();
void query();
}
1234567
我们需要一个真实对象来完成这些增删改查操作
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
123456789101112131415
需求来了,现在我们需要增加一个日志功能,怎么实现!
设置一个代理类来处理日志! 代理角色
//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
1234567891011121314151617181920212223242526
测试访问类:
public class Client {
public static void main(String[] args) {
//真实业务
UserServiceImpl userService = new UserServiceImpl();
//代理类
UserServiceProxy proxy = new UserServiceProxy();
//使用代理类实现日志功能!
proxy.setUserService(userService);
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
1234567891011121314
OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
【聊聊AOP:纵向开发,横向开发】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wl3vPYS5-1656760867336)(Spring.assets/7b69fa91d008ac85b28eac0116780dd4-165669008266938.png)]
需要了解两个类:Proxy:代理、InvocationHandler:调用处理程序
InvocationHandler
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XnGEHIwD-1656760867337)(Spring.assets/7a0edbb5979e61f1d458e6efabd73623-165669008266940.png)]
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
12345
Proxy
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ywokEeyS-1656760867337)(Spring.assets/eeef760d784e1ce6fd99d86f0fce3927-165669008266942.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E8KxG1Qy-1656760867337)(Spring.assets/608928c2771e6b9a3f91cd8406763290-165669008266944.png)]
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
12345
代码实现
抽象角色和真实角色和之前的一样!
Rent . java 即抽象角色
//抽象角色:租房
public interface Rent {
public void rent();
}
1234
Host . java 即真实角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
123456
ProxyInvocationHandler. java 即代理角色
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
// proxy : 代理类 method : 代理类的调用处理程序的方法对象.
// 处理代理实例上的方法调用并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//核心:本质利用反射实现!
Object result = method.invoke(rent, args);
fare();
return result;
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
1234567891011121314151617181920212223242526272829
Client . java
//租客
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理实例的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host); //将真实角色放置进去!
Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
proxy.rent();
}
}
123456789101112
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!
我们来使用动态代理实现代理我们后面写的UserService!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// proxy : 代理类
// method : 代理类的调用处理程序的方法对象.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String methodName){
System.out.println("执行了"+methodName+"方法");
}
}
123456789101112131415161718192021
测试!
public class Test {
public static void main(String[] args) {
//真实对象
UserServiceImpl userService = new UserServiceImpl();
//代理对象的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService); //设置要代理的对象
UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
proxy.delete();
}
}
1234567891011
【测试,增删改查,查看结果】
动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能
的统一维护的一 -种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一重要内容,是
函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合
度降低,提高程序的可重用性,同时提高了开发的效率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C6ORyuxc-1656760867338)(Spring.assets/f7a3e475cdac5b7b0bded98df0707ce5-165669008266946.png)]
提供声明式事务:允许用户自定义切面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XR8paAmw-1656760867338)(Spring.assets/fef216835fd2c9cd191321f09214fa59-165669008266948.png)]
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6XML5UpT-1656760867338)(Spring.assets/08d2886d9116a7fbe88e07e212b979d1-165669008266950.png)]
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
重点:使用AOP织入,需要导入一个依赖包
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
<scope>runtimescope>
dependency>
1234567
方式一:使用Spring的API 接口【主要是接口实现】
首先编写我们的业务接口和实现类
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
123456
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("查询用户");
}
}
123456789101112131415161718
然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
123456789
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);
}
}
123456789101112
最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.study.service.UserServiceImpl"/>
<bean id="log" class="com.study.log.Log"/>
<bean id="afterLog" class="com.study.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.study.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
1234567891011121314151617181920212223
测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
// 动态代理代理代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
12345678
Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .
Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .
方式二:自定义来实现AOP【主要是切面定义】
目标业务类不变依旧是userServiceImpl
第一步 : 写我们自己的一个切入类
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
12345678
去spring中配置
<bean id="diy" class="com.study.diy.DiyPointcut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="diyPointcut" expression="execution(* com.study.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="diyPointcut"/>
<aop:after method="after" pointcut-ref="diyPointcut"/>
aop:aspect>
aop:config>
1234567891011
测试:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
// 动态代理代理代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
12345678
方式三:使用注解实现
第一步:编写一个注解实现的增强类
// 方法三:使用注解方式实现AOP
@Aspect // 标注这个类是一个切面
public class AnnotationPointcut {
@Before("execution(* com.study.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.study.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.study.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前:");
// 获得签名
Signature signature = joinPoint.getSignature();
System.out.println("签名:"+signature);
// 执行方法
Object proceed = joinPoint.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
1234567891011121314151617181920212223
第二步:在Spring配置文件中,注册bean,并增加支持注解的配置
<bean id="annotationPointcut" class="com.study.diy.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
1234
aop:aspectj-autoproxy:说明
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的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动态代理。
12
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
步骤:
导入依赖
Junit
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
1234567
mybatis
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
123456
mysql数据库
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.26version>
dependency>
123456
spring相关
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.11version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.11version>
dependency>
123456789101112
aop织入
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
dependency>
123456
mybatis-spring【new】
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
123456
配置Maven静态资源过滤问题!
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
123456789101112
编写配置文件
测试
编写实体类
public class User {
private int id;
private String name;
private String pwd;
}
12345
编写Mapper接口
public interface UserMapper {
public List<User> selectUser();
}
123
编写Mapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user
select>
mapper>
123456789
编写配置文件
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.study.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true" />
<property name="username" value="root" />
<property name="password" value="root" />
dataSource>
environment>
environments>
<mappers>
<package name="com.study.mapper"/>
mappers>
configuration>
1234567891011121314151617181920212223
测试
@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();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user: userList){
System.out.println(user);
}
sqlSession.close();
}
12345678910111213
引入Spring之前需要了解mybatis-spring包中的一些重要类;
http://www.mybatis.org/spring/zh/index.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mheSXoac-1656760867339)(Spring.assets/9b30f045d9bc9527b1c12c7c67f4cb2d-165669008266952.png)]
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
知识基础
在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
123456
要和 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>
123
注意:SqlSessionFactory
需要一个 DataSource
(数据源)。 这可以是任意的 DataSource
,只需要和配置其它 Spring 数据库连接一样配置它就可以了。
在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder
来创建 SqlSessionFactory
的。 而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean
来创建。
在 MyBatis 中,你可以使用 SqlSessionFactory
来创建 SqlSession
。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。
SqlSessionFactory
有一个唯一的必要属性:用于 JDBC 的 DataSource
。这可以是任意的 DataSource
对象,它的配置方法和其它 Spring 数据库连接是一样的。
一个常用的属性是 configLocation
,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是
或
元素。
需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(
),数据源(
)和 MyBatis 的事务管理器(
)都会被忽略。SqlSessionFactoryBean
会创建它自有的 MyBatis 环境配置(Environment
),并按要求设置自定义环境的值。
SqlSessionTemplate
是 MyBatis-Spring 的核心。作为 SqlSession
的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession
。
模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate
来替换 MyBatis 默认的 DefaultSqlSession
实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
可以使用 SqlSessionFactory
作为构造方法的参数来创建 SqlSessionTemplate
对象。
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
bean>
123
现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public User getUser(String userId) {
return sqlSession.getMapper...;
}
}
123456789
按下面这样,注入 SqlSessionTemplate
:
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession" />
bean>
123
引入Spring配置文件beans.xml
<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">
12345
配置数据源替换mybaits的数据源
<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&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true" />
<property name="username" value="root" />
<property name="password" value="root" />
bean>
12345678
配置SqlSessionFactory,关联MyBatis
<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/study/mapper/UserMapper.xml"/>
<property name="typeAliasesPackage" value="com.study.pojo"/>
bean>
1234567891011
注册sqlSessionTemplate,关联sqlSessionFactory;
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
bean>
12345
增加Dao接口的实现类;私有化sqlSessionTemplate
public class UserMapperImpl implements UserMapper{
//我们的所有操作,都使用sqlSession来执行, 在原来,现在都使用sqlSessionTemplate
private SqlSessionTemplate sqlSession;
public SqlSessionTemplate getSqlSession() {
return sqlSession;
}
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
123456789101112131415161718
注册bean实现
<bean id="userMapper" class="com.study.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
bean>
123
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
12345678
结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
configuration>
12345678910111213
mybatis-spring1.2.3版以上的才有这个 .
官方文档截图 :
dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KED0j2xw-1656760867339)(Spring.assets/7f0718f55a70c9a641cc538bbecd2fb6-165669008266954.png)]
测试:
将我们上面写的UserDaoImpl修改一下
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
123456
修改bean的配置
<bean id="userMapper2" class="com.study.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
bean>
123
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper2.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
总结 : 整合到spring中以后可以完全不要mybatis的配置文件,除了这些方式可以实现整合之外,我们还可以使用注解来实现,这个等我们后面学习SpringBoot的时候还会测试整合!
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
事务四个属性ACID
原子性(atomicity)
一致性(consistency)
将上面的代码拷贝到一个新项目中
在之前的案例中,我们给userDao接口新增两个方法,删除和增加用户;
//添加一个用户
int addUser(User user);
//根据id删除用户
int deleteUser(int id);
mapper文件,我们故意把 deletes 写错,测试!
<insert id="addUser" parameterType="user">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
insert>
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id};
delete>
编写接口的实现类,在实现类中,我们去操作一波
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
User user = new User(7, "小王", "ewrewr");
addUser(user);
deleteUser(7);
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapper mapper = (UserMapper) context.getBean("UserMapper");
List<User> user = mapper.selectUser();
System.out.println(user);
}
报错:sql异常,delete写错了
结果 :插入成功!
没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!
以前我们都需要自己手动管理事务,十分麻烦!
但是Spring给我们提供了事务管理,我们只需要配置即可;
pring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。
编程式事务管理
声明式事务管理
使用Spring管理事务,注意头文件的约束导入 : tx
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
事务管理器
JDBC事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
配置好事务管理器后我们需要去配置事务的通知
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。
就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!
配置AOP
导入aop的头文件!
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.study.mapper.*Impl.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
aop:config>
进行测试
删掉刚才插入的数据,再次测试!
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapper mapper = (UserMapper) context.getBean("UserMapper");
List<User> user = mapper.selectUser();
System.out.println(user);
}
为什么需要配置事务?