一 spring概述
1.1 web项目开发中的耦合度问题
微信小程序搜索 万神小栈 更多资源等你发现! 如果文章对你有帮助别忘了点赞加关注喔~
-
在servlet中需要调用
service
中的方法,则需要在servlet类中通过new关键字创建service 的实例 -
public interface productService{ public list<product> list products(); }
-
public class productServiceImpl1 implements ProductService{ public list<Product> listOriducts(){ //查询热销商品 } }
-
public class productServiceImpl2 implements ProductService{ public list<Product> listOriducts(){ //查询好评商品 } }
-
public class ProductListServlet extends HttpServlet{ //在servlet中使用new 关键字 创建 productServiceImpl 对象 //增加了servlet和service的耦合度 }
-
在service实现类中需要调用DAO中的方法,也需要在service实现类通过new关键字创建DAO实现类对象
-
如果在使用new关键字创建对象 则会导致以下问题:
- 失去了面向接口编程的灵活性
- 代码的侵入性增强(增加了
耦合度
) 降低了代码的灵活性
1.2面向接口编程
面向接口编程 |
---|
![]() |
解决方案
: 在servlet中定义service接口的对象变了,不使用new关键字创建实现类对象,在servlet的实例化的时候,通过反射动态的给service对象变量赋值。
如何实现?
spring可以做到
1.3spring 介绍
spring是一个
轻量级的控制反转和面向切面的容器
框架,用来解决企业项目开发的复杂度问题–>解藕
- 轻量级: 体积小,对代码没有侵入性
- 控制反转:IOC(inverse of Control),把创建对象的工作交由Spring完成,Spring在创建对象的时候同时可以完成对象属性赋值(DI–>依赖注入)
- 面向切面:AOP(Aspect Oriented Programming)面向切面编程,可以在不改变原有业务的逻辑的情况下实现对业务的增强
- 容器: 实例的容器 管理创建的对象
spring–>润物细无声
没有具体实质性的功能 Mybatis数据库操作 Spring MVC数据接收(控制者) Spring 1.帮助创建管理对象 2.增强项目的业务 --> 帮助别人的框架使用更方便
1.4 Spring架构图
1.4.1 Core Container
Spring容器组件,用于完成实例的创建和管理
- Core 核心
- beans 依赖于核心 做对象管理或 实例管理
- Context Spring上下文或容器上下文
1.4.2 AOP Aspects
上面两个组成Spring AOP组件 实现面向切面编程
- AOP
- Aspects
1.4.3 web
Spring web组件 实际上指的是 Spring MVC框架,实现web项目的MVC控制
- web组件(Spring对web项目的支持)
- web MCVC (Spring MVC组件)
1.4.4 Data Access
Spring 数据访问组件 也是基于JDBC封装的持久层框架(即使没有Mybatis Spring也可以完成持久化)
- tx 事物管理组件
1.4.5 Test
Spring的单元测试组件 提供了Spring环境下的单元测试支持
- test
二 Spring Ioc
Spring ioc 容器组件,可以完成对象的创建 对象属性赋值 对象管理
2.1 Spring 框架部署(ioc)
2.1.1 创建Maven工程
- Java
- web
2.1.2添加 Spring ioc依赖
- core
- beans
- aop
- express
counext
导入之后不需要到入其他的了
<!--Spring beans-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!--Spring core-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!--Spring context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
2.1.3 创建Spring 配置文件
通过配置文件“告诉” Spring 容器创建什么对象 给对象属性赋什么值
- 在resource目录下 创建名为
applictionCountext.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">
<!--对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则-->
<!--通常一个框架为了让开发者能够正确配置,都会提供xml的规范文件(dtd/xsd)-->
</beans>
2.2 spring ioc使用
使用Springioc 组件创建并管理对象
2.2.1创建一个类
/**
* @author WanShen
* @date 2022年02月24日 11:30 PM
*/
public class Student {
private String stuNum;
private int stuAge;
private String stuSex;
private Date enterenceTime; //入学日期
}
2.2.2在Spring配置文件中配置实体类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过bean将实体类配置给Spring进行管理 ID表示 实体类的唯一标识-->
<bean id="student" class="com.bean.Student">
<property name="stuNum" value="张三" />
<property name="stuAge" value="13" />
<property name="stuSex" value="男" />
</bean>
</beans>
2.2.3初始化Spring对象工厂,获取对象
- ClassPathXmlApplicationContext
//通过Spring容器创建student对象
//1.初始化Spring容器
ClassPathXmlApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过Spring容器获取student对象
Student student = (Student) context.getBean("student");
System.out.println(student);
2.3ioc和DI
- ioc(inverse of Control)控制反转,依赖Spring对象工厂完成对象的创建–>工厂设计模式
- DI(dependency injection) 依赖注入,在Spring完成对象创建的同时依赖Spring容器完成对象属性的赋值
2.3.1 ioc
当我们需要通过Spring对象工厂创建某个累的对象的时候,需要将这个类交给Spring管理–>通过bean标签配置
<bean id="student" class="com.bean.Student"></bean>
<bean id="bool" class="com.bean.Book"></bean>
2.3.2 DI
通过Spring容器给创建的对象属性赋值
<bean id="student" class="com.bean.Student">
<property name="stuNum" value="张三" />
<property name="stuAge" value="13" />
<property name="stuSex" value="男" />
<property name="enterenceTime" ref="date" />
</bean>
2.4 DI依赖注入
2.4.1依赖注入三种方式
Spring容器加载配置文件之后通过反射创建类的对象,并给属性赋值;Spring通过反射实现属性注入有三种方式:
- set方法注入
- 构造器注入
- 接口注入(不常用)
2.4.2 set方法注入
在bean标签中通过配置property标签给对象属性赋值,实际上就是通过反射调用set方法完成属性的注入
简单类型及字符串
简单类型指String double这些
- 直接通过property标签的value属性赋值
日期对象
- 方式1:在property标签中通过ref引用Spring
<bean id="student" class="com.bean.Student">
<property name="enterenceTime" ref="date" />
</bean>
<bean id="date" class="java.util.Date"/>
- 方式2: 在property标签中添加子标签bean来指定对象
<bean id="student" class="com.bean.Student">
<property name="enterenceTime">
<bean class="java.util.Date" />
</property>
</bean>
集合类型
-
List
- List List中的元素是字符串或者简单类型的封装类
<property name="hobbit" value="看电影,旅游" />
<property name="hobbit"> <list> <value>看电影</value> <value>旅游</value> </list> </property>
- ListList中的元素是对象类型
<property name="hobbit"> <list> <bean class="com.bean.ClassZz" /> <bean class="com.bean.ClassZz" /> <bean class="com.bean.ClassZz" /> </list> </property>
- 第二种方法: 上面定义bean cls引用
<property name="hobbit"> <list> <ref bean="cls"></ref><!--引用容器中的bean--> <ref bean="cls"></ref> <ref bean="cls"></ref> </list> </property>
-
set
-
<property name="classZz"> <set> <!--和List元素注入方式相同--> </set> </property>
-
Map
-
<property name="map"> <map> <entry> <key> <value>k1</value> </key> <value>123</value> </entry> <entry> <key> <value>k2</value> </key> <value>456</value> </entry> </map> </property>
-
Properties 可以直接给键值对 (String类型)
<property name="properties">
<props>
<prop key="k1">123</prop>
<prop key="k2">456</prop>
</props>
</property>
2.4.3 构造器注入
使用被Spring管理的类的构造器来完成属性的赋值 常用set方法注入 构造器注入使用少
简单类型 字符串 对象 需要对应顺序 可以使用index索引
<bean id="cls" class="com.bean.ClassZz"></bean>
<bean id="stu" class="com.bean.Student">
<constructor-arg value="张三" />
<!--<constructor-arg index="0" value="张三" />-->
<constructor-arg value="18" />
<constructor-arg value="男" />
<constructor-arg value="162.5" />
<constructor-arg>
<bean class="java.util.Date"></bean>
</constructor-arg>
<constructor-arg ref="cls" />
</bean>
集合类型属性:
public class Student{
private List<String> hobbit; //爱好
private Properties properties;
private Map map;
private Set set;
public Student(Properties properties, Map map, Set set) {
this.properties = properties;
this.map = map;
this.set = set;
}
}
<bean id="stu1" class="com.bean.Student">
<constructor-arg>
<list>
<value>111list</value>
</list>
</constructor-arg>
<constructor-arg>
<map>
<entry>
<key><value>key1</value></key>
<value>values1</value>
</entry>
</map>
</constructor-arg>
<constructor-arg>
<set>
<value>set集合</value>
</set>
</constructor-arg>
<constructor-arg>
<props>
<prop key="key22">propers</prop>
</props>
</constructor-arg>
</bean>
2.5 bean的作用域
在bean标签中可以通过scope属性 指定对象的作用域
- scope=“singleton” 表示当前bean是单例模式(默认饿汉模式,Spring容器初始化阶段就会完成此对象的创建;当在bean标签中设置
lazy-init=“true”
变为懒汉模式)- scope=“prorotype”表示当前bean为非单例模式,每次通过Spring容器获取此bean对象的时候都会创建一个新的对象
-
单例
-
<bean id="cls" class="com.bean.ClassZz" scope="singleton" lazy-init="true"></bean>
-
多例
-
<bean id="cls" class="com.bean.ClassZz" scope="prototype"></bean>
2.6 bean的生命周期方法
在Bean标签中通过init-method属性指定当前bean的初始化方法 初始化方法在构造器之后执行
在Bean标签中通过destroy-method=属性指定当前bean的销毁方法 销毁方法在对象销毁之前执行
- Bean类
package com.bean;
/**
* @author WanShen
* @date 2022年02月26日 4:28 PM
*/
public class Book {
private int bookId;
private String bookName;
public void init(){
System.out.println("------init");
this.bookId=1;
this.bookName="初始值";
}
public void destroy(){
System.out.println("销毁!");
}
public Book() {
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
}
- Spring配置文件
<bean id="book" class="com.bean.Book" scope="prototype" init-method="init" destroy-method="destroy"></bean>
2.7 自动装配
自动装配: Spring在实例化当前Bean的时候从Spring容器中找到匹配的实例赋值给当前Bean的属性
- byName 根据当前bean的属性名在Spring容器中·寻找匹配的对象 如果根据name找到了bean但是类型不匹配则抛出异常
- byType 根据当前bean的属性类型在Spring容器中寻找匹配的对象 如果根据类型找到了多个类型匹配的bean也会抛出异常
<bean id="student" class="com.bean.Student" autowire="byName" ></bean>
<bean id="student" class="com.bean.Student" autowire="byType" ></bean>
2.8 Spring 工作原理
三 Spring Ioc-基于注解
Spring ioc的使用需要我们通过xml将类声明给Spring容器进行管理从而通过Spring工厂完成对象的创建及属性值的注入;
Spring除了提供基于xml的配置方式,同时提供了基于注解的配置,直接在实体类中添加注解声明给Spring容器管理 以简化开发步骤
3.1 Spring 框架部署
3.1.1创建maven 项目
略过
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
3.1.2 创建xml配置文件
- 因为Spring容器初始化时,只会加载applicationConetx.xml 那么我们在实体类中添加的注解就不会被Spring扫描 所以我们需要
在applicationContext.xml声明Spring的扫描范围
,以达Spring初始化时扫描带有注解的实体类并完成初始化工作。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围-->
<context:component-scan base-package="com.beans"/>
</beans>
3.2 ioC常用注解
3.2.1 @Component
- 类注解,声明此类被Spring容器进行管理,相当于bean标签的作用
@Component(value="stu")
value属性用于指定当前bean的id,相当于bean标签的id属性;value属性也可以省略,如果省略 当前类的id默认为类名首字母改小写- 除了@Compent之外 @Service、@Controller、@Repository这三个注解也可以将类声明Spring管理,他们主要是语义上的区别
- @Controller注解 主要声明将控制器类配置给Spring管理,例如Servlet
- @Service注解 主要声明业务处理类配置给Spring管理,Service接口的实现类
- @Respository 注解 主要声明持久化类配置给Spring管理,DAO接口
- @Component 除了控制器、service和DAO之外的类一律使用此注解声明
3.2.2 @Scope
注解
- 类注解: 写在类上面的注解叫
类注解
用于声明当前类单例模式还是非单例模式 相当于bean标签的scope属性 - @Scope(“prototype”)表示声明当前类为非单例模式(默认单例模式)
3.2.3@Lazy
注解
- 类注解:用于声明一个单例模式的Bean是否为懒汉模式
- @Lazy(true)表示声明为懒汉模式,默认为饿汉模式
3.2.4 @PostConstruct
- 方法注解: 声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性
3.2.5 @PreDestroy
- 方法注解:声明一个方法为当前类的销毁化方法(在对象从容器中释放之前执行),相当于bean标签的destroy-method属性
3.2.6 @Autowired
-
属性注解,方法注解(set方法) 声明当前属性自动装配,默认byType
-
@Autowired(required = false) 通过required属性设置当前自动装配是否为必须 默认必须(如果没有找到类型与属性类型匹配的bean则抛出异常)
- byType
- ref引用
@Autowired public void setClazz(@Qualifier("c1") Clazz clazz) { this.clazz = clazz; }
3.2.7 @Resource
- 属性注解:也用于声明属性自动装配
- 默认装配方式为byName,如果根据byName没有找到对应的bean,则继续根据byType继续寻找对应的bean根据byType如果依然没有找到bean或者找到不止一个类型匹配的bean则抛出异常。
四、代理设计模式
5.1 生活中的代理
代理设计模式的优点:将通用性的工作都交给代理对象完成✅ 被代理对象只需专注自己的核心业务。
5.2 静态代理
静态代理,代理类只能够为特定的生产代理对象,不能代理任意类
流程: BookDAO(接口) BookDAOImpl(接口实现类) Myproxy(代理类)
两个以上的话 代理类封装接口–>提供有参构造方法–> 通过代理可以两个类切换
使用代理的好处
- 被代理类中只用关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离—>
高内聚 低耦合
- 将通用的代码放在代理类中实现,提高了代码的复用性。
- 通过在代理类添加业务逻辑,实现对原有业务逻辑的拓展(增强)
5.3 动态代理
动态代理,几乎可以为所有的类产生代理对象
动态代理的实现方式有2种:
- JDK动态代理
- CGLIB动态代理
- JDK动态代理类实现:
package com.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author WanShen
* @date 2022年03月07日 11:07 PM
* JDK动态代理:是通过被代理对象实现的接口产生其代理对象的
* 1.创建一个类,实现InvocationHandler 重写invoke方法
* 2.在类中定义一个Object类型的变量,用于传递被代理对象 并提供这个对象的有参构造器
* 用于将被代理的类传递进来
* 3.定义getProxy()方法 用于创建返回对象
* 4.重写invoke方法;
*/
public class JDKDynamicProxy implements InvocationHandler {
// 被代理对象
private Object object;
public JDKDynamicProxy(Object object) {
this.object = object;
}
// 产生代理对象,返回代理对象
public Object getProxy() {
// 1.获取被代理对象的类加载器
ClassLoader classLoader = object.getClass().getClassLoader();
// 2.获取被代理对象的类实现的接口
Class<?>[] interfaces = object.getClass().getInterfaces();
// 3.产生代理对象(通过被代理对象的类加载器以及实现的接口)
// 第一个参数:被代理对象的类加载器
// 第二个参数:被代理对象实现的接口
// 第三个参数:使用产生的代理对象调用方法时用于拦截方法执行时的处理器
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, this);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
try {
// 方法执行前
begin();
returnValue = method.invoke(object, args);
// 方法执行后
commit();
} catch (Exception e) {
e.printStackTrace();
// 发生异常后
rollback();
}
return returnValue;
}
public void begin() {
System.out.println("--开启事务");
}
public void commit() {
System.out.println("--提交事物");
}
public void rollback() {
System.out.println("--回滚事务");
}
- 测试:
// 被代理对象
BookDapImpl bookDao = new BookDapImpl();
// 创建动态代理类对象,并将代理类对象传递到代理类中赋值给obj
JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(bookDao);
// proxy就是产生的代理对象:可以强转成被代理对象实现的接口类型
GeneralDAO proxy = (GeneralDAO) jdkDynamicProxy.getProxy();
// 使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定的InvocationHandler类中的invoke方法
// 调用的方法作为一个Method参数给了invoke方法
proxy.delete();
**限制:**只能为实现了接口的类产生代理;
4.3.2 CGLib动态代理
由于JDK动态代理是通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类的对象.如果一个类没有任何实现接口该如何产生代理对象呢?
CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此 即使没有实现任何接口的类也可以通过CGLib产生代理对象,CGLib动态代理 不能为final类创建代理对象;
- 添加CGLib的依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
- CGLib动态代理实现:
package com.dao;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author WanShen
* @date 2022年03月08日 12:36 PM
* 1.添加CGLib依赖
* 2.创建一个类,实现MethodInterceptor接口 同时实现interceptor方法
* 3.在类中定义一个Object类型的变量 并提供这个变量的有参构造器,用于传递被代理对象
* 4.定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的)
*/
public class CGLibDynamicProxy implements MethodInterceptor {
private Object object;
public CGLibDynamicProxy(Object object) {
this.object = object;
}
public Object getProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(object.getClass());
enhancer.setCallback(this);
Object proxy = enhancer.create();
return proxy;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
begin();
Object returnValue = method.invoke(object,objects);
commit();
return returnValue;
}
public void begin(){
System.out.println("--开启事务");
}
public void commit(){
System.out.println("--提交事物");
}
}
- 测试:
// 被代理对象
BookDapImpl bookDao = new BookDapImpl();
//通过CGLib动态代理类创建代理对象
CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(new BookDapImpl());
//代理对象实际上是被代理对象的子类,因此代理对象可以直接强转为被代理类类型
BookDapImpl proxy = (BookDapImpl) cgLibDynamicProxy.getProxy();
//使用对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法 将当前调用的方法的参数传递到intercept方法
proxy.delete();
五、Spring Aop
5.1 AOP概念
Aspect Oriented Programming面向切面编程,是一种利用“横切”的技术(底层实现就是动态代理) 对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特定的业务逻辑,对原有的业务进行增强。
基于动态代理实现在不改变原有业务的情况下对业务逻辑进行增强
5.2 Spring AOP框架部署
5.2.1 创建Maven项目
5.2.2添加依赖
- context
- aspects
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
5.2.3 创建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">
</beans>
5.3 AOP配置-基于xml
在DAO的方法添加开启事物和提交事物的逻辑
5.3.1 创建一个类,定义要添加的业务逻辑
package com.utils;
/**
* @author WanShen
* @date 2022年03月08日 5:42 PM
*/
public class TxManger {
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交食物");
}
}
5.3.2 配置AOP
<aop:pointcut id="book_all" expression="execution(* com.dao.*.*(..))"/>
execution(* com.dao.*.*(..))
第一个 * 是 所有返回类型
第二个 * 是com.dao下所有的类的所有方法
<?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 class="com.dao.BookDapImpl" id="bookDap"></bean>
<bean id="txManger" class="com.utils.TxManger"></bean>
<aop:config>
<!--声明切入点-->
<aop:pointcut id="book_all" expression="execution(* com.dao.*.*(..))"/>
<!--声明txManger为切面类-->
<aop:aspect ref="txManger">
<!--通知-->
<aop:before method="begin" pointcut-ref="book_all" />
<aop:after method="commit" pointcut-ref="book_all" />
</aop:aspect>
</aop:config>
</beans>
AOP开发步骤:
- 创建切面类,在切面类定义切点方法
- 将切面类配置给Spring容器
- 声明切入点
- 配置AOP的通知策略
5.4 切入点的声明:
<!--使用aop:pointcut标签声明切入点: 切入点可以是一个方法-->
<aop:pointcut id="book_pc1" expression="execution(* com.dao.BookDapImpl.insert())"/>
<!--BookDapImpl类中 类中所有无参数无返回值的方法-->
<aop:pointcut id="book_pc2" expression="execution(void com.dao.BookDapImpl.*())"/>
<!--BookDapImpl类 中所有无返回值的方法-->
<aop:pointcut id="book_pc3" expression="execution(void com.dao.BookDapImpl.*(..))"/>
<!--BookDapImpl类 中所有无参数的方法-->
<aop:pointcut id="book_pc4" expression="execution(* com.dao.BookDapImpl.*())"/>
<!--BookDapImpl类 中的所有方法-->
<aop:pointcut id="pc5" expression="execution(* com.dao.BookDapImpl.*(..))"/>
<!--dao包中所有类的所有方法-->
<aop:pointcut id="pc6" expression="execution(* com.dao.*.*(..))"/>
<!--所有包中所有类的所有方法-->
<aop:pointcut id="pc7" expression="execution(* *(..))"/>
5.4.2 AOP使用注意事项:
如果要使用Spring aop切面编程,调用切入点方法的对象必须通过Spring容器获取
如果一个类中的方法被声明为切入点之后, 通过spring容器获取对象 实则获取到的是一个代理对象。
如果一个类中的方法没有被声明为切入点,通过Spring容器获取的就是这个类真实创建的对象。
5.5 AOP通知策略
AOP通知策略: 就是声明将切面类中的切点方法如何织入到切入点
5.5.1 定义切面类
package com.utils;
/**
* @author WanShen
* @date 2022年03月08日 5:42 PM
*/
public class TxManger {
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交事物");
}
}
5.5.2 配置切面类
<aop:config>
<!--所有包中所有类的所有方法-->
<aop:pointcut id="pc7" expression="execution(* *(..))"/>
<!--声明txManger为切面类-->
<aop:aspect ref="txManger">
<!--aop:before 前置通知 切入到指定切点之前-->
<aop:before method="begin" pointcut-ref="pc7" />
<!--aop:before 后置通知 切入到指定切点之后-->
<aop:after method="begin" pointcut-ref="pc7" />
<!--aop:after-throwing 异常通知 切入点抛出异常之后-->
<aop:after-throwing method="begin" pointcut-ref="pc7" />
<!--aop:after-returning 方法返回值之后,对于一个java方法而言
return返回值也是方法的一部分因此"方法返回值返回之后"和"方法执行结束"
是同一个时间点,所以after和after-returning根据配置的顺序决定执行顺序
-->
<aop:after-returning method="begin" pointcut-ref="pc7" />
<!--aop:around 环绕通知-->
<aop:around method="begin" pointcut-ref="pc7" />
</aop:aspect>
</aop:config>
六、Spring AOP注解配置
6.1 Spring AOP注解配置框架部署
6.1.1 创建Maven工程
6.1.2 添加Spring依赖
- context
- aspects
6.1.3 Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--声明使用注解-->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围-->
<context:component-scan base-package="com.dao"/>
<!--基于注解配置的AOP代理-->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
</beans>
6.2 AOP注解配置 案例:
package com.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author WanShen
* @date 2022年03月09日 1:42 PM
*/
@Component //声明Spring容器管理此类
@Aspect //声明此类是切面类
public class TransactionManager {
@Pointcut("execution(* com.dao.*.*(..))")
public void Pointcut(){}
@Before("Pointcut()")
public void begin() {
System.out.println("开启事物");
}
@After("Pointcut()")
public void commit() {
System.out.println("提交事物");
}
@Around("Pointcut()")
public Object printExecudate(ProceedingJoinPoint point) throws Throwable {
long tim1 = System.currentTimeMillis();
Object v = point.proceed();
long tim2 = System.currentTimeMillis();
System.out.println("执行时间:"+(tim2-tim1));
return v;
}
}
注意:
注解使用虽然方便 但只能在源码上添加注解,因此我们的自定义提倡使用注解配置;如果使用到第三方提供的类则需要通过xml配置形式完成配置。
七、Spring整合MyBatis
Spring 量大核心思想: ioc和aop
ioc: 控制反转, Spring容器可以完成对象的创建、属性注入、对象管理等工作
AOP: 面向切面, 在不修改原有业务逻辑的情况下,实现对原有业务的增强
7.1 Spring可以对MyBatis提供哪些支持?
7.1.1SpringioC
- 需要创建数据源DataSource
- 需要创建SqlSessionFactory对象
- 需要创建sql Session对象
- 需要创建DAO对象(Mapper)
**IOC支持:**SpringioC可以为Mybatis完成DataSource、SqlSessionFactory、SqlSession以及DAO对象的创建
AOP支持使用Spring提供的事物管理切面类完成对MyBatis数据库操作中的事物管理
7.2 spring整合MyBatis准备工作
7.2.1 创建Maven工程
7.2.2 部署Mybatis框架
- 添加依赖
- Mysql驱动
- Mybatis
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
- 创建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>
</configuration>
7.2.3 部署Spring框架
- 添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!--切面 aop的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.R ELEASE</version>
</dependency>
<!--Spring对持久层的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
- 创建Spring配置文件: 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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
7.2.4 添加Spring整合Mybatis的依赖
- Mybatis-Spring 就是Mybatis提供的兼容Spring的补丁
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
7.3 Spring整合MyBatis整合配置
7.3.1整合Druid连接池
- 添加Druid到的依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<!--druid连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
- 创建Druid.properties属性文件
druid.driver=com.mysql.Driver
druid.url=jdbc:mysql://localhost:3306/date_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false;
druid.username=root
druid.password=admin
# 连接池参数
druid.pool.init=1
druid.pool.minIdle=3
druid.pool.maxActive=20
druid.pool.timeout=30000
- 在applicationContext.xml中配置DruidDataSource
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--加载druid.properties属性文件-->
<context:property-placeholder location="classpath:druid.properties" />
<bean id="driver" class="com.mysql.jdbc.Driver"/>
<!--依赖Spring容器完成对DataSource的创建-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driver" ref="driver"/>
<property name="url" value="${druid.url}" />
<property name="username" value="${druid.username}" />
<property name="password" value="${druid.password}" />
<property name="initialSize" value="${druid.pool.init}" />
<property name="minIdle" value="${druid.pool.minIdle}" />
<property name="maxActive" value="${druid.pool.maxActive}" />
<property name="maxWait" value="${druid.pool.timeout}" />
</bean>
</beans>
7.3.2 整合MyBatis-创建sqlSessionFactory
<!--依赖Spring容器完成MyBatis的SqlSessionFactory对象创建-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" autowire="byName">
<!--配置数据源-->
<!-- <property name="dataSource" ref="dataSource" />-->
<!--配置Mapper文件的位置/路径-->
<!-- <property name="mapperLocations" value="classpath:mapper/*Mapper.xml" />-->
<!--配置需要定义别名的实体类的包-->
<property name="typeAliasesPackage" value="com.wanshen.pojo"/>
<!--可选:配置Mybatis的主配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
7.3.3 整合MyBatis-创建Mapper
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<property name="basePackage" value="com.wanshen.dao" />
</bean>
7.4 Spring整合MyBatis整合ioC配置
使用Spring提供的事物管理切面类 完成DAO中增删改操作的事物管理
- propagation:传播属性=> 发生在多个业务之间事物的传递过程
外层实物的概念解释:
一个比较复杂的业务中,往往会出现在一个service方法中,调用其他的service方法(可能是同一类也可能是其他类的service方法)
某个service方法中 s1 s2 s3需要调用s1 s2 那么s3本身就是service中的方法 有事务 s1 s2也有事务
S3相对于S1 s2来讲就是 外层事物
- isolation 事务隔离
不可重复度=>一方更新导致两次查询结果不一致
7.4.1 事物的隔离级别
isolation 设置事务隔离级别: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE
READ_UNCOMMITTED: 读 未提交-> T1在执行的过程中 T2既可以读也可以写 T1可以提取到T2未提交的数据(脏读)、(不可重复读)、(幻读)
READ_COMMITTED: 读 已提交 -> T1在执行的过程中T2可以读也可以写但是T1只能读到T2提交后的数据(不可重复读)、(幻读)
REPEATABLE_READ: 可重复读 -> T1 在执行的过程中T2只能读但不能写 T2可以添加数据(幻读)
SERIALIZABLE:串行化/序列化-> T1 在执行的过程中T2既不能读也不能写
事务的隔离级别越高 会导致事务的安全性高 但是效率降低
7.4.2 事务的传播机制
propagation 事务的传播机制:
REQUIRED 如果上层方法没有事务,则创建一个新的事务 如果已存在事务则加入到事务中.
SUPPORTS 如果上层方法没有事务,则以非事务方式执行 如果已存在事务则加入到事务中.
REQUIRES_NEW 如果上层方法没有事务,则创建一个新的事务 如果已经存在事务,则将当前事务挂起.
NOT_SUPPORTED 如果上层方法没有事务,则以非事务方式执行 如果已存在事务则将当前事务挂起.
NEVER 如果上层方法没有事务,则以非事务方式执行 如果已存在事务则抛出异常.
MANDATORY 如果上层方法已经存在事务,则加入到事务中执行,如果不存在事务则抛出异常
NESTED 如果上层方法没有事务,则创建一个新的事务 如果已存在事务则嵌套到当前事务中.
一般 增删改用REQUIRED 查询用SUPPORTS 日志类用 REQUIRES_NEW
7.4.3 Spring AOP事务管理配置–XML配置
<!--1.将Spring提供的事务管理类配置给Spring容器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.通过Spring提供的tx标签 声明事务管理策略-->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
<tx:method name="delete" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
<tx:method name="update" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
<tx:method name="query" isolation="REPEATABLE_READ" propagation="NOT_SUPPORTED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="crud" expression="execution(* com.wanshen.dao.*.*(..))"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="crud" />
</aop:config>
⚠️注意: 事务在dao中不明智 应在service中 因为一个service中包含多个dao的操作
应该为:
<aop:pointcut id="crud" expression="execution(* com.wanshen.service.*.*(..))"/>
7.4.4 Spring AOP事务管理配置–注解配置
- 在applicationContext.xml配置事务管理,声明使用注解方式进行事物配置
<!--1.将Spring提供的事务管理类配置给Spring容器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.声明使用注解完成事务配置-->
<tx:annotation-driven transaction-manager="transactionManager" />
- 在需要Spring进行事物管理的方法上添加
@Transactional
注解
package com.wanshen.service;
import com.wanshen.dao.UserDao;
import com.wanshen.pojo.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
/**
* @author WanShen
* @date 2022年03月10日 7:21 PM
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.SUPPORTS)
public List<User> listUsers() {
return userDao.queryUsers();
}
}
补充知识:log4j(了解)
log4j(log for java)Java的日志框架。
log4j中的概念
日志级别:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL (从高到低),级别越高,输出的日志越少(比如设置为WARN时,是输出WARN、ERROR和FATAL级别)。
日志分类:根日志(rootLogger,全局日志)、分支日志(logger,包级别的日志),其中根日志必须存在。
log4j的使用
-
引入依赖
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency>
-
配置(必须是src/main/resources/log4j.properties),不需要会写这个配置,能看懂和修改即可
# 根日志级别ERROR,输出到stdout log4j.rootLogger=ERROR,stdout # 设置stdout的输出使用ConsoleAppender(控制台) log4j.appender.stdout=org.apache.log4j.ConsoleAppender # 设置stdout的显示方式为PatternLayout(自定义格式) log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # 设置stdout的格式 %p为日志级别 %t为线程名 %d为日期{格式} %m为主线程日志 %n为换行 log4j.appender.stdout.layout.ConversionPattern=%p [%t] %d{yyyy-MM-dd HH:mm:ss} - %m%n # 设置com.baizhi.dao包的日志级别为DEBUG,用来输出SQL语句 log4j.logger.com.baizhi.dao=DEBUG # 推荐开发项目 根级别日志为ERROR # 输出自己代码中所有的日志信息 log4j.logger.com.baizhi = DEBUG # 把dao所在的子包设置为ERROR,因为SQL语句帮助不是很大 log4j.logger.com.baizhi.dao = ERROR
-
自己使用日志对象来输出日志
//成员变量中 //参数一般为当前类的类对象 private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class); //想输出日志的地方 LOGGER.debug("debug"); LOGGER.info("info"); LOGGER.warn("warn"); LOGGER.error("error"); //也可以采用{}占位符输出形式 LOGGER.info("name: {}, id: {}", user.getName(), user.getId());
补充知识:MD5加盐加密(Spring版)
加密(了解)
加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。
信息在加密之前称为明文,在加密之后称为密文。
MD5(了解)
严格来说,MD5算法的主要作用不是加密,而是生成数据特征码。这个特征码有以下两个性质:
- 针对相同的数据,特征码一定是相同的。
- 针对不同的数据,特征码一般是不同的。
因为数据的种类远远超过特征码能表示的种类,因此存在特征码相同时,数据不同的情况。但实际上重复的概率极低。
MD5加密的缺点:如果对比较短的数据进行加密,会很容易破解。
盐(了解)
对原始数据(明文)前面或后面拼接一段数据,使得明文比较长,在经过MD5加密之后变得不容易破解。这段被用来拼接的数据就叫做盐。
注册和登录的逻辑
- 注册:接收到用户密码后,先随机生成一个盐,接下来把密码按照这个盐进行MD5加盐加密。同时把盐和加密后的密码存入数据库。
- 登录:根据用户名查询到密码和盐,把用户输入的密码也加盐加密,如果和数据库中加盐加密后的密码相同,则表示输入正确。
获得字符串的MD5特征码
java代码:
String str = "test";
String md5 = DigestUtils.md5DigestAsHex(str.getBytes());
封装工具类
public static String getSalt(){
return getSalt(20);
}
public static String getSalt(int size){
char[] pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for(int i = 0;i < size;i ++){
sb.append(pool[random.nextInt(pool.length)]);
}
return sb.toString();
}
public static String getPassword(String pass, String salt){
pass = pass + salt;
return DigestUtils.md5DigestAsHex(pass.getBytes());
}
补充知识:事务的ACID特性
数据库的事务有ACID特性:
- 原子性(atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性(consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,数据库可以自发性地完成预定的工作。
- 隔离性(isolation):数据库允许多个并发事务同时对其数据进行读写的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致的情况。
- 持久性(durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
SM整合(Spring框架结合Mybatis框架使用)
1.引入依赖
在spring依赖的基础上,加入以下依赖:
<dependency><!--用来控制事务-->
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency><!--mybatis-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency><!--mybatis结合spring使用的依赖-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency><!--mysql数据库驱动-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<dependency><!--阿里巴巴德鲁伊连接池-->
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
2.mybatis相关
建表
CREATE TABLE `t_user` (
`id` varchar(40) NOT NULL,
`name` varchar(40) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`bir` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
实体类
public class User implements Serializable{
private String id;
private String name;
private Integer age;
private Date bir;
//构造、getter/setter、toString等
}
dao接口
package com.baizhi.dao;
public interface UserDAO {
List<User> findAll();
void save(User user);
}
mapper
<?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.baizhi.dao.UserDao">
<select id="findAll" resultType="com.baizhi.entity.User">
SELECT id,name,age,bir FROM t_user
</select>
<insert id="save" parameterType="com.baizhi.entity.User">
INSERT INTO t_user (id, `name`, age, bir) VALUES (#{id}, #{name}, #{age}, #{bir})
</insert>
</mapper>
mybatis-spring官方整合中不再需要mybatis主配置文件。
3.整合配置文件
<!--引入db小配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--创建数据源对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</bean>
<!--创建sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入DataSource对象-->
<property name="dataSource" ref="dataSource"/>
<!--注入Mapper文件位置-->
<property name="mapperLocations" value="classpath:com/baizhi/mapper/*.xml"/>
<!--实体类别名(非必须,建议不使用这个而在Mapper中实体类写上类的全限定名)-->
<property name="typeAliasesPackage" value="com.baizhi.entity"/>
</bean>
<!--自动注册DAO,自动注册的id为接口首字母小写-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--sqlSessionFactoryBean的bean名字-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--所有dao接口所在的包名-->
<property name="basePackage" value="com.baizhi.dao"/>
</bean>
4.启动工厂测试DAO
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
UserDAO userDao = (UserDAO) ctx.getBean("userDao");
List<User> users = userDao.findAll();
System.out.println(users);
5.在Service层控制事务
1.Service接口
public interface UserService {
List<User> findAll();
void save(User user);
}
2.Service实现类
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
@Override
public List<User> findAll() {
return userDAO.findAll();
}
@Override
public void save(User user) {
userDao.save(user);
}
}
3.在配置文件中添加
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务属性,这相当于创建了一个专门完成事务控制的通知对象,这个id就是通知对象的id-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*"/>
<tx:method name="update*"/>
<tx:method name="delete*"/>
<tx:method name="find*" propagation="SUPPORTS"/>
<tx:method name="log*" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>
<!--配置事务切面-->
<aop:config>
<aop:pointcut id="pc" expression="within(com.baizhi.service.*ServiceImpl)"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
<!--注册所有的Service组件-->
<bean id="userService" class="com.baizhi.service.UserServiceImpl" autowire="byType"/>
事务属性特别说明:
增删改使用默认事务属性、查询使用
propagation="SUPPORTS"
、日志相关使用propagation="REQUIRES_NEW"
是常见的实现,这并不一定符合所有的业务需要。
4.启动工厂测试
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ctx.getBean("userService");
User user = new User();
user.setName("test1");
userService.save(user);
SS整合(Spring框架结合Struts2框架使用)
1.引入依赖
在spring依赖的基础上,加入以下依赖:
<!--struts2-->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.16</version>
</dependency>
<!--javaee-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--struts2-spring-plugin 版本号和struts2-core要一致-->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.16</version>
</dependency>
2.web.xml配置
<!--设置spring配置文件的位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--在项目启动时自动启动spring工厂-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3.开发Action类
public class UserAction extends ActionSupport {
public String hello(){
System.out.println("action: hello");
return ActionSupport.SUCCESS;
}
}
4.工厂管理Action
所有的Action都需要使用工厂创建
<!--配置Action的bean,scope需要为prototype-->
<bean id="userAction" class="com.baizhi.action.UserAction" scope="prototype"/>
5.配置struts.xml
<package name="hello" extends="struts-default" namespace="/hello">
<!--class为工厂中的名字-->
<action name="hello" class="userAction" method="hello">
<result name="success">/index.jsp</result>
</action>
</package>
6.部署项目测试
创建复杂对象(了解)
复杂对象:类中没有构造方法或者构造方法不能调用,如接口类型或抽象类实例。
package com.baizhi.factorybean;
public class ConnectionFactoryBean implements FactoryBean<Connection> {
@Override
//用来指定复杂对象的创建方式(返回创建好的对象)
public Connection getObject() throws Exception {
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/ajax", "root", "1234");
return connection;
}
@Override
//用来指定复杂对象的类型(返回指定复杂对象的类对象)
public Class<?> getObjectType() {
return Connection.class;
}
@Override
//用来指定复杂对象的创建次数(返回是否是单例)
public boolean isSingleton() {
return false;
}
}
<bean id="connection" class="com.baizhi.factorybean.ConnectionFactoryBean"/>
SSM整合开发时的重点注意事项(总结)
当使用配置式开发时
-
spring.xml中手动注册所有的service实现类和action类,在service bean上注入dao,在action bean上注入service。例如:
<!--注册所有service--> <bean class="com.baizhi.service.UserServiceImpl" id="userService" autowire="byType"/> <bean class="com.baizhi.service.CityServiceImpl" id="cityService" autowire="byType"/> <bean class="com.baizhi.service.AdminServiceImpl" id="adminService" autowire="byType"/> <!--注册所有action,别忘了scope="prototype",根据情况,自动注入时也可能是byName--> <bean class="com.baizhi.action.UserAction" id="userAction" scope="prototype" autowire="byType"/> <bean class="com.baizhi.action.CityAction" id="cityAction" scope="prototype" autowire="byType"/> <bean class="com.baizhi.action.AdminAction" id="adminAction" scope="prototype" autowire="byType"/>
-
所有service实现类中使用到的所有dao都是成员属性,且提供setter方法,不能通过直接通过
getMapper()
等形式进行自身管理;所有action类中的使用到的所有service都是成员属性,且提供setter方法,不能直接通过new XxxServiceImpl()
等形式进行自身管理。例如:public class UserServiceImpl implements UserService{ private UserDao userDao; private CityDao cityDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void setCityDao(CityDao cityDao) { this.cityDao = cityDao; } } public class UserAction{ private UserService userService; private CityService cityService; public void setUserService(UserService userService) { this.userService = userService; } public void setCityService(CityService cityService) { this.cityService = cityService; } }
-
事务属性name写成
save*
、update*
、delete*
、log*
、find*
并不是固定代码,这么写的前提是:- 所有service中的所有添加方法均以
save
开头 - 所有service中的所有更新方法均以
update
开头 - 所有service中的所有删除方法均以
delete
开头 - 所有service中的所有日志记录方法均以
log
开头 - 所有service中的所有查询方法均以
find
开头
- 所有service中的所有添加方法均以
-
struts.xml
中的class不要写全限定名而是写action对象在Spring工厂中的id(name)。
当使用注解式开发时
-
不要忘了在配置文件中加入
<context:component-scan base-package="com.baizhi"/>
-
不要忘了在配置文件中加入
<tx:annotation-driven transaction-manager="transactionManager"/>
,同时,不用再配置事务属性和配置事务切面。 -
所有service实现类上都使用
@Service
注册自身到工厂;并使用@Transactional
注解开启事务,同时,当前类中不是采用默认事务属性的service方法需要单独设置@Transactional
的事务属性,比如查询类方法@Transactional(propagation = Propagation.SUPPORTS)
等;其中使用到的所有dao都是成员属性,且使用@Autowired
进行自动注入。例如:@Service @Transactional public class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; @Autowired private CityDao cityDao; @Override public void save(User user) { userDao.insert(user); } @Override @Transactional(propagation = Propagation.SUPPORTS) public List<User> findAll() { return userDao.selectAll(); } }
-
所有action类上都使用
@Controller
注册自身到工厂;使用@Scope("prototype")
声明为非单例;struts.xml
中的class不要写全限定名而是写action对象在工厂中的id(name);其中使用到的所有service都是成员属性,且使用@Autowired
或@Resource
进行自动注入。例如:@Controller @Scope("prototype") public class UserAction { @Autowired private UserService userService; @Autowired private CityService cityService; }
<package name="user" extends="struts-default" namespace="/user"> <!--class为工厂中的id--> <!--使用@Controller注册的对象,默认id为类名首字母小写--> <action name="findAll" class="userAction" method="findAll"> <result name="success">/index.jsp</result> </action> </package>