Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若千个项目,每个项目用于完成特定的功能
Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基
系统架构图:上层依赖于下层
IoC(控制翻转):耦合度偏高 => 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
Spring技术对IoC思想进行了实现:
DI(依赖注入):在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
目的:充分解耦
1、导入spring的坐标spring-context,对应版本是5.2.10.RELEASE
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
2、创建配置文件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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置bean-->
<!--
id属性标示给bean起名字
class属性表示给bean定义类型
-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--配置server与dao的关系-->
<!--
property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
3、修改ServiceImpl方法
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
public class BookServiceImpl implements BookService {
// 删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
// 提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
1、name起别名
<bean id="bookDao" name="bookEbi" class="com.itheima.dao.impl.BookDaoImpl"/>
2、Bean的作用范围
<!-- 单例(默认) -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" scope="singleton"/>
<!-- 多例 -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
适合交给容器进行管理的bean:
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
不适合交给容器进行管理的bean:
- 封装实体的域对象
1、构造方法
Spring实例化Bean的方法:无参构造
<bean id="bookDao" name="bookEbi" class="com.itheima.dao.impl.BookDaoImpl"/>
2、静态工厂
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");
return new OrderDaoImpl();
}
}
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
3、实例工厂
public class AppForInstanceUser {
public static void main(String[] args) {
// 创建实例工厂对象
UserDaoFactory userDaoFactory = new UserDaoFactory();
// 通过实例工厂对象创建对象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
}
}
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
4、FactoryBean
// FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
// 代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
}
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
配置init
和destroy
方法
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>
public class AppForLifeCycle {
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 注册关闭钩子函数:在虚拟机退出之前回调此函数,关闭容器
// ctx.registerShutdownHook();
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
// 关闭容器
ctx.close();
}
}
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
System.out.println("setBookDao ...");
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy ...");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init ...");
}
}
1、简单类型
在bean中定义引用类型属性并提供可访问的set
方法
配置中使用property
标签value
属性注入简单类型对象
<!--注入简单类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>
2、引用类型
在bean中定义引用类型属性并提供可访问的set
方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
配置中使用property
标签ref
属性注入引用类型对象
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
标准书写:根据构造方法参数名称注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
解决参数类型重复问题,使用位置解决参数匹配:根据构造方法参数位置注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
1、提供setter方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
2、配置xml
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
<!-- 按类型(找实现的接口) -->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
<!-- 按名称 -->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
数组
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
<!-- <ref bean="beanId"/> -->
</array>
</property>
list集合
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
set集合
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
map集合
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
Properties
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
1、开启context命名空间
<?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
">
</beans>
2、使用context命名空间,加载指定properties文件
<context: property-placeholder location="jdbc.properties"/>
3、使用${}
读取加载的厲性值
<property name="username" value="${jdbc.username}"/>
不加载系统属性:
<context: property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
加载properties文件标准格式:
<context: property-placeholder location="classpath:*.properties"/>
从类路径或jar包中搜索并加载properties文件
<context: property-placeholder location="classpath*:*.properties"/>
方式一:类路径加载配置文件
Applicationcontext ctx = new ClassPathxmlApplicationcontext ("applicationcontext. xml");
方式二:文件路径加载配置文件
Applicationcontext ctx = new FilesystemxmlApplicationcontext("D:\\applicationcontext.xml");
加载多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext ("bean1.xml", "bean2.xml");
类路径加载配置文件
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean (BookDao.class);
bookDao.save();
BeanFactory创建完毕后,所有的bean均为延迟力载
使用@Component定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao {
// xxx
}
@component
public class BookServiceImpl implements BookService{
// xxx
}
核心配置文件中通过组件扫描加载bean
<context:component-scan base-package="com.itheima"/>
Spring提供@Component注解的三个衍生注解
使用Java类代替Spring核心配置文件
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
使用@Scope定义bean作用范围:单例、多例
@Repository
@scope("singleton")
public class BookDaoImpl implements BookDao {
// xxx
}
init:@PostConstruct
destroy:@PreDestroy
@Repository
@scope("singleton")
public class BookDaoImpl implements BookDao{
public BookDaoImpl() {
System.out.println("book dao constructor ...");
}
@PostConstruct
public void init() {
System.out.println("book init ...");
}
@PreDestroy
public void destroy() {
System.out.println("book destory ...");
}
}
@Autowired:注入引用类型,自动装配模式,默认按类型装配
@Qualifier:自动装配bean时按bean名称装配(选填)
@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao")
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
@Value:注入简单类型(无需提供set方法)
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
@Value("wmh")
// 来自prop文件
// @Value("${name}")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
@PropertySource:加载properties配置文件
不支持使用通配符
*
@Configuration
@ComponentScan("com.itheima")
@PropertySource({"jdbc.properties"})
public class SpringConfig {
}
public class JdbcConfig {
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
return ds;
}
}
使用@Import注解手动加入配置类到核心配置
@Configuration
@Import({JdbcConfig.class})
public class SpringConfig {
}
@Configuration
public class JdbcConfig {
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
return ds;
}
}
使用@ComponentScan注解扫描配置类所在的包,加载对于的配置类信息
@Configuration
@ComponentScan({"com.itheima.config"})
public class SpringConfig {
}
引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象
public class JdbcConfig {
@Value("jdbc:mysql://localhost:3306/spring")
public String url;
@Value("root")
public String username;
@Value("root")
public String password;
@Value("com.mysql.jdbc.Driver")
public String diver;
@Bean
public DataSource getDs() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setDriverClassName(diver);
return ds;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}
}
AOP:面向切面编程,一种编程范式,指导开发者如何组织程序结构(在不惊动原始设计的基础上为其进行功能增强)
OOP:面向对象编程
Spring理念:无入侵式/无侵入式
1、导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2、定义Dao接口和实现类
@Repository
public class BookDaoImpl implements BookDao {
public void update(){
System.out.println("book dao update ...");
}
}
3、定义通知类,制作通知。绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
@Component
// 设置当前类为切面类类
@Aspect
public class MyAdvice {
// 设置切入点,要求配置在方法上方
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
// 设置在切入点pt()的前面运行当前操作(前置通知)
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
切入点定义依托一个不具有实际意义的方法进行 ,即无参数,无返回值,方法体无实际逻辑
4、开启Spring对AOP注解驱动支持
@Configuration
@ComponentScan("com.itheima")
// 开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}
- 目标对象(Target):原始功能去掉共性功能对应的类产生的对象 ,这种对象是无法直接完成最终工作的
- 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
切入点:要进行增强的方法
切入点表达式:要进行增强的方法的描述方式
1、接口
execution(void com.itheima.dao.BookDao.update())
2、实现类
execution(void com.itheima.dao.impl.BookDaoImpl.update())
动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
通配符:
*
:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现..
:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写+
:专用于匹配子类类型所有代码按照标准规范开发,否则以下技巧全部失效
描述切入点通常描述接口,而不描述实现类
访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)
返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*
通配快速描述
包名书写尽量不使用..
匹配,效率过低 ,常用*
做单个包描述匹配,或精准匹配
接口名/类名书写名称与模块相关的采用*
匹配 ,例如UserService
书写成*Service
,绑定业务层接口名
方法名书写以动词进行精准匹配,名词采用*
匹配,例如getByld
书写成getBy*
,selectAll
书写成selectAll
参数规则较为复杂,根据业务方法灵活调整
通常不使用异常作为匹配规则
前置通知、后置通知
// @Before:前置通知,在原始方法运行之前执行
@Before("pt()")
public void before() {
System.out.println("before advice ...");
}
// @After:后置通知,在原始方法运行之后执行
@After("pt()")
public void after() {
System.out.println("after advice ...");
}
环绕通知
// @Around:环绕通知,在原始方法运行的前后执行
// 无返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before advice ...");
// 表示对原始操作的调用
Object ret = pjp.proceed();
System.out.println("around after advice ...");
return ret;
}
// 有返回值
@Around("pt2()")
public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before advice ...");
//表示对原始操作的调用
Integer ret = (Integer) pjp.proceed();
System.out.println("around after advice ...");
return ret;
}
环绕通知可以通过ProceedingJoinPoint
获取签名对象
// 设置环绕通知,在原始操作的运行前后记录执行时间
@Around("ProjectAdvice.servicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
// 获取执行的签名对象
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pjp.proceed();
}
long end = System.currentTimeMillis();
System.out.println("万次执行:" + className + "."+methodName+"---->" + (end-start) + "ms");
}
获取切入点方法的参数
@Before("pt()")
public void before(JoinPoint jp) {
// 获取参数
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
// 修改参数
args[0] = 666;
// 获取返回值
ret = pjp.proceed(args);
return ret;
}
获取切入点方法返回值
// 设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(JoinPoint jp, String ret) {
System.out.println("afterReturning advice ..." + ret);
}
public Object around(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = null;
// 异常处理
try {
ret = pjp.proceed(args);
} catch (Throwable t) {
t.printStackTrace();
}
return ret;
}
获取切入点方法运行异常信息
// 设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..." + t);
}
事务作用:在数据层保障一系列的数据库操作同成功同失败
Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合
注解式事务可以添加到业务方法上表示当前方法开启事务 ,也可以添加到接口上表示当前接口所有方法开启事务
1、在业务层接口上添加Spring事务管理
public interface AccountService {
// 配置当前接口方法具有事务
@Transactional
public void transfer(String out, String in, Double money);
}
2、设置事务管理器
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
// 配置事务管理器,mybatis使用的是jdbc事务
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
3、开启注解式事物驱动
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement // 开启注解式事务驱动
public class SpringConfig {
}
事务传播行为:事务协调员对事务管理员所携带事务的处理态度