初学记录笔记
1 IOC和DI
1.1 IOC:控制反转
- IOC全称Inverse of control,是一种设计理念
- 由代理人创建管理对象,消费者通过代理人获取对象
- IOC的目的是降低对象之间的耦合
- 加入IOC容器将对象统一管理,让对象关联变为弱耦合
1.2 DI:依赖注入
- IOC是设计理念,是现代程序设计遵循的标准,是宏观目标
- DI全称Dependency injection是具体的技术实现,是微观实现
- DI在Java中利用反射技术实现对象注入(Injection)
2 配置IOC容器
2.1 xml配置方式
2.1.1 xml实例化beans
- 构造方法实例化对象
<bean id="apple1" class="com.mo.spring.ioc.entity.Apple">
<!--constructor-arg代表调用默认构造方法实例化-->
<constructor-arg name="title" value="红富士"/>
<constructor-arg name="origin" value="欧洲"/>
<constructor-arg name="color" value="红色"/>
</bean>
<bean id="apple2" class="com.mo.spring.ioc.entity.Apple">
<!--利用构造方法参数位置实现对象实例化-->
<constructor-arg index="0" value="蛇果"/>
<constructor-arg index="1" value="美国"/>
<constructor-arg index="2" value="红色"/>
</bean>
- 静态工厂实例化
<!--静态工厂获取对象 factory-method需要调用的静态方法-->
<bean id="apple3" class="com.mo.spring.ioc.factory.AppleStaticFactory"
factory-method="createSweetFactory">
- 工厂实例方法实例化
<!--工厂实例方法获取对象 AppleFactoryInstance类createSweetApple()方法-->
<bean id="factoryInstance" class="com.mo.spring.ioc.factory.AppleFactoryInstance"/>
<bean id="apple4" factory-bean="factoryInstance" factory-method="createSweetApple"/>
2.1.2 从ioc容器中获取bean
//初始化IOC容器并实例化对象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// Apple apple1 = (Apple) context.getBean("apple1");
Apple apple1 = context.getBean("apple1", Apple.class);
2.1.3 bean scope属性
- 决定对象何时创建与作用范围
- 配置影响容器内对象数量
- 默认情况bean会在IOC容器创建后自动实例化,全局唯一
scope属性 | 说明 |
---|---|
singleton | 单例(默认值),每个容器有且只有一个唯一的实例,全局共享 |
prototype | 多例,每次使用都创建一个实例 |
request | web环境下,每一次独立请求存在唯一实例 |
session | web环境下,每一个session存在唯一实例 |
application | web环境下,ServletContext存在唯一实例 |
websocket | 每一次websocket存在唯一实例 |
2.2 注解配置IOC容器
- 摆脱繁琐的XML形式的bean与依赖注入
- 基于
声明式
原则,更适合轻量级应用 - 可读性
按注解类型分为三类注解:组件类型注解,自动装配注解元数据注解.
2.2.1 组件类型注解
- 声明当前类型的功能与职责
注解 | 说明 |
---|---|
@Component | 组件注解,通用注解,被该注解描述的类将被IOC容器管理并实例化 |
@Controller | 语义注解,说明当前类是MVC应用中的控制器类型 |
@Service | 语义注解,说明当前类是Service业务逻辑类 |
@Repository | 语义注解,说明当前类用于业务持久层,通常描述对应的Dao类 |
- applicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--在IOC,容器初始化时自动扫描四种组件类型并完成实例化
@Repository
@Service
@Controller
@Component-->
<context:component-scan base-package="com.mo"/>
</beans>
自动装配Autowired
分类 | 注解 | 说明 |
按类型装配 | @Autowired | 按容器对象类型动态注入属性,由Spring提供 |
@Inject | 基于JSR-330(Dependency Injection for Java), 标准,其他同@Autowired,但不支持required. | |
按名称装配 | @Named | 与Inject配合使用,JSR-330规范,按属性名自动装 配属性. |
@Resource | 基于JSR-250规范,优先按名称,再按类型智能匹配 |
@Autowired
package com.mo.spring.service;
import com.mo.spring.dao.IUserDao;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public UserService() {
System.out.println("UserService");
}
//@Autowired
//Spring IoC容器会自动通过反射技术将属性private修饰符自动改为public,直接进行赋值,不执行set方法
private IUserDao udao;
public IUserDao getUdao() {
return udao;
}
//@Autowired
//装配方法放在set方法上,会自动按类型or名称对set方法进行注入
public void setUdao(IUserDao udao) {
System.out.println("setUdao:" + udao);
this.udao = udao;
}
}
@Resource
- a.设置name属性,按name在IOC容器中注入bean
- b.未设置name属性,以属性名作为bean name在IOC容器中匹配bean,如果匹配就注入,否则按类型进行匹配,同Autowired,需要加入@Pirmary解决类型冲突.
建议:在使用@Resource对象时推荐设置name或保证属性名和bean名称一致
2.2.2 元数据注解
注解 | 说明 |
@Primary | 按类型装配时出现多个相同类型对象,拥有此注解的对象优先被注入 |
@Postconstruct | 描述方法,相当于XML中的init-method配置的注解版本 |
@Predestroy | 描述方法,相当于XML中的destroy-method配置的注解版本 |
@Scope | 设置对象的Scope属性 |
@Value | 为属性静态注入数据 |
2.3 Java Config配置IOC容器
- 摆脱XML的束缚,使用独立Java类管理对象与依赖
- 注解配置相对分散,Java Config可对配置集中管理
- 编译时进行依赖检测
注解 | 说明 |
@Configuration | 描述类,说明当前类是Java Config配置类替代XML文件 |
@Bean | 描述方法返回对象将被IOC容器管理,BeanId默认方法名 |
@ImportResource | 描述类,加载静态文件,可用@Value注解获取 |
@ComponentScan | 描述类,同XML< context:component-scan base-package="com.mo"/>标签 |
3 AOP(Aspect Oriented Programming)面向切面编程
- Spring AOP是使用AspectJWeaver实现类与方法匹配
- Eclipse AspectJ是一种基于Java平台的面向切面编程的语言
- Spring AOP利用代理模式实现对象运动时功能拓展
一些概念
名称 | 说明 |
Aspect | 切面,具体的可插拔组件功能类,通常一个切面只实现一个统用功能 |
Target Class/Method | 目标类,目标方法,指真正要执行与业务相关的方法 |
PointCut | 切入点,使用execution表达式说明切面要作用在系统的那些类上 |
JoinPoint | 连接点,切面运行过程中是包含了目标类/方法元数据的对象 |
Advice | 通知,说明具体的切面的执行时机,有5种类型 |
- PointCut 切点表达式
* -通配符
.. -包通配符
(..) -参数通配符
public void com.mo.spring.aop.service.UserService.createUser(形参1,形参2,..)
execution ( public * com.mo..*.*(..))
3.1 常用通知
注解 | 说明 |
Before Advice | 前置通知,目标方法运行前执行 |
After Returning Advice | 返回后通知,目标放返回数据后执行 |
After Throwing Advice | 异常通知,目标方法抛出异常后执行 |
After Advice | 后置通知,目标方法运行后执行 |
Around Advice | 最强大通知,自定义通知执行时机,可决定目标方法是否运行 |
- 引介增强(IntroductionInterceptor)
a.对类的增强,非方法
b.允许在运行时为目标类增加新属性或方法
c.允许在运行时改变类的行为,让类随运行环境动态变更
3.2 xml配置AOP
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userDao" class="com.mo.spring.aop.dao.UserDao"/>
<bean id="employeeDao" class="com.mo.spring.aop.dao.EmployeeDao"/>
<bean id="userService" class="com.mo.spring.aop.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="employeeService" class="com.mo.spring.aop.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
</bean>
<!--AOP配置-->
<bean id="methodAspect" class="com.mo.spring.aop.aspect.MethodAspect"/>
<aop:config>
<!--pointcut 切点,使用execution表达式描述切面的作用范围-->
<!--execution(public * com.mo..*.*(..)) 说明作用在com.mo包下的所有类的所有方法-->
<!--<aop:pointcut id="pointcut" expression="execution(public * com.mo..*.*(..))"/>-->
<aop:pointcut id="pointcut" expression="execution(public * com.mo..*Service.*(..))"/>
<!--定义切面类-->
<aop:aspect ref="methodAspect">
<!--before前置通知,代表在目标方法运行前先执行methodAspect.printExecutionTime()-->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
<!--后置通知-->
<aop:after method="doAfter" pointcut-ref="pointcut"/>
<!--返回后通知-->
<aop:after-returning method="doAfterReturning" returning="object" pointcut-ref="pointcut"/>
<!--异常通知-->
<aop:after-throwing method="doAfterThrowing" throwing="throwable" pointcut-ref="pointcut"/>
<!--环绕通知-->
<aop:around method="check" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
3.3 注解配置AOP
3.3.1 配置切面类和通知
@Component //标记当前类为组件
@Aspect //说明当前类是切面类
public class MethodChecker {
//环绕通知 参数PointCut切点表达式
@Around("execution(* com.mo..*Service.*(..))")
public Object check(ProceedingJoinPoint joinPoint) throws Throwable {
try {
long startTime = new Date().getTime();
Object ret = joinPoint.proceed();//执行目标方法
long endTime = new Date().getTime();
long duration = endTime - startTime;
if (duration >= 1000) {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String time = dateFormat.format(new Date());
System.out.println("====" + time + "className:" + className + "." + methodName + "duration:" + duration);
}
return ret;
} catch (Throwable throwable) {
// throwable.printStackTrace();
System.out.println("Exception message:" + throwable.getMessage());
throw throwable;
}
}
}
3.3.2 applicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<!--初始化IOC容器 自动扫描这个包下的@Component,为其创建bean -->
<context:component-scan base-package="com.mo"/>
<!--启用Spring AOP注解模式-->
<aop:aspectj-autoproxy/>
</beans>
3.3.3 使用
public class SpringApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.createUser();
/*
====环绕前
执行创建用户业务逻辑
新增用户数据
====2022-02-17 10:19:10 387className:com.mo.spring.aop.service.UserService.createUserduration:3011
====环绕环后
*/
}
}
4 Spring JDBC与事务管理
4.1 spring jdbc 配置与jdbcTemplate使用
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--数据源配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&PublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--jdbcTemplate 提供增删改查api-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="employeeDao" class="com.mo.spring.jdbc.dao.EmployeeDao">
<!--为Dao注入jdbcTemplate对象-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
jdbcTemplate 增删改查
public class EmployeeDao {
private JdbcTemplate jdbcTemplate;
public Employee findById(Integer eno) {
String sql = "select * from employee where eno = ?";
//查询单条属性
Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<>(Employee.class));
return employee;
}
public List<Employee> findByDname(String dname) {
String sql = "select * from employee where dname=?";
//查询复合数据
List<Employee> employeeList = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<>(Employee.class));
return employeeList;
}
public List<Map<String, Object>> findMapByDname(String dname) {
String sql = "select eno as empno,salary as sa from employee where dname=?";
//查询结果作为Map进行封装
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
return maps;
}
public void insert(Employee employee) {
String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
jdbcTemplate.update(sql, new Object[]{employee.getEno(), employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate()});
}
public int update(Employee employee) {
String sql = "UPDATE employee SET ename = ?,salary =?,dname=?,hiredate=? WHERE eno=?";
int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()});
return count;
}
public int delete(Integer eno){
String sql = "DELETE FROM employee WHERE eno=?";
int count = jdbcTemplate.update(sql, new Object[]{eno});
return count;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
4.2 编程式事务
- 事务是以一种可靠的,一致的方式,访问和操作数据库的程序单元
- 事务依赖于数据库实现,MYSQL通过事务区作为数据缓冲地带
- 编程事务是指通过代码手动提交回滚事务的事务控制方法
- SpringJDBC通过TransactionManager事务管理器实现事务控制
- 事务管理器提供commit/rollback方法进行事务提交与回滚
xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--数据源配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&PublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--jdbcTemplate 提供增删改查api-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="employeeDao" class="com.mo.spring.jdbc.dao.EmployeeDao">
<!--为Dao注入jdbcTemplate对象-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="employeeService" class="com.mo.spring.jdbc.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
使用
public void batchImport() {
//定义事务的默认标准模式
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
//开始一个事务,返回事务状态,事务状态说明当前事务的执行阶段
TransactionStatus transaction = transactionManager.getTransaction(definition);
try {
for (int i = 1; i <=5; i++) {
if (i==3){
throw new RuntimeException("测试异常");
}
Employee employee = new Employee();
employee.setEno(10+i);
employee.setEname("员工"+i);
employee.setSalary(3000f);
employee.setDname("测试部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
//提交事务
transactionManager.commit(transaction);
} catch (Exception e) {
//e.printStackTrace();
//回滚
transactionManager.rollback(transaction);
//抛出或者实际情况处理
throw e;
}
4.3 声明式事务
- 不修改源码的情况下通过配置形式自动实现事务的控制,声明式事务本质就是AOP环绕通知
- 当目标方法执行成功时,自动提交事务
- 当目标方法抛出异常时,自动事务回滚
配置过程
1.配置TransactionManager事务管理器
2.配置事通知与事务属性
3.为事务通知绑定PointCut切点
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<!--数据源配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&PublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--jdbcTemplate 提供增删改查api-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="employeeDao" class="com.mo.spring.jdbc.dao.EmployeeDao">
<!--为Dao注入jdbcTemplate对象-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="employeeService" class="com.mo.spring.jdbc.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!--1.事务管理器,用于事务创建/提交/回滚-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.事务通知配置,配置那些方法使用事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<!--目标方法名为batchImport时,启用声明式事务,运行成功提交,异常回滚-->
<tx:method name="batchImport" propagation="REQUIRED"/>
<!--以batch开头的方法都启用事务-->
<tx:method name="batch*" propagation="REQUIRED"/>
<!--设置findXX方法不需要使用事务-->
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--定义声明式事务的作用范围-->
<aop:config >
<aop:pointcut id="pointcut" expression="execution(* com.mo..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
4.4 事务传播方式
事务传播类型 | 说明 |
PROPAGATION_REQUIRED(默认) | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,就加入到这个事务中 |
PROPAGATION_MANDATORY | 使用当前事务,如果没有事务,就抛出异常 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,就抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行.如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作 |