Spring简介 Spring的英文翻译为春天,可以说是给Java程序员带来了春天,因为它极大的简化了开发。我得出一个公式:Spring = 春天 = Java程序员的春天 = 简化开发。最后的简化开发正是Spring框架带来的最大好处。 Spring是一个开放源代码的设计层面框架,它是于2003 年兴起的一个轻量级的Java 开发框架。由Rod Johnson创建,其前身为Interface21框架,后改为了Spring并且正式发布。Spring是为了解决企业应用开发的复杂性而创建的。它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。Spring 的理念:不去重新发明轮子。其核心是控制反转(IOC) 和面向切面(AOP)。
IOC本质 Spring提供的容器又称为IoC容器,什么是IoC? IoC全称Inversion of Control,直译为控制反转。那么何谓IoC?在理解IoC之前,我们先看看通常的Java组件是如何协作的。这里我们以JDBC中调用多种数据库为例子
1 2 3 4 5 6 package com.boogipop.dao; public interface UserDao { void getUser () ; }
1 2 3 4 5 6 7 8 9 10 package com.boogipop.dao;public class UserDaomysqlimp implements UserDao { @Override public void getUser () { System.out.println("mysql:getuser" ); } }
1 2 3 4 5 6 7 8 9 package com.boogipop.dao;public class UserDaooracleimp implements UserDao { @Override public void getUser () { System.out.println("oracle:getuser" ); } }
1 2 3 4 5 package com.boogipop.service;public interface Userservice { void getUser () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.boogipop.service;import com.boogipop.dao.UserDao;import com.boogipop.dao.UserDaomysqlimp;public class Userserviceimp implements Userservice { private UserDao userDao=new UserDaomysqlimp (); @Override public void getUser () { userDao.getUser(); } }
1 2 3 4 5 6 7 8 9 10 import com.boogipop.service.Userservice;import com.boogipop.service.Userserviceimp;public class Execute { public static void main (String[] args) { Userserviceimp userservice=new Userserviceimp (); userservice.getUser(); } }
结果不出意外的话就是调用mysql的sout了: 这是常规的JDBC调用方法,取决权在服务端,因为userDao属性是由后台设置的,IOC是什么呢,就是把控制权反转,也就是给到客户端,我们修改一下Userserviceimp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.boogipop.service;import com.boogipop.dao.UserDao;import com.boogipop.dao.UserDaomysqlimp;public class Userserviceimp implements Userservice { private UserDao userDao; public void setUserDao (UserDao userDao) { this .userDao = userDao; } @Override public void getUser () { userDao.getUser(); } }
这次我们可以控制userDao属性来得到想要的输出结果,在这两个例子之间发生了革命性的变化,从服务端主导控制权反转到了客户端set userDao,也就是客户端主导权 这就是控制反转IOC 的意思
HelloSpring 上面已经讲了IOC的理论知识,这里就来演示如何用Spring做到IOC,首先让我们先导入pom依赖(有坑)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > org.example</groupId > <artifactId > Spring</artifactId > <version > 1.0-SNAPSHOT</version > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > 6.0.4</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > <version > 6.0.4</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 6.0.4</version > </dependency > </dependencies > </project >
注意我这里的依赖导的是6.0x版本,这个版本的spring框架要求java在17以上,因此我们要注意一下java的版本,否则会出现版本对应错误的报错。 导入依赖后就创建我们的beans的xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="mysql" class ="com.boogipop.dao.UserDaomysqlimp" /> <bean id ="oracle" class ="com.boogipop.dao.UserDaooracleimp" /> <bean id ="Userserviceimp" class ="com.boogipop.service.Userserviceimp" > <property name ="userDao" ref ="mysql" /> </bean > </beans >
首先什么是JavaBean
呢?在java中假如一个类符合如下规范,例如:
1 2 3 4 5 6 7 8 9 10 public class Person { private String name; private int age; public String getName () { return this .name; } public void setName (String name) { this .name = name; } public int getAge () { return this .age; } public void setAge (int age) { this .age = age; } }
如果set和get都满足上述实例,那么这样的类就被我们称为JavaBean,我们的beans.xml中的Userserviceimp
就满足了这一点,定义好了beans文件就可以通过它来创建我们想要的对象了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import com.boogipop.service.Userserviceimp;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Execute { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext ("beans.xml" ); Userserviceimp userserviceimp = (Userserviceimp) context.getBean("Userserviceimp" ); userserviceimp.getUser(); } }
在这里也是和上述JDBC发生了一样的变化,现在控制权反转到了客户手上,用户只需要输入配置文件,我们就可以根据配置文件来提供对应的服务,这就是Spring实现IOC
IOC创建对象方式 上述例子我们bean类都是用默认的无参构造实例化的,而面对有参构造该如何去实现呢?
方式一(通过类型,不推荐) 1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.boogipop.pojo.User" > <constructor-arg type ="java.lang.String" value ="Boogipop" /> </bean > </beans >
1 2 3 4 5 6 7 8 9 10 11 12 import com.boogipop.pojo.User;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Execute { public static void main (String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext ("beans.xml" ); User user = (User) context.getBean("user" ); System.out.println(user); } }
行云流水一气呵成,这种方法有瑕疵,就是万一2个形参都是一样的类型就没办法了
方式二(通过索引) 1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.boogipop.pojo.User" > <constructor-arg index ="0" value ="Boogipop" /> </bean > </beans >
通过索引来指定参数,0就代表第一个
方式三(通过形参名称) 1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.boogipop.pojo.User" > <constructor-arg name ="name" value ="Boogipop" /> </bean > </beans >
三种方法任你选择,另外的还有个事情就是Spring是什么时候就帮我们创建好了对象呢? 答案是在beans.xml中写入时就已经实例化了,我们可以添加2个pojo类,写上无参构造方法:
1 2 3 4 5 6 7 8 package com.boogipop.pojo;public class Admin { public Admin () { System.out.println("i am admin" ); } }
1 2 3 4 5 6 7 8 package com.boogipop.pojo;public class BlackBan { public BlackBan () { System.out.println("i am blacklist" ); } }
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.boogipop.pojo.User" > <constructor-arg name ="name" value ="Boogipop" /> </bean > <bean id ="black" class ="com.boogipop.pojo.BlackBan" /> <bean id ="admin" class ="com.boogipop.pojo.Admin" /> </beans >
最后执行的时候你会发现另外2个虽然我们没用getbean去取出来,但是也实例化了,这就说明spring在读取beans.xml的时候就实例化了所有的类
Spring配置 别名 首先别名分为name
、alias
两种,不过说实话别名一般不用,因为bean的id实际上也就是一种别名啊,因此只有在团队项目中才会用上别名
1 2 3 4 <bean id ="admin" class ="com.boogipop.pojo.Admin" name ="u1 u2,u3;u4;u5" / <!-- 方式二-- > <bean id ="admin" class ="com.boogipop.pojo.Admin" /> <alias name ="admin" alias ="ADMIN0" />
import import也就是上面说的团队项目中会用到的,在test类中导入xml配置文件时也说了,我们可以导入多个beans配置文件,但是为了方便,一般就导入一个 在这一个beans配置文件中也可以导入其他配置文件,用的就是import标签<import resource="test.xml"/>
DI依赖注入(Dependency Injection) 环境搭建 DI就是依赖注入的意思,前面讲过用构造器去注入(创建对象),同样的还有其他两种方式,接下来介绍的就是这两种,先搭建一个复杂环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.boogipop.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> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.boogipop.pojo;public class Address { private String address; public void setAddress (String address) { this .address = address; } public String getAddress () { return address; } }
Set注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="address" class ="com.boogipop.pojo.Address" > <property name ="address" value ="Chengdu" /> </bean > <bean id ="student" class ="com.boogipop.pojo.Student" > <property name ="address" ref ="address" /> <property name ="books" > <array > <value > Internet</value > <value > Life</value > </array > </property > <property name ="card" > <map > <entry key ="ID" value ="20213000278" > </entry > <entry key ="name" value ="Boogipop" > </entry > </map > </property > <property name ="games" > <set > <value > LOL</value > <value > Arknights</value > </set > </property > <property name ="hobbys" > <list > <value > sleep</value > <value > die</value > </list > </property > <property name ="info" > <props > <prop key ="name" > Boogipop</prop > <prop key ="age" > 19</prop > </props > </property > <property name ="name" value ="Boogipop" /> <property name ="wife" > <null > </null > </property > </bean > </beans >
注意一下设置为null有两种方法,然后我们测试运行一下:
1 2 3 4 5 6 7 8 9 10 11 import com.boogipop.pojo.Student;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Exec { public static void main (String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("beans.xml" ); Student student = context.getBean("student" , Student.class); System.out.println(student); } }
在这里可以发现getbean时可以多加一个参数来指定类型,这样我们就不需要强制转换了Student{name='Boogipop', address=Address{address='Chengdu'}, books=[Internet, Life], hobbys=[sleep, die], card={ID=20213000278, name=Boogipop}, games=[LOL, Arknights], wife='null', info={name=Boogipop, age=19}}
最后结果也是成功输出
c命名空间、p命名空间注入 在这里c对应的是constructor
、p对应的是properties
在使用这两个命名空间前需要先声明:
1 2 xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
将这两条语句假如到开始的xml属性中,声明p和c,然后再创建个普通的user类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.boogipop.pojo;public class User { private String name; private int age; @Override public String toString () { return "User{" + "name='" + name + '\'' + ", age=" + age + '}' ; } public String getName () { return name; } public void setName (String name) { this .name = name; } }
首先先演示一下p命名空间:
1 <bean id ="user" class ="com.boogipop.pojo.User" p:name ="Boogipop" p:age ="19" />
c命名空间也是一个道理,只不过c命名空间有个前提就是需要对应的构造器,因为本质是通过constructor嘛
1 <bean id ="user" class ="com.boogipop.pojo.User" c:name ="Boogipop" c:age ="19" />
Bean的作用域 Bean的作用域有如下几种
Scope
Description
singleton
(默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
prototype
将单个 bean 定义的作用域限定为任意数量的对象实例。
request
将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。
session
将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
application
将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
websocket
将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
重点区分一下singleton
和prototype
两种,前者就是我们mybatis学的单例模式,所谓单例模式就是在beans中创建的对象只有一个因此假如你getbean获取2次,两次获取的bean都是一模一样的(hashcode相同),而使用prototype获取的每次都是不同的 剩下几种作用域的解释如下:
(1)当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="loginAction" class=com.foo.LoginAction" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
(2)当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
(3)当一个bean的作用域为Global Session,表示在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="user" class="com.foo.Preferences "scope="globalSession"/>
global session作用域类似于标准的HTTP Session作用域,不过仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。
自动装配Bean 原始自动装配 上面的例子中我们都是使用标签对属性进行手动装配 的,而spring可以实现自动装配 ,自动装配也分为注解和原始2种,这里先讲原始的 原始自动装配可以根据byName
和byType
来进行装配,首先是byName:
byName 定义一个people接口,来3个实现类:
1 2 3 4 5 6 package com.boogipop.dao;public interface PeoPle { void say () ; }
1 2 3 4 5 6 7 8 9 package com.boogipop.dao;public class ChinesePeople implements PeoPle { @Override public void say () { System.out.println("i am chinese people" ); } }
1 2 3 4 5 6 7 8 9 package com.boogipop.dao;public class AmericanPeople implements PeoPle { @Override public void say () { System.out.println("i am americanpeople" ); } }
1 2 3 4 5 6 7 8 9 package com.boogipop.dao;public class JapanesePeople implements PeoPle { @Override public void say () { System.out.println("i live in Tokyo" ); } }
接下来我们再定义一个测试POJO类,也就是我们需要自动装配的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.boogipop.pojo;import com.boogipop.dao.PeoPle;public class Person { private PeoPle peoPle; public void say () { peoPle.say(); } public PeoPle getPeoPle () { return peoPle; } public void setPeoPle (PeoPle peoPle) { this .peoPle = peoPle; } }
最后就是编写beans了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.dao" /> <bean class ="com.boogipop.dao.AmericanPeople" id ="americanPeople" /> <bean class ="com.boogipop.dao.ChinesePeople" id ="people" /> <bean class ="com.boogipop.dao.JapanesePeople" id ="japanesePeople" /> <bean class ="com.boogipop.pojo.Person" id ="person" autowire ="byName" /> </beans >
1 2 3 4 5 6 7 8 9 10 11 12 import com.boogipop.pojo.Person;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test { public static void main (String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("beans.xml" ); Person person = context.getBean("person" , Person.class); person.say(); } }
在3种people中我让Chinesepeople的id为people
,自动填充的方式是byName,因此被识别了,byName识别name是根据set方法后面的名字,如这里是setPeople
,那么他会去找bean中有没有叫people
的id
byType 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.dao" /> <bean class ="com.boogipop.dao.AmericanPeople" id ="americanPeople" /> <bean class ="com.boogipop.pojo.Person" id ="person" autowire ="byType" /> </beans >
这里我将其他2个people注释掉了,由于people属性是People类型的,因此根据byType会自动获取到bean中的AmericanPeople,假如取消掉另外2个people的注释,那么会报错,因为三个属性都一样,这也是一个弊端
注解自动装配 在Spring中有着和mybatis中一样智能的注解,注解会更加方便,但是注解和原始自动装配中各有优缺点,注解开发中方便,但是维护起来就是火葬场了,原始方法开发可能麻烦点,但是可读性就大大提高了
AutoWire注解 autowire注解就是实现自动装配用的,用法如下,我们把beans.xml中Person的autowire取消,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.dao" /> <bean class ="com.boogipop.dao.AmericanPeople" id ="americanPeople" /> <bean class ="com.boogipop.pojo.Person" id ="person" /> </beans >
在set方法或者属性上加上一个AutoWire注解:
1 2 3 4 5 6 7 @Autowired public void setPeople (People people) { this .people = people; } @Autowired private People people;
同样可以运行成功,在这里autowire注解是根据Type去识别的,autowire可以识别name也可以识别type,它的顺序是优先为byType,若有重复的类型则根据byName 我们在beans种多放出来一个bean:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.dao" /> <bean class ="com.boogipop.dao.AmericanPeople" id ="americanPeople" /> <bean class ="com.boogipop.dao.ChinesePeople" id ="people" /> <bean class ="com.boogipop.pojo.Person" id ="person" /> </beans >
这时候就是根据id来识别了,也就是people,无疑调用的是chinese: 然后autowire注解里有个属性: 默认为true也就是该属性默认不为空,改为false就是可以为空
Resource注解 resource注解的用法和Autowire其实一模一样,只不过有一些微小的区别:
1 2 3 4 @Resource public void setPeople (People people) { this .people = people; }
resource是jdk原生自带的,优先级为先byName再byType,但由于我这里是JDK19,无法使用,因此就不介绍了,高版本中使用AutoWire足以,Resource稍作了解吧
AutoWire+Qualifier使用 当beans中有很多重复种类,并且id又对应不上的情况,那么就得使用Qualifier注解去指定bean的ID了,我们把beans配置改为如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.pojo" /> <bean class ="com.boogipop.dao.AmericanPeople" id ="americanPeople" /> <bean class ="com.boogipop.dao.ChinesePeople" id ="chinesePeople" /> <bean class ="com.boogipop.dao.JapanesePeople" id ="japanesePeople" /> <bean class ="com.boogipop.pojo.Person" id ="person" /> </beans >
默认是报错的,因为id和type都对应不上,这时候就要指定id:
1 2 3 @Autowired @Qualifier(value = "chinesePeople") private People people;
发射成功
Spring注解开发 在上边儿学了一些自动装配的注解,装配可以实现自动化注解,那么xml配置文件可不可以用注解来实现呢?答案是可以的
Component注解 首先来讲讲component是个什么东西,英文翻译过来叫做组件,也就是beans的组件,那么就是bean了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.boogipop.pojo;import com.boogipop.dao.People;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;@Component public class Person { @Autowired @Qualifier(value = "chinesePeople") private People people; public void say () { people.say(); } public People getPeople () { return people; } public void setPeople (People people) { this .people = people; } }
在上述的pojo类中,给类定义了component后就意味着不需要再xml配置文件中再写bean了,component注解等效于<bean class="com.boogipop.pojo.Person" id="person"/>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.pojo" /> <bean class ="com.boogipop.dao.AmericanPeople" id ="americanPeople" /> <bean class ="com.boogipop.dao.ChinesePeople" id ="chinesePeople" /> <bean class ="com.boogipop.dao.JapanesePeople" id ="japanesePeople" /> </beans >
也就是现在bean标签就可以用注解替代,那么怎么实现值的简单初始化呢?
1 2 @Value("Boogipop") private String name;
使用value标签即可进行简单的初始化,另外与component等效的还有3种注解,另外三种作用一模一样,但是作用场景不同,是用于区分的:
@Controller 控制器(注入服务) 用于标注控制层,相当于struts中的action层
@Service 服务(注入dao) 用于标注服务层,主要用来进行业务的逻辑处理
@Repository(实现dao访问) 用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件
@Component (把普通pojo实例化到spring容器中,相当于配置文件中的 )
另外component也是可以自定义id的,component("")
就可以自定义id的名称
Scope注解 另外还有个@Scope
注解,写在类上,可以指定作用域,就是上述提到的
配置文件注解实现 上述操作把bean标签省略掉了,那我们能不能更进一步把XML配置文件直接给略了,答案也是可以的
Configuration标签和Bean标签 Configuration标签意思就是<beans>....</beans>
大标签,也就是说现在可以定义一个Config类来实现配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.boogipop.config;import com.boogipop.dao.AmericanPeople;import com.boogipop.dao.ChinesePeople;import com.boogipop.dao.JapanesePeople;import com.boogipop.dao.People;import com.boogipop.pojo.Person;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class Config { @Bean public Person getPerson () { return new Person (); } @Bean public People chinesepeople () { return new ChinesePeople (); } @Bean public People americanpeople () { return new AmericanPeople (); } @Bean public People japanesepeople () { return new JapanesePeople (); } }
@Configuration在这里就是beans标签,下面的Bean注解代表着bean标签,和component的作用是几乎一样的(有些小区别后面会讲)然后这里bean可以通过bean("")
指定别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.boogipop.pojo;import com.boogipop.dao.People;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Component public class Person { @Override public String toString () { return "Person{" + "name='" + name + '\'' + ", people=" + people + '}' ; } @Value("Boogipop") private String name; @Autowired @Qualifier(value = "chinesepeople") private People people; public void say () { people.say(); } public People getPeople () { return people; } public void setPeople (People people) { this .people = people; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import com.boogipop.config.Config;import com.boogipop.pojo.Person;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (Config.class); Person person = context.getBean("person" , Person.class); person.say(); System.out.println(person); } }
这里就是根据类来获取配置 其他注解也是一样去使用的:
ComponentScan标签 也就是xml配置文件中的 <context:component-scan base-package="com.boogipop.pojo"/>
:
Import标签 也就是导入其他配置类
component和bean的异同处 很多人可能注意到了上面bean和component的作用岂不是重复了?在这可以说确实是重复了,这两者也是有区别的:https://www.cnblogs.com/konglxblog/p/16441424.html
首先bean注解放在方法上,component放在类上
其次bean创建的对象都一样,component创建的不同,也就是一个是singleton模式一个是prototype模式
另外就是component需要ComponentScan去扫描,否则不会被识别出来的,ComponentScan和component需要配合使用
静态代理模式 在分析java反序列化的时候介绍了静态代理,这个就自己去看吧
动态代理模式 和之前讲的一样https://juejin.cn/post/6844903978342301709#heading-11
动态代理和静态代理的区别 我们先来复习一下class文件的加载,我们编写的.Java文件经过javac编译之后,会产生.class文件,这种.class文件是二进制文件,里面的内容是只有JVM能够识别,在代码运行之前 ,JVM会读取.class文件,解析.class文件内的信息,取出二进制数据,加载进内存中,从而生成对应的Class对象。 而静态代理和动态代理最主要的区别就是:静态代理在我们的代码运行之前 ,代理类的.class文件就已经存在,例如上述的RoomAgency.java,在经过javac编译之后,就会变成RoomAgency.class;而动态代理则与静态代理相反,在代码运行之前不存在代理类的.class文件,在代码运行时 才动态的生成代理类。
AOP实现方式一 AOP就是面向切面编程,也就是和上述动态代理大致的意思是一样的,Here we go,这里说一下我对AOP的看法吧,其实我一直很不理解为啥官网喜欢把一些简单的东西搞得不像人话一样,我觉得初学者应该都有这种感觉,不管你理解能力多强,你不理解就是不理解他说的话,但其实AOP就是一个很单纯的一个切入作用,就是你定义好了一个类,在这个类的切入点
切一刀,然后在这个点前后加一些新的东西
,比如鉴权或者是清空缓存,就是这么一个很简单的道理。。。 首先是环境搭建,我们就写一个简易版的JDBC事务:
1 2 3 4 5 6 7 8 9 package com.boogipop.service;public interface CURD { void add () ; void delete () ; void update () ; void select () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.boogipop.service;public class SimpleImpl implements CURD { @Override public void add () { System.out.println("添加了数据" ); } @Override public void delete () { System.out.println("删除了数据" ); } @Override public void update () { System.out.println("更新了数据" ); } @Override public void select () { System.out.println("查询数据" ); } }
我们接下来让每个增删改查前和增删改查后分别输出Before
和After
(不改变接口和实现类的代码):
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.boogipop.cutface;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class Before implements MethodBeforeAdvice { @Override public void before (Method method, Object[] args, Object target) throws Throwable { System.out.println("Before" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.boogipop.cutface;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class After implements AfterReturningAdvice { @Override public void afterReturning (Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("After" ); } }
这两个就是我们要插入的内容,接下来回到之前说的beans文件中,我们需要在xml中注册:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:p ="http://www.springframework.org/schema/p" 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/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd" > <bean id ="after" class ="com.boogipop.cutface.After" /> <bean id ="before" class ="com.boogipop.cutface.Before" /> <bean id ="simple" class ="com.boogipop.service.SimpleImpl" /> <aop:config > <aop:pointcut id ="point" expression ="execution(* com.boogipop.service.SimpleImpl.*(..))" /> <aop:advisor advice-ref ="before" pointcut-ref ="point" /> <aop:advisor advice-ref ="after" pointcut-ref ="point" /> </aop:config > </beans >
配置中有个expresssion,有关excution表达式:https://blog.csdn.net/ABCD898989/article/details/50809321 https://blog.csdn.net/loongshawn/article/details/72303040 其实就是和通配符一样 实现效果如下,成功在方法执行前加上了before和after
AOP实现方式二 上述是通过继承了spring的切面类实现的,我们也可以自定义我们的切面类
1 2 3 4 5 6 7 8 9 10 11 package com.boogipop.cutface;public class Selfcut { public void after () { System.out.println("self after" ); } public void before () { System.out.println("self before" ); } }
我们可以把这个类当做切面切入进去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:p ="http://www.springframework.org/schema/p" 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/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd" > <bean id ="after" class ="com.boogipop.cutface.After" /> <bean id ="before" class ="com.boogipop.cutface.Before" /> <bean id ="simple" class ="com.boogipop.service.SimpleImpl" /> <bean id ="selfcut" class ="com.boogipop.cutface.Selfcut" /> <aop:config > <aop:aspect ref ="selfcut" > <aop:pointcut id ="point" expression ="execution(* com.boogipop.service.SimpleImpl.*(..))" /> <aop:after method ="after" pointcut-ref ="point" /> <aop:before method ="before" pointcut-ref ="point" /> </aop:aspect > </aop:config > </beans >
同样可以实现相同的功能
参考文献:
Spring中使用AOP的两种方式(基于注解和XML配置)_Luck_ZZ的博客-CSDN博客_基于xml配置的aop需要开启<aop:aspectj-autoproxy />吗
注解实现AOP 记得导入织入的包
1 2 3 4 5 <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9 .19 </version> </dependency>
AOP同样可以完全使用注解完成,在这里就全部使用注解包括beans的配置文件,我也一并用注解代替,但是首先先介绍一下单纯用注解实现AOP而不省去配置文件的做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:p ="http://www.springframework.org/schema/p" 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/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd" > <bean id ="simple" class ="com.boogipop.service.SimpleImpl" /> <bean id ="selfcut" class ="com.boogipop.cutface.Selfcut" /> <aop:aspectj-autoproxy /> </beans >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.boogipop.cutface;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Before;@Aspect public class Selfcut { @Before("execution(* com.boogipop.service.SimpleImpl.*(..))") public void before () { System.out.println("===self before===" ); } @After("execution(* com.boogipop.service.SimpleImpl.*(..))") public void after () { System.out.println("===self after===" ); } @Around("execution(* com.boogipop.service.SimpleImpl.*(..))") public void around (ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前" ); Object proceed = jp.proceed(); System.out.println("环绕后" ); System.out.println(jp.getSignature()); } @AfterReturning("execution(* com.boogipop.service.SimpleImpl.*(..))") public void afterreturn () { System.out.println("after return" ); } }
顺序为环绕前->执行前->方法执行->返回后通知->执行后->环绕执行后(这里是spring6的顺序,spring5貌似不一样,不过问题不大),这就是注解和xml结合的使用,下面我将尝试全注解实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.boogipop.config;import com.boogipop.cutface.Selfcut;import com.boogipop.service.CURD;import com.boogipop.service.SimpleImpl;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration @ComponentScan("com.boogipop") @EnableAspectJAutoProxy public class Config { @Bean public Selfcut selfcut () { return new Selfcut (); } @Bean public CURD curd () { return new SimpleImpl (); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.boogipop.cutface;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Before;@Aspect public class Selfcut { @Before("execution(* com.boogipop.service.SimpleImpl.*(..))") public void before () { System.out.println("===self before===" ); } @After("execution(* com.boogipop.service.SimpleImpl.*(..))") public void after () { System.out.println("===self after===" ); } @Around("execution(* com.boogipop.service.SimpleImpl.*(..))") public void around (ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前" ); Object proceed = jp.proceed(); System.out.println("环绕后" ); System.out.println(jp.getSignature()); } @AfterReturning("execution(* com.boogipop.service.SimpleImpl.*(..))") public void afterreturn () { System.out.println("after return" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 import com.boogipop.config.Config;import com.boogipop.service.CURD;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Testproxy { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (Config.class); CURD curd = context.getBean("curd" , CURD.class); curd.select(); } }
结果也是一样的,注解真方便
整合Mybatis 环境搭建 先引入一下所需的依赖包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.30</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 6.0.4</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > <version > 3.0.1</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 6.0.4</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.2</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.19</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <properties resource ="db.properties" > <property name ="useless" value ="useless" /> </properties > <settings > <setting name ="logImpl" value ="STDOUT_LOGGING" /> </settings > <typeAliases > <package name ="com.boogipop.pojo" /> </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="${driver}" > </property > <property name ="url" value ="${url}" > </property > <property name ="username" value ="${username}" > </property > <property name ="password" value ="${password}" > </property > </dataSource > </environment > </environments > <mappers > <mapper resource ="UserMapper.xml" /> </mappers > </configuration >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.boogipop.mapper.UserMapper" > <select id ="getuserlist" resultType ="User" > select * from user </select > <select id ="getuserbyname" resultType ="User" parameterType ="String" > select * from user where name=#{name} </select > <insert id ="adduser" parameterType ="User" > insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd}); </insert > <delete id ="deluser" parameterType ="int" > delete from user where id=#{id} </delete > <update id ="cguser" parameterType ="User" > update user set name=#{name},pwd=#{pwd} where id=#{id} </update > <insert id ="mapadduser" parameterType ="Map" > insert into user(id,name) values(#{id},#{name}) </insert > <select id ="getuserbylike" parameterType ="Map" resultType ="User" > select * from user where name like concat("%",#{name},"%") </select > </mapper >
1 2 3 4 driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&seUnicode=true&characterEncoding=UTF-8 username=root password=jiayou357753
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.boogipop.pojo;import org.apache.ibatis.type.Alias;@Alias("User") public class User { private int id; private String name; private String pwd; @Override public String toString () { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}' ; } public void setId (int id) { this .id = id; } public void setName (String name) { this .name = name; } public void setPwd (String pwd) { this .pwd = pwd; } public int getId () { return id; } public String getName () { return name; } public String getPwd () { return pwd; } public User (int id, String name, String pwd) { this .id = id; this .name = name; this .pwd = pwd; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.boogipop.utils;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;public class MybatisUtil { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlsession () { return sqlSessionFactory.openSession(true ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.boogipop.mapper;import com.boogipop.pojo.User;import java.util.List;import java.util.Map;public interface UserMapper { List<User> getuserlist () ; User getuserbyname (String name) ; Boolean adduser (User user) ; Boolean deluser (int id) ; Boolean cguser (User user) ; Boolean mapadduser (Map<String,Object> map) ; User getuserbylike (Map<String,Object> map) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import com.boogipop.mapper.UserMapper;import com.boogipop.pojo.User;import com.boogipop.utils.MybatisUtil;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;public class Exec { @Test public void selectall () { SqlSession sqlsession = MybatisUtil.getSqlsession(); UserMapper mapper = sqlsession.getMapper(UserMapper.class); List<User> getuserlist = mapper.getuserlist(); for (User user : getuserlist) { System.out.println(user); } sqlsession.close(); } }
经过一系列的配置终于把先前的mybatis拿过来了,熟悉之后很自然
方式一 回顾一下mybatis中我们是如何进行JDBC事务处理的,我们首先定义了mybatis核心配置文件 使用JDBC连接了mysql,随后准备了util类,返回了我们sqlsession,之后又添加了mapper和pojo以及Mapper.xml 配置文件,最后在核心配置文件注册了mapper 我们调用mapper就通过util获取sqlsession,随后获取mapper,最后执行事务,提交事务,关闭sqlsession,在这些步骤中:获取sqlsession、获取mapper、关闭sqlsession。这几个步骤重复了,这时候我们就可以用spring-mybaits 包去整合它们
1 2 3 4 jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql: jdbc.username=root jdbc.password=jiayou357753
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <!-- 注意这里--> <context:component-scan base-package ="com.boogipop.mapper" /> <!--Datasource:使用spring的数据源替换mybatis的配置,选一个连接池 c3p0,jdbc,druid 这里我们使用spring提供的jdbc:org.springframework.jdbc.datasource --> <context:property-placeholder location="db.properties" /> <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 获取sqlsessionfactory--> <bean id="sqlsessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean" > <!--绑定mybatis配置文件--> <property name="dataSource" ref="datasource" /> <property name="configLocation" value="classpath:mybatis-config.xml" /> <property name="mapperLocations" value="classpath:com/boogipop/mapper/UserMapper.xml" /> </bean> <bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlsession" > <!--只能使用构造器创建,因为没有set方法--> <constructor-arg index="0" ref="sqlsessionfactory" /> </bean> </beans>
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration> <settings> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <typeAliases> <package name="com.boogipop.pojo" /> </typeAliases> </configuration>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.boogipop.mapper.UserMapper" > <select id="getuserlist" resultType="User" > select * from user </select> <select id="getuserbyname" resultType="User" parameterType="String" > select * from user where name=#{name} </select> <insert id="adduser" parameterType="User" > insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd}); </insert> <delete id="deluser" parameterType="int" > delete from user where id=#{id} </delete> <update id="cguser" parameterType="User" > update user set name=#{name},pwd=#{pwd} where id=#{id} </update> <insert id="mapadduser" parameterType="Map" > insert into user (id,name) values(#{id},#{name}) </insert> <select id="getuserbylike" parameterType="Map" resultType="User" > select * from user where name like concat ("%" ,#{name},"%" ) </select> </mapper>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.boogipop.mapper;import com.boogipop.pojo.User;import java.util.List;import java.util.Map;public interface UserMapper { List<User> getuserlist () ; User getuserbyname (String name) ; Boolean adduser (User user) ; Boolean deluser (int id) ; Boolean cguser (User user) ; Boolean mapadduser (Map<String,Object> map) ; User getuserbylike (Map<String,Object> map) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.boogipop.mapper;import com.boogipop.pojo.User;import org.mybatis.spring.SqlSessionTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.List;import java.util.Map;@Component public class UserMapperImpl implements UserMapper { @Autowired private SqlSessionTemplate sqlsession; @Override public List<User> getuserlist () { UserMapper mapper = sqlsession.getMapper(UserMapper.class); return mapper.getuserlist(); } @Override public User getuserbyname (String name) { return null ; } @Override public Boolean adduser (User user) { return null ; } @Override public Boolean deluser (int id) { return null ; } @Override public Boolean cguser (User user) { return null ; } @Override public Boolean mapadduser (Map<String, Object> map) { return null ; } @Override public User getuserbylike (Map<String, Object> map) { return null ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import com.boogipop.mapper.UserMapper;import com.boogipop.mapper.UserMapperImpl;import com.boogipop.pojo.User;import com.boogipop.utils.MybatisUtil;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.List;public class Exec { @Test public void selectall () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("ApplicationContext.xml" ); UserMapper userMapperImpl = context.getBean("userMapperImpl" , UserMapper.class); List<User> getuserlist = userMapperImpl.getuserlist(); for (User user : getuserlist) { System.out.println(user); } } }
有一说一我还是喜欢原来的用法,我不知道这傻逼意味何在,徒增BUG,这东西能不用就不用真的,真傻比我草,在这我们还保留了一部分的mybatis配置文件,其实也是可以省略的,mybatis配置文件中剩下的是日志和别名的设置,其中日志可能无法替换,但是别名可以用typeAliasesPackage
标签代替
坑点: 这里的db.properties一定要注意,变量不要直接用username和password: 明明密码对的,被拒绝了,找了好久原因,后来发现将db.properties配置文件里的username改为jdbc.username就好了,可能是因为username被系统变量占用了。 当spring容器在进行启动的时候,会读取当前系统某些环境变量的配置,当前系统的用户名使用username来表示的,所以做好的方式是添加前缀,使用jdbc.username.https://www.cnblogs.com/fps2tao/p/14109885.html
方式二 根据官网的来说,你还可以通过让实现类继承sqlsessiondaosupport类从而获取sqlsession: 根据官网的解释我们也来实操一遍:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.mapper" /> <context:property-placeholder location ="db.properties" /> <bean id ="datasource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </bean > <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/boogipop/mapper/UserMapper.xml" /> </bean > <bean class ="org.mybatis.spring.SqlSessionTemplate" id ="sqlsession" > <constructor-arg index ="0" ref ="sqlsessionfactory" /> </bean > <bean id ="userMapper2" class ="com.boogipop.mapper.UserMapperImpl2" > <property name ="sqlSessionFactory" ref ="sqlsessionfactory" /> </bean > </beans >
1 2 3 4 5 6 7 public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { @Override public List<User> getuserlist () { SqlSession sqlSession = getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.getuserlist(); }
通过继承的方式也可以达到相同的目的
我自己比较推荐的方式三 还有一种方式就不需要我们自己写实现类了,让spring自己去创建Mapper,这样我觉得其实是最方便的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.boogipop.mapper" /> <context:property-placeholder location ="db.properties" /> <bean id ="datasource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </bean > <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/boogipop/mapper/UserMapper.xml" /> </bean > <bean class ="org.mybatis.spring.SqlSessionTemplate" id ="sqlsession" > <constructor-arg index ="0" ref ="sqlsessionfactory" /> </bean > <bean id ="userMapper2" class ="com.boogipop.mapper.UserMapperImpl2" > <property name ="sqlSessionFactory" ref ="sqlsessionfactory" /> </bean > <bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name ="sqlSessionFactoryBeanName" value ="sqlsessionfactory" /> <property name ="basePackage" value ="com.boogipop.mapper" /> </bean > </beans >
1 2 3 4 5 6 7 8 9 @Test public void selectall3 () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("ApplicationContext.xml" ); UserMapper userMapper = context.getBean("userMapper" , UserMapper.class); List<User> list = userMapper.getuserlist(); for (User user : list) { System.out.println(user); } }
然后直接获取id为接口名字首字母小写 的bean,获取到的就是注册好的Mapper:
假如被这个bean的名字所困扰,也可以直接改bean的名字: 在接口上直接用注解注册个bean即可,猜测spring是在创建对象的时候就对我们的usermapepr3做了处理,将其变成了mapper
Spring声明式事务 首先什么是事务呢?事务遵循ACID原则:
原子性:原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,回滚指的是不做变化
一致性:事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。 如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态
隔离性:指的是不同事务在提交的时候,最终呈现出来的效果是串行的,换句话说,既是不同事务,按照提交的先后顺序执行,再换句话说,对于事务本身来说,它所感知的数据库,应该只有它自己在操作。那么最简单的方法,我们按照定义来实现数据库事务的隔离性即可,很明显这在同时并发有多个事务要执行的环境下是没有执行效率的,一个事务的执行,必然会阻塞其他事务的执行。所以SQL的标准制定者对此作出妥协,提出隔离性的四个等级,其中最高级隔离等级才是串行执行。在没有到达串行执行等级的情况下,事务又是并发执行的,总是或多或少会存在问题,这在后面的例子当中会讲到。
持久性:持久性就是说在事务成功提交了之后,事务所变更的数据一定会保存起来,而不会因为任何故障导致数据丢失
以上就是事务的一些基本概念,也就是说事务属于逻辑上的处理,为了让程序有顺序有原则的去执行,不会因为别的事务破坏另一个事务执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <?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:tx ="http://www.springframework.org/schema/tx" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd" > <context:component-scan base-package ="com.boogipop.mapper" /> <context:property-placeholder location ="db.properties" /> <bean id ="datasource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </bean > <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/boogipop/mapper/UserMapper.xml" /> </bean > <bean class ="org.mybatis.spring.SqlSessionTemplate" id ="sqlsession" > <constructor-arg index ="0" ref ="sqlsessionfactory" /> </bean > <bean id ="userMapper2" class ="com.boogipop.mapper.UserMapperImpl2" > <property name ="sqlSessionFactory" ref ="sqlsessionfactory" /> </bean > <bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name ="sqlSessionFactoryBeanName" value ="sqlsessionfactory" /> <property name ="basePackage" value ="com.boogipop.mapper" /> </bean > <bean class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" id ="transactionManager" > <property name ="dataSource" ref ="datasource" /> </bean > <tx:advice id ="txadvice" transaction-manager ="transactionManager" > <tx:attributes > <tx:method name ="getuserlist" propagation ="REQUIRED" /> <tx:method name ="getuserbyname" propagation ="REQUIRED" /> </tx:attributes > </tx:advice > <aop:config > <aop:pointcut id ="point" expression ="execution(* com.boogipop.mapper..*.*(..))" /> <aop:advisor advice-ref ="txadvice" pointcut-ref ="point" /> </aop:config > </beans >
这样的话执行起来就会符合上述的ACID事务原则,其中要注意propagation的属性,一共有七种类型的事务,详细参考可以看一开始的链接 另外事务的实现还可以直接使用@Transactional
注解去实现
小结 完结撒花咯,下一站就是SpringMVC紧跟着就是SpringBoot,这时候又得新学一些东西,加油加油