文章目录
- (补充)注解完成自动注入(一)
- (补充 ) Spring IOC 扫描器完成自动实例化非常好用,简便)
一、引言
1.1 原生web开发中存在哪些问题?
传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。
部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤)。
侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。
二、Spring框架
2.1 概念
Spring是一个项目管理框架,同时也是一套Java EE解决方案。为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。
2.2 访问与下载
官方网站:https://spring.io/
下载地址:http://repo.spring.io/release/org/springframework/spring/
三、Spring架构组成
Spring架构由诸多模块组成,可分类为
Spring架构组成 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h7tBmQQo-1672022500413)(Pictures/001.png)] |
GroupId | ArtifactId | 说明 |
---|---|---|
org.springframework | spring-beans | 提供了BeanFactory,Spring将管理对象称为Bean |
org.springframework | spring-aop | 基于代理的AOP支持 |
org.springframework | spring-aspects | 基于AspectJ 的切面 |
org.springframework | spring-context | 建立在Core和Beans模块的基础之上,ApplicationContext接口是Context模块的焦点 |
org.springframework | spring-context-support | 支持将常见的第三方类库集成到 Spring 应用上下文 |
org.springframework | spring-core | 提供了框架的基本组成部分,包括控制反转(IOC)和依赖注入(DI)功能 |
org.springframework | spring-expression | Spring 表达式语言,SpEL |
org.springframework | spring-instrument | JVM 引导的仪表(监测器)代理 |
org.springframework | spring-instrument-tomcat | Tomcat 的仪表(监测器)代理 |
org.springframework | spring-jdbc | 支持包括数据源设置和 JDBC 访问支持 |
org.springframework | spring-jms | 支持包括发送/接收JMS消息的助手类 |
org.springframework | spring-messaging | 对消息架构和协议的支持 |
org.springframework | spring-orm | 对象/关系映射,包括对 JPA 和 Hibernate 的支持 |
org.springframework | spring-oxm | 对象/XML 映射(Object/XML Mapping,OXM) |
org.springframework | spring-test | 单元测试和集成测试支持组件 |
org.springframework | spring-tx | 事务基础组件,包括对 DAO 的支持及 JCA 的集成 |
org.springframework | spring-web | web支持包,包括客户端及web远程调用 |
org.springframework | spring-webmvc | REST web 服务及 web 应用的 MVC 实现 |
org.springframework | spring-webmvc-portlet | 用于 Portlet 环境的MVC实现 |
org.springframework | spring-websocket | WebSocket 和 SockJS 实现,包括对 STOMP 的支持 |
org.springframework | spring-jcl | Jakarta Commons Logging 日志系统 |
四、自定义工厂
4.1 配置文件
userDao=com.qf.dao.UserDaoImpl
userService=com.qf.service.UserServiceImpl
4.2 工厂类
/**
* 自定义工厂
*/
public class MyFactory {
private Properties properties = new Properties();
public MyFactory(){}
public MyFactory(String config) throws IOException {
// 加载配置文件
properties.load(MyFactory.class.getResourceAsStream(config));
}
// 获取对象
public Object getBean(String beanName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 获得类路径
String classPath = properties.getProperty(beanName);
if(classPath!=null){
Class claz = null;
// 反射:加载类对象
claz = Class.forName(classPath);
// 反射:获得对象
return claz.newInstance();
}
return null;
}
}
测试,可以获取到对象
@Test
public class FactoryTest {
@Test
public void testFactory() throws IOException, IllegalAccessException, InstantiationException, ClassNotFoundException {
// /bean.properties:加了“/”表示从classpath路径下找,读取时不用getClassLoader()
// bean.properties:路径不为/开头,从当前类所在包下取,读取时要getClassLoader()
MyFactory factory = new MyFactory("/bean.properties");
UserDaoImpl userDao = (UserDaoImpl) factory.getBean("userDao");
System.out.println(userDao);
}
}
五、构建Maven项目
5.1 新建项目
使用IDEA打开已创建的文件夹目录 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mwkWTGxu-1672022500414)(Pictures/002.png)] |
5.2 选择Maven目录
选择Maven项目 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yu1OpiYv-1672022500414)(Pictures/003.png)] |
5.3 GAV坐标
GAV坐标 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nu0eroBG-1672022500415)(Pictures/004.png)] |
六、Spring环境搭建
6.1 pom.xml中引入Spring常用依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qf</groupId>
<artifactId>hello-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
</dependencies>
</project>
6.2 创建Spring配置文件
命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、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工厂编码
定义目标Bean类型
public class MyClass{
public void show(){
System.out.println("HelloWorld");
}
}
applicationContext.xml中的< beans >内部配置bean标签
<!-- 配置实例(id:“唯一标识” class="需要被创建的目标对象全限定名") -->
<bean id="mb" class="com.qf.bean.MyBean"></bean>
调用Spring工厂API(ApplicationContext接口)
public class TestFactory{
/**
* 程序中的对象都交由Spring的ApplicationContext工厂进行创建。
*/
public static void main(String[] args){
//1. 读取配置文件中所需创建的bean对象,并获得工厂对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 通过id获取bean对象
MyClass mc = (MyClass) ctx.getBean("mb");
//3. 使用对象
mc.show();
}
}
BeanFactory 与ApplicationContext
BeanFactory 是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用BeanFactory 来实例化、配置和管理 Bean。
BeanFactory:是IOC容器的核心接口, 它定义了IOC的基本功能,我们看到它主要定义了getBean方法。getBean方法是IOC容器获取bean对象和引发依赖注入的起点。方法的功能是返回特定的名称的Bean
如果说BeanFactory是Spring的心脏,那么ApplicationContext就是完整的躯体了,BeanFactorty接口提供了配置框架及基本功能,但是无法支持spring的aop功能和web应用。而ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展.BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
总结
- BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的生命周期。
- ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能
八、依赖与配置文件详解
Spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖Jar包实现功能。
8.1 Spring依赖关系
Spring常用功能的Jar包依赖关系 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dMD131qt-1672022500415)(Pictures/image-20191230164517693.png)] |
8.2 schema
配置文件中的顶级标签中包含了语义化标签的相关信息
- xmlns:语义化标签所在的命名空间。
- xmlns:xsi:XMLSchema-instance 标签遵循Schema标签标准。
- xsi:schemaLocation:xsd文件位置,用以描述标签语义、属性、取值范围等。
九、IoC(Inversion of Control )控制反转【重点
】
Inverse Of Controll:控制反转(对象的创建) 依赖注入(对象与对象之间的引用)
反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健
9.1 项目中强耦合问题
public class UserDaoImpl implements IUserDao{....}
public class UserServiceImpl implements IUserService {
// !!!强耦合了UserDaoImpl!!!,使得UserServiceImpl变得不稳健!!
private IUserDao userDao = new UserDaoImpl();
@Override
public void add() {
userDao.add();
}
}
9.2 解决方案
// 不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements IUserService {
// !!!不再耦合任何DAO实现!!!,消除不稳健因素!!
//为userDao定义set/get,允许userDao属性接收spring赋值
private IUserDao userDao;
@Override
public void add() {
userDao.add();
}
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
}
<!--创建userDaoImpl对象-->
<bean id="userDaoImpl" class="com.qf.dao.impl.UserDaoImpl"></bean>
<!--创建userServiceImpl对象-->
<bean id="userServiceImpl" class="com.qf.service.impl.UserServiceImpl">
<!-- 由spring为userDao属性赋值,值为id="userDaoImpl"的bean -->
<property name="userDao" ref="userDaoImpl"/>
</bean>
此时,如果需要更换其他UserDao实现类,则UserServiceImpl不用任何改动!
则此时的UserServiceImpl组件变得更加稳健!
9.3:IOC容器Bean对象的实例化方式
- 构造器实例化(Bean对象给了有参构造时,必须提供无参构造,不然出错)
- 静态工厂实例化(了解)
- 实例化工厂实例化(了解)
十、DI(Dependency Injection)依赖注入【重点
】
10.1 概念
在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
10.2 Set注入
创建对象时,Spring工厂会通过Set方法为对象的属性赋值。
10.2.1 定义目标Bean类型
public class User {
private Integer id;
private String password;
private String sex;
private Integer age;
private Date bornDate;
private String[] hobbys;
private Set<String> phones;
private List<String> names;
private Map<String,String> countries;
private Properties files;
//Getters And Setters
}
10.2.2 基本类型 + 字符串类型 + 日期类型
<bean id="u1" class="com.qf.entity.User">
<!--base field-->
<property name="id" value="1001" />
<property name="password" value="123456" />
<property name="sex" value="male" />
<property name="age" value="20" />
<property name="bornDate" value="1990/1/1" /><!--注意格式"/"-->
</bean>
10.2.3 容器类型
<bean id="u1" class="com.qf.entity.User">
<!--Array-->
<property name="hobbys">
<array>
<value>Run</value>
<value>Swim</value>
<value>Climb</value>
</array>
</property>
<!--Set-->
<property name="phones">
<set>
<value>13777777777</value>
<value>13888888888</value>
<value>13999999999</value>
</set>
</property>
<!--List-->
<property name="names">
<list>
<value>tom</value>
<value>jack</value>
<value>marry</value>
</list>
</property>
<!--Map-->
<property name="countries">
<map>
<entry key="CN" value="China" />
<entry key="US" value="America" />
<entry key="KR" value="Korea" />
</map>
</property>
<!--Properties-->
<property name="files">
<props>
<prop key="first">One</prop>
<prop key="second">Two</prop>
<prop key="third">Three</prop>
</props>
</property>
</bean>
10.2.4 自建类型
<!--次要bean,被作为属性-->
<bean id="addr" class="com.qf.entity.Address">
<property name="position" value="北京市海淀区" />
<property name="zipCode" value="100001" />
</bean>
<!--主要bean,操作的主体-->
<bean id="u2" class="com.qf.entity.User">
<property name="address" ref="addr" /><!--address属性引用addr对象-->
</bean>
<!--次要bean,被作为属性-->
<bean id="userDao" class="com.qf.dao.impl.UserDaoImpl" />
<!--主要bean,操作的主体-->
<bean id="userService" class="com.qf.service.impl.UserServiceImpl">
<property name="ud" ref="userDao" /><!--ud属性引用userDao对象-->
</bean>
10.3 构造注入【了解】
创建对象时,Spring工厂会通过构造方法为对象的属性赋值。
10.3.1 定义目标Bean类型
public class Student {
private Integer id;
private String name;
private String sex;
private Integer age;
//Constructors
public Student(Integer id , String name , String sex , Integer age){
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
}
10.3.2 注入
<!--构造注入-->
<bean id="u3" class="com.qf.entity.Student">
<constructor-arg name="id" value="1234" /> <!-- 除标签名称有变化,其他均和Set注入一致 -->
<constructor-arg name="name" value="tom" />
<constructor-arg name="age" value="20" />
<constructor-arg name="sex" value="male" />
</bean>
10.4 自动注入【了解】
不用在配置中 指定为哪个属性赋值,及赋什么值.
由spring自动根据某个 “原则” ,在工厂中查找一个bean,为属性注入属性值
public class UserServiceImpl implements IUserService {
//为userDao定义set/get,允许userDao属性接收spring赋值
private IUserDao userDao;
@Override
public void add() {
System.out.println("userService...add");
userDao.add();
}
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
}
<bean id="userDao" class="com.qf.dao.impl.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于类型自动注入值 -->
<bean id="userService" class="com.qf.service.impl.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.qf.dao.impl.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于名称自动注入值 -->
<bean id="userService" class="com.qf.service.impl.UserServiceImpl" autowire="byName"></bean>
autowire=“byName”:按名称自动依赖注入,找id名称和属性名称一样的bean给属性进行注入
autowire=“byType”:按类型自动依赖注入(找同一个接口类型的bean),类型注入失败则按名称注入,都不行则注入失败(同一类型的只能有一个 ,不然会出错)
(补充)注解完成自动注入(一)
1.前提: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启自动化注入(装配)环境-->
<context:annotation-config/>
<!--配置bean-->
<bean id="userDao" class="com.sin.dao.UserDao"/>
<bean id="userService" class="com.sin.service.UserService"/>
</beans>
注解一: @Resource(使用简单) 自动完后注入:
/**
* 1.注解实现自动注入(反射)
* 2.默认通过属性字段匹配随影bean对象(属性字段与id属性值一样)
* 3.字段不一致,类型匹配
* 4.set方法可以不用(以后就不提供了)
* 5.也可以注解在set方法级别
* 6.@Resource=(name="id")这种写法id要与标签的id属性值相匹配,
* 当注入借口时,实现类有多个时,可以使用这种上面id形式唯一对应
*/
public class UserService{
@Resource//注解自动注入
private UserDao userDao;
public void show(){
userDao.show();
System.out.println("userService...");
}
}
注解二: @Autowired :
- 与 @Resource 基本一致
- 不同点:注解默认使用的是类型查找bean对象,与属性名字段没有关系
- 如果想通过制定的名称去查找,需要再加个注解结合 @Qualifier(value=“id”)
(补充 ) Spring IOC 扫描器完成自动实例化非常好用,简便)
1.前提: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--Spring IOC扫描器 bean对象的统一管理-->
<context:component-scan base-package="com.sin"/>
</beans>
-
只需在对应的类上加上对应的注解就行
-
配置文件中 注意bean对象要在扫描范围内 :<context:component-scan base-package=“com.sin”/>
-
@Repository:dao层的
-
@Service service层的
-
@Controller controller 层的
-
@Component 任意层的
实例化时: 默认都是通过要实例化类的首字母小写获取对
@Controller
public class ControllerTest {
@Resource
private ServiceType serviceType;
public void test(){
serviceType.test();
System.out.println("来啦自动实例化...");
}
}
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
默认都是通过要实例化类的首字母小写获取对
ControllerTest controllerTest = (ControllerTest) context.getBean("controllerTest");
controllerTest.test();
}
}
十一、Bean细节(作用域)
11.1 控制简单对象的单例、多例模式
配置< bean scope=“singleton | prototype” />
<!--
singleton(默认):每次调用工厂,得到的都是同一个对象。
prototype:每次调用工厂,都会创建新的对象。
-->
<bean id="userServiceImpl" class="com.qf.service.impl.UserServiceImpl" scope="singleton"/>
- 注意:需要根据场景决定对象的单例、多例模式。
- 可以共用:Service、DAO、SqlSessionFactory(或者是所有的工厂)。
- 不可共用:Connection、SqlSession、ShoppingCart。
补充:
Bean的作用域
singleton作用域(单例作用域)
Spring IOC容器在启动时,会将所有在singleton作用域中的bean对象实例化,并设置到单例缓存池中
lazy-init属性(懒加载)
如果设置为true,表示懒加载,容器在启动时,不会实例化bean对象,在程序调用时才会实例化如果设置false(默认),表示不懒加载,容器启动则实例化bean对象
lazy-init属性为什么要设置为false?
1.可以提前发现潜在的配置文件
2.Bean对象在启动时就会设置在单例缓存池中,使用时不需要再去实例化bean对象,提高程序运行
什么对象适合作为单例对象?(什么对象适合交给IOc容器实例化?)
-
无状态的对象
-
(不存在改变当前对象状态的成员变量)使用对象:controller层、service层、dao层
11.2 FactoryBean创建复杂对象【了解】
作用:让Spring可以创建复杂对象、或者无法直接通过反射创建的对象。
FactoryBean解决复杂对象创建 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ybGkAFZh-1672022500416)(Pictures/image-20190419235128663.png)] |
11.2.1 实现FactoryBean接口
接口方法描述 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-viq8V6aE-1672022500416)(Pictures/image-20190419234550731.png)] |
- 注意:isSingleton方法的返回值,需根据所创建对象的特点决定返回true/false。
- 例如:Connection 不应该被多个用户共享,返回false。
- 例如:SqlSessionFactory 重量级资源,不该过多创建,返回true。
11.2.2 配置application.xml
配置与获取方式 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xHtG7Pl5-1672022500417)(Pictures/image-20190419235939298.png)] |
11.2.3 特例
获取FactoryBean接口的实现类对象,而非getObject()所生产的对象。 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4qLpIgz-1672022500417)(Pictures/image-20190420000713143.png)] |
十二、Spring工厂特性
12.1 饿汉式创建优势
工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。
提高程序运行效率。避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)
12.2 生命周期方法
自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。
自定义销毁方法:添加“destroy-method”属性,Spring则会在销毁对象之前,调用此方法。
销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象。
分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。
<bean id="userServiceImpl" class="com.qf.service.impl.UserServiceImpl" autowire="byType" scope="singleton" init-method="init" destroy-method="destory">
</bean>
public class UserServiceImpl implements IUserService {
//为userDao定义set/get,允许userDao属性接收spring赋值
private IUserDao userDao;
@Override
public void add() {
System.out.println("userService...add");
userDao.add();
}
@Override
public void init() {
System.out.println("init");
}
@Override
public void destory() {
System.out.println("destory");
}
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
}
@Test
public void test() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService userService = (IUserService) context.getBean("userServiceImpl");
userService.add();
context.close();
}
12.3 生命周期注解
初始化注解、销毁注解,则bean标签中不用再加init-method属性及destroy-method属性
public class UserServiceImpl implements IUserService {
//为userDao定义set/get,允许userDao属性接收spring赋值
private IUserDao userDao;
@Override
public void add() {
System.out.println("userService...add");
userDao.add();
}
@PostConstruct //初始化
@Override
public void init() {
System.out.println("init");
}
@PreDestroy //销毁
@Override
public void destory() {
System.out.println("destory");
}
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
}
application.xml文件要开启扫描注解
<context:component-scan base-package="com.qf"/>
12.4 生命周期阶段
**单例bean:**singleton
随工厂启动创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》随工厂关闭销毁
**多例bean:**prototype
被使用时创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》JVM垃圾回收销毁
十三、代理设计模式
13.1 概念
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。
功能分离 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dEu3wCDu-1672022500418)(Pictures/image-20190420002535800.png)] |
13.2 静态代理设计模式
通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。
静态代理 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5GZeqq9o-1672022500418)(Pictures/image-20190420004330551.png)] |
- 代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法。
- 静态代理的问题
- 代理类数量过多,不利于项目的管理。
- 多个代理类的辅助功能代码冗余,修改时,维护性差。
13.3 动态代理设计模式
动态创建代理类的对象,为原始类的对象添加辅助功能。
13.3.1 JDK动态代理实现(基于接口)
public class JDKProxy {
public static void main(String[] args){
//目标对象
IUserService userService = new UserServiceImpl();
//设置回调函数处理类,添加额外功能
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start");
Object invoke = method.invoke(userService, args);
System.out.println("end");
return invoke;
}
};
//得到代理对象
IUserService userServiceProxy = (IUserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler);
//调用方法
userServiceProxy.hello();
}
}
13.3.2 CGlib动态代理实现(基于类)
static{
//用于生成cglib字节码文件,生成文件的保存路径为:D:\
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\");
}
public class CglibProxy {
public static void main(String[] args){
//CGLIB提供的字节码增强类
Enhancer enhancer = new Enhancer();
//设置父类型,等价于实现原始类接口
enhancer.setSuperclass(UserServiceImpl.class);
//设置回调函数(额外功能代码)
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("begin");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("end");
return invoke;
}
});
//创建子类代理对象
UserServiceImpl userServiceProxy = (UserServiceImpl) enhancer.create();
userServiceProxy.hello();
}
}
这种实现方式存在三个明显需要改进的地方
- 目标类的所有方法都添加了辅助功能,而有时,这并不是我们所期望的,我们可能只希望对业务类中的某些特定方法添加辅助功能;
- 我们通过硬编码的方式指定了织入辅助功能的织入点,即在目标类业务方法的开始和结束前织入代码;
- 我们手工编写代理实例的创建过程,为不同类创建代理时,需要分别编写相应的创建代码,无法做到通用。
十四、面向切面编程【重点
】
14.1 概念
AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
14.2 AOP开发术语
连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。相当数据库里的记录
切入点(Pointcut):被Spring切入连接点。相当于查询语句
通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。
目标对象(Target):代理的目标对象
织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
代理(Proxy):被AOP织入通知后,产生的结果类。
切面(Aspect):由切点和通知组成,将通知织入切面所指定的连接点中。
14.3 作用
Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。另一种方式是aspectJ
14.4 环境搭建
引入AOP相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
application.xml引入AOP命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
</beans>
14.5 开发流程
定义原始类
package com.qf.service;
public interface IUserService {
public void save();
}
package com.qf.service.impl;
public class UserServiceImpl implements IUserService {
public void save() {
System.out.println("save method executed...");
}
}
定义通知类(添加额外功能),添加spring-aop依赖
package com.qf.advice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
public class MyAdvice implements MethodBeforeAdvice {//实现前置通知接口
@Override
public void before(Method method, Object[] objects, @Nullable Object o) throws Throwable {
System.out.println("before advice executed...");
}
}
测试类代码
@Test
public void testBeforeAdvice(){
//目标对象
IUserService userService = new UserServiceImpl();
//通知
MyAdvice beforeAdvice = new MyAdvice();
//创建代理工厂
ProxyFactory factory = new ProxyFactory();
//设置目标对象
factory.setTarget(userService);
//设置通知
factory.addAdvice(beforeAdvice);
//得到代理对象
IUserService proxy = (IUserService) factory.getProxy();
//调用方法
proxy.save();
}
14.6 通知(增强)类(实现下面的接口)(不推荐使用)
定义通知类,达到通知效果
前置通知:MethodBeforeAdvice
后置通知:AfterReturningAdvice //有异常不执行,方法会因异常而结束,无返回值
异常通知:ThrowsAdvice
环绕通知:MethodInterceptor (methodInvocation.proceed()//要调用下这个方法)
public void afterThrowing(Method method, Object[] args, Object target, Exception e) throws Throwable {
System.out.println("method=" + method.getName());
System.out.println("抛出异常:" + e.getMessage());
System.out.println("成功回滚事务");
}
14.7 AspectJ配置切面的方式一(注解)(推荐使用)
定义切面类
@Aspect
public class MyAspect {
@Before("execution(* save(..))")
public void start(){
System.out.println("before advice executed...");
}
}
定义bean标签
<!--自动为Spring容器中那些匹配@AspectJ切面范围的Bean创建代理,完成切面织入-->
<aop:aspectj-autoproxy/>
<!--原始对象-->
<bean id="us" class="com.qf.service.impl.UserServiceImpl" />
<!--辅助对象-->
<bean class="com.qf.aspect.MyAspect" />
测试代码
@Test
public void testBeforeAdvice2(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService us = (IUserService) context.getBean("us");
us.save();
}
14.8 通知注解
@Before
前置增强,相当于BeforeAdvice的功能
@AfterReturning
后置增强,相当于AfterReturningAdvice
@Around
环绕增强,相当于MethodInterceptor
@AfterThrowing
抛出增强,相当于ThrowsAdvice
@After 最终通知
Final增强,不管是抛出异常或者是正常退出,该增强都会得到执行,该增强没有对应的增强接口,可以把它看成ThrowsAdvice和AfterReturningAdvice的混合物,一般用于释放资源,相当于try{}finally{}
环绕增强
ProceedingJoinPoint
proceed
14.9 AspectJ配置切面方式二(xml)(推荐使用)
定义切入点(PointCut)和 增强(辅助)信息
形成切面(Aspect)
<!--原始对象-->
<bean id="us" class="com.qf.service.impl.UserServiceImpl" />
<!--辅助对象-->
<bean id="transactionManager" class="com.qf.aspect.TransactionManager"/>
<!--配置切面信息-->
<aop:config>
<aop:aspect ref="transactionManager">
<!--切点-->
<aop:pointcut id="pt" expression="execution(* save(..))"/>
<!--增强(通知)-->
<aop:before method="start" pointcut-ref="pt"/>
<aop:after method="commit" pointcut-ref="pt"/>
<aop:around method="around" pointcut-ref="pt"/>//环绕通知
</aop:aspect>
</aop:config>
//环绕通知的辅助类
//@Around("execution(* save(..))")注解的方式
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前");
String result =(String) pjp.proceed();
if (result!=null){
result= result.toUpperCase();
}
System.out.println("环绕通知后");
return result;
测试代码
@Test
public void testBeforeAdvice2(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService us = (IUserService) context.getBean("us");
us.save();
}
14.10 通配切入点示例
根据表达式通配切入点
<!--匹配参数-->
<aop:pointcut id="myPointCut" expression="execution(* *(com.qf.entity.User))" />
<!--匹配方法名(无参)-->
<aop:pointcut id="myPointCut" expression="execution(* save())" />
<!--匹配方法名(任意参数)-->
<aop:pointcut id="myPointCut" expression="execution(* save(..))" />
<!--匹配返回值类型-->
<aop:pointcut id="myPointCut" expression="execution(com.qf.entity.User *(..))" />
<!--匹配类名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.service.impl.UserServiceImpl.*(..))" />
<!--匹配包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.service.impl.*.*(..))" />
<!--匹配包名、以及子包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.service..*.*(..))" />
14.11 AOP小结
通过AOP提供的编码流程,更便利的定制切面,更方便的定制了动态代理。
进而彻底解决了辅助功能冗余的问题;
业务类中职责单一性得到更好保障;
辅助功能也有很好的复用性。
14.12 JDK和CGLIB选择
spring底层,包含了jdk代理和cglib代理两种动态代理生成机制
基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
class DefaultAopProxyFactory{
// 该方法中明确定义了 JDK代理和CGLib代理的选取规则
// 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
public AopProxy createAopProxy(){...}
}
十五、Spring + MyBatis【重点
】
15.1 创建一个web工程spring-mybatis,并配置数据源
将数据源配置到项目中
<dependencies>
<!--MyBatis核心依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--MySql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>
15.1.1 引入jdbc.properties配置文件
#jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
15.1.2 整合Spring配置文件和properties配置文件
<!--application.xml-->
<?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"
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:property-placeholder location="classpath:jdbc.properties" />
<!--与PooledDataSource集成(二选一)-->
<bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--与DruidDataSource集成(二选一)-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!--基本配置-->
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</bean>
15.1.3 Druid连接池可选参数
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!--基本配置-->
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- druid后台监控显示SQL -->
<property name="filters" value="stat,wall,log4j"/>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${jdbc.init}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000"/>
</bean>
15.1.4 Druid监控中心
<!--web.xml-->
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
15.1.5 测试监控中心
配置tomcat,并访问protocol://ip:port/project/druid/index.html ,需要通过浏览器去访问数据界面才会有内容
例如:http://localhost:8080/druid/index.html
druid监控平台 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y7b72R97-1672022500419)(Pictures\druid监控平台.png)] |
15.2 整合MyBatis
将 SqlSessionFactory、DAO、Service 配置到项目中
15.2.1 导入依赖
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- spring+mybatis集成依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
15.2.2 配置SqlSessionFactory
<!-- 工厂bean:生成SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入连接池 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 加载mapper.xml文件信息 ,如果映射文件和dao接口 同包且同名,则此配置可省略-->
<property name="mapperLocations">
<list>
<value>classpath:com/qf/dao/*.xml</value>
</list>
</property>
<!-- 为 dao-mapper文件中的实体 定义缺省包路径
如:<select id="queryAll" resultType="User"> 中 User类可以不定义包
-->
<property name="typeAliasesPackage" value="com.qf.entity"></property>
</bean>
15.2.3 配置MapperScannerConfigurer
管理DAO实现类的创建,并创建DAO对象,存入工厂管理
扫描所有DAO接口,去构建DAO实现
将DAO实现存入工厂管理
DAO实现对象在工厂中的id是:“首字母小写的-接口的类名”,
例如:UserMapper==>userMapper , OrderDAO==>orderDAO
<!-- mapperScannerConfigurer -->
<bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- dao接口所在的包 如果有多个包,可以用逗号或分号分隔
<property name="basePackage" value="com.a.dao,com.b.dao"></property>
-->
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.getList();
//生成代理类 动态代理实现
<property name="basePackage" value="com.qf.dao"></property>
<!-- 如果工厂中只有一个SqlSessionFactory的bean,此配置可省略 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
15.2.4 service编码
public class UserServiceImpl implements IUserService {
private UserMapper userMapper;
@Override
public List<User> list() {
return userMapper.list();
}
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
15.2.5 配置mapper
public interface UserMapper {
List<User> list();
}
<?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.qf.dao.UserMapper">
<select id="list" resultType="com.qf.entity.User">
select * from t_users
</select>
</mapper>
15.2.6 配置Service
<bean id="userServiceImpl" class="com.qf.service.impl.UserServiceImpl">
<!-- 注意ref中的值是对应DAO接口的首字母小写的接口名 -->
<property name="userMapper" ref="userMapper"></property>
</bean>
15.2.7 pom文件配置扫描mapper.xml,并让其编译
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.xml</include><!-- 默认(新添加自定义则失效) -->
<include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService userService = (IUserService) context.getBean("userServiceImpl");
List<User> list = userService.list();
for (User user : list) {
System.out.println(user);
}
}
十六、事务【重点
】
16.1 配置DataSourceTransactionManager
事务管理器,其中持有DataSource,可以控制事务功能(commit,rollback等)。
<!-- 1. 引入一个事务管理器,其中依赖DataSource,借以获得连接,进而控制事务逻辑 -->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
注意:DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一个DataSource的Bean,否则事务控制失败!!!
####16.2 配置事务通知
基于事务管理器,进一步定制,生成一个额外功能:Advice。
此Advice可以切入任何需要事务的方法,通过事务管理器为方法控制事务。
application.xml引入tx声明
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>
<tx:advice id="txAdvice" transaction-manager="tx">
<tx:attributes>
<!--<tx:method name="insertUser" rollback-for="Exception" isolation="DEFAULT"
propagation="REQUIRED" read-only="false"/>-->
<!-- 以query开头的方法,切入此方法时,采用对应事务实行 -->
<tx:method name="query*" propagation="SUPPORTS"/>
<tx:method name="find*" propagation="SUPPORTS"/>
<!-- 剩余所有方法 切入此方法时,采用对应事务实行-->
<tx:method name="*"rollback-for="Exception" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
16.3 事务属性
16.3.1 隔离级别
16.3.1.1 概念
isolation
隔离级别
名称 | 描述 |
---|---|
default | (默认值)(采用数据库的默认的设置) (建议) |
read-uncommited | 读未提交 |
read-commited | 读提交 (Oracle数据库默认的隔离级别) |
repeatable-read | 可重复读 (MySQL数据库默认的隔离级别) |
serialized-read | 序列化读 |
隔离级别由低到高为:read-uncommited < read-commited < repeatable-read < serialized-read
16.3.1.2 特性
安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。
并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。
16.3.1.3 并发问题
事务并发时的安全问题
问题 | 描述 |
---|---|
脏读 | 一个事务读取到另一个事务还未提交的数据。大于等于 read-commited 可防止 |
不可重复读 | 一个事务内多次读取一行数据的相同内容,其结果不一致。大于等于 repeatable-read 可防止 |
幻影读 | 一个事务内多次读取一张表中的相同内容,其结果不一致。serialized-read 可防止 |
16.3.2 传播行为
propagation
传播行为
当涉及到事务嵌套(Service调用Service)时,可以设置:
SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询)
REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。 (默认值)(适合增删改)
16.3.3 读写性
readonly
读写性
true:只读,可提高查询效率。(适合查询)
false:可读可写。 (默认值)(适合增删改)
16.3.4 事务超时
timeout
事务超时时间
当前事务所需操作的数据被其他事务占用,则等待。
- 100:自定义等待时间100(秒)。
- -1:由数据库指定等待时间,默认值。(建议)
16.3.5 事务回滚
rollback-for
回滚属性
如果事务中抛出 RuntimeException,则自动回滚
如果事务中抛出 CheckException(非运行时异常 Exception),不会自动回滚,而是默认提交事务
处理方案 : 将CheckException转换成RuntimException上抛,或 设置 rollback-for=“Exception”
16.4 编织
将事务管理的Advice 切入需要事务的业务方法中,注意需要引入aspectj的依赖
<aop:config>
<aop:pointcut expression="execution(* com.qf.service.UserServiceImpl.*(..))" id="pc"/>
<!-- 组织切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
web.xml配置监听器,启动spring容器 servlet+spring+mybatis CRUD
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
十七、注解开发
17.1 声明bean
用于替换自建类型组件的 <bean…>标签;可以更快速的声明bean
@Service 业务类专用
@Repository dao实现类专用
@Controller web层专用@Component 通用
@Scope 用户控制bean的创建模式
// @Service说明 此类是一个业务类,需要将此类纳入工厂 等价替换掉 <bean class="xxx.UserServiceImpl">
// @Service默认beanId == 首字母小写的类名"userServiceImpl"
// @Service("userService") 自定义beanId为"userService"
@Service //声明bean,且id="userServiceImpl"
@Scope("singleton") //声明创建模式,默认为单例模式 ;@Scope("prototype")即可设置为多例模式
public class UserServiceImpl implements IUserService {
...
}
17.2 注入(DI)
用于完成bean中属性值的注入
- @Autowired 基于类型自动注入
- @Resource 基于名称自动注入
- @Qualifier(“userDao”) 限定要自动注入的bean的id,一般和@Autowired联用
- @Value 注入简单类型数据 (jdk8种+String)
@Service
public class UserServiceImpl implements UserService {
@Autowired //注入类型为UserDAO的bean
@Qualifier("userMapper") //如果有多个类型为userMapper的bean,可以用此注解从中挑选一个
private UserMapper userMapper;
}
@Service
public class UserServiceImpl implements UserService {
@Resource("userMapper") //注入id=“userMapper”的bean
private UserMapper userMapper;
}
17.3 事务控制
用于控制事务切入
@Transactional
工厂配置中的 <tx:advice… 和 <aop:config… 可以省略 !!
//类中的每个方法都切入事务(有自己的事务控制的方法除外)
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class,timeout = -1)
public class UserServiceImpl implements IUserService {
...
//该方法自己的事务控制,仅对此方法有效
@Transactional(propagation=Propagation.SUPPORTS)
public List<User> queryAll() {
return userDao.queryAll();
}
public void save(User user){
userDao.save(user);
}
}
17.4 注解所需配置
<!-- 告知spring,哪些包中 有被注解的类、方法、属性 -->
<!-- <context:component-scan base-package="com.qf.a,com.xx.b"></context:component-scan> -->
<context:component-scan base-package="com.qf"></context:component-scan>
<!-- 告知spring,@Transactional在定制事务时,基于txManager=DataSourceTransactionManager -->
<tx:annotation-driven transaction-manager="txManager"/>
17.5 AOP开发
17.5.1 注解使用
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAspect {
// 定义切入点
@Pointcut("execution(* com.qf.service.impl.UserServiceImpl.*(..))")
public void pc(){}
@Before("pc()") // 前置通知
public void mybefore(JoinPoint a) {
System.out.println("target:"+a.getTarget());
System.out.println("args:"+a.getArgs());
System.out.println("method's name:"+a.getSignature().getName());
System.out.println("before~~~~");
}
@AfterReturning(value="pc()",returning="ret") // 后置通知
public void myAfterReturning(JoinPoint a,Object ret){
System.out.println("after~~~~:"+ret);
}
@Around("pc()") // 环绕通知
public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
System.out.println("interceptor1~~~~");
Object ret = p.proceed();
System.out.println("interceptor2~~~~");
return ret;
}
@AfterThrowing(value="pc()",throwing="ex") // 异常通知
public void myThrows(JoinPoint jp,Exception ex){
System.out.println("throws");
System.out.println("===="+ex.getMessage());
}
}
17.5.2 配置
<!-- 添加如下配置,启用aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
十八、集成JUnit(方便测试后端代码)
18.1 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
18.2 编码
可以免去工厂的创建过程;
可以直接将要测试的组件注入到测试类。
@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner启动测试
@ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件位置
public class SpringTest{//当前测试类也会被纳入工厂中,所以其中属性可以注入
@Autowired // 注入要测试的组件
private IUserService userService;
@Test
public void test(){
userService.deleteUser(8);
}
}