Spring
1、Spring入门(IOC)
首先要在pom文件里导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.6</version>
</dependency>
创建一个实体类
package com.zxjava.pojo;
public class Hero {
public Hero() {
}
public Hero(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("这位英雄是"+name);
}
}
编写一个配置文件,把实体类注册到beans中,并给name属性一个值为 “小提莫” ,这里给属性赋值是通过Set方法赋值的
<?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="hero" class="com.zxjava.pojo.Hero">
<property name="name" value="小提莫"/>
</bean>
</beans>
编写一个测试类
import com.zxjava.pojo.Hero;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hero hero = (Hero) context.getBean("hero");
hero.show();
}
}
看一下输出结果
展示一下目录结构
总结:
- 之前创建一个对象通过new创建,现在这个对象是由Spring创建的,Spring创建对象的方式就是通过实体类的无参构造创建的
- Hero对象的属性是由Spring容器设置的,是通过实体类的set方法注入的,如果把实体类的set方法去掉,运行测试类就会报错
2、依赖注入(DI)
DI不是IOC,DI是属于IOC的
依赖注入的方式大致分为2中,分别是Set注入和构造器注入
在上一小节Spring入门的就是通过set方法注入
2.1 、通过有参构造方法来创建
-
根据index参数下标设置:
在配置文件beans.xml中重写bean标签
<bean id="hero" class="com.zxjava.pojo.Hero"> <constructor-arg index="0" value="盖伦"/> </bean>
-
根据参数名字设置
<bean id="hero" class="com.zxjava.pojo.Hero"> <constructor-arg name="name" value="轮子妈"/> </bean>
-
根据参数类型设置
<bean id="hero" class="com.zxjava.pojo.Hero"> <constructor-arg type="java.lang.String" value="艾希"/> </bean>
2.2、Spring配置别名
-
通过alias
<alias name="hero" alias="superHero"/>
-
通过name
<bean id="hero" name="superHero,superHero2" class="com.zxjava.pojo.Hero"> <constructor-arg type="java.lang.String" value="艾希"/> </bean>
name的功能比较好,在bean标签中如果没有id,那么name就是默认标识符,如果配置了id那么name就是别名,name可以设置多个别名,可以用逗号、分号、空格隔开
2.3、Set注入
Set注入会演示不同属性类型的注入方式,比如String、List、Map等等。
首先创建两个实体类
package com.zxjava.pojo;
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 + '\'' +
'}';
}
}
package com.zxjava.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 money;
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 getMoney() {
return money;
}
public void setMoney(String money) {
this.money = money;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
public void show () {
System.out.println("name=" + name
+ ",address=" + address.getAddress()
+ ",books="
);
for (String book : books) {
System.out.println("<<" + book + ">>");
}
System.out.println("\n爱好:" + hobbys);
System.out.println("card" + card);
System.out.println("games" + games);
System.out.println("money" + money);
System.out.println("info" + info);
}
}
通过配置文件展示不同属性的注入方式
<bean id="address" class="com.zxjava.pojo.Address">
<property name="address" value="北京"/>
</bean>
<bean id="student" class="com.zxjava.pojo.Student">
<!--常量注入-->
<property name="name" value="小王"/>
<!--bean注入-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>坏蛋是怎样炼成的</value>
<value>三国演义</value>
<value>水浒传</value>
</array>
</property>
<!--List注入-->
<property name="hobbys">
<list>
<value>打游戏</value>
<value>唱歌</value>
</list>
</property>
<!--List注入-->
<property name="card">
<map>
<entry key="身份证" value="111111"/>
<entry key="银行卡" value="222222"/>
</map>
</property>
<!--Set注入-->
<property name="games">
<set>
<value>lol</value>
<value>cf</value>
</set>
</property>
<!--Null注入-->
<property name="money"><null/></property>
<!--Properties注入-->
<property name="info">
<props>
<prop key="姓名">小王</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
编写测试类查看运行结果
@Test
public void studentTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
student.show();
}
2.4、p命名注入
p(properties)命名注入,依然要设置set方法
p命名注入需要在文件中加入约束文件
xmlns:p="http://www.springframework.org/schema/p"
<bean id="hero" class="com.zxjava.pojo.Hero" p:name = "瑞兹"/>
2.5 、c命名注入
c(Constructor)命名注入,依然要设置有参构造方法
c命名注入需要在文件中加入约束问价
xmlns:c="http://www.springframework.org/schema/c"
<bean id="hero" class="com.zxjava.pojo.Hero" c:name="小法师"/>
3、自动装配
回顾
首先创建3个实体类(人、猫、狗)
package com.zxjava.pojo;
public class Cat {
public void giao(){
System.out.println("喵喵喵~");
}
}
package com.zxjava.pojo;
public class Dog {
public void giao(){
System.out.println("汪汪汪~");
}
}
package com.zxjava.pojo;
import com.zxjava.pojo.Cat;
import com.zxjava.pojo.Dog;
public class Person {
private String name;
private Dog dog;
private Cat cat;
public Person() {
}
public Person(String name, Dog dog, Cat cat) {
this.name = name;
this.dog = dog;
this.cat = cat;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
编写配置文件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"
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="cat" class="com.zxjava.pojo.Cat"/>
<bean id="dog" class="com.zxjava.pojo.Dog"/>
<bean id="person" class="com.zxjava.pojo.Person">
<property name="name" value="小王"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person) context.getBean("person");
person.getCat().giao();
person.getDog().giao();
}
输入结果
3.1、通过配置xml自动装配
-
byName方式
只需修改配置文件的bean
<bean id="person" class="com.zxjava.pojo.Person" autowire="byName"> <property name="name" value="小王"/> </bean>
如果把cat或dog的bean id修改了,就会报空指针
-
byType方式
<bean id="person" class="com.zxjava.pojo.Person" autowire="byType"> <property name="name" value="小王"/> </bean>
3.2、通过注解自动装配
(@Autowired @Qualifier @Resource)
首先在spring配置文件中引入context文件头
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
开启注解支持,并把实体类注册
<context:annotation-config/>
<bean id="cat" class="com.zxjava.pojo.Cat"/>
<bean id="dog" class="com.zxjava.pojo.Dog"/>
<bean id="person" class="com.zxjava.pojo.Person"/>
在person实体类中使用注解自动装配
@Autowired
private Dog dog;
@Autowired
private Cat cat;
@Autowired是通过类型自动装配的,不支持用名称装配,所以就有个@Qualifier,@Qualifier可以通过名称装配,但是@Qualifier不能单独使用
修改一下beans.xml
<bean id="cat1" class="com.zxjava.pojo.Cat"/>
<bean id="cat2" class="com.zxjava.pojo.Cat"/>
<bean id="dog1" class="com.zxjava.pojo.Dog"/>
<bean id="dog2" class="com.zxjava.pojo.Dog"/>
此时只是用@Autowired就会报错,因为cat和dog分别有两个一样的类型,所以此时就用到了@Qualifier,修改Person,见下:
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
除此之外还有**@Resource**,如果有指定的name属性则先会根据byName方式进行装配,若不成功,则会再根据byType的方式进行装配
@Resource(name = "dog1")
private Dog dog;
@Resource(name = "cat1")
private Cat cat;
4、使用注解开发
首先要在beans.xml中指定注解扫描包并且在spring配置文件中引入context约束
<context:component-scan base-package="com.zxjava.pojo"/>
现在不需要再beans.xml中注入bean了只需要在实体类上加上@Component(“xxx”)注解即可
package com.zxjva.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("hero")
public class Hero {
@Value("提莫")
private String name;
@Override
public String toString() {
return "Hero{" +
"name='" + name + '\'' +
'}';
}
}
@Component有3个衍生注解:
- @Controller:用于web层
- @Service:用于service层
- @Repository:用于dao层
这3个衍生注解与@Component的作用一样
5、纯Java类进行配置
纯java类进行配置就不需要beans.xml了,需要创建一个配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
//注册一个bean,这里的返回值就bean的类型,方法名就是bean的id
@Bean
public Hero hero(){
return new Hero();
}
}
创建一个实体类
import org.springframework.beans.factory.annotation.Value;
public class Hero {
@Value("盖伦")
private String name;
@Override
public String toString() {
return "Hero{" +
"name='" + name + '\'' +
'}';
}
}
进行测试
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest {
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Hero hero = (Hero) context.getBean("hero");
System.out.println(hero.toString());
}
}
6、静态代理
代理模式就类似于租房
- 租房就是一个抽象角色(一般用接口实现)
- 房东就是真实角色(被代理的角色)
- 中介就是代理角色(代理真实角色后,一般会有一些其他操作)
- 客户就是使用代理角色来实现一些操作
首先创建一个抽象角色(租房):
public interface Rent {
public void rent();
}
创建一个真实角色(房东):
房东的动作只有一个,就是租房,而中介会多一些操作
public class Landlord implements Rent{
@Override
public void rent() {
System.out.println("房东租房...");
}
}
创建一个代理角色(中介):
中介代理了房东,会多选房、收中介费等其他操作
public class Proxy implements Rent{
Landlord landlord;
public Proxy() {
}
public Proxy(Landlord landlord) {
this.landlord = landlord;
}
@Override
public void rent() {
chooseHouse();
landlord.rent();
moeny();
}
public void chooseHouse(){
System.out.println("选房");
}
public void moeny(){
System.out.println("收钱");
}
}
创建一个客户:
public class Client {
public static void main(String[] args) {
Landlord landlord =new Landlord();
Proxy proxy = new Proxy(landlord);
proxy.rent();
}
}
再举一个静态代理的例子加深理解
创建一个抽象角色
package moreAction;
public interface Service {
void add();
void delete();
void update();
void query();
}
创建一个真实角色
package moreAction;
public class ServiceImpl implements Service{
@Override
public void add() {
System.out.println("增加");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("修改");
}
@Override
public void query() {
System.out.println("查询");
}
}
创建一个代理角色,在真实角色的每个方法中再添加一些其他操作:
package moreAction;
public class ServiceImplProxy implements Service{
ServiceImpl serviceImpl;
public ServiceImplProxy(ServiceImpl serviceImpl) {
this.serviceImpl = serviceImpl;
}
@Override
public void add() {
log("add");
System.out.println("增加");
}
@Override
public void delete() {
log("delete");
System.out.println("删除");
}
@Override
public void update() {
log("update");
System.out.println("修改");
}
@Override
public void query() {
log("query");
System.out.println("查询");
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
创建一个用户:
package moreAction;
public class Client {
public static void main(String[] args) {
ServiceImpl service = new ServiceImpl();
ServiceImplProxy serviceImplProxy = new ServiceImplProxy(service);
serviceImplProxy.add();
serviceImplProxy.delete();
serviceImplProxy.update();
serviceImplProxy.query();
}
}
7、动态代理
动态代理与静态代理一样,静态代理的代理类是提前写好的,而动态代理的代理类是动态生成的。
动态代理还是以房东中介这个例子介绍:
首先创建一个租房的接口:
public interface Rent {
void rent();
}
在创建一个真实角色(房东):
public class Landlord implements Rent {
@Override
public void rent() {
System.out.println("房东租房...");
}
}
接下来就要写一个增强类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LandlordInvocationHandler implements InvocationHandler {
private Rent rent;
public LandlordInvocationHandler(Rent rent) {
this.rent = rent;
}
/**
* invoke三个参数:
*
* proxy:就是代理对象,newProxyInstance方法的返回对象
*
* method:调用的方法
*
* args: 方法中的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前...");
Object invoke = method.invoke(rent,args);
System.out.println("方法执行后...");
return invoke;
}
}
创建一个用户:
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Landlord landlord = new Landlord();
/**
* newProxyInstance,方法有三个参数:
*
* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
*
* loader: 用哪个类加载器去加载代理对象
*
* interfaces:动态代理类需要实现的接口
*
* h:动态代理方法在执行时,会调用h里面的invoke方法去执行
*
*/
Rent rent = (Rent) Proxy.newProxyInstance(landlord.getClass().getClassLoader(), landlord.getClass().getInterfaces(), new LandlordInvocationHandler(landlord));
rent.rent();
}
}
在加强类中还有Rent属性,为了使增强类更加通用,还有加深一下动态代理的理解,请看下面的例子:
创建一个Service接口
public interface Service {
void add();
void delete();
void update();
void select();
}
创建一个真实角色
public class ServiceImpl implements Service{
@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("查询方法");
}
}
编写一个增强类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ServiceInvocationHandler implements InvocationHandler {
private Object target;
public ServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前...");
Object invoke = method.invoke(target,args);
System.out.println("方法执行后...");
return invoke;
}
}
编写一个app类,用于测试
public class App {
public static void main(String[] args) {
ServiceImpl serviceIml = new ServiceImpl();
/**
* newProxyInstance,方法有三个参数:
*
* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
*
* loader: 用哪个类加载器去加载代理对象
*
* interfaces:动态代理类需要实现的接口
*
* h:动态代理方法在执行时,会调用h里面的invoke方法去执行
*
*/
Service service = (Service) Proxy.newProxyInstance(serviceIml.getClass().getClassLoader(), serviceIml.getClass().getInterfaces(), new ServiceInvocationHandler(serviceIml));
service.add();
service.delete();
}
}
8、AOP
AOP是面向切面编程,AOP与代理模式类似,利用AOP可以使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高了开发的效率。
了解以下名词(不好理解可根据代码理解):
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
导入一个依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
8.1、第一种实现方式
编写接口和实现类
public interface Service {
void add();
void delete();
void update();
void select();
}
public class ServiceImpl implements Service{
@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("查询方法");
}
}
写两个增强类,一个前置增强类,一个后置增强类
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//args : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] args, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法执行了...");
}
}
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"方法执行了,返回值是"+returnValue);
}
}
在spring的文件中注册 , 并实现aop切入实现 , 注意导入约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="service" class="com.zxjava.demo.ServiceImpl"/>
<bean id="beforeLog" class="com.zxjava.demo.BeforeLog"/>
<bean id="afterLog" class="com.zxjava.demo.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression(修饰词 返回值 类名 方法名 参数):表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.zxjava.demo.ServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
编写一个测试类
import com.zxjava.demo.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Service service = (Service) context.getBean("service");
service.add();
}
}
执行结果
8.2、第二种实现方式
自定义类来实现AOP
新建一个切入类
public class MyPointCut {
public void before(){
System.out.println("方法执行之前...");
}
public void after(){
System.out.println("方法执行之后...");
}
}
在beans.xml中配置
<bean id="myPointCut" class="com.zxjava.demo.MyPointCut"/>
<aop:config>
<aop:aspect ref="myPointCut">
<aop:pointcut id="pointcut" expression="execution(* com.zxjava.demo.ServiceImpl.*(..))"/>
<aop:before pointcut-ref="pointcut" method="before"/>
<aop:after pointcut-ref="pointcut" method="after"/>
</aop:aspect>
</aop:config>
测试结果
8.3、第三种实现方式
使用注解实现
首先创建一个切面类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnoPointCut {
@Before("execution(* com.zxjava.demo.ServiceImpl.*(..))")
public void before(){
System.out.println("======方法执行之前======");
}
@After("execution(* com.zxjava.demo.ServiceImpl.*(..))")
public void after(){
System.out.println("======方法执行之后======");
}
@Around("execution(* com.zxjava.demo.ServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
jp.proceed();
System.out.println("环绕后");
}
}
在beans.xml中注册并加上支持注解的配置
<bean id="annoPointCut" class="com.zxjava.demo.AnnoPointCut"/>
<aop:aspectj-autoproxy/>
9、Spring整合MyBatis
首先导入Spring和MyBatis都需要的包和配置静态资源过滤配置
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version> <version>5.1.10.RELEASE</version>
</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
9.1、回顾MyBatis
提前展示一下要创建的目录结构:
配置MyBatis的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<package name="com.zxjava.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/zxjava?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="971214"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.zxjava.dao"/>
</mappers>
</configuration>
新建一个实体类
package com.zxjava.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private int id;
private String name;
private String password;
}
创建一个接口
package com.zxjava.dao;
import com.zxjava.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
List<User> selectUser();
}
编写一个接口对应的映射文件
<?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.zxjava.dao.UserMapper">
<select id="selectUser" resultType="user">
select * from User
</select>
</mapper>
测试类
import com.zxjava.dao.UserMapper;
import com.zxjava.pojo.User;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}
}
9.2、整合方式一
引入MyBatis的核心配置文件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
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
配置数据源,在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://localhost:3306/zxjava?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="971214"/>
</bean>
配置SqlSessionFactory并关联MyBatis
(在这里还可根据property标签配置设置、别名等)
<!--在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory的
,而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建
SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource对象,
它的配置方法和其它 Spring 数据库连接是一样的。
一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联MyBatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
注册sqlSessionTemplate,关联sqlSessionFactory
(SqlSessionTemplate中没有set方法,只能通过构造器注入)
<!--SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,
这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
编写一个实现类
package com.zxjava.dao;
import com.zxjava.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper{
//sqlSession不用我们自己创建了,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
然后把这个实现类注册到beans.xml中
<bean id="userMapperImpl" class="com.zxjava.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
测试
@Test
public void Spring_MyBatis_test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapperImpl userMapperImpl = context.getBean("userMapperImpl", UserMapperImpl.class);
List<User> users = userMapperImpl.selectUser();
for (User user : users) {
System.out.println(user);
}
}
这样Spring和MyBatis就整合完成了,通过在beans.xml中配置数据源、SqlSessionFactory等,MyBatis中的配置可以省去(习惯把和留在MyBatis核心配置文件中)
9.3整合方式二
直接让实现类继承SqlSessionDaoSupport就可直接获得sqlSession,这样就不需要注册sqlSessionTemplate了
编写实现类
package com.zxjava.dao;
import com.zxjava.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
把实现类在beans.xml中注册
<bean id="userMapperImpl2" class="com.zxjava.dao.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
这样Spring和MyBatis就整合完成了。
10、声明式事务
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
在接口中新增一个增加和删除接口
void insertUser(User user);
void deleteUser(int id);
编写映射文件(这里故意把删除语句写错)
<insert id="insertUser" parameterType="com.zxjava.pojo.User">
insert into user(id,name,password) values (#{id},#{name},#{password})
</insert>
<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>
编写实现类
package com.zxjava.dao;
import com.zxjava.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUser() {
User user = new User(4,"小李子","1212");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.insertUser(user);
mapper.deleteUser(4);
return mapper.selectUser();
}
@Override
public void insertUser(User user) {
}
@Override
public void deleteUser(int id) {
}
}
编写测试类
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper userMapperImpl = context.getBean("userMapperImpl", UserMapper.class);
userMapperImpl.selectUser();
}
在执行测试类之前,数据库中有3条数据,在执行测试类之后,即使执行时报错(删除语句故意写错),还会插入一条数据,这就像我们去银行取钱,钱取出来了,但是银行卡没有扣钱,所以我们的需求是,插入和删除同时成功或失败。
用Spring管理事务,首先导入约束
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
配置事务管理器
<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="selectUser" propagation="REQUIRED"/>
<!-- <tx:method name="*"/>-->
</tx:attributes>
</tx:advice>
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
配置AOP,配置之前导入约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.zxjava.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
这样再去测试的话,如果删除没有成功,插入也不会成功,就实现了这些动作要么全部完成,要么全部不起作用。