| 了解 | 掌握 | 案例 |
Spring是什么 | 轻量级 非侵入 | IOC 反转控制 把创建管理对象的权限交给spring
AOP ⾯向切⾯编程 对方法、接口级别的拦截器,解耦 | |
Spring快速⼊⻔ | | 1. 在pom.xml中导⼊Spring开发的基本包坐标 spring-context
2. 编写Dao接⼝和实现类
3. 在resources中创建Spring核⼼配置⽂件 applicationContext.xml
4. 把Dao交给Spring的Bean进⾏管理 依赖注入(又叫DI) ①set方法注入 ②构造方法注入
5. 使⽤Spring的API获得Dao的Bean实例 | 4.依赖注入
①set方法注入
private UserDao userDao
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
【在 applicationContext.xml 中】
<bean id="userDao" class="com.itheima.web.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itheima.web.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
②构造方法注入
private UserDao userDao
public UserServiceImpl() {
}
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
【在 applicationContext.xml 中】
<bean id="userDao" class="com.itheima.web.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itheima.web.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean> |
常用属性 | <Bean>标签
id属性:在容器中Bean实例的唯⼀标识,不允许重复
class属性:要实例化的Bean的全限定名
scope属性:Bean的作⽤范围,常⽤是Singleton(默认)和prototype
<property>标签:set方法注入,属性注⼊
name属性:setXxx()方法的Xxx,属性名称
value属性:注⼊的普通属性值
ref属性:注⼊的对象引⽤值
<list>标签
<map>标签
<properties>标签
<constructor-arg>标签
<import>标签:导⼊其他的Spring的分⽂件
resource:引入其他文件的路径 | 加载外部jdbc.properties
<context:property-placeholder location="classpath:jdbc.properties"/> | |
重点API | ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
getBean("id")
getBean(Class) | | |
Spring原始注解 | | [注解] [说明]
@Component 使用在类上用于实例化Bean
@Controller 使用在web层类上用于实例化Bean
@Service 使用在service层类上用于实例化Bean
@Repository 使用在dao层类上用于实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入,如果只使用@Autowired,就只按种类选择
@Qualifier 结合@Autowired一起使用用于根据名称 id 进行依赖注入
@Resource 相当于@Autowired+@Qualifier,按照名 id 称进行注入
@Value 注入普通属性
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法
[注解的组件扫描] - 一般扫描除controller层之外的
<context:component-scan base-package="com.itheima"></context:component-scan> | |
Spring新注解 | | [注解] [说明]
@Configuration 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan 用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package="com.itheima"/>一样
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource 用于加载.properties 文件中的配置
@Import 用于导入其他配置类 | @Configuration
@ComponentScan("com.itheima")
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {
}
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
@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(name="dataSource")
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
@Test
public void testAnnoConfiguration() throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.save();
DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
} |
Spring整合Junit | | ① 在pom.xml导入spring集成Junit的坐标 spring-context spring-test junit
② 自己新建一个测试类,类名上面使用 @Runwith(SpringJUnit4ClassRunner.class) 注解替换原来的运行期
③ 类名上面使用 @ContextConfiguration("classpath:applicationContext.xml") 或 @ContextConfiguration(classes = {被@Configuration指定配置类的类名.class})指定配置文件或配置类
④ 成员变量上面使用 @Autowired 注入需要测试的对象
⑤ 创建测试方法,上面使用 @Test 进行测试 | |
AOP 相关概念 | 底层:反射
[两种动态代理技术]
JDK 代理 : 基于接口的动态代理技术
cglib 代理 : 基于父类的动态代理技术
[相关术语]
Target(目标对象):代理的目标对象
Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
· Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
· Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
· Aspect(切面):是切入点和通知(引介)的结合
· Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
[切点表达式]
execution( [修饰符] 返回值类型 包名.类名.方法名(参数) )
例如:
execution(public void com.itheima.aop.Target.method())
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..)) // 重点掌握这种
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
[环绕增强方法]
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前置增强...");
Object proceed = pjp.proceed();
System.out.println("环绕后置增强...");
return proceed;
} | 【JDK动态代理(手写)】
Target target = new Target(); //创建目标对象
//创建代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass()
.getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("前置增强代码...");
Object invoke = method.invoke(target, args);
System.out.println("后置增强代码...");
return invoke;
}
}
);
proxy.method();
【cglib动态代理(手写)】
Target target = new Target(); //创建目标对象
Enhancer enhancer = new Enhancer(); //创建增强器
enhancer.setSuperclass(Target.class); //设置父类
enhancer.setCallback(new MethodInterceptor() { //设置回调
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("前置代码增强....");
Object invoke = method.invoke(target, objects);
System.out.println("后置代码增强....");
return invoke;
}
});
Target proxy = (Target) enhancer.create(); //创建代理对象
proxy.method(); | |
AOP-XML | <aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
[通知标签类型]
前置通知 aop:before
后置通知 aop:after-returning
环绕通知 aop:around
异常抛出通知 aop:after-throwing
最终通知 aop:after
[XML切点表达式的抽取]
<aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
<aop:before method="before" pointcut-ref="myPointcut"></aop:before> | 【使用步骤】
① 在pom.xml导入 AOP 相关坐标 spring-context aspectjweaver
② 创建目标接口和目标类(内部有切点)
③ 创建切面类(内部有增强方法)
④ 导入aop命名空间
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
导入aop约束路径
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
⑤ 在 applicationContext.xml 中
使用标签 <bean> 将目标类和切面类的对象创建权交给 spring
使用标签 <aop> 配置织入关系
⑥ 测试代码(Spring整合Junit) | ⑤ 使用标签 <bean> 将目标类和切面类的对象创建权交给 spring
<bean id="target" class="com.itheima.aop.Target"></bean> // 配置目标类
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean> // 配置切面类
使用标签 <aop> 配置织入关系
<aop:config>
<aop:aspect ref="myAspect">
<aop:pointcut id="myPointcut" expression="execution(* com.itheima.proxy.JDKproxy.Target.*(..))"/>
<aop:before method="before" pointcut-ref="myPointcut"/>
<aop:around method="around" pointcut-ref="myPointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
<aop:after method="after" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config> |
AOP-注解 | [通知注解类型]
前置通知 @Before
后置通知 @AfterReturning
环绕通知 @Around // 注意 配置环绕通知时,spring-context 与 aspectjweaver 1.8.4以下版本一起使用会引发jar包冲突
异常抛出通知 @AfterThrowing
最终通知 @After
[注解切点表达式的抽取]
@Pointcut("execution(* com.itheima.aop.*.*(..))") // 注意 若想使用@Pointcut注解,aspectjweaver最低版本是1.8.9
public void myPoint(){};
@Before("MyAspect.myPoint()")
public void before(){
System.out.println("前置代码增强.....");
} | 【使用步骤】
① 在pom.xml导入 AOP 相关坐标 spring-context aspectjweaver
② 创建目标接口和目标类(内部有切点)
③ 创建切面类(内部有增强方法)
④ 导入aop命名空间
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
导入aop约束路径
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
⑤ 使用注解 @Component 将目标类和切面类的对象创建权交给 spring
使用注解 @Aspect 标注切面类
使用通知注解 如 @Before 等标注通知方法
在 applicationContext.xml 中
配置注解扫描的组件扫描
<context:component-scan base-package="com.itheima"></context:component-scan>
配置aop自动代理
<aop:aspectj-autoproxy/>
⑥ 测试代码(Spring整合Junit) | |
JdbcTemplate | tx:事务
JdbcTemplate自动提交事务 | 【使用步骤】
① 在pom.xml导入 AOP 相关坐标 spring-jdbc spring-tx
② 创建数据库表与实体类
③ 创建jdbc.properties配置文件
④ 用Spring管理并创建JdbcTemplate对象
⑤ 执行数据库操作(增删改查) | ④ 在 applicationContext.xml 配置文件中
<context:property-placeholder location="classpath:jdbc.properties"/> // 读取jdbc.properties文件,注意,这里要引入context的命名空间
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> // 数据源DataSource
<property name="driverClass" value="${jdbc.drvier}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> // Spring管理JdbcTemplate
<property name="dataSource" ref="dataSource"></property>
</bean>
⑤ 自定义一个测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
自定义增删改查方法 使用jdbcTemplate.update query 和sql语句
} |
事务控制-XML | [4种隔离级别]
1. ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
2. ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
3. ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
4. ISOLATION_SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
脏读(针对未提交的事务):即事务A读到了事务B还没有提交的数据。如果事务A对数据进行了更新,但是事务A并没有提交,但是事务B这个时候看到了事务A没有提交的更新。当事务A进行了回滚,那么刚刚事务B看到的数据就是脏数据。也就是脏读。
例子:A 给 B 转了100万,但是 A 还没有提交,此时 B 查询自己账户,多了100万。然后 A 发现转错人了,回滚了事物。然后 B 100万就没了。在这个过程中 B 查到了没有提交的数据(多出的100万),这就是脏读。
不可重复读(在一个事务里面读取了两次某数据,读出来的数据不一致,针对修改操作):即同一事务在事务执行过程中对同一个数据进行了多次读取,但是每一次读取的数据结果都不相同。原因是在两次读取间隔,数据别其他人修改了,导致了统一事务两次读取结果不一致。
例子:A 查询银行余额为100万,B 这个时候取走了50万,此时余额变成了50万,A 再一次查询余额,变成了50万。对 A 而言两次结果不一致就是不可重复读。
幻读(在一个事务里面的操作中发现了为被操作的数据,针对增删操作):即在事务 A多次读取数据集的过程中中,事务 B 对数据进行了新增操作或者删除操作,导致事务 A多次读取的数据集不一致。
例子:A 修改当前公司所有职员信息的时候,B 向其中插入了一个新的职员,这个时候A 提交的时候发现了一个自己没有修改过的职员的信息,对 A 而言就像是产生了幻觉。
[7种传播行为]
1. REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
2. SUPPORTS:如果有事务,就会使用事务,如果没有,就不使用事务的方式执行。
3. MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
4. REQUERS_NEW:每次都会新建事务它与REQUIRED的区别在于事务的回滚程度,调用B时,即使A有事务,B也会自己开启一个事务,那么这样就是存在两个不同的事务,如果B已经提交事务,A发生异常会进行回滚,那么B也是不会回滚的,若B发生异常,A进行捕获B的异常后,B进行回滚,A可以正常提交事务并执行
5. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
6. NEVER:以非事务方式运行,如果当前存在事务,抛出异常
7. NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED 类似的操作
[超时时间]:设置调用此方法的最大时间,超出则强行中断,不在进行查询
[是否只读]:声明此事务方法中只能做查询 | 【使用步骤】
① 在pom.xml导入 AOP 相关坐标 spring-context aspectjweaver spring-jdbc spring-tx spring-test c3p0 mysql-connector-java junit
② 创建实体和对应数据库
③ 创建持久层
④ 创建业务层
⑤ 配置applicationContext.xml
⑥ 创建Controller层并测试 | ⑤ 配置applicationContext.xml
<!--加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--管理dao层对象-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="template" ref="jdbcTemplate"></property>
</bean>
<!--目标对象 内部的方法就是切点-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置平台事务管理器 理解为切面类-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--通知 事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--设置事务的属性信息的-->
<tx:attributes>
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务的aop织入-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config> |
事务控制-注解 | | 【使用步骤】
① dao层
② service层配置注解事务
③ 配置applicationContext.xml | ① dao层
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
其他方法...
}
② service层配置注解事务
@Service("accountService")
@Transactional // 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置,属性同 xml 配置方式,例如隔离级别、传播行为等
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional // 注解使用在方法上,只有此方法用这个注解的配置,就近原则
业务方法...
}
③ 配置applicationContext.xml
<!--注解组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--事务的注解驱动,开启并识别事务注解 transaction-manager="平台事务管理器id"-->
<tx:annotation-driven transaction-manager="transactionManager"/> |