目录
一、@Configuration和@Component的区别
九、IoC(Inversion of Control )控制反转【重点】
十、DI(Dependency Injection)依赖注入【重点】
15.1.2 整合Spring配置文件和properties配置文件
15.2.3 配置MapperScannerConfigurer
一、jdbcTemplate使用(xml配置)
底层就是使用aop思想
依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!-- jdbcTemplate-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- 数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.9</version>
</dependency>
<!-- 数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
public class User {
private Integer id;
private String name;
1.先配置数据源
2.把数据源设置到jdbcTemplate中
<?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">
<!-- 1.先配置数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="jdbc:mysql:///test?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<!-- 连接池基本信息可以省略-->
<!-- <property name="maxActive" value="100"/>-->
<!-- <property name="initialSize" value="20"/>-->
<!-- <property name="maxWait" value="5000"/>-->
<!-- <property name="minIdle" value="10"/>-->
</bean>
<!-- 2.配置jdbcTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!-- jdbcTemplate设置数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<bean class="com.qf.demo.dao.UserDao" id="userDao">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
public class JdbcTemplateDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext cxt =new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = cxt.getBean(UserDao.class);
User user = new User();
user.setId(3);
user.setName("文静");
int i = userDao.addUser(user);
System.out.println(i);
}
也可以使用配置文件
<!-- 引入配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 1.先配置数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="maxActive" value="${db.maxActive}"/>
<property name="initialSize" value="${db.initialSize}"/>
<property name="maxWait" value="${db.maxWait}"/>
<property name="minIdle" value="${db.minIdle}"/>
</bean>
一、jdbcTemplate使用(java代码配置)
package com.qf.demo02.dao;
import com.qf.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
//注册到spring容器中
@Repository
public class UserDao {
//自动注入spring容器中的jdbcTemplate
@Autowired
JdbcTemplate jdbcTemplate;
public int addUser(User user){
int update = jdbcTemplate.update("insert into user (id,name) values (?,?)", user.getId(), user.getName());
return update;
}
public int deleteUserById(Integer id){
int update = jdbcTemplate.update("delete from user where id =?", id);
return update;
}
// 更加id查询对象,BeanPropertyRowMapper,相当于DBUtils里面的BeanHandler
public User getUserById(Integer id){
User user = jdbcTemplate.queryForObject("select * from user where id = ?", new BeanPropertyRowMapper<User>(User.class), id);
return user;
}
//查询所有,BeanPropertyRowMapper
public List<User> getAllUsers(){
List<User> query = jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<User>(User.class));
return query;
}
}
1.配置类注解
2.扫描包注解
3.加载配置文件注解PropertySource("classpath:db.properties")、context:property-placeholder标签
4.注册springBean字节
package com.qf.demo02.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
//扫描这个包下的注解,注册到spring容器中
@ComponentScan(basePackages = "com.qf.demo02")
//@PropertySource注解作用是加载db.properties配置文件,他的作用类似于
//xml的 context:property-placeholder标签
@PropertySource("classpath:db.properties")
public class JavaConfig {
//@Value这个注解表示将db.url的值绑定在url变量上
@Value("${db.url}")
String url;
@Value("${db.password}")
String password;
@Value("${db.username}")
String username;
@Value("${db.maxWait}")
Long maxWait;
@Value("${db.minIdle}")
int minIdle;
@Value("${db.initialSize}")
Integer initialSize;
@Value("${db.maxActive}")
Integer maxActive;
// 原始的方法
// @Bean表示这个方法的返回值注册到spring中
// @Bean
// DataSource dataSource(){
// DruidDataSource ds = new DruidDataSource();
// ds.setUsername("root");
// ds.setPassword("root");
// ds.setUrl("jdbc:mysql:///test?serverTimeZone=Asia/Shanghai");
// return ds;
// }
@Bean
DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUrl(url);
ds.setPassword(password);
ds.setUsername(username);
ds.setMaxActive(maxActive);
ds.setMaxWait(maxWait);
ds.setInitialSize(initialSize);
ds.setMinIdle(minIdle);
return ds;
}
@Bean
JdbcTemplate jdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
}
一、AOP
Calculator
package com.qfedu.demo.service;
public interface Calculator {
int add(int a, int b);
void minus(int a, int b);
}
CalculatorImpl
package com.qfedu.demo.service;
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
// int i = 1 / 0;
return a + b;
}
@Override
public void minus(int a, int b) {
System.out.println(a + "-" + b + "=" + (a - b));
}
}
LogAspect
package com.qfedu.demo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
/**
* 这是我的日志切面类
* <p>
* Spring AOP 包含五种通知:
* <p>
* 1. 前置通知:目标方法执行直接触发
* 2. 后置通知:目标方法执行之后触发
* 3. 返回通知:当目标方法有返回值的时候触发
* 4. 异常通知:当目标方法抛出异常的时候触发(全局异常处理可用他)
* 5. 环绕通知:以上四个的集大成者
*/
public class LogAspect {
/**
* 这是前置通知,目标方法执行之前,这个方法会被触发
* 目标方法也就是我们要拦截的方法
*/
public void before(JoinPoint jp) {
//获取目标方法的名称(被拦截的方法名称)
String name = jp.getSignature().getName();
System.out.println(name + " 方法开始执行了。。。");
}
public void after(JoinPoint jp) {
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println(name + " 方法执行结束了...");
}
/**
* 返回通知
* <p>
* 我想知道目标方法的返回值到底是多少?
* <p>
* 注意这个接收目标方法返回值的参数的类型,必须要匹配,用了 int,那么当返回值类型为 int 的时候,才会进入到当前方法中
*
* @param jp
*/
public void returning(JoinPoint jp, Object result) {
System.out.println("返回通知。。。" + result);
}
/**
* 异常通知
* 只有当目标方法抛出异常的时候,这个方法会被触发
* <p>
* 注意,只有拦截的异常类型能够匹配上实际抛出的异常,这个方法才会被触发。
* 例如如果这里的参数是 ArithmeticException,实际抛出的异常是空指针异常,那么这个方法就不会被触发
* <p>
* 如果向拦截所有异常,那么这里的参数类型可以使用 Exception
*
* @param jp
*/
public void exception(JoinPoint jp, ArithmeticException e) {
System.out.println(jp.getSignature().getName() + " 方法抛出异常了 " + e.getMessage());
}
/**
* 环绕通知,这个是集大成者
*/
public Object around(ProceedingJoinPoint pjp) {
//这个方法类似于之前的 method.invoke() 方法
try {
//这一句其实是在调用目标方法
System.out.println("====这里就相当于前置通知====");
long startTime = System.nanoTime();
Object proceed = pjp.proceed();
long endTime = System.nanoTime();
System.out.println(pjp.getSignature().getName() + " 方法执行耗时:" + (endTime - startTime));
System.out.println("====这里就相当于后置通知====");
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("====这里相当于异常通知");
}
return null;
}
}
Demo
package com.qfedu.demo;
import com.qfedu.demo.service.Calculator;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意,这个地方拿到的对象实际上是 Spring AOP 利用 JDK 动态代理给 Calculator 接口自动生成的一个对象
Calculator calculator = ctx.getBean("calculator", Calculator.class);
System.out.println("calculator.getClass() = " + calculator.getClass());
calculator.add(3, 4);
calculator.minus(4, 5);
}
}
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: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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="com.qfedu.demo.service.CalculatorImpl" id="calculator"/>
<bean class="com.qfedu.demo.LogAspect" id="logAspect"/>
<!--
开始 AOP 的配置
-->
<aop:config>
<!--配置切点:即要拦截哪些方法?id 就是切点名称,expression 就是切点的表达式
execution(int com.qfedu.demo.service.CalculatorImpl.add(int,int)) 表示拦截 com.qfedu.demo.service.CalculatorImpl.add 方法
一开始的 * 表示返回值任意
后面的 * 表示方法名任意
.. 表示参数任意:参数可有可无;如果有参数的话,参数类型、个数也是任意的
execution(* com.qfedu.demo.service.CalculatorImpl.*(..)) 拦截 CalculatorImpl 类中的所有方法
execution(* com.qfedu.demo.service.*.*(..)) 拦截 com.qfedu.demo.service 包下的所有类的所有方法
-->
<aop:pointcut id="pc1" expression="execution(* com.qfedu.demo.service.*.*(..))"/>
<aop:aspect ref="logAspect">
<!--这是定义一个前置通知,即将方法拦截下来之后,目标方法执行之前会触发的方法-->
<aop:before method="before" pointcut-ref="pc1"/>
<!--配置后置通知-->
<aop:after method="after" pointcut-ref="pc1"/>
<!--返回通知,returning 表示目标方法的返回值映射的参数-->
<aop:after-returning method="returning" pointcut-ref="pc1" returning="result"/>
<aop:after-throwing method="exception" pointcut-ref="pc1" throwing="e"/>
<aop:around method="around" pointcut-ref="pc1"/>
</aop:aspect>
</aop:config>
</beans>
一、java代码配置AOP
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.18</version>
</dependency>
<!-- 如果是java代码去配置AOP,还需要aspectweaver和aspectjrt,但是由于spring-context
已经依赖了aspectweaver,所有我们这里只需要加aspectjrt
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>
package com.qf.demo.service;
import org.springframework.stereotype.Component;
/**
* 把它放到spring容器中
*/
@Component
public class CalculatorImpl implements Calculator{
public int add(int a, int b) {
return a + b;
}
public void minus(int a, int b) {
System.out.println(a+"-"+b+"="+(a-b));
}
}
package com.qf.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
/**
* LogAdvice
*
* LogAspect=pointcut+LogAdvice
*
* @Component(将一些身份不明的注册到容器中) 表示将LogAspect注入到spring容器中
* @Aspect 表示当前类是一个切面
* @EnableAspectJAutoProxy 开启切面的自动的代理,对那些方法进行自动代理
*/
@Component
@Aspect
@EnableAspectJAutoProxy
public class LogAspect {
/**
* 统一定义切点
*
*/
@Pointcut("execution(* com.qf.demo.service.*.*(..))")
public void pc(){
}
@Before("pc()")
// @Before("execution(* com.qf.demo.service.*.*(..))")
public void before(JoinPoint jp){
System.out.println(jp.getSignature().getName()+"方法执行了。。。");
}
@After("pc()")
// @After("execution(* com.qf.demo.service.*.*(..))")
public void after(JoinPoint jp){
System.out.println(jp.getSignature().getName()+"方法执行了。。。");
}
@AfterReturning(value = "execution(* com.qf.demo.service.*.*(..))",returning = "r")
public void returning(JoinPoint jp,Object r){
System.out.println(jp.getSignature().getName()+"方法返回值为"+r);
}
@AfterThrowing(value = "execution(* com.qf.demo.service.*.*(..))",throwing = "e")
public void throwing(JoinPoint jp,Exception e){
System.out.println(jp.getSignature().getName()+"抛出"+e.getMessage()+"异常");
}
@Around("execution(* com.qf.demo.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp){
try {
/**
* 这个方法可以串改参数和修改放回值
*/
// Object proceed = pjp.proceed(new Object[]{99,99});
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
一、@Configuration和@Component的区别
package com.qfedu.demo.config;
import com.qfedu.demo.model.Author;
import com.qfedu.demo.model.Book;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
//@Component
public class BookConfig {
/**
* 向 Spring 容器中注册一个 Author 对象
* @return
*/
@Bean
Author author() {
Author author = new Author();
author.setName("罗贯中");
author.setAge(80);
return author;
}
/**
* 向 Spring 容器注册一个 Book 对象
* @return
*/
@Bean
Book book() {
Book book = new Book();
book.setName("三国演义");
/**
* 由于 author 方法上有一个 @Bean 注解,所以当这里去调用 author 方法的时候,Spring 会先去 Spring 容器中查看容器中是否存在一个 author 对象,如果存在,则直接使用,那么此时就不会真正的去执行 author 方法了。
*/
//如果类上的注解是 @Component,而不是 @Configuration,那么此时 author 方法会被直接调用(不会先去 Spring 容器中查找了)
book.setAuthor(author());
return book;
}
@Bean
Author author01() {
Author author = new Author();
author.setAge(55);
author.setName("曹雪芹");
return author;
}
/**
* 可以直接在方法中添加一个 Author 参数,book01 方法并不是我们自己去调用的,这个方法是 Spring 调用的,那么 Spring 在调用的时候,会发现这个方法需要一个 author 参数,那么 Spring 会自动的去检查 Spring 容器中是否存在一个 Author 对象,如果存在,则直接在这里使用。
*
* 但是我们这里情况特殊,因为我们有两个 author 对象,我们可以通过注解告诉 Spring 容器这里需要的是哪个 author 对象
*
* @Qualifier("author01") 注解表示告诉 Spring 容器,我这里需要的 author 名为 author01。
* 当然,如果 Spring 容器中,本来就只有一个 Author 对象,那么这里就不需要注解了。
* @param author
* @return
*/
@Bean
Book book01(@Qualifier("author01") Author author) {
Book book = new Book();
book.setName("红楼梦");
book.setAuthor(author);
return book;
}
}
一、spring静态工厂模式
//静态工厂模式
public class OkHttpFactory {
public static OkHttpClient okHttpClient(){
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(5, TimeUnit.SECONDS)
.connectTimeout(5,TimeUnit.SECONDS)
.build();
return okHttpClient;
}
}
xml
<!-- 静态工厂-->
<!-- 因为是静态方法,所以可以直接调用,将来他会调这个方法,获取okHttpClient类,并且把它注册到spring容器中-->
<bean class="com.qf.demo.OkHttpFactory" factory-method="okHttpClient" id="okHttpClient">
</bean>
一、实例化工厂模式
//实例化工厂模式
public class OkHttpFactory2 {
public OkHttpClient okHttpClient(){
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(5, TimeUnit.SECONDS)
.connectTimeout(5,TimeUnit.SECONDS)
.build();
return okHttpClient;
}
}
xml
<!-- 实例化工厂-->
<!-- 因为不是静态的方法,所以要有这个对象才可以调用方法-->
<!-- 1.首先要有这个对象-->
<bean class="com.qf.demo.OkHttpFactory2" id="okHttpFactory2"></bean>
<!-- 2.通过对象去调用方法-->
<bean class="okhttp3.OkHttpClient" id="okHttpClient2" factory-bean="okHttpFactory2" factory-method="okHttpClient"></bean>
一、factoryBean
package com.qf.factoryBean;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.FactoryBean;
import java.util.concurrent.TimeUnit;
/**
* 这是一个spring官方提供的工厂类
*/
public class OkHttpClientFactoryBean implements FactoryBean<OkHttpClient> {
/**
* 返回真正的工厂对象,对我们来说,返回的就是应该是一个OkHttpClient实例
* @return
* @throws Exception
*/
public OkHttpClient getObject() throws Exception {
return new OkHttpClient.Builder()
.readTimeout(5, TimeUnit.SECONDS)
.connectTimeout(5,TimeUnit.SECONDS)
.build();
}
/**"
* 放回实例的类型,对我们来说,放回的就是OkHttpClient实例
* @return
*/
public Class<?> getObjectType() {
return OkHttpClient.class;
}
/**
* 放回的实例,是否是单例
* @return
*/
public boolean isSingleton() {
return true;
}
// factorybean用来提供实例的类
}
xml
<!--注意,这个地方,虽然我们写class是OkHttpClientFactoryBean,但是spring容器自动检测该类是一个FactoeyBean实例,
所有最终注册Spring容器对象是该类中的getObject方法的返回值-->
<bean class="com.qf.factoryBean.OkHttpClientFactoryBean" id="okHttpClient" />
一、引言
EJB
1.1 原生web开发中存在哪些问题?
传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。
部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤)。
侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。
二、Spring框架
2.1 概念
Spring是一个项目管理框架,同时也是一套Java EE解决方案。
Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。
2.2 访问与下载
官方网站:Spring | Home
下载地址:JFrog
三、Spring架构组成
Spring架构由诸多模块组成,可分类为
Spring架构组成 |
---|
GroupId | ArtifactId | 说明 |
---|---|---|
org.springframework | spring-beans | Beans 支持,包含 Groovy |
org.springframework | spring-aop | 基于代理的AOP支持 |
org.springframework | spring-aspects | 基于AspectJ 的切面 |
org.springframework | spring-context | 应用上下文运行时,包括调度和远程抽象 |
org.springframework | spring-context-support | 支持将常见的第三方类库集成到 Spring 应用上下文 |
org.springframework | spring-core | 其他模块所依赖的核心模块 |
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;
}
}
五、构建Maven项目
5.1 新建项目
使用IDEA打开已创建的文件夹目录 |
---|
5.2 选择Maven目录
选择Maven项目 |
---|
5.3 GAV坐标
GAV坐标 |
---|
六、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");
}
}
spring-context.xml中的< beans >内部配置bean标签
<!-- 配置实例(id:“唯一标识” class="需要被创建的目标对象全限定名") -->
<bean id="mc" class="com.qf.spring.part1.factory.MyClass" />
调用Spring工厂API(ApplicationContext接口)
public class TestFactory{
/**
* 程序中的对象都交由Spring的ApplicationContext工厂进行创建。
*/
public static void main(String[] args){
//1. 读取配置文件中所需创建的bean对象,并获得工厂对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
//2. 通过id获取bean对象
MyClass mc = (MyClass) ctx.getBean("mc");
//3. 使用对象
mc.show();
}
}
八、依赖与配置文件详解
Spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖Jar包实现功能。
8.1 Spring依赖关系
Spring常用功能的Jar包依赖关系 |
---|
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 UserDAO{....}
public class UserServiceImpl implements UserService {
// !!!强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不稳健!!
private UserDAO userDAO= new UserDAOImpl();
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}
9.2 解决方案
// 不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements UserService {
// !!!不再耦合任何DAO实现!!!,消除不稳健因素!!
private UserDAO userDAO;
// 为userDAO定义set/get,允许userDAO属性接收spring赋值
//Getters And Setters
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}
<bean id="userDAO" class="com.qf.spring.part1.injection.UserDaoImpl"></bean>
<!-- UserServiceImpl组件 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl">
<!-- 由spring为userDAO属性赋值,值为id="userDAO"的bean -->
<property name="userDAO" ref="userDAO"/>
</bean>
此时,如果需要更换其他UserDAO实现类,则UserServiceImpl不用任何改动!
则此时的UserServiceImpl组件变得更加稳健!
十、DI(Dependency Injection)依赖注入【重点
】
三种属性注入方式:
-
构造方法注入
-
set 方法注入(推荐)
-
p名称空间注入(本质上还是 set 方法注入)
各种注入属性:
-
基本数据类型,直接使用标签的 value 属性注入
-
对象
-
外部定义好一个对象,然后通过 ref 引用对象
-
直接在需要的地方通过 bean 标签定义一个对象(局限性,定义好的 bean 无法复用)
-
-
List 集合:list
-
数组:array
-
Map:map
-
Properties :props
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.spring.part1.injection.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.spring.part1.injection.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.spring.part1.injection.Address"> <property name="position" value="北京市海淀区" /> <property name="zipCode" value="100001" /> </bean> <!--主要bean,操作的主体--> <bean id="u2" class="com.qf.spring.part1.injection.User"> <property name="address" ref="addr" /><!--address属性引用addr对象--> </bean>
<!--次要bean,被作为属性--> <bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" /> <!--主要bean,操作的主体--> <bean id="userService" class="com.qf.spring.part1.injection.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.zcg.spring.day1.t2.ioc.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 UserService { private UserDAO userDAO; //Getters And Setters .... }
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" /> <!-- 为UserServiceImpl中的属性基于类型自动注入值 --> <bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" /> <!-- 为UserServiceImpl中的属性基于类型自动注入值 --> <bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byName"></bean>
十一、Bean细节
11.1 控·制简单对象的单例、多例模式
配置< bean scope="singleton | prototype" />
<!-- singleton(默认):每次调用工厂,得到的都是同一个对象。 prototype:每次调用工厂,都会创建新的对象。 --> <bean id="mc" class="com.qf.zcg.spring.day1.t1.basic.MyClass" scope="singleton" />
-
注意:需要根据场景决定对象的单例、多例模式。
-
可以共用:Service、DAO、SqlSessionFactory(或者是所有的工厂)。
-
不可共用:Connection、SqlSession、ShoppingCart。
11.2 FactoryBean创建复杂对象【了解】
作用:让Spring可以创建复杂对象、或者无法直接通过反射创建的对象。
FactoryBean解决复杂对象创建 |
---|
11.2.1 实现FactoryBean接口
接口方法描述 |
---|
-
注意:isSingleton方法的返回值,需根据所创建对象的特点决定返回true/false。
-
例如:Connection 不应该被多个用户共享,返回false。
-
例如:SqlSessionFactory 重量级资源,不该过多创建,返回true。
11.2.2 配置spring-context.xml
配置与获取方式 |
---|
11.2.3 特例
获取FactoryBean接口的实现类对象,而非getObject()所生产的对象。 |
---|
十二、Spring工厂特性
12.1 饿汉式创建优势
工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。
提高程序运行效率。避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)
12.2 生命周期方法
自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。
自定义销毁方法:添加“destroy-method”属性,Spring则会在销毁对象之前,调用此方法。
销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象。
分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。
12.3 生命周期注解(面试)
初始化注解、销毁注解
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@PostConstruct //初始化
public void init(){
System.out.println("init method executed");
}
@PreDestroy //销毁
public void destroy(){
System.out.println("destroy method executed");
}
12.4 生命周期阶段
单例bean:singleton
随工厂启动创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》随工厂关闭销毁
多例bean:prototype
被使用时创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》JVM垃圾回收销毁
十三、代理设计模式
- jdk动态代理
- 优势:
- 官方提供的,可以直接使用,不需要添加依赖
- 效率高
- 劣势
- 只支持有接口的类
- 优势:
- cglib动态代理
- 优势:
- 无论类是否有接口,都支持通过cglib创建对象
- 劣势:
- 第三方工具,使用时需要添加依赖
- 优势:
spring中AOP底层就是动态代理:
- 在spring中,如果代理对象有接口,默认就自动使用JDK动态代理,如果没有接口,就使用cglib动态代理。
- spring boot2.0之前和spring是一样的:2.0之后,springboot中的AOP统一使用cglib动态代理
13.1 概念
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。
功能分离 |
---|
13.2 静态代理设计模式
通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。
静态代理 |
---|
-
代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法。
-
静态代理的问题
-
代理类数量过多,不利于项目的管理。
-
多个代理类的辅助功能代码冗余,修改时,维护性差。
-
13.3 动态代理设计模式
动态创建代理类的对象,为原始类的对象添加辅助功能。
有两种实现方式:
-
基于 JDK(不需要额外引入jar):被代理的对象存在接口。
-
基于 cglib(需要引入外部jar):被代理的对象可以没有接口。
13.3.1 JDK动态代理实现(基于接口)
//目标 final OrderService os = new OrderServiceImpl(); //额外功能 InvocationHandler handler = new InvocationHandler(){//1.设置回调函数(额外功能代码) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start..."); method.invoke(os, args); System.out.println("end..."); return null; } }; //2.创建动态代理类 Object proxyObj = Proxy.newProxyInstance(ClassLoader , Interfaces , InvocationHandler);
13.3.2 CGlib动态代理实现(基于继承)
final OrderService os = new OrderServiceImpl(); Enhancer cnh = new Enhancer();//1.创建字节码曾强对象 enh.setSuperclass(os.getClass());//2.设置父类(等价于实现原始类接口) enh.setCallback(new InvocationHandler(){//3.设置回调函数(额外功能代码) @Override public Object invoke(Object proxy , Method method, Object[] args) throws Throwable{ System.out.println("start..."); Object ret = method.invoke(os,args); System.out.println("end..."); return ret; } }); OrderService proxy = (OrderService)enh.create();//4.创建动态代理类 proxy,createOrder();
十四、面向切面编程【重点
】
AOP 的实现:
选项 | spring-aspects | aspectj(首选) |
---|---|---|
实现类 | spring-aspects | aspectjweaver,aspectjrt |
实现方式 | 通过实现接口来定义通知 | 通过方法+注解来实现通知 |
14.1 概念
AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
14.2 AOP开发术语
连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。
切点(Pointcut):被Spring切入连接点。
通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知、返回通知等。
目标对象(Target):代理的目标对象
引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。
织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
代理(Proxy):被AOP织入通知后,产生的结果类。
切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。
14.3 作用
Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。
14.4 环境搭建
引入AOP相关依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.6.RELEASE</version> </dependency>
spring-context.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.aaron.aop.basic; public interface UserService { public void save(); }
package com.qf.aaron.aop.basic; public class UserServiceImpl implements UserService { public void save() { System.out.println("save method executed..."); } }
定义通知类(添加额外功能)
package com.qf.aaron.aop.basic; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyAdvice implements MethodBeforeAdvice { //实现前置通知接口 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("before advice executed..."); } }
定义bean标签
<!--原始对象--> <bean id="us" class="com.qf.aaron.aop.basic.UserServiceImpl" /> <!--辅助对象--> <bean id="myAdvice" class="com.qf.aaron.aop.basic.MyAdvice" />
定义切入点(PointCut)
形成切面(Aspect)
<aop:config> <!--切点--> <aop:pointcut id="myPointCut" expression="execution(* save())" /> </aop:config>
<aop:config> <!--组装切面 --> <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut" /> </aop:config>
14.6 AOP小结
通过AOP提供的编码流程,更便利的定制切面,更方便的定制了动态代理。
进而彻底解决了辅助功能冗余的问题;
业务类中职责单一性得到更好保障;
辅助功能也有很好的复用性。
14.7 通知类【可选】
定义通知类,达到通知效果
前置通知:MethodBeforeAdvice 后置通知:AfterAdvice 返回通知:AfterReturningAdvice //有异常不执行,方法会因异常而结束,无返回值 异常通知:ThrowsAdvice 环绕通知:MethodInterceptor
14.8 通配切入点
根据表达式通配切入点
<!--匹配参数--> <aop:pointcut id="myPointCut" expression="execution(* *(com.qf.aaron.aop.basic.User))" /> <!--匹配方法名(无参)--> <aop:pointcut id="myPointCut" expression="execution(* save())" /> <!--匹配方法名(任意参数)--> <aop:pointcut id="myPointCut" expression="execution(* save(..))" /> <!--匹配返回值类型--> <aop:pointcut id="myPointCut" expression="execution(com.qf.aaron.aop.basic.User *(..))" /> <!--匹配类名--> <aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.UserServiceImpl.*(..))" /> <!--匹配包名--> <aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.*.*(..))" /> <!--匹配包名、以及子包名--> <aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop..*.*(..))" />
14.9 JDK和CGLIB选择
spring底层,包含了jdk代理和cglib代理两种动态代理生成机制
基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
class DefaultAopProxyFactory{ // 该方法中明确定义了 JDK代理和CGLib代理的选取规则 // 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理 public AopProxy createAopProxy(){...} }
14.10 后处理器
spring中定义了很多后处理器;
每个bean在创建完成之前 ,都会有一个后处理过程,即再加工,对bean做出相关改变和调整;
spring-AOP中,就有一个专门的后处理器,负责通过原始业务组件(Service),再加工得到一个代理组件。
常用后处理器 |
---|
14.10.1 后处理器定义
/** * 定义bean后处理器 * 作用:在bean的创建之后,进行再加工 */ public class MyBeanPostProcessor implements BeanPostProcessor{ /** * 在bean的init方法之前执行 * @param bean 原始的bean对象 * @param beanName * @return * @throws BeansException */ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("后处理器 在init之前执行~~~"+bean.getClass()); return bean; } /** * 在bean的init方法之后执行 * @param bean postProcessBeforeInitialization返回的bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("后处理器 在init之后执行~~~"+bean.getClass()); return bean;// 此处的返回是 getBean() 最终的返回值 } }
14.10.2 配置后处理器
<!-- 配置后处理器,将对工厂中所有的bean声明周期进行干预 --> <bean class="com.qianfeng.beanpostprocessor.MyBeanPostProcessor"></bean>
14.10.3 bean生命周期
构造 》 注入属性 满足依赖 》 后处理器前置过程 》 初始化 》后处理器后置过程 》 返回 》 销毁
14.10.4 动态代理源码(了解)
// AbstractAutoProxyCreator是 AspectJAwareAdvisorAutoProxyCreator的父类 // 该后处理器类中的 wrapIfNecessary方法即动态代理生成过程 AbstractAutoProxyCreator#postProcessAfterInitialization(Object bean, String beanName){ if (!this.earlyProxyReferences.contains(cacheKey)) { // 开始动态定制代理 return wrapIfNecessary(bean, beanName, cacheKey); } }
十五、Spring + MyBatis【重点
】
15.1 配置数据源
将数据源配置到项目中
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配置文件
<!--spring-context.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="driver" value="${driverClass}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${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}"/> <!-- 配置初始化大小、最小、最大 --> <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
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> <!-- 注入dao-mapper文件信息 ,如果映射文件和dao接口 同包且同名,则此配置可省略--> <property name="mapperLocations"> <list> <value>classpath:com/qf/spring/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是:“首字母小写的-接口的类名”,
例如:UserDAO==>userDAO , OrderDAO==>orderDAO
<!-- mapperScannerConfigurer --> <bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- dao接口所在的包 如果有多个包,可以用逗号或分号分隔 <property name="basePackage" value="com.a.dao,com.b.dao"></property> --> <property name="basePackage" value="com.qf.spring.dao"></property> <!-- 如果工厂中只有一个SqlSessionFactory的bean,此配置可省略 --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean>
15.2.4 配置Service
<bean id="userService" class="com.qf.spring.service.UserServiceImpl"> <!-- 注意ref中的值是对应DAO接口的首字母小写的接口名 --> <property name="userDAO" ref="userDAO"></property> </bean>
十六、事务【重点
】
ACID
- 原子性
- 一致性
- 隔离性
- 持久性
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可以切入任何需要事务的方法,通过事务管理器为方法控制事务。
<tx:advice id="txManager" transaction-manager="tx"> <tx:attributes> <!--<tx:method name="insertUser" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>--> <!-- 以User结尾的方法,切入此方法时,采用对应事务实行--> <tx:method name="*User" rollback-for="Exception"/> <!-- 以query开头的方法,切入此方法时,采用对应事务实行 --> <tx:method name="query*" propagation="SUPPORTS"/> <!-- 剩余所有方法 --> <tx:method name="*"/> </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 切入需要事务的业务方法中
<aop:config> <aop:pointcut expression="execution(* com.qf.spring.service.UserServiceImpl.*(..))" id="pc"/> <!-- 组织切面 --> <aop:advisor advice-ref="txManager" pointcut-ref="pc"/> </aop:config>
十七、注解开发
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 UserService { ... }
17.2 注入(DI)
用于完成bean中属性值的注入
@Autowired 基于类型自动注入
@Resource 基于名称自动注入
@Qualifier("userDAO") 限定要自动注入的bean的id,一般和@Autowired联用
@Value 注入简单类型数据 (jdk8种+String)
@Service public class UserServiceImpl implements UserService { @Autowired //注入类型为UserDAO的bean @Qualifier("userDAO2") //如果有多个类型为UserDAO的bean,可以用此注解从中挑选一个 private UserDAO userDAO; }
@Service public class UserServiceImpl implements UserService { @Resource("userDAO3") //注入id=“userDAO3”的bean private UserDAO userDAO; /* @Resource //注入id=“userDAO”的bean private UserDAO userDAO; */ }
public class XX{ @Value("100") //注入数字 private Integer id; @Value("shine") //注入String private String name; }
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 UserService { ... //该方法自己的事务控制,仅对此方法有效 @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.spring.service.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>4.3.6.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
18.2 编码
可以免去工厂的创建过程;
可以直接将要测试的组件注入到测试类。
@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner启动测试 @ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件位置 public class SpringTest{//当前测试类也会被纳入工厂中,所以其中属性可以注入 @Autowired // 注入要测试的组件 @Qualifier("userDAO") private UserDAO userDAO; @Test public void test(){ // 测试使用userDAO userDAO.queryUser(); .... } }