一、Spring简介
Spring官网
Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。
二、环境构建
首先根据导入一下spring-webmvc的包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
三、IOC核心本质
首先我们编写一下对应测试的代码
com.imperfect.dao.UserDao
import com.imperfect.service.UserService;
import com.imperfect.service.UserServiceImpl;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:06
*/
public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
com.imperfect.dao.UserDaoImpl
package com.imperfect.dao;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:01
*/
public class UserDaoImpl implements UserDao {
public void getUser() {
System.out.println("获取用户信息");
}
}
com.imperfect.dao.UserDaoMysqlImpl
package com.imperfect.dao;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:09
*/
public class UserDaoMysqlImpl implements UserDao{
public void getUser() {
System.out.println("获取Mysql用户信息");
}
}
com.imperfect.dao.UserDaoOracleImpl
package com.imperfect.dao;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:10
*/
public class UserDaoOracleImpl implements UserDao {
public void getUser() {
System.out.println("获取Oracle用户信息");
}
}
com.imperfect.service.UserService
package com.imperfect.service;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:02
*/
public interface UserService {
public void getUser();
}
com.imperfect.service.UserServiceImpl
package com.imperfect.service;
import com.imperfect.dao.UserDao;
import com.imperfect.dao.UserDaoImpl;
import com.imperfect.dao.UserDaoMysqlImpl;
import com.imperfect.dao.UserDaoOracleImpl;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:02
*/
public class UserServiceImpl implements UserService {
private UserDao userDao=new UserDaoImpl();
private UserDao userDaoMysql=new UserDaoMysqlImpl();
private UserDao userDaoOracle=new UserDaoOracleImpl();
public void getUser() {
userDao.getUser();
}
}
Test测试类代码
import com.imperfect.service.UserService;
import com.imperfect.service.UserServiceImpl;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:06
*/
public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
整体项目代码结构:
利用我们本来Java中组合的概念,当我们需要在业务层调用Dao层的时候,往往需要在业务层中通过调用不同的Dao层从而实现对应各种业务。
例如:这里如果我们需要调用UserDaoImpl的业务的时候,我们就需要手动修改getUser方法中对应的调用的dao层。需要调用userDaoMysql层的时候,我们就需要手动getUser方法中对应的调用的dao层。需要调用userDaoOracle层的时候,我们就需要手动getUser方法中对应的调用的dao层。
现在我们修改一下UserServiceImpl这个类
com.imperfect.service.UserServiceImpl
新增以下代码
private UserDao dao;
//利用set进行动态实现值的注入
public void setUserDao(UserDao dao){
this.dao=dao;
}
现在我们再来编写一下测试类
import com.imperfect.dao.UserDaoMysqlImpl;
import com.imperfect.service.UserService;
import com.imperfect.service.UserServiceImpl;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:06
*/
public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.setUserDao(new UserDaoOracleImpl());
userService.getUser();
}
}
这里对程序发生了一个质的变化
相比于我们一开始写的代码
之前的业务:用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码。
现在:我们使用了一个set接口进行动态实现值的注入。
区别:之前我们程序是主动的去创建对象,控制权在程序员的手上。现在程序不再具有主动性,而是变成了被动的接收对象。
这种思想从本质上解决了问题,我们不再需要去管理对象的创建了。系统的耦合性大大降低。
这就是IOC的原型。
四、Hello,Spring
首先我们编写一下对应的代码
com.imperfect.pojo.Hello
package com.imperfect.pojo;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 14:42
*/
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
src/main/resources/beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.imperfect.pojo.Hello">
<property name="str" value="Spring">
</property>
</bean>
</beans>
根据Spring官方文档的介绍:我们可以构建ApplicationContext容器来接收对象
编写对应的测试类
import com.imperfect.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 15:03
*/
public class Test {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象都在Spring中管理了
Object hello =(Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
测试结果
五、IOC创建对象
1、使用无参构造来创建对象
com.imperfect.pojo.Hello
public Hello() {
System.out.println("使用了无参构造方法");
}
2、使用有参构造来创建对象
com.imperfect.pojo.Hello
增加以下代码
public Hello(String str) {
System.out.println("通过有参构造来创建对象");
this.str = str;
}
src/main/resources/beans.xml
<!--第一种方式 通过下标赋值-->
<bean id="hello" class="com.imperfect.pojo.Hello">
<constructor-arg index="0" value="imperfect">
</constructor-arg>
</bean>
<!--第二种方式 通过类型赋值-->
<bean id="hello" class="com.imperfect.pojo.Hello">
<constructor-arg type="java.lang.String" value="imperfect">
</constructor-arg>
</bean>
<!-- 第三种方式 直接通过参数名来赋值-->
<bean id="hello" class="com.imperfect.pojo.Hello">
<constructor-arg name="str" value="imperfect">
</constructor-arg>
</bean>
六、依赖注入之Set注入
首先我们构建一下测试的环境
com.imperfect.pojo.Address
package com.imperfect.pojo;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 15:46
*/
public class Address {
private String adddress;
public String getAdddress() {
return adddress;
}
public void setAdddress(String adddress) {
this.adddress = adddress;
}
@Override
public String toString() {
return "Address{" +
"adddress='" + adddress + '\'' +
'}';
}
}
com.imperfect.pojo.Student
package com.imperfect.pojo;
import java.util.*;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 15:46
*/
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;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
src/main/resources/beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.imperfect.pojo.Address">
<property name="adddress" value="广州"></property>
</bean>
<bean id="student" class="com.imperfect.pojo.Student">
<!-- 第一种普通值的注入-->
<property name="name" value="imperfect"></property>
<!-- 第二种bean的注入-->
<property name="address" ref="address"></property>
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
</array>
</property>
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
</list>
</property>
<property name="card">
<map>
<entry key="身份证" value="123132312123"></entry>
<entry key="银行卡" value="1231322322123"></entry>
</map>
</property>
<property name="games">
<set>
<value>英雄联盟</value>
<value>王者荣耀</value>
</set>
</property>
<property name="wife">
<null/>
</property>
<property name="info">
<props>
<prop key="学号">123123</prop>
<prop key="姓名">imperfect</prop>
</props>
</property>
</bean>
</beans>
MyTest
import javafx.application.Application;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 15:50
*/
public class MyTest {
public static void main(String[] args) {
ApplicationContext context =new ClassPathXmlApplicationContext("beans.xml");
Object student = context.getBean("student");
System.out.println(student.toString());
}
}
测试结果:
七、Bean作用域
根据Spring官方文档的介绍
1、单例模式(Spring默认机制)
关于单例模式后续会另外出一篇文章来进行详细讲解
2、原型模式
每次从容器中get的时候都会产生一个新对象
<bean id="student" class="com.imperfect.pojo.Student" scope="prototype">
3、其余的request、session、application,这个只能在Web开发中使用
八、Bean的自动装配
自动装配是Spring满足bean依赖的一种方式
Spring会在上下文自动寻找,并自动给bean装配属性
首先先编写对应的测试代码
com.imperfect.pojo.Cat
package com.imperfect.pojo;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 16:34
*/
public class Cat {
public void shout(){
System.out.println("miao");
}
}
com.imperfect.pojo.Dog
package com.imperfect.pojo;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 16:34
*/
public class Dog {
public void shout(){
System.out.println("wang");
}
}
com.imperfect.pojo.Person
package com.imperfect.pojo;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 16:34
*/
public class Person {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
src/main/resources/beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.imperfect.pojo.Cat"></bean>
<bean id="dog" class="com.imperfect.pojo.Dog"></bean>
<!-- 会自动在容器上下文中查找,和自己set方法名字对应的bean-->
<bean id="person" class="com.imperfect.pojo.Person" autowire="byName">
<property name="name" value="imperfect"></property>
</bean>
</beans>
编写测试类
import com.imperfect.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/19 16:38
*/
public class MyTest {
@Test
public void test1(){
ApplicationContext context =new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person) context.getBean("person");
person.getCat().shout();
person.getDog().shout();
}
}
执行结果如下:
九、使用注解开发
1、声明Bean的注解
在类上加上这些注解后,Spring启动后会自动初始化这些类,生成对应的实例对象
@Controller:在展现层使用(MVC->SpringMVC)
@Service:业务逻辑是服务层(service)使用
@ Repository:数据访问层(dao)使用
@Component:组件没有明确的角色 前面的四个实质上没有什么区别
注:在SpringMVC中@Controller被赋予了特殊的功能,不能倍其他的注解代替
@Bean:注解声明当前的方法返回的是一个Bean,这个Bean的类型是对应的方法返回值类型,Bean的名称默认是方法名,也可以自定义:@Bean(name=“自定义名称”)
2、注入Bean的注解
在属性或setter方法上加上这些注解(@Resource、@Autowired),Spring会自动的按照Bean类型(type)或Bean名称(name)进行装配组合Bean
@Resource:
默认按 byName 自动注入,由J2EE提供。
@Resource装配顺序
(1). 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常;
(2). 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常;
(3). 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常;
(4). 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
(5).@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入。
@ Autowired:
注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解(指定Bean的名称)一起使用。
3、初始化配置
@Configuration: 在类上加上这个注解,表明当前的类时个配置类,这个类中可能存在0个或多个@Bean
@ComponentScan: 扫描包注解,这个注解会扫描指定的包下所有的加有@Controller;@Repository;@Component;@service注解的类,然后初始化成Bean
十、使用java的方式配置Spring
完全不适用Spring的xml,全权交给java来进行控制
首先我们先构建一下对应的环境
com.imperfect.config.MyConfig
package com.imperfect.config;
import com.imperfect.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 9:58
*/
@Configuration
public class MyConfig {
@Bean
public User getUser(){
return new User();
}
}
com.imperfect.pojo.User
package com.imperfect.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 9:57
*/
@Component
public class User {
@Value("imperfect")
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
编写测试类
import com.imperfect.config.MyConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 10:11
*/
public class Test {
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(MyConfig.class);
Object getUser = context.getBean("getUser");
System.out.println(getUser.toString());
}
}
测试结果
@Configuraion代表这是一个配置类,就和我们之前看的beans.xml一样
这个也会被Spring容器托管,注册到容器中,因为它本来就是一个@Component
十一、静态代理
1、什么是静态代理?
两个对象之间的操作不直接进行,通过第三方进行代理来实现。就比如说房客租房子这件事情,租房的两个对象是房客和房东,静态代理便是,在房客和房东之间多了一个角色,这便是中介,代理便是中介,现在租房这件事情便是房客和中介,以及中介和房东之间的事情,房客不直接和房东进行租房的操作。
2、编写代码
com/imperfect/demo01/Process.java
package com.imperfect.demo01;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:31
*/
public interface Process {
public void charge ();
}
代理类:com/imperfect/demo01/Proxy.java
package com.imperfect.demo01;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:33
*/
public class Proxy implements Process {
Customer customer;
public void setCustomer(Customer customer){
this.customer=customer;
}
public void charge() {
customer.charge();
}
}
被代理类:com/imperfect/demo01/Customer.java
package com.imperfect.demo01;
import org.springframework.context.annotation.Profile;
import java.lang.annotation.Annotation;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:32
*/
public class Customer implements Process {
public void charge() {
System.out.println("Customer1需要控诉");
}
}
测试类:com/imperfect/demo01/Test.java
package com.imperfect.demo01;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:36
*/
public class Test {
public static void main(String[] args) {
Customer customer=new Customer();
Proxy proxy=new Proxy();
proxy.setCustomer(customer);
proxy.charge();
}
}
测试结果:
十二、动态代理
1、什么是动态代理?
动态代理指的是:在程序的执行过程中,使用jdk的反射机制,创建代理对象,并动态的指定代理的目标类
动态代理的实现方式常用有两种:
- 使用JDK代理
- 通过CDLIB代理
jdk动态代理
jdk动态代理是基于Java的反射机制实现的,使用jdk反射包下的Proxy和InvocationHandler实现代理对象的动态创建
2、编写代码
com/imperfect/demo02/Process.java
package com.imperfect.demo02;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:48
*/
public interface Process {
void charge();
}
动态代理类:com/imperfect/demo02/DynamicProxyHandler.java
package com.imperfect.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:49
*/
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public void setProxy(Object object){
this.object=object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(object, args);
//return method.invoke(object, method);
// return result;
}
// 获取代理对象
public Object getProxy() {
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
this);
}
}
被代理对象Customer1:com/imperfect/demo02/Customer1.java
package com.imperfect.demo02;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:48
*/
public class Customer1 implements Process {
public void charge() {
System.out.println("Customer1需要控诉");
}
}
被代理对象Customer2:com/imperfect/demo02/Customer2.java
package com.imperfect.demo02;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:49
*/
public class Customer2 implements Process {
public void charge() {
System.out.println("Customer2需要控诉");
}
}
测试类:com/imperfect/demo02/Test.java
package com.imperfect.demo02;
/**
* @author : Imperfect(lxm)
* @Des:
* @date : 2023/1/20 15:52
*/
public class Test {
public static void main(String[] args) {
Customer1 customer1=new Customer1();
DynamicProxyHandler dynamicProxyHandler=new DynamicProxyHandler();
dynamicProxyHandler.setProxy(customer1);
Process proxy = (Process)dynamicProxyHandler.getProxy();
proxy.charge();
}
}
十三、注解实现AOP
在创建AOP之前我们需要先导入一下相对应AOP的jar包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
1、编写代码
我们首先自定义一个注解com/imperfect/Annotation/MethodDetail.java
package com.imperfect.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodDetail {
}
编写一个切面AOP
com/imperfect/Aop/MethodDetailAop.java
package com.imperfect.Aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MethodDetailAop {
@Around("@annotation(com.imperfect.Annotation.MethodDetail)")
public void escapeAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("执行方法前");
Object proceed = null;
proceed = joinPoint.proceed(joinPoint.getArgs());
System.out.println("执行方法后");
}
}
编写一个需要被代理的接口com/imperfect/Service/UserService.java
package com.imperfect.Service;
import org.springframework.stereotype.Component;
@Component
public interface UserService {
public void add();
public void save();
public void update();
}
编写一个具体的实现类
com/imperfect/Service/UserServiceImpl.java
package com.imperfect.Service;
import com.imperfect.Annotation.MethodDetail;
import org.springframework.stereotype.Component;
@Component()
public class UserServiceImpl implements UserService{
@MethodDetail
public void add() {
System.out.println("执行添加方法");
}
public void save() {
System.out.println("执行保存方法");
}
public void update() {
System.out.println("执行修改方法");
}
}
编写测试代码
Test.java
import com.imperfect.Service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserService userServiceImpl = (UserService) context.getBean("userServiceImpl");
userServiceImpl.add();
}
}
测试结果:
十四、Spring整合MyBatis
首先我们需要先导入整合时所需要的jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Spring</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-09-myBatis</artifactId>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
</project>
编写pojo类:com/imperfect/pojo/User.java
package com.imperfect.pojo;
import jdk.nashorn.internal.objects.annotations.Constructor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String id;
private String name;
private String password;
}
com/imperfect/mapper/UserMapper.java
package com.imperfect.mapper;
import com.imperfect.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectAll();
}
Mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.imperfect.mapper.UserMapper">
<select id="selectAll" resultType="com.imperfect.pojo.User">
select * from user;
</select>
</mapper>
com/imperfect/mapper/UserMapperImpl.java
package com.imperfect.mapper;
import com.imperfect.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public List<User> selectAll() {
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
return mapper.selectAll();
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 使用Spring数据源来替换MyBatis-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://数据库地址以及数据库?userUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="账号"/>
<property name="password" value="密码"/>
</bean>
<!-- sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!-- 绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.imperfect.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
</beans>
mybatis-config.xml
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--每一个mapper.xml都需要在mybatis配置文件中注册-->
<mappers>
<mapper resource="Mapper/UserMapper.xml"/>
</mappers>
</configuration>
编写测试类
Test.java
import com.imperfect.mapper.UserMapper;
import com.imperfect.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.util.List;
public class Test {
@org.junit.jupiter.api.Test
public void Test() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = (UserMapper) context.getBean("userMapper");
List<User> users = userMapper.selectAll();
for (User user : users) {
System.out.println(user.toString());
}
}
}
测试结果如下
十五、声明式事务
1、简介
Spring支持编程式事务管理和声明式事务管理两种方式。
其中声明式注解是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
2、编写代码
首先我们在Spring的application.xml配置文件中配置一下声明式事务,并且打开通过注解的方式控制事务。
<!-- 配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!-- 使用注解来进行驱动-->
<tx:annotation-driven/>
编写对应的mapper.xml
<insert id="insert" parameterType="com.imperfect.pojo.User">
insert into user values(#{id},#{password},#{name})
</insert>
<delete id="delete" parameterType="int">
deletes from user where id={#id}
</delete>
其中的delete语句为错误的语句,我们特意地去造成SQL的错误,从而导致程序出错,看一下事务会不会提交。
编写对应的业务代码
@Transactional
public List<User> selectAll() {
User user = new User("5", "im", "love");
insert(user);
delete(4);
return sqlSessionTemplate.getMapper(UserMapper.class).selectAll();
}
执行结果如下:
代码出现报错,并且第一个新增用户的业务操作也没有提交,保证了事务的ACID原则。